Restlet使用之Guice扩展

如果你像我一样对SpringFramework的配置感到头疼,并且不需要SpringFramework的深度支持,只需要Ioc,选择Guice吧!

https://code.google.com/p/guice/
https://github.com/restlet/restlet-framework-java/tree/master/incubator/org.restlet.ext.guice

下面通过使用 Gucie 的AOP支持在Restlet实现一个简单的节流阀。

示例代码片段:
配置Logger
初始化Guice, 分离Module(这里将资源和服务分离,方便对服务层进行单元测试)
映射 Public & Protected 路由

@Override
    public Restlet createInboundRoot() {
        // org.restlet.engine.loggerFacadeClass=org.restlet.ext.slf4j.Slf4jLoggerFacade
        SLF4JBridgeHandler.install();
        System.setProperty("org.restlet.engine.loggerFacadeClass", "org.restlet.ext.slf4j.Slf4jLoggerFacade");
      
        Context ctx = getContext();

        logger.info("Initializing Gucie...");
        //
        final Module serviceModule = new ServiceModule();
        // Resource module
        final Module serverResourceModule = new ResourceModule();
        // We use explicit Injector creation.
        // Initialize Guice
        Injector injector = RestletGuice.createInjector(serviceModule, serverResourceModule);
        // Initialize Restlet Guice extension.
        FinderFactory di = injector.getInstance(FinderFactory.class);

        logger.info("Gucie Initialized.");
        logger.info("Mapping routes...");
        // public router
        Router router = new Router(ctx);
        PathMappingHelper.mapPublic(router, di);
        // protected router
        Router sercurityRouter = new Router(ctx);
        PathMappingHelper.mapSecurity(sercurityRouter, di);
        logger.info("Routes mapped.");

        logger.info("Attaching security guard...");

        // Security guard, verify token
        // eg:
        // HTTP header
        // Authorization: token ASD12312312ASDASD12312123123
        ChallengeAuthenticator guard = new ChallengeAuthenticator(getContext(), ChallengeScheme.CUSTOM, "token");
        guard.setVerifier(injector.getInstance(Verifier.class));
        guard.setNext(sercurityRouter);

        router.attach(guard);
        logger.info("Security guard attached");

        return router;
    }

Resource 模块映射,重点在于bindInterceptor

public class ResourceModule extends AbstractModule {

    @Override
    protected void configure() {
    
        bind(Verifier.class).to(TokenVerifier.class);

        bind(AccountResource.class).to(AccountResourceImpl.class);
   
        bindInterceptor(Matchers.any(), Matchers.annotatedWith(Throttling.class), new ThrottlingBlocker(
                getProvider(TokenStore.class)));

    }
}

Service 模块映射,本例没有用到,忽略之。

接下来看看如何实现节流阀(throttling)

首先定义注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Throttling {

}

实现MethodIntercepter
TokenStore是一个简单的缓存接口,可以使用一个Memcache的实现,用来存储counter信息,当然也可以使用数据库实现。
MethodInterceptor 是AOP联盟定义的标准接口。
这里使用Provider提供对象的延迟绑定。
这里我们假设所有接口都是以用户作为隔离 /account/{account}/,限制为 50次请求每API每秒每账户,2000次请求每API每小时每账户。

public class ThrottlingBlocker implements MethodInterceptor {

    private Provider<tokenstore> provider;

    public ThrottlingBlocker(Provider</tokenstore><tokenstore> provider) {
        this.provider = provider;
    }

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        ServerResource resource = (ServerResource) invocation.getThis();
        String key = (String) resource.getRequest().getAttributes().get("account");
        Object result = null;
        if (key != null) {
      
            // Step 1 - Find throttling info
            // Step 2 - Execute method
            // Step 3 - Increase counter
            // Step 4 - Write throttling info.

            // {accountId}_{classFullName}#{methodName}
            String throttlingKey = key + "_" + invocation.getClass().getName() + "#" + invocation.getMethod().getName();
            String totalThrottlingKey = throttlingKey + "_total";
            Object co = provider.get().get(throttlingKey);
            Object tco = provider.get().get(totalThrottlingKey);
            long count = 0L;
            long total = 0L;
            if (co != null) {
                count = Long.parseLong(String.valueOf(co));
            }
            if (tco != null) {
                total = Long.parseLong(String.valueOf(tco));
            }

            if (total > 2000) {
                throw new RuntimeException("Throttling total quote(2000/hour) exceeded!");
            }
            // 50 requests/second/account
            if (count > 50) {
                throw new RuntimeException("Throttling quote(50/second) exceeded!");
            }

            result = invocation.proceed();
            count++;
            total++;
            provider.get().set(throttlingKey, count, 1 * 1000);
            provider.get().set(totalThrottlingKey, total, 60 * 60 * 1000);

        } else {
            result = invocation.proceed();
        }
        return result;
    }

