Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RpcContext获取参数问题 #11876

Open
a4363220 opened this issue Mar 21, 2023 · 25 comments
Open

RpcContext获取参数问题 #11876

a4363220 opened this issue Mar 21, 2023 · 25 comments

Comments

@a4363220
Copy link

a4363220 commented Mar 21, 2023

system:centos7.9
jdk:11
dubbo:3.1.4
序列化:kryo1.0.1
问题:在消费者透传信息给提供者时,在提供者的方法第一行是可以获取到透传信息,但是在方法执行期间和执行末尾获取透传信息则为空,但是相同的案例 我把dubbo版本切回到3.1.0版本则一切正常,不太理解是因为什么原因造成的,还是官方已经得知该问题并修复了。

提供者代码如下:

    @Override
    public PageVo<QuerySysUserByPageVo> querySysUserByPage(PageDto pageDto, QuerySysUserByPageDto dto) {
        log.info("查询用户分页开始:{}",AdminRpcContext.getUser());
        // 统计
        Integer totalRowSize = userMapper.querySysUserByCount(dto);
        if (totalRowSize == 0) return null;
        // 计算分页
        PageOperationResultBo page = PageOperationTools.operationPage(pageDto, totalRowSize);
        // 查询分页结果
        List<QuerySysUserByPageVo> voList = userMapper.querySysUserByPage(page, dto);
        // 填充角色以及岗位
        voList.forEach(vo -> {
            vo.setRolesVo(userRoleMapper.querySysRolePersonalInfoList(vo.getId()));
            vo.setPostsVo(userPostMapper.querySysPostPersonalInfoList(vo.getId()));
        });
        log.info("查询用户分页完成:{}",AdminRpcContext.getUser());
        return PageVo.build(page, voList);
    }

3.1.4版本获取rpc透传信息大致打印如下:

2023-03-21 15:04:03.038 [DubboServerHandler-172.18.0.1:20100-thread-5] INFO  c.l.p.user.service.service.sys.SysUserServiceImp - 查询用户分页开始:AdminUserBo(id=1)
2023-03-21 15:04:03.038 [DubboServerHandler-172.18.0.1:20100-thread-5] INFO  c.l.p.service.core.mybatis.MybatisInterceptor - 拦截器获取:AdminUserBo(id=1)
2023-03-21 15:04:03.055 [DubboServerHandler-172.18.0.1:20100-thread-6] INFO  c.l.p.service.core.mybatis.MybatisInterceptor - 拦截器获取:AdminUserBo(id=1)
2023-03-21 15:04:04.039 [DubboServerHandler-172.18.0.1:20100-thread-6] INFO  c.l.p.service.core.mybatis.MybatisInterceptor - 拦截器获取:AdminUserBo(id=1)
2023-03-21 15:04:04.042 [DubboServerHandler-172.18.0.1:20100-thread-6] INFO  ShardingSphere-SQL - SQLStatement: MySQLSelectStatement(table=Optional.empty, limit=Optional.empty, lock=Optional.empty, window=Optional.empty)
2023-03-21 15:04:04.042 [DubboServerHandler-172.18.0.1:20100-thread-6] INFO  ShardingSphere-SQL - Actual SQL(simple): [master_0] ::: 1
2023-03-21 15:04:04.049 [DubboServerHandler-172.18.0.1:20100-thread-6] INFO  c.l.p.service.core.mybatis.MybatisInterceptor - 拦截器获取:AdminUserBo(id=1)
2023-03-21 15:04:04.081 [DubboServerHandler-172.18.0.1:20100-thread-5] INFO  c.l.p.s.core.mybatis.hanlder.MybatisQueryHandler - 旧sql:[1],数据权限:select count(*)
2023-03-21 15:04:04.200 [DubboServerHandler-172.18.0.1:20100-thread-5] INFO  c.l.p.service.core.mybatis.MybatisInterceptor - 拦截器获取:null
2023-03-21 15:04:04.211 [DubboServerHandler-172.18.0.1:20100-thread-7] INFO  c.l.p.service.core.mybatis.MybatisInterceptor - 拦截器获取:null
2023-03-21 15:04:04.215 [DubboServerHandler-172.18.0.1:20100-thread-7] INFO  ShardingSphere-SQL - SQLStatement: MySQLSelectStatement(table=Optional.empty, limit=Optional.empty, lock=Optional[org.apache.shardingsphere.sql.parser.sql.common.segment.dml.predicate.LockSegment@4233467d], window=Optional.empty)
2023-03-21 15:04:04.215 [DubboServerHandler-172.18.0.1:20100-thread-7] INFO  ShardingSphere-SQL - Actual SQL(simple): [master_0] ::: 1
2023-03-21 15:04:04.220 [DubboServerHandler-172.18.0.1:20100-thread-7] INFO  c.l.p.service.core.mybatis.MybatisInterceptor - 拦截器获取:null
2023-03-21 15:04:04.221 [DubboServerHandler-172.18.0.1:20100-thread-7] INFO  ShardingSphere-SQL - SQLStatement: MySQLSelectStatement(table=Optional.empty, limit=Optional.empty, lock=Optional.empty, window=Optional.empty)
2023-03-21 15:04:04.221 [DubboServerHandler-172.18.0.1:20100-thread-7] INFO  ShardingSphere-SQL - Actual SQL(simple): [master_0] ::: 1
2023-03-21 15:04:04.225 [DubboServerHandler-172.18.0.1:20100-thread-7] INFO  c.l.p.service.core.mybatis.MybatisInterceptor - 拦截器获取:null
2023-03-21 15:04:04.226 [DubboServerHandler-172.18.0.1:20100-thread-7] INFO  ShardingSphere-SQL - Logic SQL: select

