服务框架演变过程

标签: 产品总结 SOA 服务框架 服务治理 网络通信 | 发表时间:2011-01-28 14:22 | 作者:bluedavy rockmaple
出处:http://blog.bluedavy.com

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

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

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

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

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

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

服务治理
最早的时候,我们压根就不知道服务治理到底该做些什么,基本都是随着使用者的增多才逐渐形成的。
由于在调用服务时,不需要配置调用服务的目标地址,有些时候出错了,连调的是哪台服务器出现的错误都不知道,因此在初期的时候经常有使用者会来问,调的服务到底是哪台机器呢,于是一方面我们是在执行出错的日志里增加了调用的目标地址的说明,另一方面就是做了一个简单的系统,用于查询服务的调用者和提供者在什么机器上。
当功能需要由调用服务来完成时,排错成为了大问题,在这个问题上我们现在还是支持的不够,现在的查错仍然是人肉较多,并且要判断问题出在哪里会比较复杂,尤其是出现A –> B服务 –> C服务这种时候。
随着服务越来越多,当服务由于某些原因要废弃方法或做重大改造时,需要通知相关的服务提供者,以便做相应的调整和测试等,但最早服务框架在这方面没提供什么支持,因此服务的提供者只能通过邮件来进行调查,但这样非常容易出现遗漏等,出现过好几次问题,于是开始做服务的依赖关系分析。
除了上面升级产生的问题外,还有一个问题是依赖的关系越来越复杂,导致系统的稳定性很差,因此需要管理起依赖,也就是最好服务的调用需要授权,这个也是服务框架初期的版本中没有考虑到的,于是只能进行改造来提供支持。
使用服务框架的应用越来越多,开始出现了使用到的服务框架多个版本的现象,而最开始我们没有办法知道有哪些版本在使用,这种情况的杯具就是当我们发现某个版本出现致命bug时,根本不知道该通知谁升级,另外在解决问题的时候也非常麻烦。
在软件负载均衡实现上,我们支持了按接口、方法和参数对集群进行划分,这个时候就造成了在加减机器的时候必须明确的知道是要操作哪部分的机器,这块如果支持不够就会造成机器的维护非常麻烦,这还是我们在继续努力的一块。
在系统出现问题时,有些时候查找和修复需要花费较长的时间,因此如果能够通过路由的调整来隔离故障,那么对于系统的稳定性可以带来很大的帮助,于是我们实现了服务的降级,可以在运行时屏蔽某些调用者对于某些服务的某些方法的调用。
在这样的情况下,形成了我们目前的服务治理:服务信息查询、依赖分析和管理、服务调用/执行报表、服务框架版本分布、服务路由调整以及服务降级,但即使是这样,也仍然是不够的,还需继续努力。

从上面四个大方面的演变过程可以看到,大多时候是因为需求的推动发展,并且可以看到有些事情,由于一开始没做,导致了后面花了非常大的精力去挽救,因此如果能再来一次的话,我觉得可以在一开始就设计的更为周到,但不是说一开始要做的多完整,而是要埋好伏笔,避免后面不断的调整系统的设计,也希望上面这些演进过程的分享可以让你至少少走一次弯路,那我就觉得很开心了。

bonus:
除了上面围绕这四大块的演变外,在服务框架的整个发展过程中,我们还有一些其他的教训:
1、服务的上线和下线
其实很多情况下,我们是希望应用在更新前先下线服务,避免出现一些事务等问题,而在上线时最好是能做到逐步放入流量,这样可以让应用完成热身后再来接受大的请求量,避免出现应用还没进入状态时请求量大造成无法支撑的现象。
2、外部定制化支持不够
例如如果应用想介入调用过程或执行过程,做下特殊处理时,我们暂时是不支持的。
3、日志打爆
例如当应用出现问题时,我们没有对错误的日志做限制,导致有些时候会把最后一根救命稻草给砍断,后来我们才增加了日志输出时的流量控制。

相关 [服务 框架] 推荐:

微服务框架-基础框架

