Go-Kit JSON-RPC 实战指南:用 EndpointCodec 构建标准 JSON-RPC 2.0 服务
发布时间:2026/9/30 7:02:18来源:尧图网络
微服务后端RPC框架【免费下载链接】kitA standard library for microservices.项目地址https://gitcode.com/gh_mirrors/ki/kit点击查看免费下载JSON-RPC 是一种轻量级远程过程调用协议它以人类可读的 JSON 报文完成跨服务方法调用天然适合微服务之间或前后端之间的 RPC 风格 API。go-kit 的transport/http/jsonrpc包把 JSON-RPC 2.0 绑定到了标准 go-kit Endpoint 之上服务端以一个http.Handler承载全部方法客户端则把远程方法封装成可复用、可组合的endpoint.Endpoint。读完本文你将掌握用EndpointCodec注册与路由 JSON-RPC 方法、用解码器/编码器处理params与result、定制错误对象与错误码、配置服务端/客户端钩子以及通过客户端封装直接调用远程 JSON-RPC 服务的完整实战方案。JSON-RPC 与 go-kit 的结合方式在 go-kit 的架构中transport/http/jsonrpc位于 transport/http/jsonrpc 目录它把 JSON-RPC 2.0 协议封装为一种传输绑定binding。从整体设计上看服务端是一个 HTTP HandlerJSON-RPC server 实现net/http.Handler接收所有发往特定 URL例如/rpc的 POST 请求。它读取 Request Object 中的method属性据此把请求路由到对应的处理代码见 server.go 的Server类型声明。每个 JSON-RPC 方法是一个EndpointCodec一个 go-kitEndpoint定义于 endpoint/endpoint.go前后夹着解码器和编码器。解码器从 JSON-RPC 请求的params中拆出领域对象交给 Endpoint编码器接收 Endpoint 的输出编码为 JSON-RPC 的result字段。协议版本与媒体类型固定包内常量Version 2.0、ContentType application/json; charsetutf-8定义于 request_response_types.go。这一端点 编解码的抽象意味着你只关心业务逻辑Endpoint、请求形状Decoder、响应形状Encoder传输层细节全部由 jsonrpc 包接管。完整示例构建一个 Add求和服务原文档以两个整数相加的服务为例将其暴露在http://localhost/rpc。对sum方法的请求是发往http://localhost/rpc的 POST请求体如下{ id: 123, jsonrpc: 2.0, method: sum, params: { A: 2, B: 2 } }下面按路由表 → 解码器 → 编码器 → 装配服务端的次序逐步实现。1. EndpointCodecMap方法路由表服务端把方法名 → 处理单元的映射表称为EndpointCodecMap。它的 key 是 JSON-RPC 方法名value 是EndpointCodec结构定义见 encode_decode.go。这里我们把sum方法路由到sumEndpoint及其编解码函数jsonrpc.EndpointCodecMap{ sum: jsonrpc.EndpointCodec{ Endpoint: sumEndpoint, Decode: decodeSumRequest, Encode: encodeSumResponse, }, }2. Decoder从 params 提取领域对象解码器的类型签名是DecodeRequestFunc func(context.Context, json.RawMessage) (request interface{}, err error)见 encode_decode.go。注意它拿到的只是 Request Object 中params属性的原始 JSON不是整个请求对象返回值将作为 Endpoint 的输入。对本例输出应当是SumRequesttype SumRequest struct { A, B int } func decodeSumRequest(ctx context.Context, msg json.RawMessage) (interface{}, error) { var req SumRequest err : json.Unmarshal(msg, req) if err ! nil { return nil, err } return req, nil }SumRequest接下来会被传入 Endpoint。端点完成业务计算后控制权交给编码器。3. Encoder把结果编码为 result 字段编码器类型签名是EncodeResponseFunc func(context.Context, interface{}) (response json.RawMessage, err error)见 encode_decode.go。它接收 Endpoint 的输出构建将写入 Response Object 的result字段的原始 JSON。本例的求和结果是一个普通intfunc encodeSumResponse(ctx context.Context, result interface{}) (json.RawMessage, error) { sum, ok : result.(int) if !ok { return nil, errors.New(result is not an int) } b, err : json.Marshal(sum) if err ! nil { return nil, err } return b, nil }4. 端点的实现按上述解码器与编码器的约定sumEndpoint需要接收SumRequest并返回intfunc sumEndpoint(ctx context.Context, request interface{}) (interface{}, error) { sumReq, ok : request.(SumRequest) if !ok { return nil, errors.New(request is not a SumRequest) } return sumReq.A sumReq.B, nil }这正是标准 go-kit Endpoint 的形态func(ctx context.Context, request interface{}) (response interface{}, err error)。5. 装配 Server 并启动将 EndpointCodec解码器 端点 编码器组装完成后用jsonrpc.NewServer构造服务端再注册到标准库的 HTTP 路由上handler : jsonrpc.NewServer(jsonrpc.EndpointCodecMap{ sum: jsonrpc.EndpointCodec{ Endpoint: sumEndpoint, Decode: decodeSumRequest, Encode: encodeSumResponse, }, }) http.Handle(/rpc, handler) http.ListenAndServe(:80, nil)NewServer的完整签名与默认行为见 server.go默认使用DefaultErrorEncoder处理错误、使用log.NewNopLogger()不记录日志可通过ServerOption覆盖。完成上述全部代码后开篇的示例请求会得到如下响应{ jsonrpc: 2.0, result: 4 }服务端源码解析ServeHTTP 的处理流水线Server.ServeHTTPserver.go完整展示了请求的生命周期这也是理解 jsonrpc 包的关键HTTP 方法校验非 POST 请求直接返回405 must POST对应测试TestCanRejectNonPostRequest见 server_test.go。执行ServerBefore钩子在请求体解码前对原始http.Request做加工返回值注入 context。解析 Request Objectjson.NewDecoder(r.Body).Decode(req)解析失败则返回ParseError-32700错误响应。注入上下文把请求 ID 存入requestIDKey、把方法名存入ContextKeyRequestMethod见 request_response_types.go后续编解码器可通过 context 读取。执行ServerBeforeCodec钩子此时 JSON 请求体已解码为Request结构但方法解码器尚未调用——这是检查 RPC 请求内容的最后机会。按 method 查路由表s.ecm[req.Method]查不到时返回MethodNotFoundError-32601。调用解码器ecm.Decode(ctx, req.Params)失败则走错误编码。调用 Endpointecm.Endpoint(ctx, reqParams)失败同样走错误编码。执行ServerAfter钩子在响应写入客户端前对http.ResponseWriter做加工。调用编码器并写回响应组装Response{ID, JSONRPC: Version, Result}设置Content-Type后 JSON 编码写出。值得注意的一点即便业务出错HTTP 状态码默认仍是200 OK见DefaultErrorEncoder中的w.WriteHeader(http.StatusOK)错误信息通过 JSON-RPCerror对象携带。这一设计与 JSON-RPC 规范错误是响应对象的一部分保持一致。服务端可选项ServerOption 一览NewServer的第二个参数是变长的ServerOption全部定义于 server.go选项作用ServerBefore(...httptransport.RequestFunc)在请求体解码之前对http.Request执行钩子函数常用于鉴权、提取 Header 等ServerBeforeCodec(...RequestFunc)JSON 请求体已解码为Request之后、方法解码器调用之前执行可检查method/id/params等 RPC 内容ServerAfter(...httptransport.ServerResponseFunc)端点调用之后、任何内容写入客户端之前执行ServerErrorEncoder(ee httptransport.ErrorEncoder)自定义错误编码器可自行控制错误格式与 HTTP 状态码ServerErrorLogger(logger log.Logger)记录非致命错误默认不记录任何日志NopLoggerServerFinalizer(f httptransport.ServerFinalizerFunc)每次 HTTP 请求结束时执行适合统计、审计默认不注册ServerBeforeCodec与ServerBefore的区别正是 jsonrpc 绑定相对普通 HTTP 传输多出的能力前者可以访问已解析的 JSON-RPCRequest类型为RequestFunc func(context.Context, *http.Request, Request) context.Context而后者只能操作原始 HTTP 请求。错误处理Error 对象、标准错误码与自定义编码jsonrpc 包把 JSON-RPC 规范中的 Error Object 建模为Error结构error.gotype Error struct { Code int json:code Message string json:message Data interface{} json:data,omitempty }标准错误码包内预定义了 JSON-RPC 2.0 规范的标准错误码常量error.go常量取值含义ParseError-32700服务端解析 JSON 文本失败InvalidRequestError-32600收到的 JSON 不是合法的 Request 对象MethodNotFoundError-32601方法不存在或不可用InvalidParamsError-32602方法参数非法InternalError-32603服务端内部错误每个错误码还配有规范默认消息errorMessagemaperror.go可通过ErrorMessage(code)查询Error.Error()在Message为空时回退到默认消息error.go。默认错误编码器的行为DefaultErrorEncoderserver.go负责把 Go error 转成 JSON-RPC 错误响应其行为规则始终设置Content-Type: application/json; charsetutf-8若错误实现了httptransport.Headerer则复制其自定义 Header默认使用InternalError-32603作为错误码若错误实现了ErrorCoder接口即具有ErrorCode() int方法则使用其返回的错误码HTTP 状态码固定为200 OK错误以 JSON-RPCerror字段返回并回显请求 ID。ErrorCoder接口定义于 server.go。包内五种内部错误类型parseError、invalidRequestError、methodNotFoundError、invalidParamsError、internalError都已实现该接口error.go因此服务端内部错误会自动携带正确的错误码。自定义错误编码器业务上若想让特定错误映射到特定 HTTP 状态码可覆盖ServerErrorEncoder。测试TestServerErrorEncoderserver_test.go演示了将errors.New(teapot)映射为 HTTP 418http.StatusTeapot的用法handler : jsonrpc.NewServer( ecm, jsonrpc.ServerErrorEncoder(func(_ context.Context, err error, w http.ResponseWriter) { w.WriteHeader(code(err)) }), )客户端把远程方法封装成 Endpoint除了服务端jsonrpc 包还提供客户端绑定client.go用于调用远程 JSON-RPC 方法。Client封装了目标 URL、方法名与编解码函数其Endpoint()方法返回一个可直接参与 go-kit 组合的endpoint.Endpoint。最小客户端用法u, _ : url.Parse(http://localhost/rpc) client : jsonrpc.NewClient(u, sum) sumEndpoint : client.Endpoint() // 可像普通 Endpoint 一样使用 resp, err : sumEndpoint(ctx, SumRequest{A: 2, B: 2})NewClient的默认行为client.go使用http.DefaultClient发起请求请求体编码用DefaultRequestEncoder直接json.Marshal请求对象响应解码用DefaultResponseDecoder若响应含Error则返回该错误否则把result反序列化为interface{}请求 ID 由NewAutoIncrementID(0)生成自增整数基于atomic.AddUint64见 client.go。ClientOption 一览选项作用SetClient(c httptransport.HTTPClient)替换底层 HTTP 客户端默认http.DefaultClientClientBefore(...httptransport.RequestFunc)请求发出前对http.Request加工如加 Header对应测试见 client_test.goClientAfter(...httptransport.ClientResponseFunc)收到响应后、解码前执行可把响应信息放入 context 供解码器读取ClientFinalizer(f httptransport.ClientFinalizerFunc)每次请求结束含出错时执行常用于错误日志ClientRequestEncoder(enc EncodeRequestFunc)自定义请求参数编码ClientResponseDecoder(dec DecodeResponseFunc)自定义响应解码可自行决定响应中的error是否上抛ClientRequestIDGenerator(g RequestIDGenerator)自定义请求 ID 生成器实现Generate() interface{}BufferedStream(buffered bool)置为 true 时不关闭响应 Body便于以缓冲流方式传输大文件客户端发送的请求体结构由clientRequest定义client.gojsonrpc、method、params、id四字段符合 JSON-RPC 2.0 Request Object 规范。测试TestClientHappyPathclient_test.go端到端验证了客户端请求在服务端被正确解析ID、JSONRPC版本、params与发送的请求对象完全一致且before/after/finalizer钩子均被调用。测试验证行为即规范jsonrpc 包的测试文件为本示例的每一环节提供了可运行的验证依据服务端错误路径TestServerBadDecode、TestServerBadEndpoint、TestServerBadEncodeserver_test.go分别验证解码、端点、编码失败时返回InternalError且保持 HTTP 200、回显请求 ID。非法请求拒绝TestCanRejectNonPostRequest验证非 POST 返回 405TestCanRejectInvalidJSON验证非法 JSON 返回ParseError且 ID 为空server_test.go。未注册方法TestServerUnregisteredMethod验证未注册方法返回MethodNotFoundErrorserver_test.go。客户端钩子TestBeforeAfterFuncs验证客户端 before/after/finalizer 在各种响应空 body、500、错误对象下均被调用client_test.goTestCanUseDefaults验证客户端可完全使用默认编解码。错误语义TestError与TestErrorsSatisfyErrorerror_test.go验证Error的消息覆盖逻辑、错误消息映射以及所有内部错误类型都同时满足error与ErrorCoder接口。小结go-kit 的 jsonrpc 包把协议规范与业务代码做了干净切割服务端只需提供EndpointCodecMap方法名 → EndpointCodec客户端只需提供目标 URL 与方法名即可获得符合 JSON-RPC 2.0 规范的完整通信能力。实际落地时建议用EndpointCodecMap统一管理全部方法路由保持一方法一编解码的清晰映射通过ServerBeforeCodec在解码前统一校验请求内容通过ServerFinalizer做全量请求审计业务错误实现ErrorCoder接口以携带语义化错误码必要时用自定义ServerErrorEncoder控制 HTTP 状态客户端侧用ClientResponseDecoder把响应直接解码为领域类型避免interface{}的层层断言。掌握了这套 EndpointCodec 模式你便能在 go-kit 微服务体系中快速接入标准 JSON-RPC 2.0 接口并让服务端与客户端共享同一套方法语义。赞分享微服务后端RPC框架【免费下载链接】kitA standard library for microservices.项目地址https://gitcode.com/gh_mirrors/ki/kit点击查看免费下载相关推荐用 SeaORM 与 jsonrpsee 构建 Rust JSON-RPC 服务jsonrpsee_example 实战指南用 SeaORM 与 jsonrpsee 构建 Rust JSON RPC 服务jsonrpsee_example 实战指南 导读 本文以 examples/后端数据库ORM开源KVM软件 Input Leap 真的好用吗一文讲透它的核心玩法开源KVM软件 Input Leap 真的好用吗一文讲透它的核心玩法 每天在台式机和笔记本之间来回切换你是不是也烦透了手里握着鼠标眼睛盯着两台屏幕人却桌面应用jsonschema与JSON-RPC集成构建类型安全的RPC服务终极指南jsonschema与JSON RPC集成构建类型安全的RPC服务终极指南 在现代分布式系统中JSON RPC作为一种轻量级的远程过程调用协议以其简洁的J后端上一篇5个实用技巧让每个人都能轻松保存抖音直播回放下一篇Apache APISIX ldap-auth-advanced 插件详解LDAP 搜索后绑定认证与 Consumer 身份映射实战指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网