以下系统架构均以电商系统为例来说明。
1、单体系统架构在系统最初成型的时候,功能和用户访问量都比较小,这个时候我们将系统的所有代码都放在一个工程中并部署在一台服务器上,以此减少部署节点和部署成本,例如:商品、类目、订单、用户等信息都放在一个工程中进行部署。
2、集群系统架构随着系统功能和用户访问数量的增加,一个节点部署已经不能维持现有系统的正常、稳定运行,这个时候我们会进行多节点集群部署(即水平扩展),以此来应对现有的访问量和维持系统稳定。
3、分布式系统架构随着业务的不断增加以及用户访问数量的攀升,水平扩展已经不能满足我们现有的需求。这时可以将系统拆分为多个工程,例如:用户工程、商品工程、订单工程、支付工程,进行分布式部署(即垂直扩展)。
4、微服务系统架构当子工程越来越多时,发现它们可能同时都拥有某功能相同或相似的模块,于是将这些在整个项目中冗余的功能模块抽取出来作为独立的工程,这些工程就是专门为那些调用它们的工程服务的。那么这些抽取出的功能就称为微服务,微服务应用称为服务提供者,而调用微服务的应用就称为服务消费者。
5、流动计算系统架构随着功能的扩张,微服务就需要越来越多;随着 PV 的增涨,消费者工程就需要越来越多;随着消费者的扩张,为其提供服务的提供者服务器就需要越来越多,且每种提供者都要求创建为集群。这样的话,消费者对于提供者的访问就不能再采用直连方式进行了,此时就需要服务注册中心了。提供者将服务注册到注册中心,而消费者通过注册中心进行消费,消费者无需再与提供者绑定了。提供者的宕机,对消费者不会产生直接的影响。
(二)为什么选择Dubbo框架
在实际开发过程中,我们经常会使用到远程方法调用,有Jdk自带的RMI和Hession等框架,但是当业务逐渐增加、模块越来越多之后我们需要维护的地址就越来越多,错综复杂的配置让我们摸不着头脑,而Dubbo完美的解决了这些问题,并且提供了服务注册、负载均衡、服务降级、服务容错等优质功能,优点如下:
(1)透明化的远程方法调用、像调用本地方法一样调用远程方法;只需简单配置,没有任何API侵入。
(2)软负载均衡及容错机制
(3)服务注册中心,不需要写死服务提供者地址,注册中心基于接口名自动查询提供者ip,注册中心支持目前主流的Zookeeper、Eureka、Consul、Nocas等。
(4)服务接口监控与治理,Dubbo-admin与Dubbo-monitor提供了完善的服务接口管理与监控功能,针对不同应用的不同接口,可以进行 多版本,多协议,多注册中心管理。
(三)Dubbo简介 1、什么是RPC?RPC(Remote Procedure Call Protocol) ——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。 RPC 协议假定某些传输协议的存在,如 TCP 或 UDP,为通信程序之间携带信息数据。在 OSI 网络通信模型(OSI 七层网络模型, OSI, Open System Interconnection,开放系统互联)中, RPC 跨越了传输层和应用层。 RPC 使得开发包括网络分布式多程序在内的应用程序更加容易。RPC 采用客户机/服务器模式(即 C/S 模式)。请求程序就是一个客户机,而服务提供程序就是一个服务器。首先,客户机调用进程发送一个有进程参数的调用信息到服务进程,然后等待应答信息。在服务器端,进程保持睡眠状态直到调用信息到达为止。当一个调用信息到达,服务器获得进程参数,计算结果,发送答复信息,然后等待下一个调用信息,最后,客户端调用进程接收答复信息,获得进程结果,然后调用执行继续进行。
2、Dubbo简介
Apache Dubbo |ˈdʌbəʊ| 是一款高性能、轻量级的开源Java RPC框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。(来自Dubbo官方网站)
Dubbo 中存在四大组件: Provider: 服务提供者,Consumer: 服务消费者,Registry:服务注册中心,Monitor:监控中心
(四)Dubbo使用示例(以SpringBoot为例) 1、创建dubbo-common工程,用来存放公共的实体类以及业务接口(本工程创建为普通的maven工程即可)(1)创建Maven工程
(2)根据数据业务表创建业务实体
(3)创建业务接口
2、创建服务提供者工程(dubbo-provider)(1)创建dubbo-provider工程(Springboot工程)
(2)引入相关依赖(dubbo-spring-boot-starter依赖在中央仓库无法找到,可以转至GitHub阿里开源库,以查看依赖信息)
(3)将默认生成的application.properties文件后缀修改为application.yml,并添加以下配置
(4)创建数据持久化操作接口(集成Jpa实现)
(5)创建业务接口实现类并实现对应方法(此处的@Service注解是使用dubbo包下的,而非Spring的注解)
(6)在启动类上添加以下注解,扫描相关包
(7)叮叮叮~~~ 这样服务提供者就启动成功啦
3、创建服务消费者工程(dubbo-consumer)(1)创建dubbo-consumer工程(Springboot工程)
(2)修改application.yml配置文件
(3)调用服务提供者暴露的服务
(4)启动消费者服务,并测试是否消费成功
等等!!! 你以为这就完了??? NO ! NO ! NO ! 这才刚刚开始
(五)Dubbo高级应用 1、多版本控制
当系统进行升级时,一般都是采用“灰度发布”过程。即在低压力时段,让部分消费者先调用新的提供者实现类,其余的仍然调用老的实现类,在新的实现类运行没有问题的情况下,逐步让所有消费者全部调用成新的实现类。多版本控制就是实现灰度发布的。简单来说,就是对于一些系统中需要保留之前版本的接口实现,而又需要对原有业务逻辑进行更改,例如:APP的更新等。这种情况下可以使用Dubbo的多版本控制来实现,即一个接口多种业务的实现,并为同一接口的业务实现增加version属性,来标记接口实现的不同版本。
(1)将dubbo-provier服务提供者的实现类UserServiceImpl,修改为OldUserServiceImpl,新建NewUserServiceImpl来实现查询id为1的用户列表,并在@Service注解增加version属性,属性值分别为1.0和1.1
(2)修改服务消费者工程,在@Reference注解上指定注入的版本号,即可实现多版本控制
(3)测试多版本控制
2、服务分组
服务分组与多版本控制的使用方式几乎是相同的,只要将 version 替换为 group 即可。但使用目的不同。使用版本控制的目的是为了升级,将原有老版本替换掉,将来不再提供老版本的服务,所以不同版本间不能出现相互调用。而分组的目的则不同,其也是针对相同接口,给出了多种实现类。但不同的是,这些不同实现并没有谁替换掉谁的意思,是针对不同需求,或针对不同功能模块所给出的不同实现。这些实现所提供的服务是并存的,所以它们间可以出现相互调用关系。例如,对于支付服务的实现,可以有微信支付实现与支付宝支付实现等。
(1)在接口类中增加pay()方法,用来模拟线上支付,并在服务提供者dubbo-provider中分别实现pay()方法,分组标记分别为weixin和aliPay,并分别模拟实现微信支付和支付宝支付
(2)消费者dubbo-consumer调用
(3)测试服务分组
3、多协议支持
除了 Dubbo 服务暴露协议 Dubbo 协议外,Dubbo 框架还支持另外 8 种服务暴露协议:RMI 协议、Hessian 协议、HTTP 协议、WebService 协议、Thrift 协议、Memcached 协议、Redis协议、Rest 协议。但在实际生产中,使用最多的就是 Dubbo 服务暴露协议。
(1)在配置文件中增加如下配置
(2)在业务实现类中增加protocal属性,用来指定传输协议,但dubbo推荐使用其dubbo协议
4、负载均衡(1)在dubbo-commo工程中创建测试接口类TestService,并声明方法String sayHello();
(2)讲服务提供者dubbo-pervider工程复制一份,重命名为dubbo-pervider-1,并修改相关配置文件的端口号;
(3)在两个服务提供者中分别创建TestService的实现类TestServiceImpl,并实现sayHello()方法,分别返回“我是第一个服务提供者”和“我是第二个服务提供者”;
(4)Dubbo负载均衡策略
Dubbo内置了四种负载均衡策略,分别为random策略、roundrobin策略、leastactive策略、consistenthash策略。(官方说明以上四种策略,但是我在看dubbo源码的时候发现了另外一种策略:shortestresponse策略)
random策略:顾名思义,随机负载均衡策略就是从多个 Provider 中随机选择一个。但是 Dubbo 中的随机负载均衡有一个权重的概念,即按照权重设置随机概率。比如说,有10个 Provider,并不是说,每个 Provider 的概率都是一样的,而是要结合这10个 Provider 的权重来分配概率。Dubbo中,可以对 Provider 设置权重。比如机器性能好的,可以设置大一点的权重,性能差的,可以设置小一点的权重,权重会对负载均衡产生影响。
roundrobin策略:轮询负载均衡,就是依次的调用所有的 Provider。和随机负载均衡策略一样,轮询负载均衡策略也有权重的概念。 轮询负载均衡算法可以让RPC调用严格按照我们设置的比例来分配。不管是少量的调用还是大量的调用。但是轮询负载均衡算法也有不足的地方,存在慢的 Provider 累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上,导致整个系统变慢。
leastactive策略:最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差,使慢的机器收到更少。
consistenthash策略:使用一致性 Hash 算法,让相同参数的请求总是发到同一 Provider。
shortestresponse策略:最短响应负载均衡,过滤成功调用响应时间最短的调用者数量,并计算这些调用者的权重和数量 ,如果调用者只有一个,则直接调用它 ;如果有多个调用者且权重不同,则根据总权重来随机调用 ;如果调用者有多个且权重相同,则随机调用。(根据源码说明总结,若有误欢迎指点)
(5)指定负载均衡策略
A、修改服务提供者,在@Service中添加loadbalance属性,来指定负载均衡策略;
B、修改服务消费者,在@Referance中添加loadbalance属性,来指定访问时负载均衡策略;
(6)测试结果
5、集群容错集群容错指的是,当消费者调用提供者集群时发生异常的处理方案。Dubbo 内置了 6 种集群容错策略。
(1)容错策略
Failover策略:故障转移策略。当消费者调用提供者集群中的某个服务器失败时,其会自动尝试着调用其它服务器。该策略通常用于读操作,例如,消费者要通过提供者从 DB 中读取某数据。但重试会带来服务延迟。
Failfast策略:快速失败策略。消费者端只发起一次调用,若失败则立即报错。通常用于非幂等性的写操作,比如新增记录。
Failsafe策略:失败安全策略。当消费者调用提供者出现异常时,直接忽略本次消费操作。该策略通常用于执行相对不太重要的服务,例如,写入审计日志等操作。
Failback策略:失败自动恢复策略。消费者调用提供者失败后, Dubbo 会记录下该失败请求,然后定时自动重新发送该请求。该策略通常用于实时性要求不太高的服务,例如消息通知操作。
Forking策略:并行策略。消费者对于同一服务并行调用多个提供者服务器,只要一个成功即调用结束并返回结果。通常用于实时性要求较高的读操作,但其会浪费较多服务器资源。
Broadcast策略:广播策略。广播调用所有提供者,逐个调用,任意一台报错则报错。通常用于通知所有提供者更新缓存或日志等本地资源信息。
(2)配置容错策略
A、服务提供者:在@Service注解内添加cluster属性来指定容错的策略,添加retries属性来指定当服务调用失败时重试的次数
B、服务消费者:在@Reference注解内添加cluster属性来指定容错的策略,添加retries属性来指定当服务调用失败时重试的次数
6、服务降级(1)什么是服务降级
当服务器压力在一定时间内访问量剧增的情况下,根据当前业务情况及流量对一些服务有策略的降低服务级别,以释放服务器资源,以此来保证核心任务的正常运行。
(2)服务降级的方式
A、部分服务暂停
B、全部服务暂停
C、随机拒绝服务
D、部分服务延迟
(3)服务降级配置
Mock Null方式:服务降级配置在服务消费者工程即可,在@Reference注解内添加mock=“return null”属性,当服务提供者能正常提供服务时,该接口正常使用,当需要服务降级时(暂停服务),并不会报错而是会返回null。
当用户查询时使用Mock Null返回空,用户体验上没有什么问题,但是当用户进行插入、更新、删除等操作时,如果还返回空这是说不过去的,此时可以使用Mock Class方式:首先在接口所在的包下创建文件(文件名严格遵守接口类名+Mock),并实现接口类中的相关方法,如果抛出异常信息,提示失败等。(以下仅为举例,实际项目中可使用自定义异常或者封装的返回对象等实现)
7、服务限流为了防止某个消费者的 QPS 或是所有消费者的 QPS 总和突然飙升而导致的重要服务的失效,系统可以对访问流量进行控制,这种对集群的保护措施称为服务限流。
Dubbo 中能够实现服务限流的方式较多,可以划分为两类:直接限流与间接限流。
直接限流:通过对连接数量直接进行限制来达到限流的目的。
间接限流:通过一些非连接数量设置来达到限制流量的目的。
以下使用直接限流方式来举例:
在直接限流方式中,有几种不同的实现方式:在服务端限流、在消费端限流以及两端限流等。
(1)executes 限流(服务提供者端):在@Service注解内添加executes属性,并设定最大访问数量值即可。
(2)accepts 限流(服务提供者端):在配置文件的dubbo.protocals中设置accepts 属性的值即可。
(3)actives 限流 (两端):
A、服务消费者端:@Referance注解内添加actives,并设定最大访问数量值即可。
B、服务提供者端:@Service注解内添加actives,并设定最大访问数量值即可。
附:dubbo示例代码 https://gitee.com/liuyh8/dubbo-demo



