ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

go-micro RPC 客户端调用指南:使用 Go Micro Client 调用运行中的微服务

go-micro RPC 客户端调用指南:使用 Go Micro Client 调用运行中的微服务 go-micro RPC 客户端调用指南使用 Go Micro Client 调用运行中的微服务【免费下载链接】go-microA Go agent harness and service framework项目地址: https://gitcode.com/gh_mirrors/go/go-micro导读在 go-micro 中服务间的通信以 RPC 为核心一个服务发布自己的 Handler其他服务则通过内置的 Client 发起同步调用。本文以官方示例文档 rpc-client.md 为骨架完整讲解创建一个 caller 客户端 → 构造 RPC 请求 → 同步调用 helloworld 服务的 Say.Hello 端点的完整链路并结合仓库源码剖析NewRequest、Call的底层实现、默认超时/重试机制以及可用的调用选项。读完本文你将能独立编写并运行一个 go-micro RPC 客户端并掌握其超时、重试、服务发现等核心行为。示例代码最小可用的 RPC 客户端原文档给出的完整示例caller.go如下package main import ( context fmt go-micro.dev/v6 ) type Request struct { Name string } type Response struct { Message string } func main() { svc : micro.NewService(caller) svc.Init() req : svc.Client().NewRequest(helloworld, Say.Hello, Request{Name: John}) var rsp Response if err : svc.Client().Call(context.TODO(), req, rsp); err ! nil { fmt.Println(error:, err) return } fmt.Println(rsp.Message) }这个程序虽然不到 30 行却涵盖了 go-micro 客户端调用的全部关键要素服务初始化NewServiceInit、请求构造NewRequest、同步调用Call与响应解码rsp。下面逐层拆解。一、服务创建与初始化NewService与Init客户端程序同样以服务的形式存在。micro.NewService(caller)创建了一个名为caller的服务它在 micro.go 中被定义为func NewService(name string, opts ...Option) Service { return service.New(append([]Option{service.Name(name)}, opts...)...) }名称参数最终会通过service.Name(n)写入 server 的注册信息见 service/options.go。svc.Init()是初始化入口在 service/service.go 中首次调用会解析命令行 flagscmd.Init并按需注入 Auth、Broker、Registry、Transport、Client、Config、Server、Store 等组件此后再次调用只会应用传入的新选项。对纯客户端程序而言svc.Init()确保svc.Client()返回的客户端已具备默认的注册中心、选择器、传输层与编解码器这是后续Call能正确寻址的前提。二、构造请求NewRequest(service, endpoint, request)req : svc.Client().NewRequest(helloworld, Say.Hello, Request{Name: John})NewRequest接收三个核心参数参数示例值含义servicehelloworld目标服务的注册名称用于服务发现endpointSay.Hello目标端点的完整路径格式为Handler.MethodrequestRequest{Name: John}请求体未编码的结构体指针在 client/rpc_request.go 中newRequest将这三者连同客户端的默认ContentType一起封装为rpcRequestEndpoint()与Method()均取endpoint值请求体Body()在调用时才会被编解码器序列化若未显式指定ContentType使用客户端的默认值DefaultContentType来自 client/options.go 的NewOptions。Say.Hello这样的端点命名约定与服务端 Handler 一一对应服务端通过svc.Handle(new(Say))注册Say结构体其中Hello(ctx, req, rsp)方法即对外暴露的Say.Hello端点。三、同步调用Call(ctx, req, rsp)if err : svc.Client().Call(context.TODO(), req, rsp); err ! nil { fmt.Println(error:, err) return } fmt.Println(rsp.Message)Call执行的是**同步、一元unary**调用rsp在调用成功后会被反序列化为响应结构体。其底层实现在 client/rpc_client.go调用链大致为合并调用选项以客户端默认CallOptions为基准叠加本次传入的CallOption节点选择next()client/rpc_client.go通过Selector从注册中心查询helloworld服务的节点列表Call依次尝试每个节点超时控制若 context 未设置 deadline则用RequestTimeout创建context.WithTimeout默认 30 秒发送与接收call()client/rpc_client.go从连接池取出连接用newRPCCodec建立 RPC 编解码通道stream.Send(req.Body())发送请求、stream.Recv(resp)接收响应错误处理失败时按Retry策略判断是否重试每次重试前调用Backoff计算等待时间。调用成功后rsp.Message即为服务端返回的Hello John。四、配套服务端一个可运行的完整 Demo示例文档假设已存在一个运行中的helloworld服务。对应的服务端示例见 hello-service.md 与 examples/hello-world/main.gopackage main import ( context fmt log go-micro.dev/v6 ) type Request struct { Name string json:name } type Response struct { Message string json:message } type Greeter struct{} func (g *Greeter) Hello(ctx context.Context, req *Request, rsp *Response) error { rsp.Message Hello req.Name log.Printf(Received request: %s, req.Name) return nil } func main() { service : micro.NewService(greeter, micro.Address(:8080)) service.Init() if err : service.Handle(new(Greeter)); err ! nil { log.Fatal(err) } service.Run() }将两个文件放在同一模块模块名go-micro.dev/v6见 go.mod下分别运行服务端与客户端# 终端一启动服务端 go run service.go # 终端二启动客户端输出 Hello John go run caller.go需要注意客户端按服务名helloworld查找节点因此客户端里的service参数必须与服务端注册名称一致若服务端命名为greeter客户端就应调用NewRequest(greeter, Greeter.Hello, ...)。当服务通过micro.Address(:8080)固定监听地址、且未接入外部注册中心时也可以直接使用client.WithAddress(127.0.0.1:8080)跳过服务发现。五、源码视角Client 接口与默认行为svc.Client()返回的是 client.Client 接口它定义了 go-micro 客户端的完整能力type Client interface { Init(...Option) error Options() Options NewMessage(topic string, msg interface{}, opts ...MessageOption) Message NewRequest(service, endpoint string, req interface{}, reqOpts ...RequestOption) Request Call(ctx context.Context, req Request, rsp interface{}, opts ...CallOption) error Stream(ctx context.Context, req Request, opts ...CallOption) (Stream, error) Publish(ctx context.Context, msg Message, opts ...PublishOption) error String() string }Call用于同步一元调用即本文演示的场景Stream用于双向流式调用请求构造时需配合client.StreamingRequest()见 client/options.goPublish/NewMessage用于基于 Broker 的异步发布。默认客户端的默认参数定义在 client/options.go参数默认值说明DefaultRetries5单次Call最多尝试 6 次1 次初始 5 次重试DefaultRequestTimeout30s整个调用的超时上限DefaultConnectionTimeout5s单次连接/请求的超时重试基于此值DefaultPoolSize100连接池大小DefaultPoolTTL1m连接池中连接的存活时间DefaultBackoff指数退避重试前的等待策略DefaultRetryRetryOnError默认出错即重试这些默认值解释了客户端的行为特征一次Call在 30 秒内最多重试 5 次单次请求超时 5 秒连接从 100 大小的池中复用。六、实战进阶按调用粒度控制超时与重试除默认值外Call的CallOption允许按单次调用覆盖默认行为全部定义在 client/options.goreq : svc.Client().NewRequest(helloworld, Say.Hello, Request{Name: John}) var rsp Response err : svc.Client().Call(context.TODO(), req, rsp, client.WithRequestTimeout(10*time.Second), // 整体超时 10 秒 client.WithRetries(2), // 最多重试 2 次 client.WithAddress(127.0.0.1:8080), // 跳过服务发现直连固定地址 )常用CallOption一览选项作用client.WithRequestTimeout(d)覆盖整个调用的超时client.WithConnectionTimeout(d)覆盖单次连接超时应低于请求超时以支持重试client.WithRetries(n)覆盖重试次数client.WithRetry(fn)/client.WithBackoff(fn)自定义重试判定与退避策略client.WithAddress(addr...)指定目标地址绕过服务发现client.WithServiceToken()用服务自身的 auth token 覆盖授权头client.WithCache(d)设置响应缓存时长从实现上看client/rpc_client.go 的Call会先把默认CallOptions拷贝一份再逐个应用本次传入的CallOption——因此这些选项只影响当前这一次调用不会污染客户端的全局配置。七、两种常见的调用错误场景找不到服务节点当注册中心中没有helloworld服务的节点时Selector.Select返回ErrNotFoundCall会包装为InternalServerError错误信息形如service helloworld: ... not found。此时应确认服务端是否已启动并完成注册或改用WithAddress直连。调用超时若服务端无响应Call按RequestTimeout默认 30 秒返回Timeout错误。若希望快速失败并触发重试应调低ConnectionTimeout并保持其低于请求超时正如 client/options.go 中的注释所强调的。结语本文从官方 RPC Client 示例 出发完整还原了一个 go-micro 客户端从创建服务、构造请求到同步调用的全过程并借助 client/client.go、client/rpc_client.go、client/options.go 等源码剖析了超时、重试、连接池与服务发现等底层机制。掌握NewRequest与Call之后你可以进一步探索 Stream 双向流与 Publish 事件发布构建更丰富的服务间通信模式。【免费下载链接】go-microA Go agent harness and service framework项目地址: https://gitcode.com/gh_mirrors/go/go-micro创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表