2023-03-21 15:04:04.281 [DubboServerHandler-172.18.0.1:20100-thread-5] INFO  ShardingSphere-SQL - Actual SQL(simple): [master_0] ::: 1
2023-03-21 15:04:04.288 [DubboServerHandler-172.18.0.1:20100-thread-5] INFO  c.l.p.service.core.mybatis.MybatisInterceptor - 拦截器获取:null
2023-03-21 15:04:04.318 [DubboServerHandler-172.18.0.1:20100-thread-5] INFO  ShardingSphere-SQL - SQLStatement: MySQLSelectStatement(table=Optional.empty, limit=Optional.empty, lock=Optional.empty, window=Optional.empty)
2023-03-21 15:04:04.318 [DubboServerHandler-172.18.0.1:20100-thread-5] INFO  ShardingSphere-SQL - Actual SQL(simple): [master_0] ::: 1
2023-03-21 15:04:04.324 [DubboServerHandler-172.18.0.1:20100-thread-5] INFO  c.l.p.service.core.mybatis.MybatisInterceptor - 拦截器获取:null
2023-03-21 15:04:04.369 [DubboServerHandler-172.18.0.1:20100-thread-5] INFO  c.l.p.user.service.service.sys.SysUserServiceImp - 查询用户分页完成:null

3.1.0版本获取rpc透传信息大致打印如下:

