应用架构发展
这里的架构演进应该是从服务的角度。应该说,随着业务的发展和应用规模的扩大,系统的一些公共服务会被抽取出来,独立开发、部署和维护,以解决并发、扩展和维护的问题。
传统垂直架构
有些地方也叫单体应用,用mvc模式开发:
所有应用代码统一打包,代码所有接口本地api调用,很少存在远程服务调用;单机或主备,应用做集群部署;DB主从等。
这没什么不好。发展初期多是这种情况。没那么大,也不需要考虑高并发大流量的可扩展性。简单粗暴。解决业务需求就行了,住着也能过得更好。
但是我们必须了解这个简单架构的一些问题:
1.随着业务的不断发展,功能逐渐增多,应用的开发和维护成本变高,部署效率下降。随便改个代码,编译一次就要十几分钟。悲剧,我们有一个系统才开发了3年,就发生这种情况。一旦打包、编译和部署,13分钟就结束了。
2.不同的人负责不同的部分。有些常用代码和常用代码是分开写的,不能重复使用。如果只有util没问题,但是一些外部服务是重复的,那就开心了(不过这种情况的发生不一定是架构问题,更可能是管理问题);
3.不断的提新需求,不断的改代码,有时候测试不到位,指定哪里埋了bug,系统投产后就垮了,牵一发而动全身;
4.可维护性、可靠性和可扩展性变差。
既然有这些问题,我们就要去解决,商家也会提出要求。你得解决他们,不然会影响我的业务发展和ipo上市,辛苦的编码员就要开始工作了。
我们不提那些拆分应用的方法,但是从拆分后的应用交互来看,原来的本地api交互变成了远程api的调用,rpc就出现在这里。当然也用e***和webservice。其实拆分之后挺麻烦的,随便一个分布式事务就能把人弄死。
RPC体系结构
远程过程调用,远程方法调用,屏蔽底层实现细节,像调用本地方法一样调用远程服务。
上一个作者的图片:
这个图表对于大多数rpc框架来说是通用的,并且实现了几个技术要点:
1.服务提供者发布服务:服务接口定义、数据结构、服务提供者信息等。
2.客户端远程调用:通常被jdk的代码代理拦截;
3.底层通讯:netty现在应该用的比较多,当然也有支持http的;
4.序列化:关注反序列化的性能,如xml、json、hessiaon、pb、protostuff、kryo等。
作者给出了一个简单的socket实现实现远程调用的演示,并对以上技术点进行了说明。
常用的rpc框架
1.节俭;
2.Hadoop的avro-RPC;
3.黑森;
4.gRPC
单说rpc就不多说了,但是如果加上服务治理,复杂度会呈几何级增长。服务治理的东西太多了,动态注册、动态发现、服务管控、调用链分析等等。这些问题单靠rpc框架是解决不了的,所以现在常用的服务框架通常指rpc+服务治理两点。
SOA服务架构
Soa架构应该出现在rpc之前,解决异构系统之间的交互。通常,它是通过ESB和WSDL实现的。一般来说,它的粒度比较粗。服务治理也存在问题。
微服务
MSA也是面向服务的架构风格,ing和服务划分比较流行。
1.细粒度的原子服务;
2.独立部署,主要是容器;
分享一篇文章:云起胖子关于微服务的文章。
MSA和SOA的比较:
服务拆分粒度:soa首要解决的是异构系统的服务化,微服务专注服务的拆分,原子服务;服务依赖:soa主要处理已有系统,重用已有的资产,存在大量服务间依赖,微服务强调服务自治,原子性,避免依赖耦合的产生;服务规模:soa服务粒度大,大多数将多个服务合并打包,因此服务实例数有限,微服务强调自治,服务独立部署,导致规模膨胀,对服务治理有挑战;架构差异:微服务通常是去中心化的,soa通常是基于ESB的;服务治理:微服务的动态治理,实时管控,而soa通常是静态配置治理;交付:微服务的小团队作战。
感觉有了docker,微服务的概念突然火了起来,总结就是微服务+容器+DevOps。
分布式服务框架介绍
背景
应用程序从集中式迁移到分布式
随着业务的发展,功能增加,传统架构模式开发、测试、部署的整个流程变长,效率变低,后台服务压力变大。压力只能通过硬件扩容暂时缓解,但根本问题无法解决:
应用规模变大,开发维护成本变高,部署效率降低;代码复用:原来是本地api调用,导致一些公用功能可能是按需开发,不统一,随意等问题;交付面临困难:主要是业务变得复杂,新增修改测试变得困难,拉长整个流程。
万能法宝:拆分,把大系统拆成小系统,独立伸缩。
纵向:分业务模块;横向:提炼核心功能,公共业务;
需要服务治理
核心服务细化后,服务数量会增加,需要一些运营控制。此时,需要服务治理:
服务生命周期管理;服务容量规划;运行期治理;服务安全。
服务框架介绍
杜博
阿里的开源Dubbo应该是业界最著名的分布式服务框架。看了公司的rpc框架,Dubbo的扩展性比我们好很多。我们的框架每次升级,都有很多变化。我们应该改天看看Dubbo的源代码,了解一下可伸缩性。
HSFHSF
淘宝的体量决定了他对极致性能的追求,HSF的跨机房特性也相当给力。
Coral Service
珊瑚服务
我从来没听说过这个,但是我消息不灵通。
框架设计
架构原则
这个图表可以总结rpc的一些一般原则:
精致:
Rpc层:底层通信框架、通信协议、序列化和反序列化;服务发布订阅;服务治理;
函数
性能
性能
可靠性
发了,面试会问,用池的话说,知识点。
服务治理
通讯框架通信框架
技术要点
长连接:主要是链路的创建过程到最后的关闭,耗时耗资源;每次调用都要创建的话,调用时延的问题,很可能链路创建的耗时比代码真正执行时长还多;BIO还是NIO:主要是线程模型的选择,推荐篇文章 IO – 同步,异步,阻塞,非阻塞 (亡羊补牢篇);自研还是使用开源NIO框架:一般来说还是使用开源吧,技术成熟,社区支持,现在netty和mina使用较多了吧。
在功能设计方面,作者给出了基于netty的演示服务器和客户端的代码,个人理解:
1.通用api;
2.可扩展性,封装底层,提供上层接口、隔离协议和底层通信;
可靠性设计
谈分布式系统必须谈可靠性。
链接有效性
心跳用于确认双方C和S的存活,保证链路的可用性。心跳检测机制分为三个级别:
1.tcp层,即tcp的keep-alive,作用于整个tcp协议栈;
2.协议层的心跳检测主要存在于长连接协议中,比如***pp协议;
3.应用层心跳,服务双方定时发送心跳消息;
第二种没听说过,常用的是1和3。一般使用netty时,用netty的读写空休闲来实现心跳。
断开连接
无论网络瘫痪,服务器瘫痪,还是心跳超时,链路都不可用,关闭。此时,需要重新连接链路。有一点需要注意的是,短时间连接后,不要马上重新连接。给系统留出释放资源的时间,这可以由调度程序来处理。
消息缓存重新传输
底层消息不会立即发送(也会导致半包卡住),链断后,消息会丢失。如有业务需求,断链后再转发消息。
资源释放
主要是断链之后,一定要保证资源的销毁和释放,包括一些线程池、内存等的释放。
性能设计
表现不佳的三宗罪
对于底层通信框架,主要有以下几种:
1.传播模式的选择主要是屏蔽和不屏蔽那些东西;
2.序列化和反序列化(后面有一章是关于序列化的);
3.线程模型,主要是服务器选择什么样的线程模型来处理消息。
沟通绩效三原则
现在有了以上3个问题,我们来优化一下:
传输:BIONIOAIO的选择;选择自定义协议栈,便于优化;服务端线程模型,单线程处理还是线程池,线程池是一个,还是分优先级,Reactor还是其他什么的。
在这一节中,作者谈到了netty的优势。
序列化和反序列化
这通常被称为编码和解码。通常,的通信框架提供了编码和解码的接口,并且内置了一些常用的序列化和反序列化工具。
与通信框架和协议的关系可以这样理解:通信框架是一个通道,运行在其上的码流数据是各种序列化编码的各种协议。
功能设计
在各种序列化框架中要考虑的主要问题有:
序列化框架本身的功能的丰富,支持的数据类型;多语言的支持;兼容性,往大了说:服务接口的前后兼容;协议的兼容;支持的数据类型的兼容。性能,目的是最少的资源,最快的速度,最大的压缩:序列化后码流大小;序列化的速度;序列化的资源占用。
在扩展性这一节中,作者谈到了netty对序列化的一些内置支持。但是在实际开发中,一般不会用到这些东西,都是提供序列化反序列化接口来自己扩展定义,所以扩展性很重要。
常用的序列化,如xml,json,hessian,kryo,pb,ps,取决于需要支持的种类。您可以搜索每个序列化的性能和压缩大小。
协议栈
本章主要讲述自定义协议栈的设计,交互的过程,以及其他与上一章通信框架重复的可靠性设计。
通信模型
服务提供者和消费者之间采用单链路、长连接通信和链路创建过程:
1.客户端发送握手请求,携带节点ID等认证信息;
2.服务器验证:节点ID的有效性、重复登录、ip地址的黑白列表等。通过后,返回握手响应消息;
3.链接建立后,客户端发送服务消息;
4.客户端-服务器心跳维护链接;
5.当服务器退出时,关闭连接,客户端察觉到连接关闭,客户端连接也关闭。
协议消息定义
通过attachment兼容了扩展性。作者还讲了将消息头的通用序列化和消息体的自定义序列化,看需求吧,我们公司的框架没做这部分支持,做了简化,将消息头和消息体统一封装,然后再加一个序列化方式组成一条消息发送。扩展性是通过连接兼容的。作者还谈到了消息头的一般序列化和消息体的自定义序列化。再来看需求。我们公司的框架不支持这部分,但是简化了。我们统一封装了消息头和消息体,然后添加了序列化方法,形成消息发送。
安全设计
内部的,不一定需要认证,也有基于系统,域名,ip的黑白名单,安全认证的;外部开发平台的话,基于秘钥认证;
服务路由
路由指的是服务提供者的集群部署,以及消费者如何从服务列表中选择合适的服务提供者进行调用。
透明路由
基于zk的服务注册中心的发布订阅;消费者本地缓存服务提供者列表,注册中心宕机后,不影响已有的使用,只是影响新服务的注册和老服务的下线。
负载平衡
随机轮循服务调用时延一致性Hash有个一致性hash算法,挺有意思的,redis的客户端shard用的
粘性连接不应该被普遍使用。大部分服务商都是无状态的,一旦有了状态,也不利于扩张。这些是点对点连接,负载平衡主要在客户端执行。有一种场景取决于服务器负载,即服务器服务配置了域名。
本地路由优先级策略
injvm:jvm也提供了消费端的服务,可以改成优先本jvm,对于消费端来说,不需关注提供者;innative:injvm比较少,多得是可能是这种,一个物理机部署多个虚拟机,或者一个容器部署多个服务提供者,消费者不需远程调用,本机,本地或本机房优先。
路由规则
除了上面提供的各种路由负载平衡之外,还允许自定义路由规则:
–路由:主要通过条件表达式实现;
–脚本路由:通过脚本解析实现。
事实上,应该有一个客户端自定义路由通过代码。这些主要是为了可扩展性。
路由策略定制
自定义路由方案:
1.灰度;
2.引流;
路由策略:
1.框架提供接口扩展;
2.配置平台提供路由脚本配置;
配置的路由
本地配置:包括服务提供者和消费者,全局配置3种;注册中心:路由策略统一注册到服务注册中心,集中化管理;动态下发:配置后动态下发各服务消费端。
集群容错
是指服务调用失败后,根据容错策略进行自动容错处理。
集群容错场景
通信链路故障:通信过程中,对方宕机导致链路中断;解码失败等原因Rest掉链接;消费者read-write socketchannel发生IOException导致链路中断;网络闪断故障;交换机异常导致链路中断;长时间Full GC导致;服务端超时:服务端没有及时从网络读取客户端请求消息,导致消息阻塞;服务端业务处理超时;服务端长时间Full GC;服务端调用失败:服务端解码失败;服务端流控;服务端队列积压;访问权限校验失败;违反SLA策略;其他系统异常;
业务执行异常不属于服务器异常。
容错策略
这是一个很好的画面,关系很清晰。
失败自动切换(Failover):调用失败后切换链路调用;服务提供者的防重;重试次数和超时时间的设置。失败通知(FailBack):失败后直接返回,由消费端自行处理;失败缓存(Failcache):主要是失败后,缓存重试重发,注意:缓存时间、缓存数量;缓存淘汰算法;定时重试的周期T、重试次数;快速失败(Failfast):失败不处理,记录日志分析,可用于大促期间,对非核心业务的容错。
容错策略扩展
容错接口的开放;屏蔽底层细节,用户自定义;支持扩展。
其实还有一点,也很重要,就是容错后支持本地mcok。必须支持呼叫失败后的链路切换和快速失败,但缓存重传是不必要的。
本文来自西绿柿投稿,不代表舒华文档立场,如若转载,请注明出处:https://www.chinashuhua.cn/24/608177.html