- - 人月神话的BLOG
上面一篇文章对SpringBoot框架做了一下简单验证,在文中也写到SpringBoot重点还是在单个微服务模块的开发,已经对于微服务接口开放的便捷性上,而对于微服务基础架构和管控治理层面没有太多支持. 对于微服务基础框架可以看作是微服务治理架构的核心内容,包括了对微服务模块的全生命周期管理能力,这个能力包括了微服务网关APP,DevOps,Docker和云集成,安全,负载均衡,服务注册和发现等诸多能力.

谈Dubbo服务框架

- - 人月神话的BLOG
Dubbo 是阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和 Spring框架无缝集成. 它最大的特点是按照分层的方式来架构,使用这种方式可以使各个层之间解耦合(或者最大限度地松耦合). 从服务模型的角度来看,Dubbo采用的是一种非常简单的模型,要么是提供方提供服务,要么是消费方消费服务,所以基于这一点可以抽象出服务提供方(Provider)和服务消费方(Consumer)两个角色.

服务框架演变过程

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

Tim:服务管理框架的尝试

- Shengbin - python.cn(jobs, news)
大型软件系统开发需要模块化,在分布式系统中,模块化通常是将功能分成不同的远程服务(RPC)来实现. 比如可以用Java RMI、Web Service、Facebook开源的Thrift等一些技术. 同样,在一个大型系统中,服务化之后服务的可维护、可管理、可监控以及高可用、负载均衡等因素同服务本身同样重要.

服务管理框架的尝试

- mk - Tim[后端技术]
大型软件系统开发需要模块化,在分布式系统中,模块化通常是将功能分成不同的远程服务(RPC)来实现. 比如可以用Java RMI、Web Service、Facebook开源的Thrift等一些技术. 同样,在一个大型系统中,服务化之后服务的可维护、可管理、可监控以及高可用、负载均衡等因素同服务本身同样重要.

分布式服务框架:Zookeeper

- - 标点符
Zookeeper是一个高性能,分布式的,开源分布式应用协调服务. 它提供了简单原始的功能,分布式应用可以基于它实现更高级的服务,比如同步,配置管理,集群管理,名空间. 它被设计为易于编程,使用文件系统目录树作为数据模型. 服务端跑在java上,提供java和C的客户端API. Zookeeper是Google的Chubby一个开源的实现,是高有效和可靠的协同工作系统,Zookeeper能够用来leader选举,配置信息维护等,在一个分布式的环境中,需要一个Master实例或存储一些配置信息,确保文件写入的一致性等.

微服务框架和工具大全

- - IT瘾-geek
引言:不去重新发明轮子总是更好的. 本文探讨了14个已经可用并能提供使微服务的开发和部署更容易的平台、框架和功能. 本文还补充了每个工具将如何有助于建立良好的微服务架构的简要概述.   在《Java微服务》一书中,我们使用 Spring Cloud,它提供使微服务非常容易地开发所需的所有工具和平台.

服务化框架技术选型

- - 企业架构 - ITeye博客
基本的服务化框架包括如下模块:统一的RPC框架,服务注册中心,管理平台. 有了这三个模块,就能实现基本的服务化. 为什么一定要是统一的RPC框架,而不是随便啥框架,这里主要是为了技术对齐,减少开发人员的学习成本,减少团队间沟通成本. 好,那么选择一个RPC框架,我们都需要考量什么东西呢. 代码规范:例如是对已有代码透明,还是代码生成;.

服务间通信之Http框架

- - CSDN博客推荐文章
2.jersey代理连接池. 1.1 java原生HttpURLConnection. 这个用的不多,在正式项目中几乎没有用过,写一些小的demo的时候偶尔用过,使用的原因更多是当时懒得再引入其他第三方的http框架了,用法如下:. 不多说,用法还是挺复杂的,简单的请求甚至需要数十行才能完成,并且不易于理解,当时写完调试了半天才通,挺后悔用原生的HttpURLConnection来进行当时功能的测试,感觉比我当时写的一个http-server还要麻烦,在正式的项目中一般不会用到原生http请求类.