技术开发 频道

服务化架构的演进与实践

  【IT168 资讯】嘉宾介绍:

  李林锋,现任华为PaaS平台架构师,8年Java NIO通信框架、平台中渐渐架构设计和开发经验,主导设计和开发的华为分布式服务框架已经在全球数十个国家成功商用。精通Netty、Mina、RPC框架、企业ESB总线、分布式服务框架等技术,《分布式服务框架原理与实践》作者,公司综采技术创新奖获得者。

  正文:

  大家晚上好,首先自我介绍下。我是李林锋,目前在华为从事PaaS平台架构设计,《Netty权威指南》和《分布式服务框架原理与实践》作者。今晚给大家分享的话题是服务化架构的演进与实践。

服务化架构的演进与实践

  大家可以通过PPT中的联系方式联系我,也可以扫码关注Netty之家公众号。我们首先看下今晚话题的大纲。

服务化架构的演进与实践

  在做服务化之前,我们首先看下传统应用架构存在的问题,然后看下如何实施服务化改造,最后看下服务化架构的演进趋势。

服务化架构的演进与实践

  第一个就是成本问题,我记得08年做过一个项目,本质是个后端的处理系统,但是局限于当时的架构,我们采用的是MVC的架构。

  项目组被分成若干个小组,各负责一块儿,前端Tomcat HTTP+XML接入,后端自己写代码处理业务逻辑。

  项目研发后期之后,公司引入了CI构建,要做代码重复性检查,发现我们的整个项目代码重复率为 25%。

  什么原因呢? 本质上Tomcat仅仅是我们的WEB运行容器,里面的很多逻辑都是本地的API调用,大家各自写自己的,跨小组协同是非常困难的,导致功能相同的接口被反复开发。

服务化架构的演进与实践

  第二个就是运维效率问题

  1、随着项目的发展,很多功能打包成一个war包,一个人的代码编译或者功能出错,需要重新归档和回归测试,交付效率越来越低

  2、核心业务逻辑和非核心的混在一起,一旦非核心的业务发生OOM或者死循环,会导致核心业务也不能正常运行,可靠性差

  3、对于很多新加入团队的新员工,学习和适应成本都很高:本地代码在不断的迭代和变更,最后形成了一个个垂直的功能孤岛,只有原来的开发者才理解接口调用关系和功能需求,新加入人员或者团队其它人员很难理解和维护这些代码。

  还有就是依赖管理,尽管现在Maven工程、OSGi等可以管理接口和模块依赖,但是都存在一些缺点,在此不具体展开,感兴趣的可以参考 《分布式服务框架原理与实践》中的服务发布和引用、分布式消息跟踪章节,了解更多细节。

服务化架构的演进与实践

  回归了问题,我们再看下解决策略。

  1、拆分:由大及小,分而治之。目前常用的拆分策略有水平拆分和垂直拆分两种。

  2、解耦:通过服务化和订阅、发布机制对应用调用关系解耦,支持服务的自动注册和发现。本质就是 透明化路由,消费者只需要知道我依赖什么服务即可,不关心服务的具体位置信息,双方只有接口契约关系。

  3、独立原则:服务可以独立打包、发布、部署、启停、扩容和升级,核心服务独立集群部署。这个原则实际就是 微服务架构倡导的原则:微服务可以独立的部署、升级和运维,实现一定程度的自治。关于微服务的部署和开发,今晚不重点展开介绍,感兴趣的可以参考服务框架一书中的20章节,专门介绍微服务。

  4、分层:梳理和抽取核心应用、公共应用,作为独立的服务下沉到核心和公共能力层,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。无论我们是做互联网,还是其它IT行业,都有一个共性。经常发生变更的总是集中在20%左右的功能上,而变更频繁的就是前端展示Portal和元数据映射、编排。后端的接口和能力通常是比较稳定的。核心应用、公共应用,作为独立的服务下沉到核心和公共能力层,逐渐形成稳定的服务中心,独立集群和部署。前后端分离,使前端可以更灵活的适配业务需求的变更。

