转自:http://www.58maisui.com/2016/06/28/a⑶27/?ref=myread
本次分享的大纲以下:
- 传统利用开发面临的挑战
- 服务化实践
- 服务化不是银弹
- 服务化架构的演进方向
1 、传统利用开发面临的挑战
挑战1– 研发本钱高
主要体现在以下几个方面:
- 代码重复率高
在实际项目分工时,开发都是各自负责几个功能,即使开发之间存在功能堆叠,常常也会选择自己实现,而不是类库同享,主要缘由以下:
- 从技术架构角度看,传统垂直架构的特点是本地API接口调用,不存在业务的拆分和相互调用,使用到甚么功能就本地开发,非常方便,不需要过度依赖于其它功能模块;
- 从考核角度来看,同享很难推行。开发只需要对自己开发的模块交付质量负责,没有义务为他人提供并保护公共类库,这个非常耗费本钱;
- 时间依赖很难把控:对公共类库的使用者而言,依赖他人提供此功能,但是功能提供者可能有更重要的事情在做,提供时间没法满足使用者。与其坐等他人提供,还不如自己开发效力高;
跨地域、跨开发小组调和很困难,业务团队可能跨地域研发,内部通常也会分成多个开发小组,各开发小组之间的调和和沟通本钱非常高。
- 需求变更困难
代码重复率变高以后,已有功能变更或新需求加入都会非常困难,以充值缴费功能为例,不同的充值渠道开发了相同的限额保护功能,当限额保护功能产生变更以后,所有重复开发的限额保护功能都需要重新修改和测试,很容易出现修改不1致或被遗漏,致使部份渠道充值功能正常,部份存在Bug的问题,示例以下:
- 没法满足新业务快速创新和敏捷交付
挑战2– 运维效力低
在传统的MVC架构中,业务流程是由1长串本地接口或方法调用串连起来的,臃肿而冗杂,而且常常由1个人负责开发和保护。随着业务的发展和需求变化,本地代码在不断的迭代和变更,最后构成了1个个垂直的功能孤岛,只有原来的开发者才理解接口调用关系和功能需求,1旦原本的开发者离职或调到其他项目组,这些功能模块的运维就会变得非常困难:
当垂直利用愈来愈多时,连架构师都没法描写利用间的架构关系,随着业务的发展和功能膨胀,这类架构很容易产生腐化。
- 测试、部署本钱高:业务运行在1个进程中,因此系统中任何程序的改变,都需要对全部系统重新测试并部署
- 可伸缩性差:水平扩大只能基于全部系统进行扩大,没法针对某1个功能模块按需扩大
- 可靠性差:某个利用BUG,例如死循环、OOM等,会致使全部进程宕机,影响其它合设的利用
如何解决传统单体架构面临的挑战?
解决对策:1、拆分 2、解耦 3、透明 4、独立 5、分层。
- 拆分:对利用进行水平和垂直拆分,例如商品中心、计费中心、定单中心等。
- 解耦:通过服务化和定阅、发布机制对利用调用关系解耦,支持服务的自动注册和发现
- 透明:通过服务注册中心管理服务的发布和消费、调用关系
- 独立:服务可以独立打包、发布、部署、启停、扩容和升级,核心服务独立集群部署
- 分层:梳理和抽取核心利用、公共利用,作为独立的服务下沉到核心和公共能力层,逐步构成稳定的服务中心,使前端利用能更快速的响应多变的市场需求
2、服务化实践
服务的定阅发布机制
它的核心理念是实现服务消费者和服务提供者的解耦,让服务消费者能够像使用本地接口1样消费远真个服务提供者,而不需要关心服务提供者的位置信息,实现透明化调用。
关键技术点:服务的定阅、发布机制、服务的健康状态检测和高HA。
经常使用的服务注册中心有Zookeeper、ETCD,和基于数据库的配置中心。
大家在技术选型的时候,需要根据自己的业务实际情况进行选择。例如超大范围集群,服务实例数超过10W,Zookeeper就会存在性能问题。
现在开源的散布式配置服务很多,如无特殊需求,建议选择开源方案。
服务化实践-零侵入
实际上,完全的零侵入很难做到,即便是声明式配置,配置本身也是代码的1部份,只不过相比于代码类库依赖,它不是编译器依赖。
1种好的做法是,服务的发布和消费通过声明式或注解的方式,而不是直接调用服务框架的接口,例如Thrift。客户端需要调用Thrift提供的类库访问服务端,这就是代码API级的依赖,对业务代码侵入比较大。
1种比较成熟的实践是 利用Spring的扩大机制,通过XML的方式实现服务的发布和消费。
服务化实践-容错和路由
单体利用服务化以后,通常采取散布式集群的部署模式。
这会带来两个问题:
- 服务如何路由;
- 远端服务访问失败以后,如果进行容错。
大部份的容错和路由策略可以抽象到散布式服务框架中,通过策略配置的方式提供给用户使用,下降用户的开发本钱。
从业务扩大性角度看,服务框架通常会提供扩大点,供业务做路由和容错定制。例如,业务希望根据手机号码和地市进行路由:
服务化实践-本地短路策略
在电信行业中,小机还是很普遍,利用通常会合设,例如服务提供者和消费者部署到同1台主机上。
为了提升性能,下降时延,常常会提供本地短路策略,具体策略以下:
服务化实践-多样化调用方式
服务的调用方式,主要有3种:同步服务调用、异步服务调用、并行服务调用。最经常使用、简单的就是同步服务调用。
异步服务调用的工作原理以下:
详细步骤以下:
- 消费者调用服务端发布的接口,接口调用由散布式服务框架包装成动态代理,发起远程服务调用;
- 通讯框架异步发送要求消息,如果没有产生I/O异常,返回;
- 要求消息发送成功后,I/O线程构造Future对象,设置到RPC上下文中;
- 用户线程通过RPC上下文获得Future对象;
- 构造Listener对象,将其添加到Future中,用于服务端应对异步回调通知;
- 用户线程返回,不阻塞等待应对;
- 服务端返回应对消息,通讯框架负责反序列化等;
- I/O线程将应对设置到Future对象的操作结果中;
- Future对象扫描注册的监听器列表,循环调用监听器的operationComplete方法,将结果通知给监听器,监听器获得到结果以后,继续后续业务逻辑的履行,异步服务调用结束。
并行服务调用,目的是为了提升服务调用的并行度,下降E2E时延。
服务化实践-高性能、低时延
服务框架的性能,主要强调3个要素:1、I/O通讯;2、序列化框架;3、线程调用模型。
如果使用Java语言,I/O框架推荐 Netty。
序列化框架推荐:Thrift、Avro序列化框架、PB等。线程调度模型建议参考Reactor。
1种线程模型的参考实现方式:Netty的线程模型
无锁化串行设计理念
服务化实践-故障隔离
故障隔离非常重要,由于常常会采取同步服务调用模式,核心服务和非核心服务共用同1个线程池和消息队列,非核心服务处理慢常常会阻塞核心服务,致使雪崩现象。
故障隔离的核心技术点以下:
1. 支持服务部署到不同线程/线程池中
2. 核心服务和非核心服务隔离部署
服务化实践-服务治理
随着业务范围的不断扩大,小服务资源浪费等问题逐步显现,需要能够基于服务调用的性能KPI数据进行容量管理,公道分配各个服务的资源占用,提高机器的利用率。
线上业务产生故障时,需要对故障业务做服务降级、流量控制、流量迁移等,快速恢复业务。
随着开发团队的不断扩大,服务的上线愈来愈随便,乃至产生功能相同、服务名不同的服务同时上线。上线容易下线难,为了规范服务的上线和下线,在服务发布前,需要走服务预发布流程,由架构师或项目经理对需要上线的服务做发布审核,审核通过的才能够上线。
为了满足服务线下管控、保障线上高效运行,需要有1个统1的服务治理框架对服务进行统1、有效管控,保障服务的高效、健康运行。
服务治理是散布式服务框架的1个可选特性,虽然从服务开发和运行角度看它不是必须的,但是如果没有服务治理功能,散布式服务框架的服务SLA很难得到保障,服务化也很难真正实行成功。
从架构上看,散布式服务框架的服务治理分为3层:
第1层为服务治理展现层,它主要由服务治理Portal组成,提供可视化的界面,方燕服务运维人员进行治理操作。
第2层为服务治理SDK层,它主要由以下几部份组成:
- 服务治理元数据:服务治理元数据主要包括服务治理实体对象,包括服务模型、利用模型、治理组织模型、用户权限模型、数据展现模型等。元数据模型通过Data Mapper和模型扩大,向上层界面屏蔽底层服务框架的数据模型,实现展现层和服务框架的解耦,元数据也能够用于展现界面的定制扩大;
- 服务治理接口:服务治理Portal调用服务治理接口,实现服务治理。例如服务降级接口、服务流控接口、服务路由权重调剂接口、服务迁移接口等。服务接口与具体的协议无关,它通常基于散布式服务框架本身实现,可以是Restful接口,也能够是内部的私有协议;
- 服务治理客户端类库:由于服务治理服务本身通常也是基于散布式服务框架开发,因此服务治理Portal需要集成份布式服务框架的客户端类库,实现服务的自动发现和调用;
- 调用示例:客户端SDK需要提供服务治理接口的参数说明、注意事项和给出经常使用的调用示例,方便前端开发人员使用;
- 集成开发指南:服务治理SDK需要提供集成开发指南,指点使用者如何在开发环境中搭建、集成和使用服务治理SDK。
第3层为后台服务治理服务层:它通常由1组服务治理服务组成,可以单独部署,也能够与利用合设。斟酌到硬朗性,通常选择独立集群部署。治理服务的可靠性由散布式服务框架本身来保证,治理服务宕机或异常,不影响业务的正常使用。服务治理服务通常其实不随服务框架发布,治理服务是可选的插件,单独随服务治理框架交付。
服务化实践-高可靠性
关键技术点设计以下:
- 服务无状态设计
- 服务注册中心集群,宕机不影响业务运行
- 服务提供者集群,集群容错屏蔽服务提供者故障
- 服务健康状态检测,基于时延等性能KPI指标
- 服务治理中心集群,宕机不影响业务运行
- 服务级故障隔离
- 核心服务独立部署和集群
- 跨机房路由和异地容灾
3、服务化不是银弹
服务化会带来很多收益,但是它却不是银弹。
服务化不是银弹-时延问题
在服务化之前,业务通常都是本地API调用,本地方法调用性能消耗较小。服务化以后,服务提供者和消费者之间采取远程网络通讯,增加了额外的性能消耗。
服务化不是银弹-问题定位
在散布式环境下,如何高效的进行问题定界定位和日志检索
服务化不是银弹-事务1致性
服务化、散布式部署以后,有逻辑关联关系的多个数据库操作被打散到集群中各个独立的服务实例中,引入散布式环境下的事务1致性问题。
服务化不是银弹-前后台直接通讯问题
前后台直接通讯问题以下:
存在的问题以下:
- 客户端需求和每一个微服务暴露的细粒度API不匹配
- 微服务使用的RPC私有协议,不是阅读器友好或防火墙友好的
- 微服务难以重构。随着时间推移,我们可能想要更改系统划分成服务的方式。如果客户端与微服务直接通讯,那末履行这类重构就非常困难了
服务化不是银弹-团队协作问题
- 同享服务注册中心问题:为了方便开发测试,常常会在线下共用1个所有服务同享的服务注册中心,这时候,1个正在开发中的服务发布到服务注册中心,可能会致使1些消费者不可用。
- 多团队进度协同问题:服务提供者和消费者相互依赖问题,开发依赖、测试依赖等。
- 接口前向兼容性问题:由于线上的Bug修复、内部重构和需求变更,服务提供者会常常修改内部实现,包括但不限于:接口参数变化、参数字段变化、业务逻辑变化和数据表结构变化。在实际项目中常常会产生服务提供者修改了接口或数据结构,但是并没有及时知会到所有消费者,致使服务调用失败
4、未来演进方向-微服务架构
微服务的划分原则是难点,根据华为的经验:微服务划分不是1步到位,而是不断的迭代和演进,终究找到合适自己团队和业务的微服务划分原则。
未来演进方向-基于Docker部署微服务
使用Docker部署微服务的优点总结:
- 1致的环境:线上线下环境1致
- 避免对特定云基础设施提供商的依赖
- 下降运维团队负担
- 高性能:接近裸机的性能
- 多租户
未来演进方向-云端微服务
利用云平台的弹性资源调度,动态性等,可以实现微服务的Dev&Ops
最后我们1起回顾下服务化的演进历程:
Q&A
Q1:上面提到服务化缺点的第3条接口变更问题,请问微服务是如何解决这个问题的呢?或说微服务相比之下甚么优势会避免这个问题?
A1:根据我们团队的经验,主要从以下几个方面下降影响:1、微服务的接口就是契约,制定 接口兼容性规范;触及到技术和管理两个层面;2、微服务鼓励只做1件事情,因此它更加稳定;3、基于消费者契约测试,快速发现兼容性问题。
Q2:微服务架构里,散布式事务如何做的,对数据1致性要求较高的系统是不是合适拆分成微服务,或说微服务的粒度如何掌控?
A2:散布式事务是难点,策略以下:1)如果业务上能够承受非强1致性,建议通过事务补偿的方式做终究1致性,可以基于MQ等中间件来实现;2)如果是转账、实时计费、充值等对实时性要求高的,常常选择强1致性事务,就需要引入TCC等散布式事务框架。不管如何,只要做散布式,事务1致性就会成为问题,跟是不是是微服务没必定关系。
Q3:生产环境中的服务注册中心必定是同享的,那如何去做灰度发布或A/B Test呢?
A3:1种比较好的服务灰度策略是:1)服务框架提供灰度规则框架,包括后台引擎和前台Portal,由业务配置灰度规则;2)散布式服务框架支持灰度规则推送和业务自定义路由;3)前端SLB ,例如Ngix做灰度插件,接收灰度规则。消息从前端门户接入到后端服务路由,都支持基于规则的路由分发策略,实现灰度发布。
Q4:Netty的无锁化串行会比有锁的并行性能更高吗?有案例吗?华为现在都是用Docker部署利用吗?
A4:Netty的无锁化串行性能问题:1)在实际项目中,线程池争用模式和串行模式我们都使用过,Netty的无锁化串行模式性能更高。Docker部署利用:华为的公有云和私有云都支持基于Docker部署利用,由客户根据需要自主选择。
Q5:IO通讯是怎样保证每次连接成功的呢?
A5:NIO通讯本身其实不保证每次连接都成功,它的连接是异步的,你可以根据以下两种策略取得异步链接的结果:1)发起连接以后主动调用同步方法等待结果返回,阻塞式;2)获得异步连接Future,添加Listener监听器监听连接结果,这类模式是异步回调,不会阻塞当前线程。
Q6:使用zk作为服务注册中心,对与某个服务当客户端连接数很多时候节点变化会引发羊群效应,怎样处理这类问题呢?或说如何避免这类问题呢?
A6:这个问题真是好!通常而言,大家会使用服务注册中心做服务可用性检测,如果发现某个服务节点不可用,就会将其从注册中心中删除。但是,有1种场景是ZK检测的结果跟客户端和服务端实际的连接状态不1致。从ZK看,服务提供者可使用。但是由于服务消费者跟提供者之间的链路已中断,跟ZK的链路却是正常,这类情况下就会出现状态不1致问题。所以,只依托ZK做状态检测还不够,需要服务提供者和消费者的链路层做双向心跳检测。
Q7:我现在做的系统是zk做注册中心服务把地址注册上去(临时节点),客户端拿地址要求,http的,现在发现如果是公网调用的话,对公网资源要求还挺多的,zk公网, 利用公网;为了减少对公网需求,中间加1层nginx,把nx地址注册上去,不过又得加个http探测监控程序,异常还得删掉注册数据,不知道这类做法是不是妥当?
A7:Ng监听ZK注册的服务提供者URL便可,问题不大。
Q8:用Netty做同通讯框架,监控上报应当怎样设计更完善?
A8:建议的方式以下:Netty本身不用告警,监听Netty的异常事件,然后通过MQ吐出去,监控系统定阅通讯框架的事件主题,实现通讯框架和监控系统解耦。
Q9:SOA和微服务架构的区分和联系是?看起来好像啊!
A9:1) 服务拆分粒度:SOA首先要解决的是异构利用的服务化;微服务强调的是服务拆分尽量小,最好是独立的原子服务;
2) 服务依赖:传统的SOA服务,由于需要重用已有的资产,存在大量的服务间依赖;微服务的设计理念是服务自治、功能单1独立,避免依赖其它服务产生耦合,耦合会带来更高的复杂度;
3) 服务范围:传统SOA服务粒度比较大,多数会采取将多个服务合并打成war包的方案,因此服务实例数比较有限;微服务强调尽量拆分,同时很多服务会独立部署,这将致使服务范围急剧膨胀,对服务治理和运维带来新的挑战;
4) 架构差异:微服务化以后,服务数量的激增会引发架构质量属性的变化,例如企业集成总线ESB(实总线)逐步被P2P的虚拟总线替换;为了保证高性能、低时延,需要高性能的散布式服务框架保证微服务架构的实行;
5) 服务治理:传统基于SOA Governance的静态治理转型为服务运行态微治理、实时生效;
6) 敏捷交付:服务由小研发团队负责微服务设计、开发、测试、部署、线上治理、灰度发布和下线,运维全部生命周期支持,实现真实的DevOps。
总结:量变引发质变,这就是微服务架构和SOA 服务化架构的最大差异。
Q10:如果要将现有单机服务重构到微服务,应当斟酌哪些问题?数据迁移的安全问题怎样解决?有甚么实践方案吗?
A10:需要斟酌的问题以下:1)当前单机利用是不是能够满足业务发展需要,有无必要做服务化改造和散布式部署;2)评估迁移的工作量,和人员技能培训等。3)自研服务框架还是使用开源的方案。
数据迁移安全问题:如果内网,通常不会触及到复杂的安全控制问题;如果跨公网,建议加入API Gateway统1做安全管控。
实践方案:公然的资料,可以参考淘宝的服务化实践、京东的服务化实践等。其实华为也有,不过遗憾的是目前政策不允许公然出来。
Q11:麻烦李老师介绍下你们华为内部基于netty做socke通讯的协议设计的最好实践。
A11:这个问题很大,简单介绍下思路。在11年和13年的时候我分别主持设计了华为基于Mina和Netty的统1NIO通讯框架。设计要点以下:1)要熟习Netty的线程调度模型、经常使用的类库等,能够熟练使用Netty;2)NIO通讯框架的分层原则,哪些该做、哪些不该做,需要辨认出来;3)扩大点,预留足够的扩大点给上层利用协议栈做扩大;4)可之内置配置化的安全策略、握手认证、心跳检测等机制;5)可服务性设计,包括日志、性能KPI指标等。
作者介绍 李林锋
- 从事华为软件PaaS平台的架构设计和开发工作,8年多NIO、平台中间件领域设计、开发和运维经验,精通NIO通讯框架、散布式服务框架、PaaS平台等;
- 参与设计和开发某网关平台;
- 曾取得公司总裁技术创新奖;
- 《散布式服务框架原理与实践》作者。