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