服务化架构的演进与实践

  服务的订阅-发布机制:

  1、根据服务提供者的配置信息,注册服务提供者地址信息到注册中心;

  2、消费者根据消费的服务名,关注发布某个特定服务的提供者列表;

  3、有地址变更,服务注册中心将变更内容推送到消费者,消费者更新本地的缓存表。

  服务注册中心可以采用Zookeeper,如果要管理的服务信息量非常大,也可以考虑使用其他的技术,例如基于数据库、ETCD等。

服务化架构的演进与实践

  在服务化实践过程中,我们强调对业务代码的侵入尽量低。

  传统的RPC框架,例如Facebooke的Thrift,Hadoop的Avro,Messagepack等都需要使用它们的IDL/或者接口,一旦业务切换RPC框架,就需要涉及大量的代码变更。

  对业务侵入的强弱,也是技术选型的一个关键。

  大家可以看下PPT的示例,服务提供者实际上只需要声明自己发布的服务接口,即可实现服务发布,平台对业务接口没有直接侵入,消费者也可以直接使用业务的接口,不需要依赖平台的类库。

  这实际就是依赖的解耦,阿里的Dubbo基本上做到了,但是HSF对应用还是有侵入。感兴趣的朋友可以对比下Dubbo和HSF的服务开发和配置。

服务化架构的演进与实践

  我们首先谈下路由。

  对于常用的路由策略,例如轮循、随机等通常是内置的策略。除了上面常用的策略,通常也要支持基于负载的加权路由、基于应用自定义的路由扩展。因此,路由接口通常需要提供开放性,给业务扩展或者重载。

  我们再谈下,集群容错。

  首先,我们看下为什么要容错。服务化之前,接口调用时本地JVM调用,不需要走网络,不需要序列化,不需要走路由。服务化之后,新增了:1、序列化;2、网络;3、路由;4、服务提供者反射调用.....任何一个环节失败,都会导致本次服务调用失败。

  通常,底层的路由失败、网络失败、服务提供者没有应答等都需要由服务框架统一屏蔽,不需要每个业务开发者自己实现重试、二次路由等。因此,分布式服务框架需要提供 集群容错功能

  常用的容错策略包括:1、自动切换到下一个可用的服务(FailOver) 2、失败重试; 3、快速失败; 4、失败回调等。

  实际上,集群容错也有很多陷阱,拿FailOver为例,如果很不幸,连续尝试N个服务最后终于成功,如果原来的业务超时是 10S,尝试N次之后,实际耗时为10N,如果N大于10,整个服务调用就达到了100S,显然应用无法承受。

  因此,我们需要对失败切换的总次数和间隔,以及超时时间做限制,防止出现这种前端已经超时了,后端还在拼命找可用的服务。

服务化架构的演进与实践

  路由的一种特例,就是本地服务调用短路。

  它的原理是:在一些场景下,服务提供者和消费者可能合设,合设存在两种场景:

  1、JVM内部合设,实际就是jar/war合设。2、主机或者VM虚拟机内部合设,两个用户,或者共用1个用户,两套软件。在上述场景中,往往会采用本地路由短路策略:优先调用本JVM内部服务提供者、其次是相同主机或者VM的、最后是跨网络调用。

  本JVM内部调用,可以不走序列化和网络,直接API调用。本主机内部跨进程短路调用,可以不走网络。提升了可靠性、性能,降低了时延,在实际中,业务经常会使用该策略。

服务化架构的演进与实践

  介绍完路由之后,我们再一起看下服务调用的形式。

  常用的调用策略有三种:1、同步调用 2、异步调用 3、并行调用。

服务化架构的演进与实践

  同步调用很简单,就是模拟了本地API接口调用模式,调用接口,等待应答,然后返回。它的最大缺点就是当服务提供者处理比较慢时,会挂住客户端的业务线程。直到服务端返回应答或者客户端超时。

