返回首页
当前位置: 主页 > 编程语言 > JAVA教程 >

服务框架演变过程概述

时间:2015-01-12 22:47来源:电脑教程学习网 www.etwiki.cn 编辑:admin

我们的服务框架已经持续做了三年了,在厂内广泛的使用,目前部署在服务框架上的服务为2k+,每天经过服务框架的服务执行次数为120亿+,摸高到150亿+,三年的发展并非一帆风顺,由于经验的原因,还是摔了不少跤的,在这篇blog中,来给大家分享下,希望能够给要做服务框架或服务化的同学带来一些帮助,少走一些弯路,不一定要一开始就做成完整的服务框架,但至少先做好铺垫,避免在广泛使用后再来挽救。

我们的服务框架主要由四个部分的功能组成:
1、标准的服务的交互方式
2、高性能网络通信
3、软件负载均衡
4、服务治理
这四个部分都经历了一些演变,才形成了最后的结构,尤其是服务治理这块,在各种SOA的文章中,都会说到服务治理这块,但基本都不会说服务治理到底要做什么,我们是在经历过很多后,终于让服务治理这块由一堆实际的功能组成,下面分别来看看以上这四块的演变过程。

标准的服务的交互方式
在服务框架的第一个版本中,最早我们希望对应用完全不侵入,做到应用不需要依赖服务框架的任何包,也不需要在应用里面定义服务,因此策略是在应用的外部写一个xml文件,用来描述对外提供的服务,或者要调用的服务,这个版本提供给应用方使用后,发现使用起来非常复杂,因为这个xml文件和应用是不在一起的,一方面不知道该放在代码仓库的什么地方;另一方面对于部署维护而言也是非常的麻烦。
于是决定改进这块,厂内应用基本都是基于spring的,因此还是决定提供一个类来方便直接在spring bean的xml中发布服务和配置调用服务的代理bean,在这样的机制下发布服务和调用服务的配置大致就如下了:
<bean class="发布服务的类" or class="调用服务的proxy类">


在改造成这种方式后,应用方使用起来就比较透明和方便了,并且也只是在运行时对服务框架会产生依赖,后来也就基本没变过了,兄弟厂有改造成<...:service>这样的方式的,用起来更简单一点,不过需要加schema,所以我们这边还是没去这么做。
在服务的定义上碰到的主要需求是最早我们只支持一种通信协议以及只支持接口级的超时配置,后续随着需求调整了一次服务的定义,增加了方法级超时的配置以及通信协议的配置。

高性能网络通信
在第一个版本中,我们选择了基于JBoss Remoting来实现,在最初一个访问量不大的应用中上线时表现挺正常的,可惜的是在后面一个访问量较大的应用上线后出现了问题,最后查证原因主要是出在了超时设置上,当时采用了默认的60秒超时,刚好当时提供服务的应用出现了处理慢的现象,导致前端的web应用被拖的支撑不住,而在当时的JBoss Remoting版本中,其实这个超时设置是有bug的,因此如果要修复就必须直接修改JBoss Remoting的代码。
另外一个问题是JBoss Remoting的连接池方式,在通过硬件负载设备访问提供服务的集群的情况下,一旦重启,会很容易出现连接严重不均衡的现象。
鉴于上面的现象,觉得还是自己掌握整个通信过程比较靠谱,于是选择了基于Mina来实现整个网络通信,自行实现异步转同步、超时、连接管理,连接的使用采用了每目标地址单个连接的方式,这样一方面是可以避免连接池造成的连接不均衡的问题,另一方面也避免调用端建立太多连接,导致服务提供者连接会不够用,伸缩性差的问题,另外,由于都是内网的请求,因此采用长连接方式。
基本上一直以来都没对上面的网络通信机制做过调整,不过单连接在序列化/反序列化对象消耗时间较长时,会影响到其他的请求或响应,这个一方面是由于Mina的实现机制,另一方面是要结合应用场景来做适当的处理。
在序列化上我们支持了默认的Java和Hessian两种,但杯具的是当时的Hessian版本较老,并且Hessian新版本与旧版本的兼容做的很差,导致后来我们一直很难升级Hessian的版本。
在网络通信上,我们经历过的教训主要是最早的通信协议上没带版本号,导致升级时的兼容性比较难处理;还有就是在设计之初我们是不考虑用于跨语言场景的,但后面跨语言的场景出现了,于是就很被动了;还有一点是如果发送的对象过大或过多造成内存不够的现象,最早的时候没做限制。

软件负载均衡
在最初的几个版本中,我们都是通过硬件负载设备来访问服务提供者集群的,但有一次出现过硬件负载设备出故障的现象,导致应用出现故障,但又没办法修复,只能等待硬件负载设备问题的解决,在这种情况下,我们觉得随着服务越来越多,请求量越来越大,中间的硬件负载设备很容易成为最大的风险,于是决定自己做软件负载均衡。
我们对于软件负载均衡最重要的一点要求是,当服务调用者调用服务时,是直接和服务提供者交互的,不需要通过任何中间的机器,当时考察了下现有的,觉得只能自己做了,于是我们就基于这个要求实现了自己的软件负载均衡,不过我们实现软件负载均衡的方法随着机器数越来越多,也碰到了不少的挑战,由于这个涉及实现机制,就不在这里细说了。
最早在选址上我们仅支持随机选择,但由于集群中会出现机器配置相差比较远的现象,因此决定加入权重的支持,以便分配不同的流量。
一个应用通常会对外提供多种不同功能的服务,这些服务对资源的消耗以及对整体系统的重要性是不一样的,而服务框架对于所有服务的执行都是放在同一线程池中的,因此会出现某些不重要又耗资源或执行慢的服务把线程池占满,导致重要的服务无法执行的现象,对于这种现象,我们提供了两种方案来进行解决:一是分线程池,二是按接口将集群再进行划分。
分线程池的方法有些时候仍然是不够的,例如耗资源或执行慢的服务有可能同时把共享的资源消耗完了,那这个时候即使分线程池也仍然会导致重要的服务出问题的现象。
按接口将集群再进行划分的好处就很明显了,在不改变代码结构的基础上,在软件负载均衡上实现按接口的路由即可,于是我们实现了这个机制。
随着按接口路由实现后,又碰到了一个接口中不同的方法的重要性、执行速度以及消耗资源不同的问题,于是相继我们又支持了按方法以及按参数的路由,这个时候我们的不经过中间节点调用的软件负载均衡机制发挥出了巨大的优势,如果是硬件负载设备,一方面是没办法做到这样的7层路由,另一方面硬件负载设备一旦开启7层路由,性能下降会非常明显,而在我们的软件负载均衡实现机制下,则完全不会有这样的问题。

顶一下
(0)
0%
踩一下
(0)
0%
标签(Tag):服务框架演变过程
------分隔线----------------------------
------分隔线----------------------------
发表评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
验证码:点击我更换图片
推荐内容