2023-03-21 15:08:07.774 [DubboServerHandler-172.18.0.1:20100-thread-3] INFO  c.l.p.user.service.service.sys.SysUserServiceImp - 查询用户分页开始:AdminUserBo(id=1)
2023-03-21 15:08:07.790 [DubboServerHandler-172.18.0.1:20100-thread-3] INFO  c.l.p.service.core.mybatis.MybatisInterceptor - 拦截器获取:AdminUserBo(id=1)
2023-03-21 15:08:07.842 [DubboServerHandler-172.18.0.1:20100-thread-4] INFO  c.l.p.service.core.mybatis.MybatisInterceptor - 拦截器获取:AdminUserBo(id=1)
2023-03-21 15:08:08.749 [DubboServerHandler-172.18.0.1:20100-thread-4] INFO  ShardingSphere-SQL - SQLStatement: MySQLSelectStatement(table=Optional.empty, limit=Optional.empty, lock=Optional.empty, window=Optional.empty)
2023-03-21 15:08:08.749 [DubboServerHandler-172.18.0.1:20100-thread-4] INFO  ShardingSphere-SQL - Actual SQL(simple): [master_0] ::: 1
2023-03-21 15:08:08.900 [DubboServerHandler-172.18.0.1:20100-thread-3] INFO  ShardingSphere-SQL - Logic SQL: SELECT count(*) FROM sys_user WHERE id = ? AND create_user_id IN (1)
2023-03-21 15:08:08.900 [DubboServerHandler-172.18.0.1:20100-thread-3] INFO  ShardingSphere-SQL - SQLStatement: MySQLSelectStatement(table=Optional.empty, limit=Optional.empty, lock=Optional.empty, window=Optional.empty)
2023-03-21 15:08:08.900 [DubboServerHandler-172.18.0.1:20100-thread-3] INFO  ShardingSphere-SQL - Actual SQL(simple): [master_0] ::: 1
2023-03-21 15:08:08.907 [DubboServerHandler-172.18.0.1:20100-thread-3] INFO  c.l.p.service.core.mybatis.MybatisInterceptor - 拦截器获取:AdminUserBo(id=1)
2023-03-21 15:08:08.922 [DubboServerHandler-172.18.0.1:20100-thread-5] INFO  c.l.p.service.core.mybatis.MybatisInterceptor - 拦截器获取:AdminUserBo(id=1)
2023-03-21 15:08:08.926 [DubboServerHandler-172.18.0.1:20100-thread-5] INFO  ShardingSphere-SQL - SQLStatement: MySQLSelectStatement(table=Optional.empty, limit=Optional.empty, lock=Optional[org.apache.shardingsphere.sql.parser.sql.common.segment.dml.predicate.LockSegment@5d9c13de], window=Optional.empty)
2023-03-21 15:08:08.926 [DubboServerHandler-172.18.0.1:20100-thread-5] INFO  ShardingSphere-SQL - Actual SQL(simple): [master_0] ::: 1
2023-03-21 15:08:08.930 [DubboServerHandler-172.18.0.1:20100-thread-5] INFO  c.l.p.service.core.mybatis.MybatisInterceptor - 拦截器获取:AdminUserBo(id=1)
2023-03-21 15:08:08.931 [DubboServerHandler-172.18.0.1:20100-thread-5] INFO  ShardingSphere-SQL - SQLStatement: MySQLSelectStatement(table=Optional.empty, limit=Optional.empty, lock=Optional.empty, window=Optional.empty)
2023-03-21 15:08:08.931 [DubboServerHandler-172.18.0.1:20100-thread-5] INFO  ShardingSphere-SQL - Actual SQL(simple): [master_0] ::: 1
2023-03-21 15:08:08.935 [DubboServerHandler-172.18.0.1:20100-thread-5] INFO  c.l.p.service.core.mybatis.MybatisInterceptor - 拦截器获取:AdminUserBo(id=1)
2023-03-21 15:08:08.935 [DubboServerHandler-172.18.0.1:20100-thread-5] INFO  ShardingSphere-SQL - SQLStatement: MySQLSelectStatement(table=Optional.empty, limit=Optional.empty, lock=Optional.empty, window=Optional.empty)
2023-03-21 15:08:08.935 [DubboServerHandler-172.18.0.1:20100-thread-5] INFO  ShardingSphere-SQL - Actual SQL(simple): [master_0] ::: 1
2023-03-21 15:08:09.046 [DubboServerHandler-172.18.0.1:20100-thread-3] INFO  ShardingSphere-SQL - Logic SQL: SELECT users.id, users.dept_id, dept.name AS deptName, users.username, users.nickname, users.status, users.update_user_id, users.update_time, users.create_user_id, users.create_time FROM sys_user AS users LEFT JOIN sys_dept AS dept ON users.dept_id = dept.id WHERE users.id = ? AND users.create_user_id IN (1) ORDER BY users.id DESC LIMIT ?, ?
2023-03-21 15:08:09.046 [DubboServerHandler-172.18.0.1:20100-thread-3] INFO  ShardingSphere-SQL - SQLStatement: MySQLSelectStatement(table=Optional.empty, limit=Optional[org.apache.shardingsphere.sql.parser.sql.common.segment.dml.pagination.limit.LimitSegment@1919f13b], lock=Optional.empty, window=Optional.empty)
2023-03-21 15:08:09.046 [DubboServerHandler-172.18.0.1:20100-thread-3] INFO  ShardingSphere-SQL - Actual SQL(simple): [master_0] ::: 1
2023-03-21 15:08:09.052 [DubboServerHandler-172.18.0.1:20100-thread-3] INFO  c.l.p.service.core.mybatis.MybatisInterceptor - 拦截器获取:AdminUserBo(id=1)
2023-03-21 15:08:09.090 [DubboServerHandler-172.18.0.1:20100-thread-3] INFO  ShardingSphere-SQL - SQLStatement: MySQLSelectStatement(table=Optional.empty, limit=Optional.empty, lock=Optional.empty, window=Optional.empty)
2023-03-21 15:08:09.091 [DubboServerHandler-172.18.0.1:20100-thread-3] INFO  ShardingSphere-SQL - Actual SQL(simple): [master_0] ::: 1
2023-03-21 15:08:09.095 [DubboServerHandler-172.18.0.1:20100-thread-3] INFO  c.l.p.service.core.mybatis.MybatisInterceptor - 拦截器获取:AdminUserBo(id=1)erPost.post_id = post.id
2023-03-21 15:08:09.098 [DubboServerHandler-172.18.0.1:20100-thread-3] INFO  ShardingSphere-SQL - SQLStatement: MySQLSelectStatement(table=Optional.empty, limit=Optional.empty, lock=Optional.empty, window=Optional.empty)
2023-03-21 15:08:09.098 [DubboServerHandler-172.18.0.1:20100-thread-3] INFO  ShardingSphere-SQL - Actual SQL(simple): [master_0] ::: 1
2023-03-21 15:08:09.102 [DubboServerHandler-172.18.0.1:20100-thread-3] INFO  c.l.p.user.service.service.sys.SysUserServiceImp - 查询用户分页完成:AdminUserBo(id=1)

可以看到在3.1.0版本上从方法执行到末尾都可以正常拿到RpcContext参数

@LaiDeJi
Copy link

LaiDeJi commented Mar 21, 2023

不知道你的和我的问题对不对,我的问题是消费者setAttachment但是提供者get不到,看源码发现它走的是已废弃的代码getAttachment()方法。其中会if (value instanceof String),判断成立强转为string,不成立直接return null。

