dubbo微服务开发-服务间调用上下文参数传递

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>

在这里插入图片描述

两岸猿声啼不住,轻舟已过万重山。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值