1.创建springboot项目,引入maven依赖
<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>
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-nacos-spring-boot-starter</artifactId>
</dependency>
</dependencies>
2.定义服务接口
public interface ContextService {
String invoke(String param);
}
2.1 服务提供方实现代码
@DubboService
public class ContextServiceImpl implements ContextService{
@Override
public String invoke(String param) {
//ServerAttachment接收客户端传递过来的参数
Map<String, Object> serverAttachments =
RpcContext.getServerAttachment().getObjectAttachments();
System.out.println("ContextService serverAttachments:" +
JSON.toJSONString(serverAttachments));
//往客户端传递参数
RpcContext.getServerContext().setAttachment("serverKey","serverValue");
StringBuilder s = new StringBuilder();
s.append("ContextService param:").append(param);
return s.toString();
}
}
2.2 服务消费方调用代码
//往服务端传递参数
RpcContext.getClientAttachment().setAttachment("clientKey1","clientValue1");
String res = contextService.invoke("context1");
//接收传递回来参数
Map<String, Object> clientAttachment =
RpcContext.getServerContext().getObjectAttachments();
System.out.println("ContextTask clientAttachment:" +
JSON.toJSONString(clientAttachment));
System.out.println("ContextService Return : " + res);
3.注意事项
setAttachment 设置的 KV 对,在完成下面一次远程调用会被清空,即多次远程调用要多次设置。这个跟Threadlocal里面set有点类似,不同的是,tl中在每次请求完成以后需要手动remove保存的数据,避免内存泄漏。在dubbo自动清空传递的上下文参数,不需要程序员操心了。
下面是dubbo中服务间上下文参数传递的设计:
4.基于filter扩展机制实现自动添加token
有时候微服务之间调用的时候需要传递用户信息(token),这些用户信息往往就是一个token,通过token可以检查用户是否登录、是否有权限等。对于从进入网关的所有请求,我们可以统一处理,登录、鉴权等,然后把用户信息传递给dubbo微服务,不需要重复写很多设置上下文参数的代码。但是dubbo微服务之间调用的时候需要用到用户信息,这个时候最简单的做法的就是:在每个调用其他服务之前都通过RpcContext.getClientAttachment().setAttachment("userInfo",userInfo);
设置用户信息,然后在服务提供方通过:RpcContext.getServerAttachment().getAttachment("userInfo");
获取用户信息,这种方式可以实现,就是代码有些冗余,而且不利于维护和修改。
dubbo作为一个高性能微服务框架,这个需求早就给我们准备好了解决方案,就是用filter来解决。像springcloud中解决方案都是差不多,openfeign通过自定义过滤器来拦截服务间调用请求并添加用户信息到请求头,这样实现了无侵入的用户信息传递。
4.1 实现filter接口
// 实现org.apache.dubbo.rpc.Filter
// 这是在服务消费方设置用户信息,调用其他服务的时候就会把用户信息带上发送给服务提供方。
public class ConsumerFilter implements Filter {
public Result invoke(Invoker<?> invoker,
Invocation invocation) throws RpcException {
// before filter ...
RpcContext.getClientAttachment().setAttachment("userInfo",userInfo);
Result result = invoker.invoke(invocation);// remote invoke
// after filter ...
return result;
}
}
// 实现org.apache.dubbo.rpc.Filter
// 这是在服务提供方获取用户信息,当rpc调用请求到来的时候,可以获取消费方发送来的用户信息。
public class ProviderFilter implements Filter {
public Result invoke(Invoker<?> invoker,
Invocation invocation) throws RpcException {
// before filter ...
RpcContext.getServerAttachment().getAttachment("userInfo");
Result result = invoker.invoke(invocation);// remote invoke
// after filter ...
return result;
}
}
4.2 配置自定的filter
在resources目录下新建META-INF目录,然后在META-INF目录下创建dubbo目录,在dubbo目录中新建一个文本文件名字就叫:org.apache.dubbo.rpc.Filter,打开这个文件,在里面配置自定义的filter。
格式是:xxx=XxxFilter,key=value的格式,key主要不要重复就行了,还有不要跟dubbo内置的filter重名,后面的value就是自定filter的全限定类命。
# org.apache.dubbo.rpc.Filter文件中配置如下:
consumerauth=com.xx...ConsumerFilter
providerauth=com.xx...ProviderFilter
4.3 激活配置的filter
现在已经有两个filter,因为一个dubbo服务,既可以作为provider来提供服务,也可能作为consumer调用其他的dubbo服务。所以consumerauth是作为consumer时发送用户信息到provider,当然providerauth就是作为provider时获取consumer发送过来的用户信息。
虽然我们通过filter的名字,大概知道那个filter是在哪一方使用,但是dubbo不知道这个filter当地是在作为consumer时使用还是作为provider时使用,所以需要告诉dubbo这两个filter在什么时候使用。有两种方式指定filter在consumer还是provider时开启。
#
# 提示:只需要选择其中一种即可,如果你要两种都使用,请随便。
#
#
# 1. 第一种开启方式,在项目中dubbo配置中provider和consumer配置项中filter参数
#
dubbo:
# ...其他dubbo配置 nacos/protocal等
provider:
filter: providerauth
# 其他provider配置
consumer:
filter: consumerauth
# 其他consumer配置
#
# 2. 第二种方式,直接在ProviderFilter和ConsumerFilter类上面加上注解@Activate,
# 里面有个group属性,可以指定在哪一方生效。
#
@Activate(group="provider")// 作为provider时生效
public class ProviderFilter implements Filter {}
@Activate(group="consumer")// 作为consumer时生效
public class ConsumerrFilter implements Filter {}
5.项目启动报错
如果在启动dubbo服务的时候,很可能会出现类似下面的错误,不要慌:
解决办法就是引入kotlin-stdlib的依赖
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>1.8.21</version>
</dependency>