@AlbumenJ
Copy link
Member

不知道你的和我的问题对不对,我的问题是消费者setAttachment但是提供者get不到,看源码发现它走的是已废弃的代码getAttachment()方法。其中会if (value instanceof String),判断成立强转为string,不成立直接return null。

如果是 set 的 string 类型的这里不应该有问题。
另外 attachment 是提供给业务编程侧使用的,如果是在 Filter 内使用的话需要直接基于 invocation 对象操作

@AlbumenJ
Copy link
Member

@a4363220 AdminRpcContext 这个你们是怎么封装的,已经是在什么时机填充的

@robocanic
Copy link

robocanic commented Aug 24, 2023

我也遇到了类似的问题,在dubbo-samples中有一个上下文参数传递的demo https://github.com/apache/dubbo-samples/blob/4b98c856b8c084fa51d9f0d4bbd6d45fe64cdeea/10-task/dubbo-samples-develop/dubbo-samples-develop-consumer/src/main/java/org/apache/dubbo/samples/develop/ContextTask.java#L37
消费者在setAttachment之后,provider这边是get不到的。
这是consumer

public void contextServiceCall(){
        //往服务端传递参数
        RpcContext.getClientAttachment().setAttachment("clientKey1","This is client");
        String res = contextService.invoke("context1");

        //接收传递回来的参数
        Map<String, Object> serverAttachments = RpcContext.getServerContext().getObjectAttachments();
        System.out.println("ContextTask clientAttachment: " + JSON.toJSONString(serverAttachments));
        System.out.println("ContextService Return: " + res);
    }

这是provider

@DubboService
public class ContextServiceImpl implements ContextService{
    @Override
    public String invoke(String param) {
        //ServerAttachment接收传递过来的参数
        Map<String, Object> serverAttachments =
                RpcContext.getServerContext().getObjectAttachments();
        System.out.println("ContextService server Receives server Attachments:"+ JSON.toJSONString(serverAttachments));



        //往客户端传递参数

        RpcContext.getServerContext().setAttachment("serverKey","这是server");
        StringBuilder s = new StringBuilder();
        s.append("ContextService param:").append(param);
        return s.toString();
    }
}

运行后在consumer这端是可以拿到provider传的attachments的
但是provider拿不到consumer传的attachments:
这是consumer的日志截图:
image
这是provider的日志截图
image
dubbo版本:3.0.11
java版本:1.8
协议:dubbo
序列化方式:Hessian2

@AlbumenJ
Copy link
Member

image
Should be getServerAttachment()

@robocanic
Copy link

robocanic commented Aug 28, 2023

image Should be getServerAttachment()

可以了!多谢,不过我很疑惑dubbo 3 为什么将RpcContext 拆分成四个,使用起来完全没有Dubbo2.x那样简便。且从使用的example来看RpcContext设计成了client 端和server端,但dubbo里更常见的应该是provider和consumer,概念上有一些些出入。且两端参数传递的API有很大不同,使用起来确实挺容易犯错误,且就像我之前那样使用错误的API也没有报错,需要一行一行地仔细找才可以。

@AlbumenJ
Copy link
Member

AlbumenJ commented Aug 29, 2023

image Should be getServerAttachment()

可以了!多谢,不过我很疑惑dubbo 3 为什么将RpcContext 拆分成四个,使用起来完全没有Dubbo2.x那样简便。且从使用的example来看RpcContext设计成了client 端和server端,但dubbo里更常见的应该是provider和consumer,概念上有一些些出入。且两端参数传递的API有很大不同,使用起来确实挺容易犯错误,且就像我之前那样使用错误的API也没有报错,需要一行一行地仔细找才可以。

本质原因是为了防止上下文污染

比如:

1. A->B->C,如果 A->B 的上下文不希望给到 C
2. A->B->C
        ->D,如果  A->B 的上下文想原样给到 D

@AmosWong1998
Copy link

AmosWong1998 commented Sep 18, 2023

不知道你的和我的问题对不对,我的问题是消费者setAttachment但是提供者get不到,看源码发现它走的是已废弃的代码getAttachment()方法。其中会if (value instanceof String),判断成立强转为string,不成立直接return null。

如果是 set 的 string 类型的这里不应该有问题。 另外 attachment 是提供给业务编程侧使用的,如果是在 Filter 内使用的话需要直接基于 invocation 对象操作

【如果是在 Filter 内使用的话需要直接基于 invocation 对象操作】大佬这句话是什么意思没有理解?Filter里面不应该使用RpcContext或者attachment去传递业务标识吗? @AlbumenJ

比如我想在dubbo服务间透传tag标识,是不是直接在filter里面使用 invocation.setAttachment("dubbo.tag", "green");就可以了

@robocanic
Copy link

不知道你的和我的问题对不对,我的问题是消费者setAttachment但是提供者get不到,看源码发现它走的是已废弃的代码getAttachment()方法。其中会if (value instanceof String),判断成立强转为string,不成立直接return null。

