Dubbo学习笔记¶
1、是什么?¶
1.1 定义¶
一款易用、高性能的 WEB 和 RPC 框架,于解决微服务架构下的服务治理与通信问题
功能:为构建企业级微服务提供服务发现、流量治理、可观测、认证鉴权等功能
2.x文档:https://cn.dubbo.apache.org/zh-cn/docsv2.7/user/
1.2 特点¶
- 服务提供者和消费者只在启动时与注册中心交互,注册中心不转发请求
- 消费者向注册中心获取服务提供者地址列表,并根据负载算法直接调用提供者
- 注册中心和监控中心全部宕机,不影响已运行的提供者和消费者,消费者在本地缓存了提供者列表
- 注册中心和监控中心都是可选的,服务消费者可以直连服务提供者
2、简单使用¶
分析:本地调用一般在A这个service中注入B service,然后调用B这个service中提供的各种方法,但是现在要远程调用(RPC)
2.1 定义服务接口¶
【思路】把B服务的接口放到公共的地方,然后A服务引入该依赖,B服务的具体实现想在哪在哪,只需要后续能够找到具体的实现就行。
服务接口:
public interface UserService {
List<UserAddress> getUserAddressList(String userId);
}
接口依赖的实体类:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserAddress implements Serializable {
private Integer id;
private String userAddress;
private String userId;
/**
* 收件人
*/
private String consignee;
private String phoneNum;
/**
* 是否默认地址
*/
private String isDefaultAddress;
}
pom文件依赖:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
</dependency>
2.2 服务提供方对接口进行具体实现¶
2.2.1 Spring方式¶
具体实现:
public class UserServiceImpl implements UserService {
@Override
public List<UserAddress> getUserAddressList(String userId) {
UserAddress userAddress1 = new UserAddress(1, "北京", "1", "张三", "123456", "0");
UserAddress userAddress2 = new UserAddress(2, "上海", "1", "张三", "123456", "1");
return Arrays.asList(userAddress1, userAddress2);
}
}
写xml配置,一般命名为provider.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!-- provider's application name, used for tracing dependency relationship -->
<dubbo:application name="user-service-provider"/>
<!-- use zk registry center to export service -->
<dubbo:registry address="zookeeper://127.0.0.1:2181"/>
<!-- 指定通信协议和端口号 -->
<dubbo:protocol name="dubbo" port="20880"/>
<!-- service implementation, as same as regular local bean -->
<bean id="userService" class="com.tcmyxc.gmall.service.impl.UserServiceImpl"/>
<!-- 声明需要暴露的服务(写接口名),ref指向真正的服务 -->
<dubbo:service interface="com.tcmyxc.gmall.service.UserService" ref="userService"/>
</beans>
pom文件依赖:
<!-- 定义接口的坐标 -->
<dependency>
<groupId>com.tcmyxc</groupId>
<artifactId>gmall-interface</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- Dubbo -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>${dubbo.version}</version>
</dependency>
<!-- zkclient -->
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.10</version>
</dependency>
启动代码:
public class Provider {
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"provider.xml"});
context.start();
System.in.read(); // press any key to exit
}
}
2.2.2 SpringBoot方式¶
pom依赖(springboot 版本 2.6.13)
<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>
<dependency>
<groupId>com.tcmyxc</groupId>
<artifactId>gmall-interface</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- dubbo,里面自带了zk的客户端 -->
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>0.2.0</version>
</dependency>
【注意】Dubbo 支持 zkclient 和 curator 两种 Zookeeper 客户端实现
dubbo-spring-boot-starter 0.2.0 自带 curator 客户端
配置文件 application.yml
dubbo:
application:
name: boot-user-service-provider
registry:
protocol: zookeeper
address: 127.0.0.1:2181
protocol:
name: dubbo
port: 20880
具体实现:
@Component
@Service // dubbo的注解
public class UserServiceImpl implements UserService {
@Override
public List<UserAddress> getUserAddressList(String userId) {
UserAddress userAddress1 = new UserAddress(1, "北京", "1", "张三", "123456", "0");
UserAddress userAddress2 = new UserAddress(2, "上海", "1", "张三", "123456", "1");
return Arrays.asList(userAddress1, userAddress2);
}
}
启动类:
@Component
@Service // dubbo的注解
public class UserServiceImpl implements UserService {
@Override
public List<UserAddress> getUserAddressList(String userId) {
UserAddress userAddress1 = new UserAddress(1, "北京", "1", "张三", "123456", "0");
UserAddress userAddress2 = new UserAddress(2, "上海", "1", "张三", "123456", "1");
return Arrays.asList(userAddress1, userAddress2);
}
}
2.3 服务消费者调用服务¶
2.3.1 Spring方式¶
pom文件依赖
<dependency>
<groupId>com.tcmyxc</groupId>
<artifactId>gmall-interface</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- Dubbo -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>${dubbo.version}</version>
</dependency>
<!-- zkclient -->
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.10</version>
</dependency>
xml配置文件:
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!-- 应用名 -->
<dubbo:application name="order-service-consumer"/>
<!-- 注册中心地址 -->
<dubbo:registry address="zookeeper://127.0.0.1:2181"/>
<!-- 声明想调用的远程服务接口;生成远程服务代理 -->
<dubbo:reference id="userService"
interface="com.tcmyxc.gmall.service.UserService"
check="false"
/>
</beans>
消费者代码:
public class OrderServiceImpl implements OrderService {
@Autowired
UserService userService;
@Override
public List<UserAddress> initOrder(String userId) {
// 1、查询用户的收货地址
List<UserAddress> userAddressList = userService.getUserAddressList(userId);
userAddressList.forEach(System.out::println);
return userAddressList;
}
}
测试代码:
public class Consumer {
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"consumer.xml"});
context.start();
// Obtaining a remote service proxy
UserService userService = (UserService) context.getBean("userService");
// Executing remote methods
List<UserAddress> userAddressList = userService.getUserAddressList("1");
userAddressList.forEach(System.out::println);
}
}
2.3.2 SpringBoot方式¶
pom依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.tcmyxc</groupId>
<artifactId>gmall-interface</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- dubbo -->
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>0.2.0</version>
</dependency>
application.yml
配置文件:
dubbo:
application:
name: boot-order-service-consumer
registry:
protocol: zookeeper
address: 127.0.0.1:2181
server:
port: 8081
消费者代码
@Service // spring的注解
public class OrderServiceImpl implements OrderService {
@Reference // dubbo的注解
UserService userService;
@Override
public List<UserAddress> initOrder(String userId) {
// 1、查询用户的收货地址
List<UserAddress> userAddressList = userService.getUserAddressList(userId);
// userAddressList.forEach(System.out::println);
return userAddressList;
}
}
@RestController
public class OrderController {
@Autowired
private OrderService orderService;
@GetMapping("/initOrder/{userId}")
public List<UserAddress> initOrder(@PathVariable String userId){
return orderService.initOrder(userId);
}
}
@EnableDubbo
@SpringBootApplication
public class BootOrderServiceConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(BootOrderServiceConsumerApplication.class, args);
}
}
3、特性¶
服务重试¶
调用一次之后,如出现非业务异常(服务突然不可用、超时等),Dubbo 默认会进行额外的最多2次重试
启动时检查¶
缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常。可以关闭
比如:
<!-- 声明想调用的远程服务接口;生成远程服务代理 -->
<dubbo:reference id="userService"
interface="com.tcmyxc.gmall.service.UserService"
/>
此时 UserService
未启动,消费者启动时会直接报错:
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userService': FactoryBean threw exception on object creation; nested exception is java.lang.IllegalStateException: Failed to check the status of the service com.tcmyxc.gmall.service.UserService. No provider available for the service com.tcmyxc.gmall.service.UserService
如果你在启动的时候不关心想要调用的服务是否可用,可以通过 check="false"
关闭检查
这种方式只是让消费者正常启动了,如何后续调用想用的接口,但是该接口的服务提供者一个都没有上线,会报调用错误
com.alibaba.dubbo.rpc.RpcException
负载均衡¶
策略:
- 随机(dubbo默认策略)
- 轮询
- 最少活跃调用数,相同活跃数的随机。
- 活跃数指调用前后计数差,可以理解为请求的处理时间。
- 使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。
- 一致性 Hash。
- 相同参数的请求总是发到同一提供者。
- 当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。
服务级别、方法级别均可配置
Dubbo 提供的是客户端负载均衡,即由 Consumer 通过负载均衡算法得出需要将请求提交到哪个 Provider 实例。
Dubbo 支持在服务提供者一侧配置默认的负载均衡策略,这样所有的消费者都将默认使用提供者指定的负载均衡策略,消费者可以自己配置要使用的负载均衡策略,如果都没有任何配置, 则默认使用随机负载均衡策略。
同一个应用内支持配置不同的服务使用不同的负载均衡策略,支持为同一服务的不同方法配置不同的负载均衡策略。
点对点直连¶
- 绕过注册中心,只测试指定服务提供者。
- 以接口为单位进行配置,这时接口A将会忽略注册中心返回的服务提供者列表
- 接口直接互不影响,A接口配置接口直连并不会影响B接口
【建议】只在测试中使用该功能
在消费者的 properties
配置文件中配置即可
com.alibaba.xxx.XxxService=dubbo://localhost:20890
也可以使用注解的方式,比如:
@Reference(url = "127.0.0.1:20881") // dubbo的注解,直连服务提供者
UserService userService;
其他注意点¶
1、映射规则
dubbo.protocol.rmi.port=1099
相当于<dubbo:protocol id="rmi" name="rmi" port="1099" />