研发背景
到年底了,很多项目都要突击完成,我们自己的“问题及知识管理平台”移动端研发也到了不能再拖的地步,所以需要在后台集成移动端框架。由于后台架构采用spring mvc+hibernate,并且近期也深入的研究过spring mvc,所以就不想沿用已有成熟的整合方案《HTML5企业移动应用解决方案V1.0.doc》,尝试完全应用spring framework技术实现移动端后台架构。
根据技术特点和我的一些架构封装想法,给自己这次研发规定了几个指标:
l 请求URL简洁明了;
l 数据交互灵活友好;
l 并发能力出众;
l 开发习惯规范友好。
设计验证过程
由于本次移动端想完全寄托spring framework框架,所以验证过程其实就是尝试和对spring源码理解的过程,为了保持一个严谨的态度,即使时间紧迫也要足够耐心。
1、 请求URL简洁明了
Spring mvc提供restful请求风格,大有取代webservice技术趋势,所以URL这块没有经过太多思索选择该风格。
2、 数据交互灵活友好
当前主流的比较简单的数据交换格式无非就是json和xml,移动端相对来说对流量要求还是比较严格的,这样基础数据格式选择json。我再多提一下就是压缩json和压缩xml结构应用,其实如果想针对流量做更细致的设计,可以好好研究一下的。灵活友好并不能通过json来体现,json就相当于选择了一个语言,要想灵活就不能对说话有太多限制。我之前设计的移动端交互格式采用了类似Unieap DataCenter的结构,虽然规范,但是确实给开发人员设置了太多的框框。这部分spring mvc给我很大启发,所以这次在传输数据结构上没有做任何强行的结构限制,当然这种格式给架构研发包括异常处理带来了不小压力,为此我也花费了很长时间才找到一个合理的解决方案,最终选择spring json view+sojo+argumentResolver+exceptionResolver。
3、 并发能力出众
我之前版本研发是也针对这部分做过相关设计,所以在上一版中才采用servlet方式获取请求,毕竟所有的框架包括struts和spring mvc都是通过唯一servlet入口与j2ee平台交互的,我当初认为这样应该是性能最好的。但是当spring mvc强调请求处理能力提高40%后,我就感觉奇怪,这是怎么实现的。所以我也研究过相关源码部分,发现采用事件驱动框架重写了请求分发处理部分,这种写法应该是当前高性能请求框架必备技术,类似的有nodejs。所以这次集成我在请求处理方面没有独立研发,沿用spring mvc Controller分发机制,最大范围发挥其处理吞吐能力。
4、 开发习惯规范友好
Spring mvc这种灵活的风格上手确实比较头痛,但是上手后我就喜欢上了,框架就应该这样无拘无束,开发人员就应该只关注业务,对于什么连接、认证、数据获取、数据回传、异常处理等等的都不关心,并且能按照自己已有习惯开发自己喜欢的代码。所以我不想改变什么,我只想让移动端开发完全融合到这种风格中。
除了上述这些指标外,其实移动端还有一些硬性的指标:
1、模拟长连接
要是开放的平台到不用做这样的工作,对于需要认证才能使用的平台这个功能就是硬指标。对于这次研发我采用spring interceptor技术通过切面做拦截处理,其实效果类似原来filter。模拟的会话保存在内存中,并且有独立的守护进程控制生命周期。
2、安全
连接的安全主要是防信息泄露、防信息篡改和防非法访问上,我在第一个版本移动终端框架中基本上都实现了。信息泄露这块主要是加密,我个人感觉加密算法最好采用带辅助信息加密算法,并且尽量性能要高,所以之前的版本主要通过base64+unzip实现的信息混淆,在这个版本中我也会采用类似的方式。关于信息篡改,这块主要是通过签名方式采用“公共私密钥SHA-1”对数据进行签名。非法链接访问方面当前主流技术是Oncefilter,这块需要记录或能通过算法对访问进行合法性审计(王勇老师是这部分的专家,他的一些文档我也看了还是挺有启发的),到目前我还没有找到一个适合移动端的方案。
3、系统日志及异常处理
日志这部分没有开发,不过整体设计开发并不复杂可以用过spring AOP和hibernate AOP实现系统请求及数据日志。
异常处理针对整个框架,设置了全局异常处理机制,系统会在最顶层拦截异常,并将异常信息通过json结构返回给前台,供业务使用。
实现
处理流程描述:
1、MobileAuthInterceptor类负责拦截请求,当请求中不包含SESSIONID时,
返回{‘code‘:-2, ‘errmsg‘:‘没登陆‘},这样系统可以跳转到http://localhost:8080/test/m/login登录页面,按照要求输入用户名和密码进行校验,校验成功返回SESSIONID。
2、后续请求需要在Header中包含SESSIONID,响应请求的Controller通过定制输入参数绑定将传入的Json结构按照使用者需要映射到相应实体上,开发人员只要通过实体取出对象就可以直接使用了。
3、业务处理完后生成返回数据,保存到标准ModelAndView中,这块根据需要定制化相关视图,将展现结果转换成json字符串,并针对该字符串进行压缩后回传前台。
整个标准处理流程完成,当程序出现异常情况,架构级ExceptionResolver会捕获异常,并将异常转换成json结构回传前台,这样整个系统的数据结构就一致了,同时这部分不需要开发人员关注。
实现目录结构
Ps:Common和sys包下是系统自带功能代码,相关移动端开发代码放在tpmobile目录下。
------------------既然大家都跑题关注扩展,我也华丽分割一下----------------------------
扩展演进:
我看大家当前都在关注架构分布式和扩展性,对于有状态的系统会话的抽离和共享是一个必须要面对的问题,当前架构会话部分已经从j2ee中独立管理了,这样就给了我们很大的空间解决这个问题,网上相关文章我也看过一些,可行的方案大多是数据库会话持久方案和全局缓存(/分布式缓存方案)。我针对这两个方案分别做一下说明:
1、 数据库持久方案
这个方案主要是利用系统统一的数据库资源将生成会话内容序列化后存储在其中,当另一个请求需要会话是,通过DML查询获取信息。这样多个应用节点就可以实现统一的会话管理了,这种形式好处是实施容易,风险较低。但是缺点就是数据库压力较大,存在性能瓶颈。当然这个方案随着闪盘商业化应用,性能应该会用明显提高,不过需要实际验证。
2、 全局缓存(/分布式缓存方案)
会话内容放在内存中是单点应用特点,如果对于集群环境能统一管理并且还在内存中,整体效果肯定最理想。通过会话的独立管理,也使得业务力度级请求分发变的相当简单,但是全局缓存存在单点故障问题,分布式缓存有又存在一定的资源利用率问题,并且分布式缓存产品在当前市场中价格都较贵,所以这部分我设想了一个方案供大家参考。
环境介绍:
1、 全局缓存部分采用分布式缓存技术(Terracotta+ehcache(4G版免费))
2、 分发节点充分利用设备资源,采用全局缓存设备中的一台安装软分发设备(nginx或Senginx),并采用常规分发策略(例如:权重)。
3、 应用节点独立管理,应用节点间不通信。
4、 会话ID信息结构是带全局信息,可以设计成:一位机器号+UUID。
5、 应用节点与分发节点和全局缓存节点双网卡全连通。
处理过程:
1、 外部业务请求到来后,分发节点根据一定规则随机将请求分发给一台应用节点,如果是第一次访问会在相应节点创建会话,并将会话ID返回给客户端。(如果访问的是A服务器,SESSIONID=A+UUID)
2、 这样当后续请求到来时,分发节点又会根据请求进行分发,这时可能分发到B应用节点,B应用节点通过会话ID的语义发现会话信息保存在A设备,这时他会发送一个获取请求到‘全局分布缓存节点’,该节点与A通信获取会话信息,并且将副本存储在‘全局缓存节点’,同时响应B应用节点(B节点不存储A的相应会话信息),由于会话存在,业务执行成功。(这块有个极端情况就是该业务请求还落在A节点,这是直接在本机内存验证,并还是将会话信息保存在‘全局分布缓存节点’)
3、 这样反复执行,热点会话信息会存储在‘全局缓存节点’上以提高命中率,并且也是一个副本保证不会出现单点问题。
极端情况分析:
1、 程序访问过程中,一个应用节点宕机
由于登陆后会话信息会复制到‘全局分布缓存节点’,所以即使这样,新的请求还会由其他正常节点响应,不过也会有一些极端会话信息,不在全局缓存的热点缓存范围内,这些信息应该都是不频繁访问的登陆信息,针对他们就会跳转到登陆页面,这种情况应该是可接受的。
2、 分发节点宕机
如果分发设备宕机,对于系统来说是一个比较严重的问题,相关请求将不能正常访问。这块可以考虑分发集群和分发分层,不在本次考虑范围内。
3、 全局缓存节点宕机
如果宕机的是分布式缓存非分发节点,整个系统照常运行,如果是分发节点如2所示。
全局缓存节点缓存策略:
内存作为系统高效资源,永远是有限和宝贵的,根据这个特点全局缓存节点缓存的都是访问最频繁和最新的内容。
------------------------------2015年5月6日---------------------------------
针对sojo不能做筛选转换调整原代码:ComplexBean2MapConversion.doConvert添加对注解支持。
------------------------------2015年5月7日---------------------------------
整体设计和开发基本上完成了,不过开发完后也发现了一些不足之处:
1、由于BO采用了hibernate orm ,所以对象中冗余字段较多,虽然在转换时可以通过注解将不必要的字段过滤,但是毕竟带来了不少的控制成本,且这种过滤方式是全局的不能针对业务定制。
这部分我给开发人员的建议是,如果ORM内容与前台获取内容差别不大,就直接传给前台,前台根据需要筛选。如果差距较大,就在移动服务Controller层做一下过滤转换成新的BTO再传给前台。
2、技术上request和response转换采用了不同的技术sojo和jackson,整体不是太好,并且规则定义部分也无法重用,如果要是可以尽量都用jackson实现。