如果是 set 的 string 类型的这里不应该有问题。 另外 attachment 是提供给业务编程侧使用的,如果是在 Filter 内使用的话需要直接基于 invocation 对象操作

【如果是在 Filter 内使用的话需要直接基于 invocation 对象操作】大佬这句话是什么意思没有理解?Filter里面不应该使用RpcContext或者attachment去传递业务标识吗? @AlbumenJ

比如我想在dubbo服务间透传tag标识,是不是直接在filter里面使用 invocation.setAttachment("dubbo.tag", "green");就可以了

image
attachments会在ConsumerContextFilter(ClusterFilter的实现类)中给invocation加上。对业务编程侧来说是比较友好的,不需要扩展组件。
image

但如果你是想要通过设置"dubbo.tag"来完成流量的打标,则需要扩展ClusterFiter(不能是filter),在invoke方法中直接使用 invocation.setAttachment("dubbo.tag", "green")。不能使用attachments的形式,因为我们拓展的ClusterFilter默认是会在内置的ClusterFilter链的最后,也就是会在ConsumerContexFilter之后,所以这些attachments不会被自动加到invocation中。ClusterFilter是Dubbo3.0中新加入,可以参考#7382

而TagStateRouter.doRoute是拿的invocation.getAttachment("dubbo.tag")来做标签路由的:
image

不能通过扩展Filter的方式来进行流量的打标的原因是因为Dubbo的调用链中到Filter这里就已经完成了路由选址和负载均衡,Filter只是对已确定的provider实例做一些其他操作
具体可以参考官网文档:
https://cn.dubbo.apache.org/zh-cn/overview/mannual/java-sdk/reference-manual/architecture/service-invocation/
所以针对于你的问题,在dubbo服务间透传tag标识,其实还是看你的用途,如果要为了做路由,则扩展ClusterFilter,用invocation.setAttachment;如果只是为了传一个上下文参数,则可以不用扩展ClusterFilter,直接在业务侧编程时使用Rpc.getClientAttachment.setAttachments就可以

@AmosWong1998
Copy link

不知道你的和我的问题对不对,我的问题是消费者setAttachment但是提供者get不到,看源码发现它走的是已废弃的代码getAttachment()方法。其中会if (value instanceof String),判断成立强转为string,不成立直接return null。

如果是 set 的 string 类型的这里不应该有问题。 另外 attachment 是提供给业务编程侧使用的,如果是在 Filter 内使用的话需要直接基于 invocation 对象操作

【如果是在 Filter 内使用的话需要直接基于 invocation 对象操作】大佬这句话是什么意思没有理解?Filter里面不应该使用RpcContext或者attachment去传递业务标识吗? @AlbumenJ
比如我想在dubbo服务间透传tag标识,是不是直接在filter里面使用 invocation.setAttachment("dubbo.tag", "green");就可以了

image attachments会在ConsumerContextFilter(ClusterFilter的实现类)中给invocation加上。对业务编程侧来说是比较友好的,不需要扩展组件。 image

但如果你是想要通过设置"dubbo.tag"来完成流量的打标,则需要扩展ClusterFiter(不能是filter),在invoke方法中直接使用 invocation.setAttachment("dubbo.tag", "green")。不能使用attachments的形式,因为我们拓展的ClusterFilter默认是会在内置的ClusterFilter链的最后,也就是会在ConsumerContexFilter之后,所以这些attachments不会被自动加到invocation中。ClusterFilter是Dubbo3.0中新加入,可以参考#7382

而TagStateRouter.doRoute是拿的invocation.getAttachment("dubbo.tag")来做标签路由的: image

不能通过扩展Filter的方式来进行流量的打标的原因是因为Dubbo的调用链中到Filter这里就已经完成了路由选址和负载均衡,Filter只是对已确定的provider实例做一些其他操作 具体可以参考官网文档: https://cn.dubbo.apache.org/zh-cn/overview/mannual/java-sdk/reference-manual/architecture/service-invocation/ 所以针对于你的问题,在dubbo服务间透传tag标识,其实还是看你的用途,如果要为了做路由,则扩展ClusterFilter,用invocation.setAttachment;如果只是为了传一个上下文参数,则可以不用扩展ClusterFilter,直接在业务侧编程时使用Rpc.getClientAttachment.setAttachments就可以

跪谢大佬,这两天一直被这个问题困扰。原因就是扩展了filter,传递了dubbo.tag 但是没办法做tag路由。非常感谢大佬的耐心解答!我去试试 @robocanic

@AmosWong1998
Copy link

不知道你的和我的问题对不对,我的问题是消费者setAttachment但是提供者get不到,看源码发现它走的是已废弃的代码getAttachment()方法。其中会if (value instanceof String),判断成立强转为string,不成立直接return null。