服务化架构的演进与实践

  异步调用:

  异步调用的最大优点就是 不会阻塞业务线程,直接返回一个RPC上下文对象给消费者。

  消费者拿到Future对象之后,可以采用两种策略获得真实的应答结果:

  1、同步等待,设置一个超时时间,这个效果与同步调用差不多,最终还是把业务线程挂住了;

  2、Future-Listener机制。注册一个监听器,当获取到应答之后,会调业务注册的监听器,然后从回调上下文中获取结果,继续业务逻辑执行

服务化架构的演进与实践

  并行调用:

  并行服务调用的特点是将一系列逻辑上没有上下文依赖,天生就支持并行处理的服务做批量调用,降低业务的时延。

  例如手机开卡服务业务,当开卡成功之后,会同时发送通知短信、号卡资源中心等,这些操作逻辑上无依赖关系,就可以做并行调用。

  接下来,我们看下性能问题。很多人认为在分布式架构体系下,单点的性能已经不重要,无足轻重,其实这种观点是片面的。

  分布式服务框架对性能的诉求主要体现在两点:

  1、高并发、低时延。2、在同等情况下,单点性能提升的越多,在大规模集群组网情况下,对硬件降成本越有利。

  我们不盲目追求单点的绝对高性能,但是高性能、低时延是无法回避的现实。

服务化架构的演进与实践

  影响性能的三要素:

  1、I/O模型选择;2、序列化和反序列化CodeC框架;3、线程池模型。

  I/O的选择,建议使用Netty。

  序列化和反序列化,通常情况下建议选择性能更优的PB\Thrift、Avro等。

  线程池模型,前端NIO模型由Netty搞定,后端建议自研性能更高的线程调度机制,不必 非要选择JDK的线程池。

  谈到了线程池模型,下面我们从Netty的线程模型中汲取一点营养,借鉴下别人的处理策略:

服务化架构的演进与实践

  1. 无锁化串行设计: 避免ChannelHandler被并发调用,加锁会降低性能。实际上,就是在一个完整业务处理过程中,尽可能减少线程的切换和锁竞争。

  2. 单线程线程池模型,性能更优:1个线程1个队列。避免JDK线程池的多线程-单阻塞队列导致的激烈锁竞争。避免传统JDK线程N个线程争用要给队列的问题,线程池配置越大,争用越激烈,性能越低。

服务化架构的演进与实践

  下面我们看下服务的故障隔离。

  实现服务故障隔离的关键技术点:

  1、支持服务部署到不同线程/线程池中。2、识别核心服务和非核心服务,核心服务和非核心服务隔离部署。

  在这里需要强调下,JVM内部的服务故障隔离不能解决所有的问题。例如非核心服务的OOM、线程死循环等,依然可能会直接影响JVM本身,从而间接影响核心服务。要做到完全的服务故障隔离,需要支持不同维度的隔离策略。

  讲完功能和性能之后,我们看下服务上线之后要做的运维工作-服务治理。

服务化架构的演进与实践

  常用的服务治理措施包括:

  1、在线运行期治理:限流降级、超时控制、路由策略调整、权重调整、黑白名单、业务放通、流量迁移、服务性能KPI统计等。

  2、服务线下治理:服务上线审批、服务规划、服务质量评估、服务SLA制定、服务文档库、服务权限管理等。

  服务治理比较复杂,与每个服务框架的具体实现也有很大关系。服务治理能力的强弱,对SLA的满足度,是客户满意度的重要指标,因此非常重要。

  服务化之后,服务框架自身的可靠性非常重要。下面我们一起看下服务框架的可靠性设计。

服务化架构的演进与实践

  包含的信息量很大,在此不详细赘述,大家自己看下。最后我们一起来展望下未来服务化架构的演进方向。

  微服务架构:

服务化架构的演进与实践

  演进实例和非常好的实践:基于Docker部署微服务。

服务化架构的演进与实践

  云上的微服务:

服务化架构的演进与实践

  最后大家一起回顾下服务化架构演进的历程。

服务化架构的演进与实践

  上面介绍了很多服务化架构设计理念和实践原则,但是 如果需要完整了解服务化架构的架设原理,学习更多的相关知识,可以参考如下学习书籍:

服务化架构的演进与实践

0
相关文章