1、Dubbo概述
现在SpringCloud Alibaba比较火,用的比较多是吧,那dubbo是不是过时的呢? 并不是的,以前有人把Dubbo和SpringCloud进行对比,其实两者是不同维度的,不能对比,dubbo就是一个rpc框架,SpringCloud是一个生态,里面包括很多组件,并且dubbo3也可以和SpringCloudAlibaba一些组件进行整合,并且我认为dubbo和SpringCloud alibaba进行整合后能发挥两个的优势,dubbo的rpc调用的性能会很好,SpringCloudAlibaba服务治理能力很强。微服务中我们经常使用feign,feign是发送http请求而dubbo是rpc,性能要好。我们可以用dubbo代替feign的http请求。并且他作为一个老牌子的分布式框架用的人还是比较多的。
1.1 什么是分布式系统?
-
《分布式系统原理与范型》定义:
- “分布式系统是若干独立计算机的集合,这些计算机对于用户来说就像单个相关系统”
- 分布式系统(distributed system)是建立在网络之上的软件系统。
- 简单来说:多个(不同职责)人共同来完成一件事!
- 任何一台服务器都无法满足淘宝的双十一的数据吞吐量,一定是很多台服务器公共来完成的。
-
歇后语:“三个臭皮匠赛过诸葛亮”,就是分布式系统的真实写照
1.2 服务架构的演变
1.2.1 单体架构
单体架构应该是我们最先接触到的架构实现了,在单体架构中使用经典的三层模型,即表现层,业务逻辑层和数据访问层。
单体架构只适合在应用初期,且访问量比较下的情况下使用,
优点:
- 性价比很高
- 开发速度快
- 成本低
缺点:
- 代码耦合,开发维护困难
- 无法针对不同模块进行针对性优化
- 无法水平扩展
- 单点容错率低,并发能力差
1.2.2 集群
针对单个服务器在访问量越来越大的情况越来越吃力的情况,我们可以考虑服务器的集群化处理。
集群的部署大大提高了服务的处理能力,同时利用Nginx提供的负载均衡机制,来分发请求,使用户的体验没有改变。
1.2.3 垂直拆分
上面的集群部署是可以解决一部分的服务器压力,但是随着用户访问量的增多,集群节点增加到一定阶段的时候,其实作用就已经不是太大了,因为将所有的业务都集中在一起,造成耦合度很高,这时我们可以考虑业务的拆分。来提高系统的性能。比如将原来在一个系统里面的业务拆分为用户系统,订单系统和商品系统。也就是我们讲的垂直化拆分如下:
服务垂直化拆分后是可以大大的提高整体的服务处理能力,但是也会出现很多的冗余的代码,比如用户系统要操作订单库,要操作商品库,订单系统也有可能要操作用户库和商品库等。
优点:
- 系统拆分实现了流量分担,解决了并发问题
- 可以针对不同模块进行优化
- 方便水平扩展,负载均衡,容错率提高
缺点:
- 系统间相互独立,会有很多重复开发工作,影响开发效率
1.2.4 分布式服务
当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。此时,用于提高业务复用及整合的分布式调用是关键。
优点:
- 将基础服务进行了抽取,系统间相互调用,提高了代码复用和开发效率
缺点:
- 系统间耦合度变高,调用关系错综复杂,难以维护
1.2.5 服务治理(SOA)
针对垂直化拆分出现的问题,这时就出现了我们经常听到的SOA(面向服务的架构).什么是SOA呢?在《微服务设计》中有这么一段描述
SOA是一种设计方法,其中包括多个服务,而服务之间通过配合最终会提供一系列功能,一个服务通常以独立的形式存在于操作系统进程中,服务之间通过网络调用,而非采用进程内调用的方式进行通信。
ESB:简单来说ESB就是一根管道,用来连接各个服务节点,为了集成不同的系统,不同协议的服务,ESB要实现消息的转发解释和路由的功能,让不同的服务进行互通
- 缺点:
- 每个供应商提供的ESB产品本身有偏差,自身实现起来比较复杂;应用服务多了之后,ESB要去集成所有服务的协议,数据转换和运维部署将变的比较困难,所有服务都是通过一根管道进行通信,直接降低了通信速度。
1.2.6 微服务化
微服务架构就是使用一套小服务来开发单体应用的方式或途径,每个服务都是基于单一业务能力构建,运行在自己的进程中,并且使用轻量级的机制进行通信,通常就是HTTP或者RPC,能够通过自动化部署机制来自动部署环境。这些服务可以使用不同的编程语言,不同的数据库存储技术,并保持最低限度的集中式管理。
运维困难: 我们这二三十个服务,二三十个服务对应二三十个服务这样部署
分布式事务: 商品、交易的等等这些调用会出现调用失败,如果失败就需要回滚,这个是不是就设计到分布式事务了,本地事务就解决不了问题
定位问题:我们需要定位那个服务出现,并且需要定位具体哪台机器出现问题
1.3 Dubbo简介
1.3.1 常见的微服务框架
- Spring Cloud、Spring Cloud Alibaba
- Dubbo
- Thrift (社区活跃度不是很好)
1.3.2 Dubbo是什么
- Dubbo是分布式服务框架,是阿里巴巴的开源项目,现交给apache进行维护
- Dubbo致力于提高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案
- 简单来说,dubbo是个服务框架,如果没有分布式的需求,是不需要用的、
1.3.3 里程碑
里程碑 | 里程碑内容 |
---|---|
2011年10月 | 阿里巴巴完成Dubbo开源 |
2012年到2014年 | Dubbo开源社区蓬勃发展,多个互联网大厂进行封装使用课 |
2014年10月 | Dubbo停止更新 |
2017年9月 | 阿里巴巴重启Dubbo的维护和更新 |
2018年2月 | Dubbo进入Apache顶级孵化项目 |
2021年3月 | Dubbo 3.x正式进入发布倒计时 |
1.4 Dubbo整体架构
1.4.1 RPC
- RPC【Remote Procedure Call】是指远程过程调用,是一种进程间通信方式
- RPC基本的通信原理
- 在客户端将对象进行序列化
- 底层通信框架使用netty(基于tcp协议的socket),将序列化的对象发给服务方提供方
- 服务提供方通过socket得到数据文件之后,进行反序列化,获得要操作的对象
- 对象数据操作完毕,将新的对象序列化,再通过服务提供方的socket返回给客户端
- 客户端获得序列化数据,再反序列化,得到最新的数据对象,至此,完成一次
RPC两个核心模块:通讯(socket),序列化。
节点 | 角色说明 |
---|---|
Provider | 服务的提供方 |
Consumer | 服务的消费方 |
Registry | 服务注册与发现的注册中心 |
Monitor | 监控服务的统计中心 |
Container | 服务运行容器 |
1.4.2 Dubbo架构
1.服务容器负责启动,加载,运行服务提供者;
2.服务提供者在启动时,向注册中心注册自己提供的服务;
3.服务消费者在启动时,向注册中心订阅自己所需的服务;
4.在注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者;
5.服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用;
6.服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心;
2、构建Dubbo工程
2.1 框架依赖
- Maven
- SpringBoot 2.6.11
- Dubbo 3.1.8 + zookeeper 3.4.14
2.2 搭建Zookeeper
-
解压
-
修改zk的配置文件
进入conf,将文件zoo_sample.cfg 改为zoo.cfg
-
测试zk
启动zookeeper
执行zookeeper根目录下,bin文件中的zkServer.cmd
上面的CMD窗口不要关闭,这样zookeeper就是出于运行状态了
2.3 创建工程
2.3.1 创建父工程
mdb-dubbo-ann
父工程控制版本:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.11</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<modules>
<module>dubbo-consumer</module>
<module>dubbo-provider</module>
<module>dubbo-common</module>
</modules>
<groupId>com.msb</groupId>
<artifactId>msb-dubbo-ann</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>msb-dubbo-ann</name>
<packaging>pom</packaging>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<dubbo-version>3.1.8</dubbo-version>
</properties>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>${dubbo-version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-dubbo</artifactId>
<version>${dubbo-version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-zookeeper</artifactId>
<version>${dubbo-version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
2.3.2 创建提供者
dubbo-provider
引入依赖:
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-dubbo</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-zookeeper</artifactId>
</dependency>
增加配置
server:
port: 8002
logging:
config: classpath:logback.xml
dubbo:
application:
name: dubbo-provider
protocol:
name: dubbo
#客户端链接20880就可以访问我们的dubbo
port: 20883
registry:
address: zookeeper://127.0.0.1:2181
更改主类
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ImportResource;
// 因为是自动装配也可以不加这个注解
@EnableDubbo(scanBasePackages = "com.msb.dubbo.provider.service")
@SpringBootApplication
public class DubboProviderApplication {
public static void main(String[] args) {
SpringApplication.run(DubboProviderApplication.class);
}
}
接着增加通信端口
public interface IUserService {
User getUserById(Long id);
}
@Data
@AllArgsConstructor
@Builder
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
private String name;
private int age;
}
增加实现类
@DubboService// 定义一个dubbo服务
public class UserServiceImpl implements IUserService {
@Override
public User getUserById(Long id) {
User user = User.builder().id(id)
.age(12)
.name("天涯")
.build();
return user;
}
2.3.3 创建客户端
引入依赖
<!--这里是dubbo和SpringBoot桥梁的整合-->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
</dependency>
<!--每一种协议都会对应的一个jar比方:dubbo、rest、tripe 三种协议-->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-dubbo</artifactId>
</dependency>
<!--注册中心可以是zk,nacos -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-zookeeper</artifactId>
</dependency>
更改配置
server:
port: 8001
logging:
config: classpath:logback.xml
dubbo:
application:
name: dubbo-consumer
registry:
address: zookeeper://127.0.0.1:2181
更改主类
@EnableDubbo
@SpringBootApplication
public class DubboConsumeApplication {
public static void main(String[] args) {
SpringApplication.run(DubboConsumeApplication.class);
}
}
增加调用接口
public interface IUserService {
User getUserById(Long id);
}
@Data
@AllArgsConstructor
@Builder
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
private String name;
private int age;
}
增加业务调用处理
@RestController
public class OrderController {
@Autowired
private OrderService orderService;
@RequestMapping("/createOrder/{userId}")
public String createOrder(@PathVariable("userId") Long userId){
return orderService.createOrder(userId);
}
}
@Slf4j
@Service
public class OrderService {
// 引用对应的dubbo服务
@DubboReference
private IUserService iUserService;
public String createOrder(Long userId){
User user = iUserService.getUserById(userId);
log.info("用户用户信息:{}",user);
return "创建订单成功";
}
}
2.3.5 重构创建公共模块
dubbo-common 存放IUserService 和User
提供端和消费端
<dependency>
<groupId>com.msb</groupId>
<artifactId>dubbo-common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
2.3.6 测试
2.4 开启rest协议
如果我们的服务希望既要支持dubbo协议调用,也要能支持http调用,所以,要么仍然保留SpringMVC那一套,如果不想保留那一套,就可以开启 dubbo中的rest协议。
2.4.1 增加依赖
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-rest</artifactId>
</dependency>
2.4.2 更改配置
dubbo:
application:
name: dubbo-provider
# 这里的协议加了s,所以可以设置多个通信协议
protocols:
p1:
name: dubbo
#客户端链接20883就可以访问我们的dubbo
port: 20883
p2:
name: rest
#客户端链接20884就可以访问我们的rest
port: 20884
2.4.3 更改对应代码服务
@DubboService// 定义一个dubbo服务
@Path("/user")
public class UserServiceImpl implements IUserService {
@GET
@Path("/{userId}")
@Produces(MediaType.APPLICATION_JSON)
@Override
public User getUserById(@PathParam("userId") Long userId) {
User user = User.builder().id(userId)
.age(12)
.name("天涯")
.build();
return user;
}
}
2.4.4 测试
在消费端增加RestTemplate
@EnableDubbo
@SpringBootApplication
public class DubboConsumeApplication {
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(DubboConsumeApplication.class);
}
}
@Slf4j
@Service
public class OrderService {
@DubboReference
private IUserService iUserService;
@Autowired
RestTemplate restTemplate;
public String createOrder(Long userId){
User user = restTemplate.getForObject("http://localhost:20884/user/232",User.class);
log.info("用户用户信息:{}",user);
return "创建订单成功";
}
}
http://localhost:8001/createOrder/232
2.4.5 使用接口调用Rest
将rest协议放到common中
修改IUserService
@Path("/user")
public interface IUserService {
@GET
@Path("/{userId}")
@Produces(MediaType.APPLICATION_JSON)
User getUserById(@PathParam("userId") Long id);
}
修改consume里面内容
@Slf4j
@Service
public class OrderService {
// 指定写协议
@DubboReference(protocol = "rest")
private IUserService iUserService;
@Autowired
RestTemplate restTemplate;
public String createOrder(Long userId){
User user = iUserService.getUserById(userId);
log.info("用户用户信息:{}",user);
return "创建订单成功";
}
}
如果我们不能确定是否是走的http,我们可以DispatcherServlet#service 里面打个端点,看是否进入
3、Tripe协议
3.1 HTTP1.x协议
POST /user HTTP/1.1 // 请求行
Host: www.user.com
Content-Type: application/x-www-form-urlencoded
Connection: Keep-Alive
User-agent: Mozilla/5.0. // 以上是请求头
(此处必须有一空行 | // 空行分割header和请求内容
name=world // 请求体(可选,如get请求时可选)
它会将上面字符转化为字节流,然后发送给对方,对方解析的时候会根据空格、回车、换行符进行解析
这里我们可以知道他的请求头比较大,占用空间比较多。
3.2 Dubbo协议
-
Magic - Magic High & Magic Low (16 bits)
标识协议版本号,Dubbo 协议:0xdabb
-
Req/Res (1 bit)
标识是请求或响应。请求: 1; 响应: 0。
-
2 Way (1 bit)
仅在 Req/Res 为1(请求)时才有用,标记是否期望从服务器返回值。如果需要来自服务器的返回值,则设置为1。
-
Event (1 bit)
标识是否是事件消息,例如,心跳事件。如果这是一个事件,则设置为1。
-
Serialization ID (5 bit)
标识序列化类型:比如 fastjson 的值为6。
-
Status (8 bits)
仅在 Req/Res 为0(响应)时有用,用于标识响应的状态。
- 20 - OK
- 30 - CLIENT_TIMEOUT
- 31 - SERVER_TIMEOUT
- 40 - BAD_REQUEST
- 50 - BAD_RESPONSE
- 60 - SERVICE_NOT_FOUND
- 70 - SERVICE_ERROR
- 80 - SERVER_ERROR
- 90 - CLIENT_ERROR
- 100 - SERVER_THREADPOOL_EXHAUSTED_ERROR
-
Request ID (64 bits)
标识唯一请求。类型为long。
-
Data Length (32 bits)
序列化后的内容长度(可变部分),按字节计数。int类型。
-
Variable Part
被特定的序列化类型(由序列化 ID 标识)序列化后,每个部分都是一个 byte [] 或者 byte
dubbo协议在Dubbo框架内使用还是比较舒服的,并且dubbo协议相比于http1.x协议,性能会更好,因为请求中没
有多余的无用的字节,都是必要的字节,所以dubbo协议成为了Dubbo框架中的默认协议。
但是dubbo协议一旦涉及到跨RPC框架,比如一个Dubbo服务要调用gPRC服务,就比较麻烦了,因为发一个dubbo
协议的请求给一个gPRC服务,gPRC服务只会按照gRPC的格式来解析字节流,最终肯定会解析不成功的。
dubbo协议虽好,但是不够通用,所以这就出现了Triple协议,Triple协议是基于HTTP2,没有性能问题,另外HTTP
协议非常通用,全世界都认它,兼容起来也比较简单,而且还有很多额外的功能,比如流式调用。
3.3 Triple 协议
Triple 协议是 Dubbo3 推出的主力协议。Triple 意为第三代,通过 Dubbo1.0/ Dubbo2.0 两代协议的演进,以及云原生带来的技术标准化浪潮,Dubbo3 新协议 Triple 应运而生。
3.4 项目改造
3.4.1 父工程引入依赖版本
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-triple</artifactId>
<version>${dubbo-version}</version>
</dependency>
3.4.2 服务提供者引入依赖
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-triple</artifactId>
</dependency>
3.4.3 引入协议
dubbo:
application:
name: dubbo-provider
protocols:
p1:
name: dubbo
#客户端链接20880就可以访问我们的dubbo 20880 是默认端口
port: 20883
p2:
name: rest
#客户端链接20884就可以访问我们的rest
port: 20884
p3:
name: tri
port: 20885
3.4.4 消费方
引入依赖
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-triple</artifactId>
</dependency>
调用处更改协议
@Slf4j
@Service
public class OrderService {
@DubboReference(protocol = "tri")
private IUserService iUserService;
@Autowired
RestTemplate restTemplate;
public String createOrder(Long userId){
User user = iUserService.getUserById(userId);
log.info("用户用户信息:{}",user);
return "创建订单成功";
}
}
3.4.5 测试
http://localhost:8001/createOrder/232
3.5 Triple Streaming
3.5.1 使用场景
在一些大文件传输、直播等应用场景中, consumer或provider需要跟对端进行大量数据的传输,由于这些情况下的数据量是非常大的,因此是没有办法可以在一个RPC的数据包中进行传输,因此对于这些数据包我们需要对数据包进行分片之后,通过多次RPC调用进行传输,如果我们对这些已经拆分了的RPC数据包进行并行传输,那么到对端后相关的数据包是无序的,需要对接收到的数据进行排序拼接,相关的逻辑会非常复杂。但如果我们对拆分了的RPC数据包进行串行传输,那么对应的网络传输RTT与数据处理的时延会是非常大的。
为了解决以上的问题,并且为了大量数据的传输以流水线方式在consumer与provider之间传输,因此Streaming RPC的模型应运而生。
3.5.2 代码实战
-
common中引入jar
父工程:
<dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-common</artifactId> <version>${dubbo-version}</version> </dependency>
common中
<dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-common</artifactId> </dependency>
-
更改服务端代码
@Path("/user") public interface IUserService { // UNARY @GET @Path("/{userId}") @Produces(MediaType.APPLICATION_JSON) User getUserById(@PathParam("userId") Long id); // SERVER_STREAM 服务端流 default void sayHelloServerStream(String name, StreamObserver<String> response){ } // CLIENT_STREAM / BI_STREAM 双端流 default StreamObserver<String> sayHelloStream(StreamObserver<String> response){ return response; } }
-
服务端流处理
@DubboService// 定义一个dubbo服务 public class UserServiceImpl implements IUserService { @Override public void sayHelloServerStream(String name, StreamObserver<String> response) { response.onNext("hello 1"); response.onNext("hello 2"); response.onNext("hello 3"); response.onNext("hello 4"); response.onCompleted(); } }
@Service public class OrderService { @DubboReference(protocol = "tri") private IUserService iUserService; public String createOrder(Long userId){ iUserService.sayHelloServerStream("李华", new StreamObserver<String>() { @Override public void onNext(String data) { System.out.println("接收到的响应数据:" + data); } @Override public void onError(Throwable throwable) { System.err.println("异常处理"); } @Override public void onCompleted() { System.out.println("响应数据完成"); } }); return "创建订单成功"; } }
-
双端流
@DubboService// 定义一个dubbo服务 public class UserServiceImpl implements IUserService { @Override public User getUserById(Long userId) { User user = User.builder().id(userId) .age(12) .name("天涯") .build(); return user; } @Override public StreamObserver<String> sayHelloStream(StreamObserver<String> response) { return new StreamObserver<String>() { @Override public void onNext(String data) { // 接收到客户端发送过来的数据,进行处理,将结果返回 try { Thread.sleep(3*1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("接收到客户端数据:" + data); response.onNext("服务端影响:" + data); } @Override public void onError(Throwable throwable) { } @Override public void onCompleted() { System.out.println("服务端处理完毕"); } }; } }
-
public class OrderService {
@DubboReference(protocol = "tri")
private IUserService iUserService;
@Autowired
RestTemplate restTemplate;
public String createOrder(Long userId){
StreamObserver streamObserver = iUserService.sayHelloStream(new StreamObserver() {
@Override
public void onNext(Object data) {
System.out.println("接收到响应数据:" + data);
}
@Override
public void onError(Throwable throwable) {
}
@Override
public void onCompleted() {
System.out.println("接收到响应数据完毕");
}
});
streamObserver.onNext("request hello 1");
streamObserver.onNext("request hello 2");
streamObserver.onNext("request hello 3");
streamObserver.onCompleted();
return "创建订单成功";
}
}
4、dubbo-admin
4.1下载
4.2 更改配置
进入application.properties配置文件所在的目录
dubbo-admin-0.5.0\dubbo-admin-server\src\main\resources\application.properties
设置zk地址
登录时候用户名秘密
更改端口
4.3 进行编译
mvn clean package
找到编译后到jar包:
dubbo-admin-0.5.0\dubbo-admin-distribution\target
4.4 启动
jar -jar xxx.jar
4.5 访问
localhost:10010
4.6 使用提供jar的注意事项
注册中心: 127.0.0.1:2181
端口号: 10010
用户名/密码: root/root
5、参数设置
5.1 版本和分组
Dubbo服务中,接口并不能唯一确定一个服务,只有接口+分组+版本号才能唯一确定一个服务。
使用场景
- 当同一个接口针对不同的业务场景、不同的使用需求或者不同的功能模块等场景,可使用服务分组来区分不同的实现方式。同时,这些不同实现所提供的服务是可并存的,也支持互相调用。
- 当接口实现需要升级又要保留原有实现的情况下,即出现不兼容升级时,我们可以使用不同版本号进行区分。
使用方式
使用 @DubboService 注解,添加 group 参数和 version 参数 本示例中使用"发布和调用" 中示例代码
代码处理
服务端,我们指定组合版本,然后启动两个服务,注意两个服务的端口不能相同
@DubboService(group = "group1",version = "1.0")// 定义一个dubbo服务
public class UserServiceImpl implements IUserService {
@Override
public User getUserById(Long userId) {
User user = User.builder().id(userId)
.age(12)
.name("天涯")
.build();
log.info("服务获取用户信息:{}",user);
return user;
}
修改消费端指定版本
@DubboReference(protocol = "tri",group = "group1",version = "2.0")
private IUserService iUserService;
测试,访问只会达到对应的version=“2.0” 的版本
5.2 启动检查
启动时会在注册中心检查依赖的服务是否可用,不可用时会抛出异常
5.3 超时时间
-
由于网络或服务端不可靠,会导致调用过程中出现不确定的阻塞状态(超时)
-
为了避免超时导致客户端资源(线程)挂起耗尽,必须设置超时时间
-
在服务提供者添加如下配置:
-
返回结果
-
配置原则
dubbo推荐在Provider上尽量多配置Consumer端属性:
- 作服务的提供者,比服务使用方更清楚服务性能参数,如调用的超时时间,合理的重试次数,等等
- 在Provider配置后,Consumer不配置则会使用Provider的配置值,即Provider配置可以作消费者的缺省值。
5.4 重试次数
-
当出现失败,自动切换并重试其它服务器,dubbo重试的缺省值是2次,我们可以自行设置
-
在provider提供方配置
-
在consume配置
-
并不是所有的方法都适合设置重试次数
- 幂等方法:适合(当参数一样,无论执行多少次,结果是一样的,例如:查询,修改)
- 非幂等方法:不适合(当参数一样,执行结果不一样,例如:删除,添加)
备注: 重试是在dubbo协议下重试, tri协议下不会生效, consumer和provider同时设置重试则客户端生效
-
指定不同方法的重试,这是后我们需要引入xml
-
增加方法
-
public interface IUserService { User getUserById(@PathParam("userId") Long id); User getUserInfoById(@PathParam("userId") Long id); // 有具体实现类 }
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://dubbo.apache.org/schema/dubbo" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"> <!-- 调用远程Producer的服务 --> <dubbo:reference id="iUserService" interface="com.msb.common.service.IUserService" timeout="200" protocol="dubbo"> <dubbo:method name="getUserById" retries="3"/> <dubbo:method name="getUserInfoById" retries="1"/> </dubbo:reference> </beans>
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://dubbo.apache.org/schema/dubbo" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"> <!-- 调用远程Producer的服务 --> <dubbo:reference id="iUserService" interface="com.msb.common.service.IUserService" timeout="200" protocol="dubbo"> <dubbo:method name="getUserById" retries="3"/> <dubbo:method name="getUserInfoById" retries="1"/> </dubbo:reference> </beans>
-
这里用Autowired注入
6、过滤器
6.1 Apache Dubbo Filter介绍
-
Apache Dubbo的Filter与Servlet的Filter功能类似
-
自定义Filter需要扩展Filter接口
-
自定义Filter可以通过@Activate注解完成默认开启
-
filter 分consumer和provider
6.2 自定义Filter
//这里group就是定义提供者还是消费者
@Activate(group = "provider")
public class MyFilter implements Filter {
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
URL url = invoker.getUrl();
Class<?> anInterface = invoker.getInterface();
String simpleName = anInterface.getSimpleName();
String serviceName = invocation.getServiceName();
String methodName = invocation.getMethodName();
System.out.println("url = " + url.toFullString());
System.out.println("simpleName = " + simpleName);
System.out.println("serviceName = " + serviceName);
System.out.println("methodName = " + methodName);
// 调用下一级
Result result = invoker.invoke(invocation);
return result;
}
}
6.3 配置Filter
创建目录
然后在里面创建接口文件
文件内容:对应实现类的全路径
6.4 访问
7、上下文参数传递
7.1 使用场景
1、Dubbo系统间调用时,想传递一些通用参数,可通过Dubbo提供的扩展如Filter等实现统一的参数传递
2、Dubbo系统间调用时,想传递接口定义之外的参数,可在调用接口前使用setAttachment传递参数。
7.2 实战使用
客户端代码:
package com.msb.dubbo.consumer.filters;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;
@Activate(group = "consumer")
public class ConsumerContextFilter implements Filter {
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
Result result = null;
RpcContext.getClientAttachment()
.setAttachment("xid","1111111111111");
try {
//执行业务逻辑
result = invoker.invoke(invocation);
}finally {
//清理
RpcContext.getClientAttachment().clearAttachments();
}
return result;
}
}
客户端配置
服务端代码
package com.msb.dubbo.provider.filter;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;
import java.util.Map;
@Slf4j
@Activate(group = "provider")
public class ProviderContextFilter implements Filter {
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
//ServerAttachment接收客户端传递过来的参数
Map<String, Object> serverAttachments = RpcContext.getServerAttachment().getObjectAttachments();
log.info("ContextService serverAttachments:" + JSON.toJSONString(serverAttachments));
String xid = (String)serverAttachments.get("xid");
log.info("获取传递数据xid:{}",xid);
//执行业务逻辑
Result result = invoker.invoke(invocation);
return result;
}
}
服务端配置
8、Dubbo 工程XML配置
8.1 创建父工程
更改pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<modules>
<module>dubbo-provider</module>
<module>dubbo-consumer</module>
<module>dubbo-common</module>
</modules>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.msb</groupId>
<artifactId>msb-dubbo-xml</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>msb-dubbo-xml</name>
<packaging>pom</packaging>
<description>dubbo学习实例</description>
<properties>
<java.version>1.8</java.version>
<dubbo-version>3.1.8</dubbo-version>
</properties>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>${dubbo-version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-dubbo</artifactId>
<version>${dubbo-version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-rest</artifactId>
<version>${dubbo-version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-triple</artifactId>
<version>${dubbo-version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-common</artifactId>
<version>${dubbo-version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-zookeeper</artifactId>
<version>${dubbo-version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
8.2 创建Provider
引入依赖
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-dubbo</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-zookeeper</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
application.yml配置
server:
port: 8001
logging:
config: classpath:logback.xml
applicationContext-dubbo.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://dubbo.apache.org/schema/dubbo
http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<!-- 提供方应用信息,用于计算依赖关系,name可以随便起名,但是不能重复 -->
<dubbo:application name="dubbo-provider"/>
<!-- 使用zookeeper为注册中心,客户端使用curator -->
<dubbo:registry address="zookeeper://localhost:2181" client="curator"/>
<!--对外提供服务helloServiceAPI,服务对应的实现类是ref="HelloServiceImpl"-->
<dubbo:protocol name="dubbo" port="20885"/>
<dubbo:service id="helloServiceAPI"
interface="com.msb.service.HelloServiceAPI"
ref="HelloServiceImpl" retries="2">
</dubbo:service>
</beans>
主类配置
@ImportResource(locations = {"classpath:applicationContext-dubbo.xml"})
@SpringBootApplication
public class DubboProviderApplication {
public static void main(String[] args) {
SpringApplication.run(DubboProviderApplication.class);
}
}
服务实现
@Slf4j
public class HelloServiceImpl implements HelloServiceAPI {
@Override
public String sayHello(String message) {
log.info("接收到消息:{}",message );
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "生产者接收到数据:" + message;
}
}
dubbo-common工程以及接口创建
public interface HelloServiceAPI {
String sayHello(String message);
}
更改applicationContext-dubbo.xml
<bean id="HelloServiceImpl" class="com.msb.provider.service.impl.HelloServiceImpl"/>
8.3 创建Consumer
创建工程引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.msb</groupId>
<artifactId>dubbo-common</artifactId>
<version>0.0.2-SNAPSHOT</version>
</dependency>
<!--这里是dubbo和SpringBoot桥梁的整合-->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
</dependency>
<!--每一种协议都会对应的一个jar比方:dubbo、rest、tripe 三种协议-->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-dubbo</artifactId>
</dependency>
<!--注册中心可以是zk,nacos -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-zookeeper</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
application.yml配置
server:
port: 8002
logging:
config: classpath:logback.xml
applicationContext-dubbo.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://dubbo.apache.org/schema/dubbo
http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<!-- 提供方应用信息,用于计算依赖关系,name可以随便起名,但是不能重复 -->
<dubbo:application name="dubbo-consumer" />
<!-- 使用zookeeper为注册中心,客户端使用curator -->
<dubbo:registry address="zookeeper://localhost:2181" client="curator"/>
<!-- 调用远程Producer的服务 -->
<dubbo:reference id="helloServiceAPI" timeout="200" interface="com.msb.service.HelloServiceAPI" retries="3"/>
</beans>
主类创建
@ImportResource(locations = {"classpath:applicationContext-dubbo.xml"})
@SpringBootApplication
public class DubboConsumeApplication {
public static void main(String[] args) {
SpringApplication.run(DubboConsumeApplication.class);
}
}
业务逻辑
@RestController
public class HelloController {
@Autowired
private HelloServiceAPI helloServiceAPI;
@GetMapping("/sayHello")
public String sayHelle(){
return helloServiceAPI.sayHello("hello everyBody");
}
}
8.4 测试
http://localhost:8002/sayHello