一、软件环境
(1)自己部署服务器
所有软件及服务器自己进行管理提供,可以直接在项目中添加Spring Cloud依赖。推荐
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>{project-version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
(2)使用阿里云
如果希望使用阿里云服务。则需要导入阿里云对应的依赖。 不推荐
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>aliyun-spring-boot-dependencies</artifactId>
<version>{project-version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
二、核心组件
Spring Cloud Alibaba和 Spring Cloud Netflix 同属于Spring Cloud 下三大阵营产品之一。内部提供了一系列组件。
(1)Spring Cloud Alibaba Nacos Discovery
Nacos是由阿里巴巴推出的用于构建云本机应用程序的易于使用的动态服务发现,配置和服务管理平台。
Nacos Discovery 是使用Nacos的服务注册与发现功能。在Spring Cloud Alibaba中常使用Nacos作为注册中心。
(2) Spring Cloud Alibaba Nacos Config
Nacos Config 是利用Nacos作为分布式配置中心使用。
(3) Spring Cloud Alibaba Sentinel
Sentinel是Spring Cloud Alibaba体系中提供服务限流,断路和负载保护的解决方案。已经成功的在阿里巴巴双十一活动中运行10余年。可靠性非常高。
(4) Spring Cloud Alibaba Dubbo
Dubbo 是阿里巴巴于2011年对外提供使用的RPC框架。使用他可以实现接口发布和接口调用功能,内部还提供了负载均衡等策略。是国内使用较多的服务调用框架。
从2.7版本开始阿里巴巴把Dubbo贡献给Apache。
Spring Cloud Alibaba Dubbo是基于Dubbo-spring-boot-starter 2.7的封装,可以让Dubbo程序员几乎零成本的学习。
(5)Spring Cloud Alibaba Cloud ANS
ANS(应用程序命名服务)是EDAS(企业级分布式应用服务,平时所说的阿里云)的组件。使用ANS可以使用直接在阿里云部署好的Nacos Discover应用。
(6)Spring Cloud Alibaba Cloud ACM
Spring Cloud AliCloud ACM是Spring Cloud客户端上商业产品应用程序配置管理(ACM)的实现,并且是免费的。
使用Spring Cloud AliCloud ACM可基于Spring Cloud的编程模型快速访问ACM配置管理功能。
ACM就是阿里云中Nacos Config的实现。
(7)Spring Cloud Alibaba Cloud OSS
OSS(对象存储服务)是阿里云上的存储产品。Spring Cloud阿里云OSS提供符合Spring Cloud规范的商业化存储服务。我们提供易于使用的API,并支持将Resource集成到Spring框架中。
OSS其实就是阿里云中类似FastDFS的实现。
(8)Spring Cloud Alibaba Cloud SchedulerX
SchedulerX(分布式作业调度)是阿里云产品EDAS的组件。Spring Cloud Alibaba Cloud SchedulerX提供符合Spring Cloud规范的分布式作业调度。SchedulerX提供具有几秒钟的高精度,高稳定性和高可用性的定时作业调度服务,并支持多种作业类型,例如简单的单服务器作业,简单的多主机作业,脚本作业和网格作业。
(9)Spring Cloud Alibaba Cloud SMS
SMS(短消息服务)是一种覆盖全球的消息服务,阿里巴巴SMS提供便捷,高效和智能的通信功能,可帮助企业快速联系其客户。
Spring Cloud Alibaba Cloud SMS提供了易于使用的API,可基于Spring Cloud Alibaba SMS快速访问Alibaba Cloud的SMS服务。
三、RPC简介
(1)RFC
RFC(Request For Comments) 是由互联网工程任务组(IETF)发布的文件集。文件集中每个文件都有自己唯一编号,例如:rfc1831。目前RFC文件由互联网协会(Internet Society,ISOC)赞助发行。
RPC就收集到了rfc 1831中。可以通过下面网址查看:
RFC 1831 - RPC: Remote Procedure Call Protocol Specification Version 2RFC 1831 - RPC: Remote Procedure Call Protocol Specification Version 2 RFC 1831 - RPC: Remote Procedure Call Protocol Specification Version 2
(2)RPC
RPC在rfc 1831中收录 ,RPC(Remote Procedure Call) 远程过程调用协议。
RPC协议规定允许互联网中一台主机程序调用另一台主机程序,而程序员无需对这个交互过程进行编程。在RPC协议中强调当A程序调用B程序中功能或方法时,A是不知道B中方法具体实现的。
RPC是上层协议,底层可以基于TCP协议,也可以基于HTTP协议。一般我们说RPC都是基于RPC的具体实现,如:Dubbo框架。从广义上讲只要是满足网络中进行通讯调用都统称为RPC,甚至HTTP协议都可以说是RPC的具体实现,但是具体分析看来RPC协议要比HTTP协议更加高效,基于RPC的框架功能更多。
RPC协议是基于分布式架构而出现的,所以RPC在分布式项目中有着得天独厚的优势。基于RPC协议推出了一些框架(Dubbo),慢慢发展甚至出现了一种架构方式叫做RPC架构。RPC架构特点:一定是A项目调用B项目,调用过程中使用RPC协议。
(3)RPC和HTTP对比
1.具体实现
RPC:可以基于TCP协议,也可以基于HTTP协议。
HTTP:基于HTTP协议
2.效率
RPC:自定义具体实现可以减少很多无用的报文内容,使得报文体积更小。
HTTP:如果是HTTP 1.1 报文中很多内容都是无用的。如果是HTTP2.0以后和RPC相差不大,比RPC少的可能就是一些服务治理等功能。
3.连接方式
RPC:长连接、短链接都支持。阻塞连接、非阻塞连接都支持。
HTTP:每次连接都是3次握手。Http2.0开始支持长连接。
4.序列化性能
RPC可以基于很多序列化方式。如:thrift
HTTP 主要是通过JSON,序列化和反序列效率更低。
5.注册中心
RPC :一般RPC框架都支持注册中心。
HTTP:都是直连。
6.负载均衡
RPC:绝大多数RPC框架都带有负载均衡工具。
HTTP:一般都需要借助第三方工具。如:nginx
7.综合结论
RPC框架一般都带有丰富的服务治理等功能,更适合企业内部接口调用。
HTTP更适合多平台之间相互调用。
四、Dubbo架构
Apache Dubbo 是一个高可用的,基于Java的开源RPC框架。
Dubbo框架不仅仅是具备RPC访问功能,还包含服务治理功能(注册中心,负载均衡、容灾等)。
(1)虚实线
虚线表示异步,实线表示同步。异步不阻塞线程性能高,同步阻塞线程必须等待响应结果才能继续执行,相对性能低。
(2)Provider
提供者(RPC被调用方)。开发中是一个独立项目,一般编写持久层、业务层和事务控制代码等。
(3) Consumer
消费者(RPC调用方)。开发中是一个独立项目,编写控制层、服务层、视图层等。
(4) Container
容器(Spring容器),Dubbo完全基于Spring实现的。
(5) Registry
注册中心。放置所有Provider对外提供的信息。包含Provider的IP,访问端口,访问遵守的协议,对外提供的接口,接口中有哪些方法等相关信息。
(6) Monitor
监控中心。监控Provider的压力情况等。每隔2分钟Consumer和Provider会把调用次数发送给Monitor,由Monitor进行统计。
(7)执行流程说明
-
0 : start:启动Spring容器时会把Provider启动。
-
1 : register:把Provider相关信息注册到Registry里
-
2 : subscribe:Consumer从Registry中订阅Provider的信息
-
3 : notify:通知给Consumer
-
4 : invoke:Consumer根据Registry通知的信息进行调用Provider中方法。
-
5 : count:Consumer和Provider把调用次数信息异步发送给Monitor进行统计。
五、Dubbo支持的协议
(1)Dubbo协议(官方推荐协议)
-
优点: 采用NIO复用单一长连接,并使用线程池并发处理请求,减少握手和加大并发效率,性能较好(推荐使用)
-
缺点: 大文件上传时,可能出现问题(不使用Dubbo文件上传)
(2)关于NIO、BIO、AIO的区别
-
NIO JDK 1.4 推出的。同步非阻塞。执行代码时必须等IO操作结束才能执行下一行代码(同步),但是允许并发IO操作(非阻塞)
-
BIO 同步阻塞。执行代码时必须等IO操作结束才能执行下一行代码(同步),不允许并发IO操作(阻塞)
-
AIO JDK1.7 推出的。异步非阻塞。执行代码时IO操作代码可以相当于忽略直接执行下一行代码(异步),允许并发IO操作(非阻塞)
(3)RMI(Remote Method Invocation)协议
-
优点: JDK自带的能力。
-
缺点: 偶尔连接失败.
(4)Hessian协议
-
优点: 可与原生Hessian互操作,基于HTTP协议
-
缺点: 需hessian.jar支持,http短连接的开销大
六、Dubbo支持的注册中心
1. Zookeeper
-
优点: 支持分布式.很多周边产品.
-
缺点: 受限于Zookeeper软件的稳定性.Zookeeper专门分布式辅助软件,稳定较优
2. Nacos
-
优点: 阿里自身注册中心,无缝集成。除此还支持配置中心等。
3. Multicast
-
优点: 去中心化,不需要单独安装软件.
-
缺点: Provider和Consumer和Registry不能跨机房(路由)
4. Redis
-
优点: 支持集群,性能高
-
缺点: 要求服务器时间同步.否则可能出现集群失败问题.
5. Simple
-
优点: 标准RPC服务.没有兼容问题
-
缺点: 不支持集群
七、Zookeeper安装
(1)拉取镜像
docker pull zookeeper:3.5.5
(2)创建并运行容器
docker run -d -p 2181:2181 --name zookeeper --restart always zookeeper:3.5.5
(3)连接测试
docker exec -it zookeeper bash
zkCli.sh
或者
docker exec -it zookeeper zkCli.sh
八、常用命令
(1)连接
docker exec -it zookeeper zkCli.sh
docker exec -it zookeeper zkCli.sh -server localhost:2181
(2)退出
quit
(3)查看Zookeeper中的键
ls /
查看Zookeeper中的所有键
ls -R /
(4)查看Zookeeper中的value值
get /test
(5)设置Zookeeper中的键值对
set /test newValue
(6)创建Zookeeper中的键值对
create /test value
create /testNoValue
(7)删除Zookeeper中的键值对
删除某一个指定的节点。此节点下,不能有任何子节点。
delete /path
删除某一个指定的节点,此节点下,可以有子节点
deleteall /path
九、ZooKeeper和Eureka对比
对比项 | Zookeeper | Eureka | 备注 |
---|---|---|---|
CAP | CP | AP | |
Dubbo支持 | 已支持 | - | |
Spring Cloud支持 | 已支持 | 已支持 | |
kv服务 | 支持 | - | ZK支持数据存储,eureka不支持 |
使用接口(多语言能力) | 提供客户端(zkClient、Curator) | http协议(跨语言) | ZK的跨语言支持比较弱 |
watch支持 | 支持 | 支持 | 什么是Watch支持?就是客户端 监听服务端的变化情况。 zk通过订阅监听来实现 eureka通过轮询的方式来实现 |
集群监控 | _ | metrics | metrics,运维者可以收集并报警这些度量信息达到监控目的 |
十、使用
(1)导入依赖
<!-- dubbo启动器 -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>3.0.4</version>
</dependency>
<!-- apache 提供的zookeeper客户端访问框架 -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>5.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>5.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-x-discovery</artifactId>
<version>5.1.0</version>
</dependency>
(2)编辑配置文件
server:
port: 8001
spring:
application:
name: dubbo-contact-mgr
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai
username: root
password: root
# dubbo相关配置。
dubbo:
application: # 注意:部分低版本dubbo中。dubbo.application.id必要。
id: dubbo-contact-mgr # 可选配置,dubbo服务的唯一命名。默认${dubbo.application.name}
name: dubbo-contact-mgr # 必要配置,dubbo服务的唯一命名。
registry: # 配置注册中心地址。必要配置。
address: zookeeper://192.168.8.128:2181 # 注册中心地址
timeout: 300000 # 超时配置
protocol: # dubbo的provider启动后,监听什么端口,处理什么协议。
name: dubbo # dubbo协议。默认值。推荐使用。
port: 20881 # provider监听的端口。默认值20880。
(3)在被远程调用的服务实现类上添加@DubboService注解
@DubboService
public class ContactAPIImpl implements ContactAPI {
@Autowired
private ContactMapper contactMapper;
@Override
@Transactional
public int addContact(Contact contact) {
System.out.println("新增联系方式:" + contact.toString());
// 保证主键一定存在。
if(contact.getId() == null){
contact.setId(System.currentTimeMillis());
}
return contactMapper.insert(contact);
}
}
(4)使用@DubboReference在需要远程调用注入被远程调用的接口
@DubboService
public class StudentAPIImpl implements StudentAPI {
@Autowired
private StudentMapper studentMapper;
@DubboReference
private ContactAPI contactAPI;
/**
* 新增学生的同时,判断参数contactType和contact是否都存在。
* 如果都存在,则调用远程微服务contact,新增联系方式。
* @param student
* @param contactType 联系方式种类
* @param contact 联系方式
* @return
*/
@Override
@Transactional
public int addStudent(Student student, String contactType, String contact) {
// 如果学生数据无主键,使用当前时间戳赋值
if(student.getId() == null){
student.setId(System.currentTimeMillis());
}
int rows = studentMapper.insert(student);
// 新增学生成功。检查参数联系方式
if((contact != null && contact.trim().length() > 0) &&
(contactType != null && contactType.trim().length() > 0)){
System.out.println(contactAPI.getClass().getName());
// 参数联系方式和联系方式种类,都存在。准备调用远程服务。
// 创建联系方式对象
Contact contactObj = new Contact();
contactObj.setContact(contact);
contactObj.setContactType(contactType);
contactObj.setId(System.currentTimeMillis());
contactObj.setStudentId(student.getId());
// 新增联系方式
contactAPI.addContact(contactObj);
}
return rows;
}
}
十一、负载均衡策略
(1) Random
随机。随机访问集群中节点。访问概率和权重有关。 默认
(2)RoundRobin
轮询。访问频率和权重有关。 权重(weight):占有比例。集群中每个项目部署的服务器的性能可能是不同,性能好的服务器权重应该高一些。
(3)LeastActive
活跃数相同的随机,不同的活跃数高的放前面。
(4)ConsistentHash
一致性Hash。相同参数请求总是发到一个提供者。
十二、自定义负载均衡策略
(1)@DubboService注解
/**
* dubbo服务提供者
* DubboService - 负载均衡配置
* loadbalance - 负载均衡策略
* weight - 权重
* 在dubbo中。provider可以设置负载均衡策略。默认采用随机策略。
* 设置后,启动注册到注册中心。服务的消费者发现服务后,默认采用provider定义的负载均衡策略和权重。
*/
@DubboService(loadbalance = "roundrobin", weight = 1)
(2)@DubboReference注解
/**
* dubbo服务消费者者
* DubboReference - 负载均衡配置
* loadbalance - 负载均衡策略
* 在dubbo中。服务的消费者没有默认负载均衡策略。当服务消费者发现服务提供者后,采用provider配置的负载均衡策略和权重。如果服务消费者配置了负载均衡策略,则忽略provider配置的负载均衡策略和权重。
*/
@DubboReference(loadbalance = "random")
(3)基于配置文件设置负载均衡策略
dubbo:
provider: # 给当前应用中所有的服务提供者配置共性
loadbalance: roundrobin # 所有provider的默认负载均衡策略是什么。默认为random
weight: 3 # 所有provider节点的权重是多少。默认-1
consumer: # 给当前应用中所有的服务消费者配置共性
loadbalance: random # 所有的服务消费者默认负载均衡策略是什么。
十三、先启动Consumer解决办法
(1)基于注解的解决办法
@DubboReference(check = false)
(2)基于配置文件的解决办法
dubbo:
consumer: # 给当前应用中所有的服务消费者配置共性
check: false # 所有的服务消费者,启动时不创建provider代理对象。使用时重新发现并创建代理。
十四、配置dubbo协议的payload
Dubbo框架在使用dubbo协议实现远程服务调用的时候,默认限制请求和应答数据最大为8M。如果超出范围则抛出异常。可以通过配置设置payload容量。下述配置只提供对应的部分配置内容:
dubbo:
protocol:
payload: 838860800 # 配置请求响应传输的数据最大容量,不建议修改。默认8M
十五、分组注册和服务多版本发布
Dubbo框架支持服务分组注册和服务多版本发布。即相同的接口发布的服务,也可以用组来维护。且可以同时提供若干不同版本的服务提供者实现。
(1)@DubboService注解
@DubboService(group = "test", version = "1.0")
(2)@DubboReference注解
注意:当Provider注册服务时,约束了分组或版本,Consumer则必须提供相应配置,否则无法发现服务。配置如下:
@DubboReference(group = "test", version = "1.0")