如果是 set 的 string 类型的这里不应该有问题。 另外 attachment 是提供给业务编程侧使用的,如果是在 Filter 内使用的话需要直接基于 invocation 对象操作

【如果是在 Filter 内使用的话需要直接基于 invocation 对象操作】大佬这句话是什么意思没有理解?Filter里面不应该使用RpcContext或者attachment去传递业务标识吗? @AlbumenJ
比如我想在dubbo服务间透传tag标识,是不是直接在filter里面使用 invocation.setAttachment("dubbo.tag", "green");就可以了

image attachments会在ConsumerContextFilter(ClusterFilter的实现类)中给invocation加上。对业务编程侧来说是比较友好的,不需要扩展组件。 image
但如果你是想要通过设置"dubbo.tag"来完成流量的打标,则需要扩展ClusterFiter(不能是filter),在invoke方法中直接使用 invocation.setAttachment("dubbo.tag", "green")。不能使用attachments的形式,因为我们拓展的ClusterFilter默认是会在内置的ClusterFilter链的最后,也就是会在ConsumerContexFilter之后,所以这些attachments不会被自动加到invocation中。ClusterFilter是Dubbo3.0中新加入,可以参考#7382
而TagStateRouter.doRoute是拿的invocation.getAttachment("dubbo.tag")来做标签路由的: image
不能通过扩展Filter的方式来进行流量的打标的原因是因为Dubbo的调用链中到Filter这里就已经完成了路由选址和负载均衡,Filter只是对已确定的provider实例做一些其他操作 具体可以参考官网文档: https://cn.dubbo.apache.org/zh-cn/overview/mannual/java-sdk/reference-manual/architecture/service-invocation/ 所以针对于你的问题,在dubbo服务间透传tag标识,其实还是看你的用途,如果要为了做路由,则扩展ClusterFilter,用invocation.setAttachment;如果只是为了传一个上下文参数,则可以不用扩展ClusterFilter,直接在业务侧编程时使用Rpc.getClientAttachment.setAttachments就可以

跪谢大佬,这两天一直被这个问题困扰。原因就是扩展了filter,传递了dubbo.tag 但是没办法做tag路由。非常感谢大佬的耐心解答!我去试试 @robocanic

现在发现个问题,在本地机器上我启动了A服务和B服务,都打上了tag="green",发现是符合预期的,可以调通。
后面我改变策略,修改为两台机器上部署多个实例:
两个服务的调用关系为 A服务的rest接口 -> A服务的Dubbo接口 -> B服务的dubbo接口
第一台机器,部署A服务的实例A1,启动的时候JVM参数指定tag为"green"(-Ddubbo.provider.tag=green)
第二台机器,部署B服务的实例B1(启动时JVM参数打上tag为"green")和实例B2(无tag),当我发起rest请求时,指定dubbo.tag为green,却发现只能调用到B服务的实例B2(无tag)。然后我debug代码发现(org.apache.dubbo.rpc.cluster.router.tag.TagStateRouter#doRoute) Invoker列表只有一个,没有打tag的实例B1,如图:
image
大佬能请教下为什么嘛?
我写的Filter如下:

@Activate(group = {CommonConstants.PROVIDER, CommonConstants.CONSUMER})
public class DubboTagFilter implements ClusterFilter {

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        try {
            String tagValue = EnvContext.getDubboTag();
            // 设置到上下文中,在后续调用链路中传递
            if (StringUtils.isNotBlank(tagValue)) {
                invocation.setAttachment(TagKeyConstants.TAG_KEY, tagValue);
            }

            // 继续执行
            return invoker.invoke(invocation);
        } finally {
            // 清理上下文
            EnvContext.remove();
        }
    }
}

注意:所有服务都注册成功了,然后Nacos也能看到B1实例打上了tag为green

@AmosWong1998
Copy link

@AlbumenJ

@robocanic
Copy link

robocanic commented Sep 21, 2023

从你的截图上来看,invocation.attachments里并没有这个tag,而B1是有tag的,所以不会路由到B1
image
你的代码中的EnvContext是什么,你再debug看一下这个tag是否被正确设置到invocation的attachment中@AmosWong1998

@AmosWong1998
Copy link

从你的截图上来看,invocation.attachments里并没有这个tag,而B1是有tag的,所以不会路由到B1 image 你的代码中的EnvContext是什么,你再debug看一下这个tag是否被正确设置到invocation的attachment中@AmosWong1998

确实 invocation里面没有dubbo.tag这个key。
EnvContext:从rest请求的请求头中获取dubbo.tag的值会存放到EnvContext里面。
我想请问一下在一整个调用链路中,比如A->B->C,我是不是只在A的invocation设置了dubbo.tag就能把dubbo.tag一直传递下去? 但是事实是我发现在传递的过程中dubbo.tag会丢失 @robocanic

@AmosWong1998
Copy link

