文章目录
- Dubbo
- 前言
- 大型互联网架构目标
- 集群和分布式
- 集群
- 分布式
- 架构演进
- 1 Dubbo概述
- 1.1 Dubbo概念
- 1.2 Dubbo架构图
- 2 Dubbo快速入门
- 2.1 Zookeeper的安装
- 2.2 springBoot整合Dubbo+Zookeeper
- 2.2.1 创建项目Dubbo--provider
- 2.2.2 创建项目Dubbo--consumer
- 2.2.3 测试
- 3 Dubbo高级特性
- 3.1 dubbo-admin安装
- 3.1.1 准备环境
- 3.1.2 下载解压 Dubbo-Admin
- 3.1.3 在 dubbo-admin-develop 目录执行打包命令
- 3.1.4 启动后端
- 3.1.5 启动前端
- 3.1.6 进入dubbo-admin ui界面
- 3.2 dubbo-admin的简单使用
- 3.2.1 服务查询
- 3.2.2 服务测试
- 3.3 序列化
- 3.3.1 创建model模块,里面定义一个User类
- 3.3.2 在dubbo--provider模块调用该类
- 3.3.3 在dubbo-consumer模块调用该类
- 3.3.4 运行测试
- 3.4 地址缓存
- 3.5 超时与重试
- 3.6 多版本
- 3.7 负载均衡
- 3.8 集群容错
- 3.9 服务降级
Dubbo
前言
大型互联网架构目标
传统项目:面向企业,如OA( 办公自动化系统 ),HR( 人力资源 ),CRM( 客户资源管理系统 )…
互联网项目:面向个人用户,如淘宝,京东,微信…
- 用户体验:美观,功能,速度,稳定…
互联网项目特点:①用户多②流量大,并发高③海量数据④易受攻击无⑤功能繁琐⑥变更快
大型互联网项目架构目标
- 高性能:提供快速的访问体验。
- 高可用:网站服务一直可以正常访问。
- 可伸缩:通过硬件增加/减少,提高/降低处理能力。
- 高可扩展:系统间耦合低,方便的通过新增/移除方式,增加/减少新的功能/模块。
- 安全性:提供网站安全访问和数据加密,安全存储等策略。
- 敏捷性:随需应变,快速响应。
衡量性能指标
-
响应时间:指执行一个请求从开始到最后收到响应数据所花费的总体时间。
-
并发数:指系统同时能处理的请求数量。
-
并发连接数:指的是客户端向服务器发起请求,并建立了TCP连接。每秒钟服务器连接的总TCP数量
-
请求数:也称为QPS(Query Per Second) 指每秒多少请求.
-
并发用户数:单位时间内有多少用户
-
-
吞吐量:指单位时间内系统能处理的请求数量。
- QPS:Query Per Second 每秒查询数
- TPS:Transactions Per Second 每秒事务数。
- 一个事务是指一个客户机向服务器发送请求然后服务器做出反应的过程。客户机在发送请求时开始计时,收到服务器响应后结束计时,以此来计算使用的时间和完成的事务个数。
- 一个页面的一次访问,只会形成一个TPS;但一次页面请求,可能产生多次对服务器的请求,就会有多个QPS
集群和分布式
集群
一个项目部署在多个服务器上
集群的目的就是为了,增加服务器缓解客户端的请求压力。
-
特点:
-
可扩展性:集群的性能不限制于单一的服务实体,新的服务实体可以动态的添加到集群,从而增强集群的性能。
-
高可用行:一台服务器的宕机不会影响整体的使用。
-
高可管理:管理部署服务器时,由于每台服务器都是一样的,由此部署难度小,就像单台机器一样
-
-
必须具备的能力:
- 负载均衡:负载均衡把任务比较均匀的分布到集群环境下的计算和网络资源,以提高数据吞吐量。
- 错误恢复:如果集群中的某一台服务器由于故障或者维护需要无法使用,资源和应用程序将转移到可用的集群节点上。这种由于某个节点的资源不能工作,另一个可用节点中的资源能够透明的接管并继续完成任务的过程,叫做错误恢复。
-
缺点
- 当业务量到达一定程度时,单纯的增加服务器不能显著的提高性能。
分布式
将一个项目分为多个子项目,部署在多个服务器上
分布式目的就是为了解决业务量过大,代码量过于庞大,维护困难等问题,产生的。
引入分布式系统,既方便了代码的测试与维护,也方便了开发,不会有各个子项目之间的冲突
-
特点:
- 并发能力提高:请求通过 Nginx 负载均衡被分发到不同的服务器上,运行同样代码的服务器可以有 1 台或 N 台,通常情况下会根据实际用户访问量随时增加机器,无论是数据库或者服务,都可以做到随时水平扩展。
比如双 11 活动,平时订单少 50 台机器就够了,到了 11 订单量剧增,服务器增加到 100 台,每台机器之间相互独立,互不影响。 - 低延迟:服务器部署在各地,当一个用户发送请求,会被分发到离自己IP最近的城市。
- 并发能力提高:请求通过 Nginx 负载均衡被分发到不同的服务器上,运行同样代码的服务器可以有 1 台或 N 台,通常情况下会根据实际用户访问量随时增加机器,无论是数据库或者服务,都可以做到随时水平扩展。
-
缺点
-
分布式服务依赖网络
-
维护成本高
-
一致性,可用性,分区容错性无法同时满足
一致性©:在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)
可用性(A):保证每个请求不管成功或者失败都有响应。
分区容忍性§:系统中任意信息的丢失或失败不会影响系统的继续运作。
-
架构演进
-
单体架构
多个模块放在一个应用程序,部署集群(多机单体架构),部署在一个服务器(单机单体架构)
优点:
- 简单:开发部署都很方便,小型项目首选
缺点:
- 项目启动慢
- 可靠性差
- 可伸缩性差
- 扩展性和可维护性差
- 性能低
-
垂直架构
单体架构中多个模块拆分为多个独立的项目,形成多个独立的单体架构
优点
- 略微解决了单体的架构的缺点
缺点
- 重复功能过多,用户表发生变化,都得变化
-
分布式架构
将公共业务(重复)模块抽取出来,其他模块调用该模块,以实现服务的共享和重用。
RPC: Remote Procedure Call远程过程调用。有非常多的协议和技术来都实现了RPC的过程。比如: HTTP REST风格,JavaRMI规范、WebService SOAP协议、 Hession等等。
优点
- 解决垂直架构重复功能过多
缺点
- 服务方一旦改变,所有调用方都需要改变
-
SOA架构
SOA:(Service-Oriented Architecture,面向服务的架构)是一个组件模型,它将应用程序的不同功能单元(称为服务)进行拆分,并通过这些服务之间定义良好的接口和契约联系起来。
-
微服务架构
-
微服务架构是在SOA上做的升华,微服务架构强调的一个重点是“业务需要彻底的组件化和服务化”,原有的单个业务系统会拆分为多个可以独立开发、设计、运行的小应用。这些小应用之间通过服务完成交互和集成。
-
微服务架构= 80%的SOA服务架构思想+100%的组件化架构思想+80%的领域建模思想。
-
特点:
-
服务实现组件化:开发者可以自由选择开发技术。也不需要协调其他团队。
-
服务之间交换一般使用REST API。
-
去中心化:每个微服务有自己私有的数据库持久化业务数据。
-
自动化部署:把应用拆分成一个一个独立的单个服务,方便自动化部署,测试,运维。
-
1 Dubbo概述
1.1 Dubbo概念
-
Dubbo是阿里巴巴公司(已贡献给apache)开源的一个高性能,轻量级的Java RPC框架
RPC:远程过程调用
-
致力于提供高性能透明化的RPC远程服务调用方案,以及SOA服务治理方案
1.2 Dubbo架构图
-
角色
服务提供者(Provider):暴露服务的服务提供方,服务提供者在启动时,向注册中心注册自己提供的服务。
服务消费者(Consumer):调用远程服务的服务消费方,服务消费者在启动时,向注册中心订阅自己所需的服务,服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
注册中心(Registry):注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者
监控中心(Monitor):服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心
容器(Container):服务提供者所在的容器 -
消费者要去调用 服务提供者 ,但是不能直接调用
-
服务提供者需要运行在Container中,将服务提供者启动起来
-
启动起来后,然后向注册中心注册自己的服务
-
消费者发现从注册中心发现服务
-
消费者从注册中心获取服务,注册中心返回服务
-
拿到服务路径后,消费者调用服务生产者
-
监控中心监控服务生产者和消费者的调用次数和调用时间
-
2 Dubbo快速入门
2.1 Zookeeper的安装
使用Zookper作为注册中心
- 安装Zookeeper,解压
在官网http://zookeeper.apache.org/下载zookeeper安装包.
- 进入解压文件,进入
conf
目录下,备份zoo_example.cfg
并 将zoo_example.cfg
重命名为zoo.cfg
-
修改配置文件
-
配置系统环境变量
-
cmd里面输入
zkserver
命令启动Zookeeoer
2.2 springBoot整合Dubbo+Zookeeper
2.2.1 创建项目Dubbo–provider
-
引入依赖
<!-- Dubbo Spring Boot Starter --> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>2.7.8</version> </dependency> <!--zookeeper版本一定要匹配! --> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-registry-zookeeper</artifactId> <version>2.7.8</version> </dependency>
-
配置Dubbo
server: port: 8081 dubbo: registry: address: zookeeper://127.0.0.1:2181 application: name: dubbo-provider protocol: port: 20880 scan: base-packages: com.example.dubboprovider.service
-
创建Service层
ProviderUser
public interface ProviderUserService { String getName(); }
ProviderUserServiceImpl
@DubboService public class ProviderUserServiceImpl implements ProviderUserService { @Override public String getName() { System.out.println("======dubbo接口被调用了======"); return "DubboTest"; } }
-
DubooConsumerApplication配置运行类
@SpringBootApplication @EnableDubbo //开启Dubbo public class DubooConsumerApplication { public static void main(String[] args) { SpringApplication.run(DubooConsumerApplication.class, args); } }
2.2.2 创建项目Dubbo–consumer
-
引入依赖
<!-- Dubbo Spring Boot Starter --> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>2.7.8</version> </dependency> <!--zookeeper版本一定要匹配! --> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-registry-zookeeper</artifactId> <version>2.7.8</version> </dependency> <!--生产者依赖--> <dependency> <groupId>com.example</groupId> <artifactId>Dubbo--provider</artifactId> <version>0.0.1-SNAPSHOT</version> 、 </dependency>
-
配置yml文件
server: port: 8082 dubbo: registry: address: zookeeper://127.0.0.1:2181 application: name: dubbo-consumer protocol: port: 20881 # 协议名字好像为dubbo name: dubbo scan: base-packages: com.example.dubooconsumer.service
-
Service层
ConsumerUserService
public interface ConsumerUserService { String userName(); }
ConsumerUserServiceImpl
@DubboReference
远程注入- 从zookeeper注册中心获取userService的访问url
- 进行远程调用RPC
- 将结果封装为一个代理对象,给变量赋值
@DubboService public class ConsumerUserServiceImpl implements ConsumerUserService { @DubboReference private ProviderUserService providerUserService; @Override public String userName() { return providerUserService.getName(); } }
-
编写 UserController
@RestController @RequestMapping("/user") public class UserController { @Autowired private ConsumerUserService consumerUserService; @GetMapping("/user_name") public String userName(){ return consumerUserService.userName(); } }
2.2.3 测试
- 启动 zookeeper 服务器
- 启动服务提供者-Dubbo-provider
IDEA 启动服务提供者
- 启动服务消费者 -Dubbo-consumer
IDEA 启动服务消费者
3 Dubbo高级特性
3.1 dubbo-admin安装
dubbo-admin管理平台,是图形化的服务管理页面。
-
从注册中心中获取到所有的提供者/消费者进行配置管理
-
路由规则、动态配置、服务降级、访问控制、权重调整、负载均衡等管理功能
-
dubbo-admin是一个前后端分离的项目。前端使用vue,后端使用springboot
-
安装dubbo-admin其实就是部署该项目
3.1.1 准备环境
- jdk,Maven,node.js
3.1.2 下载解压 Dubbo-Admin
进入github,搜索dubbo-admin (https://github.com/apache/dubbo-admin)
3.1.3 在 dubbo-admin-develop 目录执行打包命令
mvn clean package
3.1.4 启动后端
切换到目录
dubbo-Admin-develop\dubbo-admin-distribution\target>
执行
java -jar .\dubbo-admin-0.5.0-SNAPSHOT.jar
3.1.5 启动前端
dubbo-admin-ui 目录下
执行命令
npm run dev
3.1.6 进入dubbo-admin ui界面
地址栏输入 http://localhost:38082/
- 账号密码都是root
3.2 dubbo-admin的简单使用
3.2.1 服务查询
*
代表查询所有服务
点击详情界面,主要分为3个区域
- 基础信息: 主要包含服务的基础信息比如服务名称、应用名称等
- 服务信息: 主要包含了服务的ip地址,端口号,注册来源,超时时间,序列化方式,权重和操作
- 元数据:主要包括服务的方法名,参数列表,返回值
3.2.2 服务测试
3.3 序列化
两个资源/网络之间传递数据,需要通过流的管道(二进制)的方式来传递
-
dubbo内部已经将序列化和反序列化的过程内部封装了
-
我们只需要在定义model类时实现serializable接口即可
-
一般会定义一个model模块,让生产者消费者都依赖该模块
3.3.1 创建model模块,里面定义一个User类
不继承
Serializable
接口
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Integer id;
private String username;
private String password;
}
3.3.2 在dubbo–provider模块调用该类
- 引入依赖
<dependency>
<groupId>com.example.dubbo_model</groupId>
<artifactId>Dubbo--model</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
- Service调用
ProviderUserService
public User getUserById(int user_id);
ProviderUserServiceImpl
@Override
public User getUserById(int user_id) {
return new User(1,"xiaowang","123456");
}
3.3.3 在dubbo-consumer模块调用该类
- Service层
ConsumerUserService
public User getUserById(int user_id);
ConsumerUserServiceImpl
@Override
public User getUserById(int user_id) {
return providerUserService.getUserById(user_id);
}
- Controller层
@GetMapping("/getUserById")
public User getUserById(int user_id) {
return consumerUserService.getUserById(user_id);
}
3.3.4 运行测试
报错
java.lang.IllegalStateException: Serialized class com.dubbo.model.User must implement java.io.Serializable
给model-user实现序列化接口
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {
private Integer id;
private String username;
private String password;
}
成功
前端页面得到User对象
{"id":1,"username":"xiaowang","password":"123456"}
3.4 地址缓存
注册中心挂了,服务是否可以正常访问?
- 可以,因为dubbo服务消费者在第一次调用时,会将服务提供方地址缓存到本地,以后在调用则不会访问注册中心。
- 当服务提供者地址发生变化时,注册中心会通知服务消费者。
3.5 超时与重试
- 超时
服务消费者调用服务生产者时候发生了堵塞,等待情形,服务消费者会一直等待下去
在某个峰值时刻,大量的请求都在同时请求服务消费者,会造成线程的大量堆积,势必会造成雪崩。
dubbo使用超时机制,设置一个超时时间,若超过该时间,还无法完成服务访问,会自动断开该访问
dubbo通过属性timeout
设置超时时间,默认为1000,单位为毫秒
该属性可在配置文件中配置,也可以在@DubboService
和@DubboReference
中设置
- 重试
如果出现网络抖动,则这一次请求就会失败。
网络抖动:因为某些原因网络,断开了一下
当这个断开时间大于超时时间,就会造成用户无法请求的影响。
dubbo使用重试机制,当发生抖动,会重新发送该次请求;
dubbo通过属性retries
来设置重试次数,默认为2
3.6 多版本
- 灰度版本:当出现新版本功能,会让一部分用户先使用新功能,用户反馈没问题时,再将所有用户迁移到新版本
- dubbo中使用
version
属性来设置和调用同一个接口的不同版本
3.7 负载均衡
当有一个服务消费者去访问服务,此时有多个服务提供者,该访问哪个?
dubbo有负载均衡策略
-
Dubbo负载均衡策略(4种)
①Random:按权重随机,默认值,
- 若多个服务权重(weight)一样,随机访问,若权重大,分配几率大。
dubbo通过
weight
属性设置权重② RoundRobin:按权重轮询
- 先轮询分配服务,然后权重大的开始轮询
③ LeastActive:最少活跃调用数,相同活跃度随机
- 服务响应最短时间,优先分配该服务
④ ConsistentHash:一致性hash,相同参数请求总是访问同一服务
3.8 集群容错
服务消费者访问服务,若某个服务提供者出错,dubbo如何调用?
通过cluster
属性设置容错模式
容错模式:
-
Failover Cluster:失败重试。默认值。当出现失败,重试其它服务器,默认重试2次,使用
retries
配置。一般用于读操作 -
Failfast Cluster:快速失败,只发送一次调用,失败立即报错。通常用于写操作
-
Failsafe Cluster:失败安全,出现异常时,直接忽略,返回一个空结果
-
Failback Cluster:失败自动恢复,后台记录失败请求,定时重发
某个服务必须调用成功,失败之后后台记录失败,定时重新调用,直至调用成功
-
Forking Cluster:并行调用多个服务器,只要成功一个即返回
-
Broadcast Cluster:广播调用所有提供者,逐个报错,任意一台报错则报错。
用于服务消费者要同步更新多个服务的数据,数据要保持同步
3.9 服务降级
服务消费者有三个服务:
例如:广告服务,日志服务,支付服务(比如支付服务比较重要),当cpu资源快满了,我们就需要把不太重要的资源释放掉,保证核心(支付服务)服务的正常运行。
服务降级方式
mock=force:return null
表示消费方该服务的方法调用都直接返回null,不发起远程调用。用来屏蔽不重要服务不可用时对方调用的影响mock=fail:return null
表示消费方对该服务的方法调用在失败后,再返回null值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响。