文章目录
- 一、写在前面
- 二、基于zookeeper:快速创建dubbo应用
-
- 1、maven包(客户端+服务端)(注意spring版本)
- [2、application.yml 配置文件(客户端+服务端)](#2、application.yml 配置文件(客户端+服务端))
- 3、定义公共接口
- 4、启动类添加注解@EnableDubbo
- 5、服务端
- 6、客户端
- 7、启动试试吧
- [8、拓展:使用 Java Config 代替注解](#8、拓展:使用 Java Config 代替注解)
- 三、拓展配置
- [附:Dubbo 支持的 Spring Boot Starter 清单](#附:Dubbo 支持的 Spring Boot Starter 清单)
一、写在前面
官方文档:https://cn.dubbo.apache.org/zh-cn/overview/
概念介绍部分就不多做介绍了,看官方文档吧。
本文实践为主,从头到尾梳理Dubbo使用的细节。
二、基于zookeeper:快速创建dubbo应用
1、maven包(客户端+服务端)(注意spring版本)
xml
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-bom</artifactId>
<version>3.3.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
xml
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-zookeeper-spring-boot-starter</artifactId>
</dependency>
2、application.yml 配置文件(客户端+服务端)
yml
dubbo:
application:
# 客户端和服务端要不一样,客户端:dubbo-springboot-demo-consumer
name: dubbo-springboot-demo-provider
protocol:
name: tri
# 随机端口
port: -1
registry:
id: zk-registry
address: zookeeper://127.0.0.1:2181
3、定义公共接口
java
package com.demo.springbootdemo;
public interface DemoService {
String sayHello(String name);
}
4、启动类添加注解@EnableDubbo
java
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
// @EnableDubbo 注解必须配置,否则将无法加载 Dubbo 注解定义的服务,@EnableDubbo 可以定义在主类上
@EnableDubbo
5、服务端
java
import org.apache.dubbo.config.annotation.DubboService;
// 定义好 Dubbo 服务接口后,提供服务接口的实现逻辑,并用 @DubboService 注解标记,就可以实现 Dubbo 的服务暴露
// 支持很多参数
// @DubboService(registry="zk-registry") 可以手动选择注册中心(通过id关联)
// @DubboService(version = "1.0.0", group = "dev", timeout = 5000)
// @DubboService(methods = {@Method(name = "sayHello", timeout = 5000)}) // 指定某个方法超时时间
@DubboService
public class DemoServiceImpl implements DemoService {
@Override
public String sayHello(String name) {
return "Hello " + name;
}
}
6、客户端
java
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class Consumer implements CommandLineRunner {
// scope="remote" is used to force mock remote service call
// 也有很多参数
// @DubboReference(version = "1.0.0", group = "dev", timeout = 5000)
@DubboReference(scope = "remote")
private DemoService demoService;
@Override
public void run(String... args) throws Exception {
String result = demoService.sayHello("world");
System.out.println("Receive result ======> " + result);
}
}
7、启动试试吧
8、拓展:使用 Java Config 代替注解
注意,Java Config 是 DubboService 或 DubboReference 的替代方式,对于有复杂配置需求的服务建议使用这种方式。
java
@Configuration
public class ProviderConfiguration {
@Bean
public ServiceConfig demoService() {
ServiceConfig service = new ServiceConfig();
service.setRegistry("zk-registry");
return service;
}
}
java
@Configuration
public class ProviderConfiguration {
@Bean
public ServiceBean demoService() {
ServiceBean service = new ServiceBean();
service.setInterface(DemoService.class);
service.setRef(new DemoServiceImpl());
service.setGroup("dev");
service.setVersion("1.0.0");
Map<String, String> parameters = new HashMap<>();
service.setParameters(parameters);
return service;
}
}
三、拓展配置
1、注册中心
yml
# application.yml
dubbo
registry
address: zookeeper://localhost:2181
# zk认证,可以不配置
username: hello
password: 1234
多级注册中心:
对于所有的 Service 服务,向所有全局默认注册中心注册服务地址。
对于所有的 Reference 服务,从所有全局默认注册中心订阅服务地址。
yml
# application.yml (Spring Boot)
dubbo
registries
beijingRegistry
address: zookeeper://localhost:2181
shanghaiRegistry
address: zookeeper://localhost:2182
# 设置默认全局的注册中心
# application.yml (Spring Boot)
dubbo
registries
beijingRegistry
address: zookeeper://localhost:2181
default: true
shanghaiRegistry
address: zookeeper://localhost:2182
default: false
# 可以在开发调试的过程中,设置本地启动的只注册在本地zk,并且调用测试环境的dubbo服务。
# 防止自己的服务注册到测试环境
dubbo:
registries:
#仅在开发时启用
private:
id: private
# register: true:服务提供者会将服务注册到这个注册中心。
# subscribe: false:服务消费者不会从这个注册中心订阅服务。
register: true
subscribe: false
address: zookeeper://127.0.0.1:2181
public:
id: public
protol: zookeeper
# register: false:服务提供者不会将服务注册到这个注册中心。
# subscribe: true:服务消费者会从这个注册中心订阅服务。
register: false
subscribe: true
address: zookeeper://test.zk.com:2181
2、版本与分组
Dubbo服务中,接口并不能唯一确定一个服务,只有 接口+分组+版本号
的三元组才能唯一确定一个服务。
当同一个接口针对不同的业务场景、不同的使用需求或者不同的功能模块等场景,可使用服务分组来区分不同的实现方式。同时,这些不同实现所提供的服务是可并存的,也支持互相调用。
当接口实现需要升级又要保留原有实现的情况下,即出现不兼容升级时,我们可以使用不同版本号进行区分。
java
@DubboService(group = "group1", version = "1.0")
public class DevelopProviderServiceV1 implements DevelopService{
@Override
public String invoke(String param) {
StringBuilder s = new StringBuilder();
s.append("ServiceV1 param:").append(param);
return s.toString();
}
}
@DubboService(group = "group2", version = "2.0")
public class DevelopProviderServiceV2 implements DevelopService{
@Override
public String invoke(String param) {
StringBuilder s = new StringBuilder();
s.append("ServiceV2 param:").append(param);
return s.toString();
}
}
java
// 客户端指定 分组和版本号
@DubboReference(group = "demo")
private DemoService demoService;
@DubboReference(group = "demo2")
private DemoService demoService2;
//group值为*,标识匹配任意服务分组
@DubboReference(group = "*")
private DemoService demoService2;
@DubboReference(group = "group1", version = "1.0")
private DevelopService developService;
@DubboReference(group = "group2", version = "2.0")
private DevelopService developServiceV2;
3、传递调用参数
java
// 客户端传递参数
RpcContext.getClientAttachment().setAttachment("index", "1"); // 隐式传参,后面的远程调用都会隐式将这些参数发送到服务器端,类似cookie,比如用于框架集成
xxxService.xxx(); // 远程调用
// ...
java
// 服务端,读取调用参数
public class XxxServiceImpl implements XxxService {
public void xxx() {
// 获取客户端隐式传入的参数,比如用于框架集成
String index = RpcContext.getServerAttachment().getAttachment("index");
}
}
参数透传问题
请注意!setAttachment 设置的 KV 对,在完成下面一次远程调用会被清空,即多次远程调用要多次设置!这一点与 Dubbo2 中的行为是不一致的!
比如,对于 Dubbo2 而言,在 A 端设置的参数,调用 B 以后,如果 B 继续调用了 C,原来在 A 中设置的参数也会被带到 C 端过去(造成参数污染的问题)。对于 Dubbo3,B 调用 C 时的上下文是干净的,不会包含最开始在 A 中设置的参数。
Dubbo3 提供的了支持参数透传的能力。通过实现以下 SPI 用户可以自行指定需要透传的参数,select 的结果(可以从 RpcClientAttachment 获取当前所有参数)将作为需要透传的键值对传递到下一跳,如果返回 null 则表示不透传参数。
java
@SPI
public interface PenetrateAttachmentSelector {
/**
* Select some attachments to pass to next hop.
* These attachments can fetch from {@link RpcContext#getServerAttachment()} or user defined.
*
* @return attachment pass to next hop
*/
Map<String, Object> select();
}
4、泛化调用
文档:https://cn.dubbo.apache.org/zh-cn/overview/mannual/java-sdk/tasks/framework/generic/
泛化调用适用于老版本 dubbo 通信协议
,如果您使用的是 3.3 及之后版本的 triple 协议,请直接使用 triple 自带的 http application/json 能力
直接发起服务调用
泛化调用(客户端泛化调用)是指在调用方没有服务提供方 API(SDK)的情况下,对服务方进行调用,并且可以正常拿到调用结果。调用方没有接口及模型类元,知道服务的接口的全限定类名和方法名的情况下,可以通过泛化调用调用对应接口。
java
private GenericService genericService;
public static void main(String[] args) throws Exception {
ApplicationConfig applicationConfig = new ApplicationConfig();
applicationConfig.setName("generic-call-consumer");
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setAddress("zookeeper://127.0.0.1:2181");
ReferenceConfig<GenericService> referenceConfig = new ReferenceConfig<>();
referenceConfig.setInterface("org.apache.dubbo.samples.generic.call.api.HelloService");
applicationConfig.setRegistry(registryConfig);
referenceConfig.setApplication(applicationConfig);
referenceConfig.setGeneric("true");
// do not wait for result, 'false' by default
referenceConfig.setAsync(true);
referenceConfig.setTimeout(7000);
genericService = referenceConfig.get();
}
public static void invokeSayHello() throws InterruptedException {
Object result = genericService.$invoke("sayHello", new String[]{"java.lang.String"}, new Object[]{"world"});
CountDownLatch latch = new CountDownLatch(1);
CompletableFuture<String> future = RpcContext.getContext().getCompletableFuture();
future.whenComplete((value, t) -> {
System.err.println("invokeSayHello(whenComplete): " + value);
latch.countDown();
});
System.err.println("invokeSayHello(return): " + result);
latch.await();
}
5、泛化实现
文档:https://cn.dubbo.apache.org/zh-cn/overview/mannual/java-sdk/tasks/framework/more/generic-impl/
泛接口实现方式主要用于服务器端没有 API 接口及模型类元的情况,参数及返回值中的所有 POJO 均用 Map 表示,通常用于框架集成,比如:实现一个通用的远程服务 Mock 框架,可通过实现 GenericService 接口处理所有服务请求。
使用场景
注册服务: 服务提供者在服务注册表中注册服务,例如 Zookeeper,服务注册表存储有关服务的信息,例如其接口、实现类和地址。
部署服务: 服务提供商将服务部署在服务器并使其对消费者可用。
调用服务: 使用者使用服务注册表生成的代理调用服务,代理将请求转发给服务提供商,服务提供商执行服务并将响应发送回消费者。
监视服务:提供者和使用者可以使用 Dubbo 框架监视服务,允许他们查看服务的执行情况,并在必要时进行调整。
6、Filter过滤器
java
package org.apache.dubbo.samples.extensibility.filter.provider;
import org.apache.dubbo.rpc.Filter;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.AsyncRpcResult;
public class AppendedFilter implements Filter {
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
Result result= invoker.invoke(invocation);
// Obtain the returned value
Result appResponse = ((AsyncRpcResult) result).getAppResponse();
// Appended value
appResponse.setValue(appResponse.getValue()+"'s customized AppendedFilter");
return result;
}
}
SPI配置:
在resources/META-INF/dubbo/org.apache.dubbo.rpc.Filter
文件中添加如下配置:
appended=org.apache.dubbo.samples.extensibility.filter.provider.AppendedFilter
配置文件:
在resources/application.properties
文件中添加如下配置,激活刚才的自定义 Filter 实现:
yml
# Apply AppendedFilter
dubbo.provider.filter=appended
除了通过配置激活 Filter 实现之外,还可以通过为实现类增加 @Activate 注解,以在满足某些条件时自动激活 Filter 实现,如:
@Activate(group="provider")
public class AppendedFilter implements Filter {}
这个 Filter 实现将在 Provider 提供者端自动被激活。
附:Dubbo 支持的 Spring Boot Starter 清单
官方文档:https://cn.dubbo.apache.org/zh-cn/overview/mannual/java-sdk/reference-manual/config/spring/spring-boot/#starter%E5%88%97%E8%A1%A8
以下是一些 dubbo-spring-boot-starter
版本对应的 SpringBoot、JDK 依赖:
版本 兼容 Spring Boot 范围
3.3.x [1.x ~ 3.x)
3.2.x [1.x ~ 3.x)
3.1.x [1.x ~ 2.x)
2.7.x [1.x ~ 2.x)
其他组件starter:
以下是 Dubbo 官方社区提供的 starter 列表(3.3.0+ 版本),方便在 Spring Boot 应用中快速使用:
dubbo-spring-boot-starter
,管理 dubbo 核心依赖,用于识别 application.properties 或 application.yml 中 dubbo. 开头的配置项,扫描 @DubboService 等注解。
dubbo-spring-boot-starter3
,管理 dubbo 核心依赖,与 dubbo-spring-boot-starter 相同,支持 spring boot 3.2 版本。
dubbo-nacos-spring-boot-starter
,管理 nacos-client 等依赖,使用 Nacos 作为注册中心、配置中心时引入。
dubbo-zookeeper-spring-boot-starter
,管理 zookeeper、curator 等依赖,使用 Zookeeper 作为注册中心、配置中心时引入(Zookeeper server 3.4 及以下版本使用)。
dubbo-zookeeper-curator5-spring-boot-starter
,管理 zookeeper、curator5 等依赖,使用 Zookeeper 作为注册中心、配置中心时引入。
dubbo-sentinel-spring-boot-starter
,管理 sentinel 等依赖,使用 Sentinel 进行限流降级时引入。
dubbo-seata-spring-boot-starter
,管理 seata 等依赖,使用 Seata 作为分布式事务解决方案时引入。
dubbo-observability-spring-boot-starter
,加入该依赖将自动开启 Dubbo 内置的 metrics 采集,可用于后续的 Prometheus、Grafana 等监控系统。
dubbo-tracing-brave-spring-boot-starter
,管理 brave/zipkin、micrometer 等相关相关依赖,使用 Brave/Zipkin 作为 Tracer,将 Trace 信息 export 到 Zipkin。
dubbo-tracing-otel-otlp-spring-boot-starter
,管理 brave/zipkin、micrometer 等相关相关依赖,使用 OpenTelemetry 作为 Tracer,将 Trace 信息 export 到 OTlp Collector。
dubbo-tracing-otel-zipkin-spring-boot-starter
,管理 brave/zipkin、micrometer 等相关相关依赖,使用 OpenTelemetry 作为 Tracer,将 Trace 信息 export 到 Zipkin。