从你的截图上来看,invocation.attachments里并没有这个tag,而B1是有tag的,所以不会路由到B1 image 你的代码中的EnvContext是什么,你再debug看一下这个tag是否被正确设置到invocation的attachment中@AmosWong1998

确实 invocation里面没有dubbo.tag这个key。 EnvContext:从rest请求的请求头中获取dubbo.tag的值会存放到EnvContext里面。 我想请问一下在一整个调用链路中,比如A->B->C,我是不是只在A的invocation设置了dubbo.tag就能把dubbo.tag一直传递下去? 但是事实是我发现在传递的过程中dubbo.tag会丢失 @robocanic

我新加了一个作用于provider的过滤器,为什么在调用链路中不会生效?这个Filter是往EnvContext里面设置dubbo.tag的值的。目前来看配置没有问题。但是在请求过程中一直没有进来

@Activate(group = CommonConstants.PROVIDER)
public class DubboProviderTagFilter implements ClusterFilter {

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        try {
            String tagValue = invocation.getAttachment(TagKeyConstants.TAG_KEY);
            if (StringUtils.isNotBlank(tagValue)) {
                EnvContext.setDubboTag(tagValue);
            }

            // 继续执行
            return invoker.invoke(invocation);
        } finally {
            // 清理上下文
            EnvContext.remove();
        }
    }
}
``` @AlbumenJ 

@robocanic
Copy link

从你的截图上来看,invocation.attachments里并没有这个tag,而B1是有tag的,所以不会路由到B1 image 你的代码中的EnvContext是什么,你再debug看一下这个tag是否被正确设置到invocation的attachment中@AmosWong1998

确实 invocation里面没有dubbo.tag这个key。 EnvContext:从rest请求的请求头中获取dubbo.tag的值会存放到EnvContext里面。 我想请问一下在一整个调用链路中,比如A->B->C,我是不是只在A的invocation设置了dubbo.tag就能把dubbo.tag一直传递下去? 但是事实是我发现在传递的过程中dubbo.tag会丢失 @robocanic

Dubbo3里面对这个链路透传上下文参数重新设计了一下,@AlbumenJ大佬上面也提到是为了防止上下文污染,具体可以参考文档:
https://www.yuque.com/apache-dubbo/dubbo3/sw5s6w?from_wecom=1

@AmosWong1998
Copy link

从你的截图上来看,invocation.attachments里并没有这个tag,而B1是有tag的,所以不会路由到B1 image 你的代码中的EnvContext是什么,你再debug看一下这个tag是否被正确设置到invocation的attachment中@AmosWong1998

确实 invocation里面没有dubbo.tag这个key。 EnvContext:从rest请求的请求头中获取dubbo.tag的值会存放到EnvContext里面。 我想请问一下在一整个调用链路中,比如A->B->C,我是不是只在A的invocation设置了dubbo.tag就能把dubbo.tag一直传递下去? 但是事实是我发现在传递的过程中dubbo.tag会丢失 @robocanic

Dubbo3里面对这个链路透传上下文参数重新设计了一下,@AlbumenJ大佬上面也提到是为了防止上下文污染,具体可以参考文档: https://www.yuque.com/apache-dubbo/dubbo3/sw5s6w?from_wecom=1

今天认真debug了一下整个链路,发现确实是dubbo.tag参数在调用某个服务的时候丢失了。举个例子:
时刻1:A -> B 有tag
时刻2:B -> C 有tag
时刻3:B -> D tag丢失
有什么办法能让B-D的tag不丢失么? @robocanic

@AmosWong1998
Copy link

代码如下,定义两个filter分别来处理consumer和provider,EnvContext是ThreadLocal的用来暂存dubbo.tag标记。

@Activate(group = CommonConstants.CONSUMER)
public class DubboConsumerTagFilter implements ClusterFilter {

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        try {
            String tagValue = EnvContext.getDubboTag();
            // 设置到上下文中,在后续调用链路中传递
            if (StringUtils.isNotBlank(tagValue)) {
                invocation.setAttachment(TagKeyConstants.TAG_KEY, tagValue);
            }

            // 继续执行
            return invoker.invoke(invocation);
        } finally {
            // 清理上下文
            EnvContext.remove();
        }
    }
}
@Activate(group = CommonConstants.PROVIDER)
public class DubboProviderTagFilter implements Filter {

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        try {
            String tagValue = invocation.getAttachment(TagKeyConstants.TAG_KEY);
            if (StringUtils.isNotBlank(tagValue)) {
                EnvContext.setDubboTag(tagValue);
            }

            // 继续执行
            return invoker.invoke(invocation);
        } finally {
            // 清理上下文
            EnvContext.remove();
        }
    }
}

@robocanic
Copy link

robocanic commented Sep 25, 2023

如果我没理解错的话你的这个调用链是这样的:
image
如果在B里面C和D的调用是串行的,那这两个调用会由一个线程来完成,会走两次ClusterFilter,而在第一次的调用结束后,你在finally块中清理掉了threadlocal变量,所以第二次调用就没有这个tag变量的:
image
解决方法最简单粗暴的就是不清理ThreadLocal中的tag,但这个做法得根据你自己的用途来评估是否可行。

@AmosWong1998
Copy link

如果我没理解错的话你的这个调用链是这样的: image 如果在B里面C和D的调用是串行的,那这两个调用会由一个线程来完成,会走两次ClusterFilter,而在第一次的调用结束后,你在finally块中清理掉了threadlocal变量,所以第二次调用就没有这个tag变量的: image 解决方法最简单粗暴的就是不清理ThreadLocal中的tag,但这个做法得根据你自己的用途来评估是否可行。

我想了想 不清理 threadLocal 可能会导致dubbo请求的tag混乱。线程池的线程会复用,不清理的话,下一次请求的tag可能会沿用上一次的tag

@AmosWong1998
Copy link

如果我没理解错的话你的这个调用链是这样的: image 如果在B里面C和D的调用是串行的,那这两个调用会由一个线程来完成,会走两次ClusterFilter,而在第一次的调用结束后,你在finally块中清理掉了threadlocal变量,所以第二次调用就没有这个tag变量的: image 解决方法最简单粗暴的就是不清理ThreadLocal中的tag,但这个做法得根据你自己的用途来评估是否可行。

我想了想 不清理 threadLocal 可能会导致dubbo请求的tag混乱。线程池的线程会复用,不清理的话,下一次请求的tag可能会沿用上一次的tag

除了不清理threadLocal 感觉无解了啊。dubbo支持某个参数一直沿着调用链路传递么? @robocanic

@AlbumenJ
Copy link
Member

如果我没理解错的话你的这个调用链是这样的: image 如果在B里面C和D的调用是串行的,那这两个调用会由一个线程来完成,会走两次ClusterFilter,而在第一次的调用结束后,你在finally块中清理掉了threadlocal变量,所以第二次调用就没有这个tag变量的: image 解决方法最简单粗暴的就是不清理ThreadLocal中的tag,但这个做法得根据你自己的用途来评估是否可行。

我想了想 不清理 threadLocal 可能会导致dubbo请求的tag混乱。线程池的线程会复用,不清理的话,下一次请求的tag可能会沿用上一次的tag

除了不清理threadLocal 感觉无解了啊。dubbo支持某个参数一直沿着调用链路传递么? @robocanic

对于非内部 key(如 tag),其他 key 都是默认透传的

@Allen123Hao
Copy link

image Should be getServerAttachment()

可以了!多谢,不过我很疑惑dubbo 3 为什么将RpcContext 拆分成四个,使用起来完全没有Dubbo2.x那样简便。且从使用的example来看RpcContext设计成了client 端和server端,但dubbo里更常见的应该是provider和consumer,概念上有一些些出入。且两端参数传递的API有很大不同,使用起来确实挺容易犯错误,且就像我之前那样使用错误的API也没有报错,需要一行一行地仔细找才可以。

本质原因是为了防止上下文污染

比如:

1. A->B->C,如果 A->B 的上下文不希望给到 C
2. A->B->C
        ->D,如果  A->B 的上下文想原样给到 D

请问,dubbo3.2.7中可以上下文可以一直透传吗?增加过滤器设置参数,A是一个服务,B、C、D在同一个服务上,例如A->B->C,B->D,在A->B前设置RpcContext.getServiceContext().setAttachment("app_name","A"),B可以拿到app_name为A,B调C前,设置RpcContext.getServiceContext().setAttachment("app_name","B"),在B->C后,参数本应该是app_name为B,但结果app_name变为了A了,怎么回事呢?

@AlbumenJ
Copy link
Member

AlbumenJ commented Nov 8, 2023

A->B,在 A 侧使用 RpcContext.getClientAttachment().setAttachment("app_name","A"),在 B 侧使用 RpcContext.getServerAttachment().getAttachment("app_name")
B->C,在 B 侧使用 RpcContext.getClientAttachment().setAttachment("app_name","B"),在 C 侧使用 RpcContext.getServerAttachment().getAttachment("app_name")

@ghost
Copy link

ghost commented Jan 22, 2024

A->B,在 A 侧使用 RpcContext.getClientAttachment().setAttachment("app_name","A"),在 B 侧使用 RpcContext.getServerAttachment().getAttachment("app_name") B->C,在 B 侧使用 RpcContext.getClientAttachment().setAttachment("app_name","B"),在 C 侧使用 RpcContext.getServerAttachment().getAttachment("app_name")

建议优化下命名,client和server的命名远比不上consumer和provider语义更清晰,理论上来说consumer和provider其实都是client,server的语义更偏向于统一管理的服务端,还有ServiceContext、ServerContext这俩肉眼几乎没区别,太容易用错了。

@AlbumenJ AlbumenJ removed the question label Mar 8, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants