为什么要有分布式
近年来微服务、分布式等名词非常的火,那么我们又为什么要进行系统拆分?如何进行拆分呢?阿里的dubbo作为分布式框架的代表,无疑是推动了整个行业技术的进步。以前中小型公司都是一个war包打天下,所有人在一个系统里面开发时常出现代码冲突,每次发布都是几十万行代码,甚至几百万行代码了;而把系统拆分成很多个服务的之后,每个人负责一个服务自己选用什么技术都可以,按照功能模块来解耦,已经是互联网行业的标配。
如何进行系统拆分
这个问题可大可小,可以扯到领域驱动模型设计上面上去,说小了也非常简单。系统拆分分布式系统、拆成多个微服务架构,是要拆很多伦的。第一轮:团队扩大项目按照模块拆分成几个服务,比如说将电商系统拆分成订单系统、商品系统、用户系统等等,差不多几个人维护一个服务。第二轮:后来项目越来越大、业务越来越复杂,可能就要按照功能点划分出去,比如订单系统又拆分成了购物车系统、价格系统、订单管理系统,每人维护各自功能的代码。
多个系统之间的通信
各个系统之间调用比如可以直接基于httpclient纯接口互相通信,但是http接口通信维护起来成本很高,还要考虑超时重试、负载均衡等乱七八糟的问题。这时候就有了dubbo、cloud全家桶这种产品出现了。dubbo说白了就是一个rpc框架,dubbo对本地接口进行代理、跟远程服务接口进行网络通信,给你处理掉负载均衡了、服务实例上下线自动感知了、超时重试了,等一大堆问题。
dubbo的工作原理
第一层:service层,接口层,就是我们开发的提供者和消费者的接口。
第二层:config层,配置层,主要是对dubbo进行各种配置的。
第三层:proxy层,服务代理层,对提供者和消费者包一层网络代理,默认使用javaAssist动态字节码生成、创建代理类,但是可以通过spi机制自定义代理规则。
第四层:registry层,服务注册层,服务启动后注册到注册中心暴露自己的信息,例如ip、port等。负责服务的注册与发现。初始化时消费者会将提供者的地址等信息拉取到本地缓存,所以注册中心挂了可以继续通信。
第五层:cluster层,集群层,封装多个服务提供者的路由以及负载均衡,将多个实例组合成一个服务。
第六层:monitor层,监控层,对rpc接口的调用次数和调用时间进行监控。
第七层:protocol层,远程调用层,封装rpc调用。不同的协议有不同的请求方式,比如http协议就是http://www.xxx.cn.xxx?id=9527的格式
第八层:exchange层,信息交换层,封装请求响应模式,同步转异步。
第九层:transport层,网络传输层,抽象mina和netty为统一接口。
第十层:serialize层,数据序列化层。
dubbo的通讯协议与序列化协议
1)dubbo协议
默认就是走dubbo协议的,单一长连接,NIO异步通信,基于hessian作为序列化协议。适用的场景就是:传输数据量很小(每次请求在100kb以内),但是并发量很高,而高并发量可能每天调用量达到上亿次!此时用长连接是最合适的,就是跟每个服务消费者维持一个长连接就可以,可能总共就100个连接。然后后面直接基于长连接NIO异步通信,可以支撑高并发请求。如果是短连接的话,这么多请求服务提供者会扛不住;同时传输数据量太大的话,会导致并发能力降低。所以一般建议是传输数据量很小,支撑高并发访问。
2)rmi协议
走java二进制序列化,多个短连接,适合消费者和提供者数量差不多,适用于文件的传输,一般较少用
3)hessian协议
走hessian序列化协议,多个短连接,适用于提供者数量比消费者数量还多,适用于文件的传输,一般较少用
4)http协议
走json序列化
5)webservice
走SOAP文本序列化
dubbo负载均衡如容错策略
负载均衡策略,官网介绍了三种 http://dubbo.apache.org/en-us/docs/dev/impls/load-balance.html
1. random loadbalance
随机调用实现负载均衡,可以设置权重来分配流量,一般就用这个默认的就可以了。
2. roundrobin loadbalance
轮询策略,每个机器负载都一样,但是容易导致性能差的机器负载过高。可以调整权重,让性能差的机器承载权重小一些。
3. leastactive loadbalance
自动感知策略,根据自动感知机器性能来分配流量。
容错机制:
1. failover cluster模式:失败自动切换,自动重试其他机器,默认就是这个,常见于读操作。
2. failsafe cluster模式:出现异常时忽略掉,常用于不重要的接口调用,比如记录日志。
3. failbackc cluster模式:失败了后台自动记录请求,然后定时重发,比较适合于写消息队列这种。
4. failfast cluster模式:一次调用失败就立即失败,常见于写操作。
5. forking cluster模式:并行调用多个,有一个成功就立即返回。
6. broadcacst cluster模式:逐个调用所有的provider。
spi机制
上面我们说过了通过spi机制来代理的,那spi到底是什么呢?简单来说就是就是service provider interface(服务提供接口),比如你有一个接口,它有三个实现类,系统运行的时候到底会找到哪个呢?这就需要spi了,根据指定的配置或者是默认的配置,去找到对应的实现类。就好像我们的jdbc接口,需要我们指定masql、或oracge驱动,这其实也是spi的思想。dubbo官网也提供了自定义spi规则的例子。
dubbo的服务治理
一个大型的分布式架构,各个服务之间的调用链路是怎样的,服务访问次数以及接口的调用时长,这些东西都必须要做到心中有数的,全都搞定之后,后面才可以来看当前系统的压力主要在哪里,如何来扩容和优化啊。
服务降级
比如说服务A调用服务B,结果服务B挂掉了,服务A重试几次调用服务B,还是不行,直接降级,走一个备用的逻辑,给用户返回响应。可以将mock修改为true,然后在跟接口同一个路径下实现一个Mock类,命名规则是接口名称加Mock后缀。然后在Mock类里实现自己的降级逻辑。
<dubbo:reference id="fooService" interface="com.test.service.FooService" timeout="10000" check="false" mock="return null"></dubbo:reference>
失败重试和超时重试
所谓失败重试,就是consumer调用provider要是失败了,比如抛异常了,此时应该是可以重试的,或者调用超时了也可以重试。某个服务的接口,要耗费5s,你这边不能干等着,你这边配置了timeout之后,我等待2s,还没返回,我直接就撤了;设置retries次数,第一次没请求成功就继续重试。
<dubbo:reference id="xxxx" interface="xx" check="true" async="false" retries="3" timeout="2000"/>
全局链路追踪
一个请求过来经过了哪些服务、哪些接口、每个接口花了多长时间、整个请求响应花了多长时间,我们肯定要做到心中有数。像cloud全家桶里面就有这些现成的插件,不过dubbo只是一个单纯的RPC框架,这些需要我们自己去实现,想要实现的非常好通常都是一个有实力的大公司的专门团队才能做的到,一般我们中小型公司不可能做的很完美,但是还是能够简单的监测一下这些信息,通过请求头啊或者aop什么的来实现就够了。