之后在你的Resource实现里简单加入注解到方法,这样就实现了一个非常简单的节流阀。

 @Throttling
 @Get("json")
 public List<message> represent() {

这里仅仅提供了实现节流阀的一种机制,当然我们也可通过其他的方式实现。比如使用 Java EE Servlet API 中的 Filter。其他拦截器,难点不是在于使用什么技术实现,而是节流策略本身,如何配置用户的请求限制,如何设置合理的参数,是否绑定安全系统等等,这一切就必须得经过实践来得出,软件开发从来不是仅仅在于技术,更在于如何针对某种需求使用最合适的解决方案,而这点,需要大量的经验和分析抽象的能力。

我们写程序不是为了提供让计算机可以理解的代码,是为了解决现实世界的问题,提高社会生产力,这是整个软件行业的价值所在!

Restlet Google Gson 扩展

做了2个版本的 Mobile Aware,一直使用Restlet作为服务框架提供Restful Web Service,之前一直都是使用Jackson作为Json转换类库,后来自从用了Google Gson,果断决定切换到Gson。以为,API更简洁,使用更方便,性能也不错,还有版本支持(这个还是很强大的,尤其对于API的版本升级意义重大)。无奈Restlet没有提供相关扩展,于是乎花了几个小时的时间写了一个Gson的扩展。

源码可以在下面的地址获得:

https://github.com/nealmi/restlet-framework-java/tree/master/modules/org.restlet.ext.gson

Restlet还是一个比较不错框架,使用简单,扩展容易,尽管有些小问题,对实际应用影响不大。

Jetty + Restlet + Guice + Gson 的组合我用起来十分顺手。最终部署的时候,打个bigjar,然后使用service wrapper配置成linux的系统服务。整个部署过程基本不会超过5分钟。

免费的Git私有代码托管服务

最近搞些小东西,因为要涉及到与别人协作,所以就考虑找个私有的Git托管服务。不想浪费时间在自己配置维护服务器上面了。

Github很有名,也很好用,只是免费版仅能用于开源项目。

经过一番比较,最后选定了两个。

Bitbucket

Atlassian公司的托管服务。

支持Git、Mercurial,免费版有5个用户限制。公有和私有仓库都可以无限制创建,有协作工具(Issue tracker,Wiki)。

Assembla

支持SVN、Git、Mercurial,免费版,无用户限制,无仓库限制,存储空间1G,没有协作工具(Issue tracker,Wiki等)。

考虑先使用 Bitbucket, 当用户不够用的时候再迁移到 Assembla。我个人更喜欢 Bitbucket。

2012 Spring

自从去年换到了现在的公司,我进入一种比较好的工作状态。

先说说公司,Hypersun – 我刚到的时候是隶属于CloudFloor, 去年年底CloudFloor被 EverBridge 收购。 我们的CEO成了 EverBridge的CEO,我呢就糊里糊涂的变成EverBridge China的一员。

再说说工作,过去的几个月,以Development Leader的角色参与开发并于2012-1-20上线了EverBridge Mobile Aware项目(Android和iOS)。

EverBridge Mobile Aware
http://www.everbridge.com/mobile-aware

我记得我说过,喜欢小公司,创业公司,年轻的我需要一个积极向上的环境,需要尽情发挥的空间。

接着谈谈技术
Restlet – Mobile Aware 服务器端API使用的 Restful Web Service 框架。我喜欢它的架构。尽管文档比较少(这帮败家孩子都忙着写书去了,不更新文档),还有一些小问题,总体感觉还不错。

Gucie – Ioc Container,不多说,非常好用。

Memcache – 缓存服务器

Android – 因为我是Java出身,没什么门槛,直接上手,不过移动开发的模式还需要适应一下,主要熟悉其API。

IOS – Objective-C,对我来说是一门新的语言。学习中,简单的开发还是可以直接上手的。Cocoa Touch,Apple的移动开发框架,熟悉中。

暂时先到这里。等有空了,把这些技术使用过程中的经验记录一下。

网站又一次迁移 – 终于我厌倦了

当时阿里云出现的时候,我本以为那将是我最后一次迁移网站,以为无论访问速度,性价比等等都已经满足我的全部需要,只是我忘记了一件事,这是天朝,这是天朝的公司,1月5日,阿里云以域名没有备案为由单方面停止了我的服务,导致多个网站无法访问,源码服务器无法访问。我的域名备案前前后后3个月,除了中间被退回一次之后,之后就杳无音讯。我不得不再次迁移,终于我厌倦了,厌倦了盈利性网站许可,非盈利行网站备案,连微博都要实名制的网络环境,终于我厌倦了,每次迁移重复配置服务器,每次写配置文件写到恶心,厌倦了每次写文章小心翼翼不能出现敏感词。我们的政府到底再恐惧什么?感觉又回到了历史书上所讲的“白色恐怖”时期。整天号称社会主义好,怎么也得拿出点实际行动来,哪怕装装样子也好。
什么时候买菜实名制? 吃饭实名制?终于大家等到了火车票的实名制,结果呢,依然一票难求,为什么呢?如果地区发展平衡了,老百姓还有必要像候鸟一样的迁徙么?
先鼓励大家买车拉动经济增长,然后开始堵车,开车开始限号,车牌开始摇号,不过都是慢性死亡而已。官员们智商都用在了贪污腐败和勾心斗角上,哪儿有空管你老百姓的死活。
凭什么北京水费电费公共交通都是最便宜的?城市你不好好规划,建什么睡城(天通苑,回龙观)。看老百姓折腾来折腾去,政府是玩儿的挺开心啊。
保障房?经适房?TMD为什么里面都是开奔驰宝马的。
我们辛辛苦苦的工作,承担着全球第二的税负,享受的社会福利还不如发达国家的乞丐。我们纳的税都养活了谁?
高速公路,你丫的收了燃油税,高速还是100%收费。确实和国际接轨接的挺好,美国收了燃油税,90%+的高速免费。收钱的地方肯定接轨,对老百姓好的因为国情不同,肯定不能接轨。为啥,中国特色。
有关部门,天朝最牛X的部门,没有它解决不了的事情。别为有关部门是什么部门,这是屁民能问的么。
。。。。。。