本文已收录至GitHub推荐阅读 Java随想录微信公众号Java随想录文章目录JSON-RPC 概述什么是 JSON-RPCJSON-RPC 的发展历程JSON-RPC 的核心特点协议规范详解协议约定与术语数据类型系统消息格式深度解析请求对象结构响应对象结构通知机制错误处理机制错误对象结构预定义错误码错误处理建议批量调用与性能优化批量请求机制批量调用的边界情况性能优化策略与 RESTful API 的对比分析设计理念差异通信模式对比选型建议安全性考量常见安全威胁安全加固措施结语分布式系统中不同服务之间需要一种可靠的方式来通信。远程过程调用RPC是一种常见的选择而JSON-RPC是其中比较简单的一种。这篇文章介绍 JSON-RPC 2.0 协议的核心内容包括消息格式、错误处理和实际应用场景。JSON-RPC 概述什么是 JSON-RPCJSON-RPC 是一种无状态、轻量级的远程过程调用协议使用 JSON 作为数据格式。它的设计目标就是简单——协议规范只有几页文档。JSON 作为数据交换格式几乎所有主流编程语言都有良好的支持包括 JavaScript、Python、Java、C#、Go 等。这使得 JSON-RPC 能够轻松实现跨语言调用。协议本身不绑定传输层可以跑在 HTTP、WebSocket、TCP Socket 等各种消息传输环境上。开发者可以根据业务需求选择合适的传输方式。JSON-RPC 的发展历程JSON-RPC 有两个主要版本1.0 和 2.0。1.0 版本最早提出了基于 JSON 的 RPC 概念但在规范性方面有所欠缺。2010 年发布的 2.0 版本做了重要升级加入了批量调用支持、统一的错误对象结构。两个版本通过jsonrpc字段来区分。JSON-RPC 的核心特点JSON-RPC 有几个值得注意的特点简洁性规范文档很短请求和响应结构清晰明了。跨语言支持任何能解析 JSON 的语言都可以实现 JSON-RPC 客户端或服务端。无状态设计每个请求独立协议不维护会话状态。这种设计简化了服务端的实现也更容易水平扩展。批量处理能力2.0 版本支持在单个请求中包含多个 RPC 调用服务端返回结果数组。双向通信通过长连接和通知机制JSON-RPC 也支持服务端主动向客户端推送消息。协议规范详解协议约定与术语JSON-RPC 规范中使用了 RFC 2119 定义的关键字MUST、MUST NOT、SHOULD、SHOULD NOT、MAY。这些术语描述了实现者必须、应该或可以遵循的行为规范。客户端Client发起请求的实体负责构造请求对象并处理响应。服务端Server接收请求并返回响应的实体处理请求并生成响应。同一个实现可以同时扮演客户端和服务端。比如在对等网络中两个节点可能既向对方发起请求也响应来自对方的请求。数据类型系统JSON-RPC 继承自 JSON 的类型系统包含六种数据类型基本类型String字符串、Number数值、Boolean布尔值、Null空值结构化类型Object对象、Array数组这些类型名称首字母必须大写包括 True 和 False。成员名称字段名在客户端与服务端之间交换时必须区分大小写。函数、方法、过程这三个术语可以互换使用都指向可以被调用的可执行单元。消息格式深度解析JSON-RPC 2.0 定义了三种核心消息类型请求对象Request Object、响应对象Response Object和通知对象Notification Object。请求对象结构请求对象是客户端向服务端发起 RPC 调用的载体{jsonrpc:2.0,method:subtract,params:[42,23],id:1}jsonrpc 字段协议版本标识值必须是字符串 “2.0”。method 字段要调用的方法名称区分大小写。以 “rpc.” 开头的方法名预留给 JSON-RPC 内部扩展如rpc.subscribe、rpc.notify。params 字段方法参数可以是数组按顺序或对象按名称。参数可选不需要时可以省略。// 索引数组参数参数顺序与服务端预期一致{jsonrpc:2.0,method:subtract,params:[42,23],id:1}// 命名对象参数参数名需与服务端方法签名匹配{jsonrpc:2.0,method:subtract,params:{minuend:42,subtrahend:23},id:2}id 字段客户端生成的唯一标识符用于关联请求和响应。id 可以是字符串、数值或 null。建议避免使用 null因为 JSON-RPC 1.0 的通知机制使用 null可能引起混淆。使用数值作为 id 时应避免小数。响应对象结构响应对象是服务端返回给客户端的执行结果// 成功响应{jsonrpc:2.0,result:19,id:1}// 错误响应{jsonrpc:2.0,error:{code:-32601,message:Method not found,data:详细错误信息},id:1}响应对象必须包含result或error之一不能同时包含两者。id必须与对应请求中的 id 保持一致。如果请求本身存在错误如无效的 JSON 格式或非法请求结构导致服务端无法确定原始 id 时响应中的 id 必须为 null。通知机制通知是一种不需要响应的请求通过省略id字段来标识{jsonrpc:2.0,method:update,params:[1,2,3,4,5]}通知适用于日志记录、事件发布、进度通知等场景。客户端只负责发送消息不需要处理响应。由于没有响应机制客户端无法得知通知是否被成功处理。在批量请求中通知请求不会产生对应的响应。错误处理机制错误对象结构JSON-RPC 2.0 定义了标准化的错误对象{code:-32603,message:Internal error,data:{details:数据库连接失败}}code 字段整数错误码标识错误类型。message 字段简短的人类可读错误描述通常是一句话。data 字段可选携带错误的附加信息可以是字符串、对象或任何有效的 JSON 值。预定义错误码JSON-RPC 2.0 在 -32768 到 -32000 范围内预留了预定义错误码错误码名称说明-32700Parse Error解析错误服务端收到的 JSON 格式无效-32600Invalid Request无效请求发送的不是有效的请求对象-32601Method Not Found方法不存在或不可调用-32602Invalid Params参数无效-32603Internal ErrorJSON-RPC 内部错误-32000 到 -32099 范围内的错误码保留给服务端自定义使用。应用程序也可以定义自己的错误码通常为负数且绝对值小于 32767。错误处理建议合理使用 data 字段code 和 message 提供错误的基本分类data 可以包含调试信息、堆栈跟踪或业务相关的详细信息。生产环境中注意控制敏感信息暴露。统一错误处理中间件在服务端统一拦截和处理错误将业务异常转换为标准化的 JSON-RPC 错误格式避免暴露内部实现细节。区分错误类型某些错误如参数校验失败是客户端问题可以提示用户修正另一些错误如数据库故障需要服务端介入处理。批量调用与性能优化批量请求机制JSON-RPC 2.0 支持在单个请求中发送多个 RPC 调用服务端返回相应的响应数组。这一机制在高并发场景下可以减少网络往返次数。// 批量请求[{jsonrpc:2.0,method:sum,params:[1,2,4],id:1},{jsonrpc:2.0,method:notify_hello,params:[7]},{jsonrpc:2.0,method:subtract,params:[42,23],id:2},{jsonrpc:2.0,method:get_data,id:9}]对应的批量响应[{jsonrpc:2.0,result:7,id:1},{jsonrpc:2.0,result:19,id:2},{jsonrpc:2.0,result:[hello,5],id:9}]通知请求没有 id 字段不会产生响应。响应数组中各元素的顺序与请求顺序无关客户端通过匹配 id 来关联请求和响应。批量调用的边界情况规范对批量调用中的边界情况有明确定义如果批量请求本身不是有效的 JSON 或不是包含至少一个值的数组服务端应返回单对象响应而非数组。空数组[]被视为无效请求。如果批量请求中的所有请求都是通知服务端不需要返回任何响应。其他情况下即使某些请求处理失败响应数组中仍应包含对应请求的错误响应。性能优化策略连接复用使用持久化连接如 HTTP Keep-Alive 或 WebSocket可以避免频繁建立和销毁连接的开销。批量聚合将多个相关的小请求合并为一个批量请求减少网络往返次数。压缩传输启用 HTTP 压缩如 gzip可以减少大型 JSON 响应体的传输体积。异步处理使用异步调用和通知机制提高客户端的并发处理能力。与 RESTful API 的对比分析设计理念差异JSON-RPC 和 RESTful 代表了两种不同的 API 设计哲学。JSON-RPC 是过程导向的关注的是做什么操作RESTful 是资源导向的关注的是操作什么资源。以用户操作为例// JSON-RPC: 直接调用方法{jsonrpc:2.0,method:createUser,params:{name:张三,email:zhangexample.com},id:1}// RESTful: 操作资源POST/users{name:张三,email:zhangexample.com}通信模式对比维度JSON-RPCRESTful语义抽象方法调用资源操作URL 角色仅作为端点地址表示资源路径HTTP 方法仅使用 POST充分利用 GET/POST/PUT/DELETE状态管理可维护会话状态倡导无状态设计灵活性更紧凑适合精确控制更灵活适合通用场景选型建议适合使用 JSON-RPC 的场景内部服务间通信接口相对稳定需要精确的方法语义和参数控制对带宽敏感需要紧凑的消息格式团队对 RPC 模式更熟悉适合使用 RESTful 的场景公开 API需要良好的可发现性资源概念明确的 CRUD 操作需要利用 HTTP 缓存机制追求 API 的自我描述能力安全性考量常见安全威胁JSON-RPC 的简洁设计虽然降低了实现复杂度但也带来了一些安全考量数据泄露JSON-RPC 通常通过 HTTP 传输未加密的通信可能被中间人攻击截获。方法枚举如果服务端没有正确限制可调用方法攻击者可能通过枚举方法名发现未公开的接口。拒绝服务恶意客户端可能发送大量请求或构造超大的批量请求耗尽服务端资源。注入攻击不安全的参数处理可能导致 SQL 注入、命令注入等安全漏洞。重放攻击截获的有效请求可能被攻击者重放造成非预期的重复操作。安全加固措施传输层加密使用 HTTPS 协议确保通信内容的机密性和完整性。身份认证与授权实现身份验证机制如 JWT、OAuth 2.0在方法层面实施细粒度的授权控制。输入验证对所有参数进行严格的类型检查和值域验证。速率限制实施请求速率限制防止恶意高频访问。方法白名单仅暴露必要的方法对未公开的方法返回 -32601 错误。日志与监控记录详细的请求日志监控异常模式及时发现潜在攻击。结语JSON-RPC 2.0 协议设计简洁在分布式系统通信中有其适用场景。规范本身很短学习和实现成本都不高JSON 格式的请求/响应便于调试和日志记录。当然JSON-RPC 也有局限性。它缺少内置的元数据机制类型系统相对简单。在需要高度标准化、复杂类型系统或强类型安全的场景中可以考虑 gRPC、Thrift 等方案。如果你的业务场景需要简单、透明的服务间通信方式JSON-RPC 是一个值得考虑的选择。