(30 gadget day 2) 最简单也最复杂的Gadget - YubiKey

晚上翻出myo准备继续写的时候,被提示需要升级,于是就update呗。于是…就update了一个小时,现在还在转…

图片描述

其他设备都在办公室,于是智能在家里翻箱倒柜…先是翻出了Chromecast,拿在手里很开心的开始看Cast文档。看了一会迫不及待的拿出来试。于是…

图片描述

找了一圈基本确认Cast决定今天不见我以后,继续寻觅下一个下手的目标。于是找到了这只TI的“智能手表”。好吧,谁还认识它?

图片描述
图片描述

这次我谨慎审视了下,自然的决定了放弃。因为很显然我基本12个小时内找不到它的专用充电器…好~吧~于是最后翻到了它:YubiKey。

图片描述

YubiKey呢,是个99.999%的人都不会有兴趣的Gadget,而且剩下的0.001%里顶多按过两三次以后就扔那了(><)

一次一密(OTP)

一次一密是一种简单粗暴有效的加密方式。基本的概念就是,每次加密用的钥匙都来个新的,跟上次用的没啥关系的。这样基本只能用遍历破解。

YubiKey OTP

YubiKey的使用很简单,插在电脑上,它就会亮。按一下那个唯一的按键,它就会往你光标在的位置“吐”一串密码。当然要登录的服务器得支持YubiKey的认证方式(所以基本没几个服务可以用,但是有LastPass)。

图片描述

YubiKey的OTP实现如下。顶上紫色的是最终产生的一个字符串。其中前面一部分是用户ID,用来对应用户的公钥。后面部分是一个由counter何session key组合产生的密码。由于每次按键都会使得内部的counter自增,所以同一个密码是不能被拿来验证两次的。另外,由于这玩意儿里面的firmware是写死的,所以也基本不用担心算法被篡改的问题。

otp_details.png

YubiCloud

刚才说到服务器要支持YubiKey的认证方式,YubiCloud就是Yubico给出的解决方案。流程很简单如下图。

图片描述

只要在验证的步骤中增加一步跟YubiCloud交互的过程就ok了。一般这种应用会要求三个凭证来作为用户身份验证:用户名,密码,YubiKey产生的OTP。先验证密码的好处是,可以不至于每次输错密码都要重新产生一个OTP。

YubiCloud提供了几个写好的Server-YubiCloud的SDK,同时也给出了兼容这个协议的独立认证服务器实现参考,还有个第三方的go写的哟。

U2F

前段时间Gmail宣布支持U2F,因为有软件的Authenticator,硬件版本的U2F对于大部分二次验证用户来说…没啥用…但…对于在笔记本上使用二次验证的用户来说,买一个他家出的U2F SECURITY KEY,可能还是可以应对“手机没电”,“清空了Authenticator数据”,“手机丢了”等情况的。况且,日子还要过,逼还要装,怎能跟那些厮一样,打开个App看密码…

好吧,我承认今天的blog过于水了…就请看在之前找题材痛苦的份儿上…

晚安~

(30 gadget day 1) MYO get started

MYO

MYO是一款类似Leap motion的新型人机交互HCI设备。它被戴在人的前臂,从而可以感知人的肌肉电流,进而识别出人的各种手势动作,比如向左向右挥手。

图片描述

官方给出的MYO的几个用途是

  • 做展示。挥挥手就能换页,或者将Prezi的控制变成它应有的样子。
  • 控制手机。比如跑步的时候挥挥手就能切换歌曲,玩1024之类的。
  • 智能家居。打个响指就能让电视开机之类的。
  • 控制那些会跑的。现在支持了Sphero和Parrot Drone。

另一个我觉得很酷的例子是Armin Van Buuren在一次演出里使用MYO来辅助增强现场表演效果。特别有未来实时大规模交互演出的感觉。那些具体的表演视频就大家自己去搜吧。

avb-myo-home.jpg

我买了两个MYO腕带,下面三天我们就来想想能用它来玩点什么~(那上面的标志是在亮的,真的挺酷)

图片描述

MYO的结构

myo-overview.png

整个MYO中间靠近皮肤的部分都布满了各种金属触点,所以戴起来的时候完全不在乎什么角度。充电用的是标准的Micro USB。

