fix: 解决客户端无法获取服务端返回的attachments
回退 https://github.com/apache/dubbo-go/pull/2854 修改,不兼容老版本的attachments的使用。代码其实内部有对attachments处理,只是漏了一些代码。补充上。
测试例子:
服务端
package main
import (
"context"
"fmt"
"dubbo.apache.org/dubbo-go/v3/common/extension"
"dubbo.apache.org/dubbo-go/v3/filter"
_ "dubbo.apache.org/dubbo-go/v3/imports"
"dubbo.apache.org/dubbo-go/v3/protocol"
"dubbo.apache.org/dubbo-go/v3/server"
greet "github.com/apache/dubbo-go-samples/helloworld/proto"
"github.com/dubbogo/gost/log/logger"
)
func init() {
extension.SetFilter("myServerFilter", NewMyServerFilter)
}
func NewMyServerFilter() filter.Filter {
return &MyServerFilter{}
}
type MyServerFilter struct {
}
func (f *MyServerFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
fmt.Println("MyServerFilter Invoke is called, method Name = ", invocation.MethodName())
fmt.Printf("request attachments = %s\n", invocation.Attachments())
return invoker.Invoke(ctx, invocation)
}
func (f *MyServerFilter) OnResponse(ctx context.Context, result protocol.Result, invoker protocol.Invoker, protocol protocol.Invocation) protocol.Result {
fmt.Println("MyServerFilter OnResponse is called")
myAttachmentMap := make(map[string]interface{})
myAttachmentMap["key1"] = "value1"
myAttachmentMap["key2"] = []string{"value1", "value2"}
result.SetAttachments(myAttachmentMap)
return result
}
type GreetTripleServer struct {
}
func (srv *GreetTripleServer) Greet(ctx context.Context, req *greet.GreetRequest) (*greet.GreetResponse, error) {
resp := &greet.GreetResponse{Greeting: req.Name}
return resp, nil
}
func main() {
srv, err := server.NewServer(
server.WithServerProtocol(
protocol.WithPort(20000),
protocol.WithTriple(),
),
server.WithServerFilter("myServerFilter"),
)
if err != nil {
panic(err)
}
if err := greet.RegisterGreetServiceHandler(srv, &GreetTripleServer{}); err != nil {
panic(err)
}
if err := srv.Serve(); err != nil {
logger.Error(err)
}
}
客户端
package main
import (
"context"
"fmt"
"dubbo.apache.org/dubbo-go/v3/client"
"dubbo.apache.org/dubbo-go/v3/common/extension"
"dubbo.apache.org/dubbo-go/v3/filter"
_ "dubbo.apache.org/dubbo-go/v3/imports"
"dubbo.apache.org/dubbo-go/v3/protocol"
greet "github.com/apache/dubbo-go-samples/helloworld/proto"
"github.com/dubbogo/gost/log/logger"
)
func init() {
extension.SetFilter("myClientFilter", NewMyClientFilter)
}
func NewMyClientFilter() filter.Filter {
return &MyClientFilter{}
}
type MyClientFilter struct {
}
func (f *MyClientFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
fmt.Println("MyClientFilter Invoke is called, method Name = ", invocation.MethodName())
invocation.SetAttachment("request-key1", "request-value1")
invocation.SetAttachment("request-key2", []string{"request-value2.1", "request-value2.2"})
return invoker.Invoke(ctx, invocation)
}
func (f *MyClientFilter) OnResponse(ctx context.Context, result protocol.Result, invoker protocol.Invoker, protocol protocol.Invocation) protocol.Result {
fmt.Println("MyClientFilter OnResponse is called")
fmt.Println("result attachment = ", result.Attachments())
return result
}
func main() {
cli, err := client.NewClient(
client.WithClientURL("127.0.0.1:20000"),
client.WithClientFilter("myClientFilter"),
)
if err != nil {
panic(err)
}
svc, err := greet.NewGreetService(cli)
if err != nil {
panic(err)
}
resp, err := svc.Greet(context.Background(), &greet.GreetRequest{Name: "hello world"})
if err != nil {
logger.Error(err)
}
logger.Infof("Greet response: %s", resp.Greeting)
}
测试的执行结果:
服务端
客户端
Amazing work.
I have been quite busy recently and might only be able to review this PR the day after tomorrow.
OK,我的工作暂时告一段落,等你反馈。
Regarding the issue of passing attachments to the new API, there are basically no problems, good job. We will discuss the rollback issue of # 2854 pr and provide the results later
Amazing code, I like your coding style.
But we don't want to send attachments through a filter. Can you implement sending attachments through context?
我建议还是采用同样的机制。但是我没测试过。 tri.AppendToOutgoingContext(ctx, k, val) tri.FromIncomingContext(ctx)
但是最好再封装一套顶层的接口。把内部AppendToOutgoingContext机制都封装掉。
如果需要我这边实现,可能要过两天我空点
我建议还是采用同样的机制。但是我没测试过。 tri.AppendToOutgoingContext(ctx, k, val) tri.FromIncomingContext(ctx)
这两个函数太低层了,我们不想暴露太低层的函数 如果要使用这两个函数的话,用户需要
import "dubbo.apache.org/dubbo-go/v3/protocol/triple/triple_protocol"
我还不想让用户直接import triple_protocol模块,而triple模块可以接受,或许可以在triple模块加入attachments的API?
如果需要我这边实现,可能要过两天我空点
对于attachments,我们想要合并一个完成度高的pr,而不是留作一个todo(因为todo不知道什么时候才会有人解决),所以辛苦你了
如果最近太忙,也可以交给我接手
我看内部的代码,context还有个attanchmentkey,感觉是不是一个重复性的东西?而且我没试过,dubbogo走其他协议,还是否经过triple的代码,triple这一层感觉不适合,正常来讲,这套机制应该和协议不相干,是否还有更上层的接口。可以你接手,没问题的。
我看内部的代码,context还有个attanchmentkey,感觉是不是一个重复性的东西?而且我没试过,dubbogo走其他协议,还是否经过triple的代码,triple这一层感觉不适合,正常来讲,这套机制应该和协议不相干,是否还有更上层的接口。可以你接手,没问题的。
我一会看一下,单是使用AppendToOutgoingContext这样的函数应该是没问题的,因为只有triple协议用header来传attachments,我注意一下其他协议attachments的使用,设计一下如何实现更好。
Quality Gate passed
Issues
0 New issues
0 Accepted issues
Measures
0 Security Hotspots
0.0% Coverage on New Code
0.0% Duplication on New Code
@ziyyun I have tested using ctx to pass attachments directly, and the effect is very good. I think this PR can be merged directly.
client:
package main
import (
"context"
"dubbo.apache.org/dubbo-go/v3/client"
"dubbo.apache.org/dubbo-go/v3/common/constant"
_ "dubbo.apache.org/dubbo-go/v3/imports"
greet "github.com/apache/dubbo-go-samples/helloworld/proto"
"github.com/dubbogo/gost/log/logger"
)
func main() {
cli, err := client.NewClient(
client.WithClientURL("127.0.0.1:20000"),
)
if err != nil {
panic(err)
}
svc, err := greet.NewGreetService(cli)
if err != nil {
panic(err)
}
ctx := context.Background()
ctx = context.WithValue(ctx, constant.AttachmentKey, map[string]any{"foo": "bar"})
resp, err := svc.Greet(ctx, &greet.GreetRequest{Name: "hello world"})
if err != nil {
logger.Error(err)
}
logger.Infof("Greet response: %s", resp.Greeting)
}
server:
package main
import (
"context"
"dubbo.apache.org/dubbo-go/v3/common/constant"
_ "dubbo.apache.org/dubbo-go/v3/imports"
"dubbo.apache.org/dubbo-go/v3/protocol"
"dubbo.apache.org/dubbo-go/v3/server"
greet "github.com/apache/dubbo-go-samples/helloworld/proto"
"github.com/dubbogo/gost/log/logger"
)
type GreetTripleServer struct {
}
func (srv *GreetTripleServer) Greet(ctx context.Context, req *greet.GreetRequest) (*greet.GreetResponse, error) {
attachments := ctx.Value(constant.AttachmentKey).(map[string]any)
logger.Infof("attachments : %v", attachments)
logger.Infof("attachments[foo] : %v", attachments["foo"])
resp := &greet.GreetResponse{Greeting: req.Name}
return resp, nil
}
func main() {
srv, err := server.NewServer(
server.WithServerProtocol(
protocol.WithPort(20000),
protocol.WithTriple(),
),
)
if err != nil {
panic(err)
}
if err := greet.RegisterGreetServiceHandler(srv, &GreetTripleServer{}); err != nil {
panic(err)
}
if err := srv.Serve(); err != nil {
logger.Error(err)
}
}
For the context you are worried about that there have an attachmentkey. Different protocols do indeed have different processing logic, but the user will not perceive it, so there is no need to worry.
---
config:
look: handDrawn
theme: neutral
---
flowchart LR
start[Start] --> protocol{Protocol}
protocol -->|Triple| infoProxy[infoProxyInvoker]
protocol -->|Dubbo| proxy[ProxyInvoker]
infoProxy --> handleTriAttachments
proxy --> handleDubboAttachments
Please check the copilot review comment.
因为我看过这段代码 ‘’‘ for key, valRaw := range dubbo3Resp.Attachments() { switch valRaw.(type) { case string: trailer[key] = []string{valRaw.(string)} case []string: trailer[key] = valRaw.([]string) default: panic(fmt.Sprintf("unsupported attachment value type %T", valRaw)) } } ’‘’ 直接panic了,所以我也没做处理。其实统一,到底能不能传非string或者[]string的值。而且到接收端,从其他结构变成string,感觉也不适合,真正的完美应该可以支持所有类型,接收端能够反序列化
因为我看过这段代码 ‘’‘ for key, valRaw := range dubbo3Resp.Attachments() { switch valRaw.(type) { case string: trailer[key] = []string{valRaw.(string)} case []string: trailer[key] = valRaw.([]string) default: panic(fmt.Sprintf("unsupported attachment value type %T", valRaw)) } } ’‘’ 直接panic了,所以我也没做处理。其实统一,到底能不能传非string或者[]string的值。而且到接收端,从其他结构变成string,感觉也不适合,真正的完美应该可以支持所有类型,接收端能够反序列化
如果想要支持所有类型的话,建议放在reqs, resp结构体中,triple的attachments是放在http协议的header中,将header字段序列化反序列化太奇怪了
ps: dubbo3 相当于老版本的triple, [dubbo3, dubbo, triple]都是单独的协议
因为我看过这段代码 ‘’‘ for key, valRaw := range dubbo3Resp.Attachments() { switch valRaw.(type) { case string: trailer[key] = []string{valRaw.(string)} case []string: trailer[key] = valRaw.([]string) default: panic(fmt.Sprintf("unsupported attachment value type %T", valRaw)) } } ’‘’ 直接panic了,所以我也没做处理。其实统一,到底能不能传非string或者[]string的值。而且到接收端,从其他结构变成string,感觉也不适合,真正的完美应该可以支持所有类型,接收端能够反序列化
如果想要支持所有类型的话,建议放在reqs, resp结构体中,triple的attachments是放在http协议的header中,将header字段序列化反序列化太奇怪了
ps: dubbo3 相当于老版本的triple, [dubbo3, dubbo, triple]都是单独的协议
同意