图片描述

开发者视角

跟Leap motion类似,MYO提供两类输入数据:空间数据(Spatial data),手势数据(Gestural data)和唯一一种反馈接口,震动(vibration command)。

空间数据包括两个维度:方向(orientation),三维的加速度向量(acceleration vector)。
手势数据包括几种特定的手势,比如握拳等。

iOS sample

当然,MYO提供了基本所有平台的SDK(当然Windows phone除外)。开发起来最方便的还是Mac啦,windows要装一堆驱动啥的。如果只是开发应用的话,基本之用跟Android/iOS的SDK打交道就好啦。如果需要底层数据,MYO也提供C lib API。另外,还有一种用Lua写的Myo Scripts

sdk-stack.png

跟各种Sensor的使用类似,MYO的数据是以回调传递事件的方式来提供。MYO提供三类事件,如前面所说所空间事件,手势事件,以及一些辅助类的事件(如连接,断开设备等)。

开发MYO应用的最好方式就是先运行sample程序。iOS的SDK里面包括一个Doc和叫HelloMyo的Sample程序。打开Sample,运行,玩玩就好了。(MYO SDK开发速度很快,所以如果发现Sample程序告知版本太低,则需要连上USB和蓝牙发射器进行一次升级)。

看看代码吧。

- (void)viewDidLoad {
    [super viewDidLoad];

    // Data notifications are received through NSNotificationCenter.
    // Posted whenever a TLMMyo connects
    [[NSNotificationCenter defaultCenter] addObserver:self
                                          selector:@selector(didConnectDevice:)
                                          name:TLMHubDidConnectDeviceNotification
                                          object:nil];
    // Posted whenever a TLMMyo disconnects.
    [[NSNotificationCenter defaultCenter] addObserver:self
                                          selector:@selector(didDisconnectDevice:)
                                          name:TLMHubDidDisconnectDeviceNotification
                                          object:nil];
...
    // Posted when a new pose is available from a TLMMyo.
    [[NSNotificationCenter defaultCenter] addObserver:self
                                          selector:@selector(didReceivePoseChange:)
                                          name:TLMMyoDidReceivePoseChangedNotification
                                          object:nil];

}

首先是注册各种事件,包括连接断开连接,这里有个Sync Gesture,指的是一种比较奇怪的动作(不大好描述)。应该是MYO用来给手臂肌肉电流做一个基本的base line吧。

最有用的可能就是TLMMyoDidReceivePoseChangedNotification了。一旦有新的姿势检测到,这个函数就能收到一次callback。来看这个Callback的内容。

- (void)didReceivePoseChange:(NSNotification *)notification {
    // Retrieve the pose from the NSNotification's userInfo with the kTLMKeyPose key.
    TLMPose *pose = notification.userInfo[kTLMKeyPose];
    self.currentPose = pose;

    // Handle the cases of the TLMPoseType enumeration, and change the color of helloLabel based on the pose we receive.
    switch (pose.type) {
        case TLMPoseTypeUnknown:
        case TLMPoseTypeRest:
        case TLMPoseTypeDoubleTap:
            // Changes helloLabel's font to Helvetica Neue when the user is in a rest or unknown pose.
            self.helloLabel.text = @"Hello Myo";
            self.helloLabel.font = [UIFont fontWithName:@"Helvetica Neue" size:50];
            self.helloLabel.textColor = [UIColor blackColor];
            break;
        case TLMPoseTypeFist:
            // Changes helloLabel's font to Noteworthy when the user is in a fist pose.
            self.helloLabel.text = @"Fist";
            self.helloLabel.font = [UIFont fontWithName:@"Noteworthy" size:50];
            self.helloLabel.textColor = [UIColor greenColor];
            break;
        case TLMPoseTypeWaveIn:
            // Changes helloLabel's font to Courier New when the user is in a wave in pose.
            self.helloLabel.text = @"Wave In";
            self.helloLabel.font = [UIFont fontWithName:@"Courier New" size:50];
            self.helloLabel.textColor = [UIColor greenColor];
            break;
        case TLMPoseTypeWaveOut:
            // Changes helloLabel's font to Snell Roundhand when the user is in a wave out pose.
            self.helloLabel.text = @"Wave Out";
            self.helloLabel.font = [UIFont fontWithName:@"Snell Roundhand" size:50];
            self.helloLabel.textColor = [UIColor greenColor];
            break;
        case TLMPoseTypeFingersSpread:
            // Changes helloLabel's font to Chalkduster when the user is in a fingers spread pose.
            self.helloLabel.text = @"Fingers Spread";
            self.helloLabel.font = [UIFont fontWithName:@"Chalkduster" size:50];
            self.helloLabel.textColor = [UIColor greenColor];
            break;
    }

    // Unlock the Myo whenever we receive a pose
    if (pose.type == TLMPoseTypeUnknown || pose.type == TLMPoseTypeRest) {
        // Causes the Myo to lock after a short period.
        [pose.myo unlockWithType:TLMUnlockTypeTimed];
    } else {
        // Keeps the Myo unlocked until specified.
        // This is required to keep Myo unlocked while holding a pose, but if a pose is not being held, use
        // TLMUnlockTypeTimed to restart the timer.
        [pose.myo unlockWithType:TLMUnlockTypeHold];
        // Indicates that a user action has been performed.
        [pose.myo indicateUserAction];
    }
}

内置的手势有这些:

TLMPoseTypeDoubleTap(拇指和中指连续点击两次)
TLMPoseTypeFist(握拳)
TLMPoseTypeWaveIn(向内挥手,戴在右手就是往左挥手)
TLMPoseTypeWaveOut(向外挥手)
TLMPoseTypeFingersSpread(按理来说是类似cross finger的意思,但似乎我没咋触发过这个pose)

基本就这么点儿代码。明天我们尝试把这个跟其他功能联系起来哈。

(30 hackdays ) 30个gadget day

Gadget days

30hackday帮助我验证了快速学习的可能性和方法。下面这30hackday我想集中在硬件Gadget的开发上,看能不能积累出一套适用于Gadget App开发的经验。

2015年2月20日开始,每天我会写一篇比较正式的博客文章(文章质量参考原作者,第二天早上2点前发出算数),内容都是关于这些硬件Gadget的应用开发。

非常希望借此机会认识对Gadget开发感兴趣的伙伴们。所以,如果在文章里发现了任何错误,请帮忙指出,我都很乐意请你喝咖啡(限北京…)~ contact: fxp007@gmail.com

开发设备如下,过程中可能略有变化,排名不分先后。MYO,Kinect for windows 2,SmartScopes,Estimote iBeacon,LittleBits,Tobii eyeX,Sphero,Philips hue,Mindstorm,ARDrone

进度

图片描述

Httpsify – 国内视频https托管方法

问题

一些浏览器会禁止http内容展现在https页面中。比如我们在squarespace里插入一个Youku的视频(Youku嵌入的视频不支持https),就会无法播放

解决方案

我们需要找一个免费的支持https的serve视频的地方。第一个选择就是七牛云,搞了半天发现qiniu的ssl证书是invalid的…
于是又想起了“可怜的”LeanCloud。下载云代码,把要托管的文件放到public下,avoscloud deploy avoscloud publish就好啦,在setting里设置一下web主机的二级域名。就能直接访问了。

squarespace

squarespace比较特别的是它要求embed video,比如youtube之类的用iframe的玩意儿。但国内的那些视频网站基本都不支持https的iframe。好吧,这个也用LeanCloud解决吧。那就写个HTML来解决它。这个HTML要能处理各种Video的请求的话就得读到url里的query。

<div id="content"></div>

  function getParameterByName(name) {
    name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
    var regex = new RegExp("[\\?&amp;]" + name + "=([^&amp;#]*)"),
      results = regex.exec(location.search);
    return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
  }
  var videoUrl = getParameterByName('video');
  if (typeof videoUrl != 'undefined') {
    var node = document.createElement("video");
    node.width = window.innerWidth;
    node.height = window.innerHeight;
    node.autoplay = true
    var controlAttr = document.createAttribute('controls');
    var srcAttr = document.createAttribute('src');
    srcAttr.value = videoUrl;
    node.setAttributeNode(srcAttr);
    node.setAttributeNode(controlAttr);
    document.getElementById('content').appendChild(node);
  }

没做任何优化,只处理了video这种query,大概意思就是这样。

CNUtils

因为众所周知的原因,很多网站因为用了font.googleapis.com而变的打开很慢很慢很慢。其实也就是那么一个文件打开慢而已。360出了个CDN造福了一大堆程序员。

于是我想,如果能做个小js库,让这些网站通过它来选择性加载不同源的数据该多好~于是就做了个cnutils

代码很简单,一个demo而已,里面用到了ip-api.com的api来判断ip国籍。欢迎友人有兴趣能做下去哈。

var xmlHttp = new XMLHttpRequest();
xmlHttp.open("GET", 'http://ip-api.com/json', false);
xmlHttp.send(null);
var result = JSON.parse(xmlHttp.responseText)
var fontSrc = (('China' === result.country) ?
        'http://fonts.useso.com' :
        'https://fonts.googleapis.com') +
    '/css?family=Open+Sans:300italic,400italic,600italic,400,300,600|Merriweather:700'
var linkElement = document.createElement('link');
linkElement.rel = 'stylesheet';
linkElement.href = fontSrc;
console.log(fontSrc)
document.head.appendChild(linkElement);

(30 hackdays day 30) StrongLoop – Javascript全栈框架

SL_Logo_Stacked_RGB.png

StrongLoop是一个基于Nodejs的强大框架,几乎包含了移动开发全栈所需要的所有功能。2013年成立,很少的员工,一个技术驱动,执行力强大的团队。也是在13年我开始接触StrongLoop,当时是为了做nodejs方面的技术选型,看了许多框架,LoopBack是我觉得最酷的一个。我还记得当时是觉得LoopBack的文档太差(主要是跟在线的版本不一样),不知道能活多久所以才放弃了它。时隔一年回来看到这个绿油油的框架,这一年可真是突飞猛进呢。

LoopBackarch7162014-3.png.pagespeed.ce.tUGpVTVgYP.png

全栈框架StrongLoop

StrongLoop基本提供了制作一个移动产品所有的框架和工具,从标准的Backend server,Devops,应用监控,。要想介绍完全StrongLoop的所有产品得写一个长篇连载了,这里只简单的浏览一遍。

  • LoopBack
    一个功能很强大的WebServer框架,隐约看到Spring的影子…

    • SDK
      这是当时我觉得StrongLoop最好用的一点:根据后端Model自动生成对应的前端SDK。
    • RESTful API
      MEAN stack推动了所有新框架默认支持RESTful API,比如Nodejs里的Node-restifySailjs之类的天生就是为了serve API的。StrongLoop还提供了一个用来设计API的工具(beta状态),当然跟RAML比还是差不少的。
    • Data model
      很容易创建数据模型,自动产生对应的RESTful API。
    • Connectors
      当然可以对接各种数据库。
  • StrongLoop Controller
    Debug, Package Management, Build, Deploy, Cluster, Log等等一堆Devops工具。
  • Application Monitor
    一堆监控服务器的模块。
  • mBaaS
    使用StrongLoop,你也可以创建自己的LeanCloud啦,包括Push,地理位置计算(Geopoint),Social Login,User management,各种Replication,Offline sync(离线同步是个好东西),对接各种Stroage云(AWS,Rackspace之类内置了的)。当然,其实这些都是LoopBack里提供的功能,意思是用StrongLoop得永生。

开始

安装StrongLoop sudo npm install -g strongloop
创建一个应用 slc loopback(找个空白的目录哦) 它只会问你项目叫啥。
创建一个Model slc loopback:model (是不是想起了Yo generator~),然后它就会问一堆乱七八糟的问题了。

图片描述

plural指的是RESTful API的route名,一个Model对应的route默认情况下会被plural(复数化),比如Post的路径是Posts。

猜一下,像LeanCloud里类似,这时候应该去访问一个类似127.0.0.1:3000/xxx的地方看到他的后台吧~嗯 xxx=explorer http://127.0.0.1:3000/explorer 就能看到一个用Swagger做的API dashboard。

图片描述

看一下项目的结构,像Meteor一样,LoopBack将JS代码分成服务端(server),前端(client),以及共用部分(common,个人认为这种纯schema的方法比Meteor分割的更清晰)。

略过后面的API Explorer,如何连Database,直接看如何在前端使用刚刚创建出来的API。LoopBack的做法是帮助你创建你当前API的各端SDK,目前只支持Android/iOS/Angularjs。如果你用别的Web框架可能就只能自力更生了。

创建自己的SDK lb-ng server/server.js client/lb-services.js 看看client/lb-services.js,不错吧~

嗯,你的前端工程师需要个文档?执行 lb-ng-doc client/lb-services.js 有时候需要在前面添加sudo(不知道为啥),然后访问 http://localhost:3030/ 就能看到文档啦~这个功能是基于Docular做的。另外,如果你点LoopBack Services进去没东西,别担心,刷新一下。

看看是不是很像早期Angular的文档~

图片描述

API Designer

相信很多程序员喜欢Parsejs或者LeanCloud的原因都是那个图形化界面的Model设计(或者叫API设计)后台。如果前面用的那个”Yo generator”的API creator看起来还不够贴心,你可以试试StrongLoop新推出的StrongLoop Studio beta。

  • 在工程根目录下sudo npm install -g http://get-studio.strongloop.com/strong-studio.tgz 安装Studio。
  • 启动Studio strong-studio,自动打开浏览器
  • 去StrongLoop官网右上角注册个账号(文档里居然写了On the bottom right is a link to go to the registration page on strongloop.com,你们不知道页面很容易改版么…)
  • 用注册的用户名登录进去看到API Composer和Profiler。
  • Bingo!各种玩耍吧!

图片描述

p.s.如果这时候你遇到了类似SyntaxError: Cannot parse package.json: Unexpected token e这样的问题,检查一下你工程根下的package.json里是不是被jslint搞乱了(I dont’t know why…)
好吧好吧,我告诉你这个beta还真是特别beta…尽量不要在之前写过代码的项目里用…

懒人们

如果你缺个可以玩耍的Mongodb:Mongolab
如果你想要个可以协作开发的IDE:Koding
如果你想要个便宜好用的VPS:Digital Ocean
如果你觉得他们家不够便宜:Serverbear
如果你连VPS都不想要,只想部署个Node项目:Nodejitsu
如果你想设计个API尽快给客户端同学,又不想搭Node,找地方部署:Apiary
如果你想做(抄)个APP,又不想花钱雇一堆人:Appdupe

30天结束

这是最后一篇30hackdays啦,终于熬出来了(吁…)。Anyway,这三十天我学到了很多,如何寻找自己想要的服务,如何快速实现原型,如何比较开发者产品的竞品…

回头看,文章的内容肯定没有Shekhar的那篇Learning 30 Technologies in 30 Days: A Developer Challenge写的详尽(确实也没人家投入那么多时间精力),但我选择的技术更广泛(或者叫更乱七八糟),对我也是一种扩展视野的过程。

希望我的这个系列能给大家一些启发。技术并不都是那么深奥难懂,拥抱它也许不能立刻涨工资,但至少在寒冷的日子里,没有wifi,孤独寂寞冷的时候,还可以打开浏览器的console,输入个while,来暖暖手。

下面

嗯,不是你想的那个,也不是用来吃的。之后我会继续时不时写一些发现的好玩的开发者服务;另外,我会写一个StrongLoop的系列文章来介绍这个框架更多的特性,希望大家能够尝试这个迅猛发展的产品。

再做个小广告:最近在做一个小班(免费),专门教高中生技术,直到达成可以自行参加Hackathon的程度。如果身边有高中生对技术感兴趣,欢迎联系我~ fxp007@gmail.com

ok,那些输了的情自行来约~

ec0a6159252dd42af84c70ce003b5bb5c9eab82a.jpg

(30 hackdays day 29) Google TTL – 让Google讲小说

最近一直在喜马拉雅听有声小说《轮转长生》。每天会更新两集。但由于我记忆力差,所以如果等着每天的小说更新,就会由于记不住之前的情节而感觉很迷茫。所以我去搜了下,发现了这个小说的文本。于是就想如果能用Text To Speech把文字变成语音的话,不就能一直听下去,而不用等播音了嘛。

于是我就去搜各种TTS API Chinese。发现了iSpeech, tts-apivoicerss之类的服务。确实,TTS技术发展了这么多年,肯定有巨多的服务提供商。但翻了一圈发现中文的效果都很差。郁闷的时候想起Google Translate的TTS,就是那个整天被人当B-Box玩的东西。

图片描述

Google-TTS

这是一个非官方的Google TTS库,但是文档里说到Playbackwill only work when running the script locally as Google's server only returns audio if you can prevent the browser from sending the Referrer HTTP Header to their server,看来是Google对referrer做了检查。不过这点后面看起来似乎没那么严格。

我们先用个简单的页面测试一下播音(注意科学上网),播放一小段中文听起来还不错。

    var tts = new GoogleTTS('zh-CN');
    tts.play('你好呀呀呀呀', 'zh-CN', function (err) {
        console.log('complete');
    });

拿小说

这就用到了之前介绍的技术Import.io。安装桌面应用,Create API from URL 2.0,找一个详情页,比如第一百五十三章 照片上的三口之家,选择文章内容的div改column名为text。就可以create啦。
看一下文章的url是连续的,好,那就用node写个循环,然后用AVOS把文章内容保存下来。

// ...省略初始化
var Chapter = AV.Object.extend('Chapter');

_.each(_.map(_.range(27888, 27996), function (num) {
    return 'http://book.guidaye.com/daomu/85/' + num + '.html';
}), function (url) {
    var q = new AV.Query(Chapter);
    q.equalTo('url', url);
    q.first()
        .then(function (chapter) {
            if (chapter) {
                console.log('exists,%s',url);
            } else {
                unirest.get('https://api.import.io/store/data/c2f29086-18a8-4c7b-9d5d-6ea1d54a9bc5/_query')
                    .query({
                        'input/webpage/url': url,
                        '_user': '[UER]',
                        '_apikey': '[API KEY]'
                    })
                    .end(function (response) {
                        new Chapter({
                            url: response.body.pageUrl,
                            text: response.body.results[0].text
                        }).save().then(function () {
                                console.log('done,%s', response.body.pageUrl);
                            }, function (err) {
                                console.log(err);
                            })
                    })
            }
        })

})

跑一遍基本就ok啦,如果有发生错误的就再执行一遍验证下。

读小说

好,我们要一个前端页面来读小说。

<div id='playing'></div>


    AV.initialize("[APPID]", "[APPKEY]");

    function speakChapter(chapterNum) {
        var url = 'http://book.guidaye.com/daomu/85/' + chapterNum + '.html';
        var Chapter = AV.Object.extend('Chapter');
        var query = new AV.Query(Chapter);
        query.equalTo('url', url);
        query.first()
                .then(function (chapter) {
                    document.getElementById('playing').text = "playing:" + url;
                    console.log('playing,%s', chapter.get('url'));
                    var tts = new GoogleTTS('zh-CN');
                    tts.play(chapter.get('text'), "zh-CN", function (err) {
                        console.log('complete,%s', chapter.url);
                    });
                }, function (err) {
                    console.log(err);
                })
    }
    //speakChapter(27889);

我们在console里实验一下speakChapter(27889);,终于开始读啦!听起来感觉还好,但还是TTS的最大普遍问题:语气不对。每个人说的,旁白说的都是一个味道…另一个问题是,读文章的时候一段以后会需要几秒钟的加载时间去加载下一段。而这个gap很有可能是一句话的中间。这就让人听起来很诡异了…“雷英…(上个厕所)…雄笑了笑”…

最后说一下浏览器问题。我发现safari打开这个页面就能正常播放,所以,也还算可忍~

好啦,按这个思路我们可以把自己喜欢的小说播放出来~如果你想把声音保存成mp3的话,可以用node-google-text-to-speech来把数据pipe到文件里,再用pushbullet类的产品把数据同步到移动端去。

29天啦,坐等吃串!

(30 hackdays day 28) Repose – 给API穿上衣服

图片描述

最近需要把一些功能拆分为HTTP API,于是找到了它。Repose是一款开源的API中间件,它可以帮你轻松实现那些API设计里最基本的功能,诸如认证,限速,验证,Logging等。Repose是Rackspace旗下的产品,所以靠谱程度很高。

当你写了个一个页面Parser,一个“1秒钟算1BIL次蛋疼指数”的程序,一个通过一个mail判断一个人是不是靠谱的“占卜服务”之后。若果你想让更多的人能享受到你的智慧,最简单的想法就是把代码开源出去。但很多人是不愿意这样做的,于是就会想到把它作成网络服务,收费提供出去。也就变成了前面二十多篇文章介绍的各种API服务。

API hosting as a Service

经过这多年的积累,Web API服务已经逐渐形成了一套相对标准的做法,从各种开放平台的做法就能看出来。

  • 注册平台账号
  • 创建App,获得AppKey(也许需要付费)
  • 将SDK嵌入自己应用
  • 用AppKey初始化SDK
  • 开始调用各种API(也许需要付费,也许会限速)

工程师用多了这类产品以后,遇到新的服务往往就会轻车熟路的不用看任何Tutorial。直到有一天,工程师决定自己也要搞一套这样的服务,问题就出现了。这几个环节具体都要如何实现呢?

于是就在API as a Service(其实就是只提供API的Software as a Service)之外,出现了个API hosting as a Service的概念(是不是觉得XaaS特别Ass…)。也就是我帮你托管你的API,帮你做掉前面那些步骤的事儿,您安心开发您的核心算法,提高效率,降低错误率就好啦。其实,对于Amazon这样的老早就看到解耦合,把业务都拆分成细小服务的公司来说,这个概念已经太古老了。

同样提供这类服务的也有不少,比如界面做的太漂亮的Apiary.io,搭建API全生态链的Mulesoft。但因为我是想搭建在自己的服务器上的,所以暂时不考虑那些托管的平台。

Repose

感慨于Docker神速的发展,Respose最简单的部署方法就是部署一个Docker container。隐隐就已经看到了一个Repose as a Service的产品了…

Repose的工作原理很简单,就是在你的裸API和用户之间做一个代理。

图片描述

文档中描述的Repose的基本功能如下。

  • Translating。API格式转换。
  • Rate。限速。
  • Authenticating。
  • Validating。保证API符合Web Application Description标准。
  • Logging。
  • Blacklisting。credentials and roles
  • Caching。
  • Role-based access control (RBAC)。基于角色访问控制。
  • Mapping the state of your API。好吧,没懂,估计是类似NAT那样的玩意儿吧。

有好几种部署方法,嵌入源码,WAR,独立Proxy server,独立Proxy server cluster。我们肯定不能假设服务器是java的,所以肯定选择独立Proxy server这种模式。

配置里最重要的就是Destination endpoint,一个典型的如下,很清晰的描述一个即将被封装的API endpoint。(又看到了xml配置文件,让一个former javaer还是感慨良多)

由于还没有图形界面配置的功能呢,所以强烈建议从Repose+101入手。装个Virtualbox,里面装docker。

// TODO 过两天把装了Repose的VM传上去

Repose里除了刚才提到的Endpoint以外还有两个重要概念,filter和service。filter就是一个request一路经过的劫难。service就是诸如分布式数据库之类的东西。

Mock Server & Config

nodejs写一个简单的mock server来假装一个牛逼服务。node app.js跑起来。

var express = require('express');
var app = module.exports = express();

app.get('/api/nbservice', function(req, res){
 res.send('cool to die');
});

app.listen(8080);

写一个简单的Repose配置

刚才说到建议装docker,其实吧,最简单的还是直接跑jar~从maven上当他们最新的jar。运行java -jar valve-2.3.5.jar start -p 8888 -c /etc/repose/执行起来。

然后呢,curl 127.0.0.1:8888/api/nbservice就能看到它跑通啦~

Freelancer

在搜索这个主题内容的时候,遇到了这个老牌Freelancer网站的一个服务,居然叫RESTful hire。其实就是在技能里要有RESTful这一项而已。难道,RESTful已经变成了一种“世界观”了么…

(30 hackdays day 27) CloudRail – 云存储的POSIX

这是一个Mashup的时代,所有你知道的API都将成为你设计开发新产品的武器。但跟App一样,太多的选择反而给开发者带来了困扰:到底哪家的服务更好,SDK用起来更舒服,以后维护的成本更低?存储是所有应用都会用到的,Cloudrail的出现正是帮助开发者摆脱SDK选择困难的工具。

图片描述

Mashape类似,Cloudrail认为存储API应该拥有共通的结构和描述方式。但是,跟Cloud Element的Document hub类应用不一样,为了让用户安心,Cloudrail并不用将数据都都经过它的服务器,而是让用户直接和存储服务提供者通信。通过下面的图能很清楚看出Cloudrail是怎么做到的。

图片描述

这就是Cloudrail最核心的技术:Self Adapting API。它的作用是保证客户端随时获得最新的API提供者的API定义,换句话说,就是保证开发者不用换SDK就可以持续访问最新的API(所以叫’No API Updates’)。Javascript的实现方法大家都能想到,就是每次都下载新的connector库。Android呢想一想估计是用ClassLoader或者DexClassLoader之类的吧,iOS就不知道了…不过也许通过WebView的js层做一个中转也是一个低效率的方案。看了下Download页面,发现果然Android版本还没有上线,剩下的是Others。只有JS版本的可以用。不过目前只是Beta嘛,可以理解~

API

CloudRail为数据存储服务定义了一系列标准接口,通过一个CloudRailInterface把各家的存储服务封装起来。比如Sample代码中,CloudRail把Dropbox和GoogleDrive的API封装为一个CloudRailInterface,通过一个CloudRailClient来统一访问。其中用户在不同平台的的标识client_id可以按照帮助文档获得到。

var CRC = CloudRailClient,
    CRI = CRC.CloudRailInterface,
    DBServiceTag = 'Dropbox',
    GDServiceTag = 'GoogleDrive',
    userIdentities = [],
    userIdentitieServiceTags = [],
    serviceDB, serviceClientDB,
    serviceGD, serviceClientGD;

/* Initialize services */

serviceDB = CRI.initService(DBServiceTag);
serviceClientDB = new CRC.ClientIdentity(DBServiceTag,
        {'client_id': 'abcd'}); // insert here your own client id

serviceGD = CRI.initService(GDServiceTag);
serviceClientGD = new CRC.ClientIdentity(GDServiceTag,
        {'client_id': '1234'}); // insert here your own client id

/* Read User Identities */

var dbUser = new CRC.NodeIdentifier(CRI.ObjectType.USER_IDENTITY, DBServiceTag),
    gdUser = new CRC.NodeIdentifier(CRI.ObjectType.USER_IDENTITY, GDServiceTag);

CRI.read(dbUser, serviceClientDB, (function(servicetag) {
    return function(res) {
        saveUserIdentity(res, servicetag);
    };
})(DBServiceTag));
CRI.read(gdUser, serviceClientGD, (function(servicetag) {
    return function(res) {
        saveUserIdentity(res, servicetag);
    };
})(GDServiceTag));

目前CloudRail提供了基本的CRUD API,看起来还是挺通用的。突然觉得,CloudRail是不是想实现一个云存储版的POSIX接口呢。

文档最后提到了一个额外代码CloudRailClient.Crypto.Hash.SHA256。看来是为了对文件作Hash用的。

Price

官网的说法是’Integrations to Cloud Storage Providers will be free forever.’,So…他们怎么活下来…

StrongLoop

最后,提一下之前用过的Nodejs框架。其中有一个很好用的功能,Auto generate SDK。在后端创建一个数据Model以后,Grunt一下就会自动把这个Model的访问代码。比如Angular的版本生成一个访问该Model的Service。

图片描述

提到这个原因是,Nodejs+browser这样的脚本语言组合太适合WebApp的开发了,这样组合所带来的灵活性也带来了很多新的可能。像CloudRail,StrongLoop这样动态接口比原来那些“死板”的接口通信拥有更强大的整合能力。也许以后就会出现一批服务,专门负责接口的维护,而文中这类技术就是这种服务的基础。不过CloudRail还会遇到很多问题,诸如版本管理,平台支持能力等。

另外,CloudRail也是个国际团队,总部在德国的曼海姆,在旧金山也有办公室,公司内居然有幼儿园…

在WordPress.com的博客.

向上 ↑