分布式 RPC 深度拆解:Dubbo 与 gRPC 底层原理、核心差异与生产级调优实战

简介: 本文深入剖析RPC核心本质与通用架构,详解Dubbo 3.x(Java生态企业级框架)和gRPC(云原生跨语言框架)的底层原理、性能差异、生产调优及避坑指南,涵盖动态代理、序列化、网络传输、服务发现、集群容错等关键模块,助力构建高可用分布式系统。

一、RPC核心本质与通用架构

RPC(Remote Procedure Call,远程过程调用)的核心价值,是屏蔽分布式系统中网络通信、序列化、服务寻址、集群容错等底层复杂度,让开发者可以像调用本地方法一样,调用部署在远程节点的服务能力,是分布式微服务架构的核心基础设施。

1.1 RPC调用全流程核心链路

一次完整的RPC调用,本质是一次跨进程的请求-响应交互,核心链路可拆解为6个核心阶段,流程如下:

1.2 RPC通用分层架构

所有成熟的RPC框架,都遵循分层设计理念,核心分为5层,每层职责单一且可扩展:

分层 核心职责 核心扩展点
服务接口层 定义服务对外暴露的接口与方法契约,屏蔽底层实现 服务注册、版本控制、分组隔离
代理层 为服务接口生成动态代理,封装远程调用的所有细节 动态代理实现(JDK/字节码生成)
序列化层 实现对象与二进制流的双向转换,解决跨进程数据传输问题 序列化协议(Hessian2/Protobuf/Kryo等)
网络传输层 实现二进制数据的跨网络可靠传输,处理网络连接、IO读写 传输协议(TCP/HTTP2)、IO模型(BIO/NIO/AIO)
集群治理层 解决分布式环境下的服务发现、负载均衡、容错降级、流量管控问题 注册中心、负载均衡策略、集群容错机制

二、Dubbo 3.x 底层核心原理

Dubbo是阿里开源的Java生态原生RPC框架,历经多年生产验证,3.x版本完成了云原生架构升级,是国内企业级微服务架构的主流选型。

2.1 Dubbo 3.x 整体架构

Dubbo 3.x 采用分层微内核架构,核心层可插拔扩展,整体架构如下:

2.2 核心模块底层原理详解

2.2.1 动态代理层

代理层是RPC框架的入口,Dubbo的代理层核心是ProxyFactory接口,提供两种动态代理实现:

  • Javassist字节码生成:Dubbo默认实现,通过字节码技术直接生成代理类的Class文件,避免JDK动态代理的反射开销,调用性能更高,支持无接口代理。
  • JDK动态代理:基于Java反射机制实现,要求服务必须定义接口,兼容性强,适合对字节码生成有限制的场景。

代理层的核心逻辑,是在消费端发起调用时,拦截所有方法调用,将方法名、参数类型、参数值、调用上下文封装为RpcInvocation请求对象,交给后续链路处理。

2.2.2 序列化层

序列化是RPC性能的核心瓶颈之一,Dubbo提供了全场景的序列化协议适配,核心特性如下:

序列化协议 核心特点 适用场景
Hessian2 Dubbo默认协议,二进制序列化,跨语言兼容,性能稳定,支持对象循环引用 常规Java微服务业务场景,默认首选
Protobuf 谷歌开源的结构化数据序列化协议,压缩比极高,序列化速度快,强类型IDL,跨语言能力强 跨语言微服务、大报文传输、高性能要求场景
Kryo/FST Java专属序列化协议,性能远超Hessian2,压缩比更高 Java生态内的高性能场景,大对象传输
Fastjson2 JSON格式序列化,可读性强,跨语言兼容 网关透传、调试场景、需要JSON明文的场景

序列化的核心性能指标有两个:序列化/反序列化耗时序列化后的二进制体积,两者直接决定RPC调用的网络开销与CPU开销。

2.2.3 网络传输层

Dubbo 3.x 基于Netty 4.x 实现高性能NIO网络传输,原生支持TCP与HTTP2双协议,核心采用Reactor主从多线程模型:

  • BossGroup:主Reactor线程组,默认线程数为CPU核心数,负责处理客户端的TCP连接请求,完成三次握手后,将连接注册到WorkerGroup。
  • WorkerGroup:从Reactor线程组,默认线程数为CPU核心数*2,负责处理已建立连接的IO读写事件,完成数据的接收与发送。

为了避免业务逻辑阻塞IO线程,Dubbo设计了灵活的线程调度模型Dispatcher,核心实现如下:

  • all:所有请求、响应、心跳、连接事件,全部派发到业务线程池处理,默认策略,适合绝大多数业务场景。
  • direct:请求消息派发到业务线程池,响应消息、心跳事件直接在IO线程处理,减少线程切换开销,适合响应处理逻辑简单的场景。
  • message:仅请求、响应消息派发到业务线程池,心跳、连接断开等事件直接在IO线程处理,是生产环境高并发场景的优选策略。
  • execution:仅请求消息派发到业务线程池,其余所有事件均在IO线程处理,极致减少线程切换,适合业务逻辑耗时稳定的场景。

2.2.4 服务注册与发现

Dubbo 3.x 完成了从接口级服务发现到应用级服务发现的架构升级,彻底对齐云原生Kubernetes的Service模型,核心优势如下:

  • 注册中心压力大幅降低:传统接口级注册,每个服务接口都会生成一条注册数据,应用级注册仅需注册应用维度的一条数据,注册数据量降低90%以上。
  • 云原生适配性更强:与K8s Service、Istio等云原生基础设施无缝对接,无需额外的适配层。
  • 元数据隔离:应用的接口元数据、配置信息统一存储在元数据中心,与注册中心的地址数据解耦,进一步提升注册中心的稳定性。

服务发现的核心流程:服务提供者启动时,将自身的应用地址、端口、元数据信息注册到注册中心;消费者启动时,从注册中心订阅对应应用的地址列表,本地缓存并监听地址变化,实现服务地址的动态感知。

2.2.5 集群容错与负载均衡

分布式环境下,服务调用不可避免会出现网络波动、节点宕机等问题,Dubbo提供了完善的集群容错机制,核心实现如下:

  • Failover:失败自动切换,默认策略,调用失败后自动重试其他节点,适合读操作等幂等性接口,可通过retries参数配置重试次数。
  • Failfast:快速失败,仅发起一次调用,失败立即报错,适合写操作等非幂等性接口,避免重复提交。
  • Failsafe:失败安全,调用出现异常时直接忽略,打印日志不抛出异常,适合日志上报、审计等非核心链路场景。
  • Failback:失败自动恢复,后台记录失败请求,定时重发,适合消息通知、短信发送等最终一致性场景。
  • Forking:并行调用多个节点,只要一个成功就返回结果,适合读操作对实时性要求极高的场景,可通过forks参数配置并行数。
  • Broadcast:广播调用所有节点,所有节点调用完成才返回,任意一个节点失败则调用失败,适合缓存刷新、配置同步等全节点更新场景。

负载均衡是集群流量分发的核心,Dubbo提供了多种高性能负载均衡策略:

  • Random:加权随机,默认策略,按照节点的权重随机分发流量,权重越高的节点被选中的概率越大,流量分发均匀。
  • RoundRobin:加权轮询,按照权重依次分发流量,解决了低权重节点流量饥饿问题,流量分发绝对均匀。
  • LeastActive:最小活跃数优先,优先分发请求到当前处理请求数最少的节点,自动解决慢节点堆积请求的问题,适合业务处理耗时差异大的场景。
  • ConsistentHash:一致性哈希,相同参数的请求始终分发到同一个节点,解决分布式会话、缓存本地化等问题,天然支持节点扩缩容的流量平滑迁移。
  • ShortestResponse:最短响应优先,优先分发请求到平均响应时间最短的节点,极致优化调用耗时,适合对延迟敏感的场景。

三、gRPC 底层核心原理

gRPC是Google开源的高性能、跨语言RPC框架,基于HTTP/2标准协议与Protocol Buffers(Protobuf)序列化协议设计,原生支持流式调用,是云原生、多语言微服务、跨平台服务调用的主流选型。

3.1 gRPC 整体架构

gRPC采用客户端-服务端架构,基于IDL定义服务契约,原生支持多语言代码生成,整体架构如下:

3.2 核心模块底层原理详解

3.2.1 IDL与代码生成

gRPC采用契约优先的设计理念,通过Protobuf IDL(接口定义语言)统一描述服务接口与数据结构,再通过protoc编译器与gRPC插件,生成对应语言的客户端与服务端代码,彻底解决跨语言的接口兼容问题。

Protobuf IDL的核心优势:

  • 强类型约束,提前发现数据结构定义问题,避免运行时类型错误。
  • 天然支持向前向后兼容,字段新增、删除不会影响旧版本服务的正常运行。
  • 一套IDL可生成几乎所有主流编程语言的代码,跨语言能力极强。

3.2.2 序列化层:Protobuf底层原理

Protobuf是gRPC默认的唯一序列化协议,也是gRPC高性能的核心支撑,其底层采用TLV(Tag-Length-Value)存储结构与Varint变长编码,核心原理如下:

  1. Varint变长编码Varint是一种紧凑的数字编码方式,每个字节的最高位是标志位,1表示后续字节仍属于当前数字,0表示当前字节是数字的最后一个字节。例如:
  • 数字1,二进制为00000001,最高位为0,编码后仅占1个字节。
  • 数字300,二进制为100101100,Varint编码后为10101100 00000010,仅占2个字节,远小于int固定的4个字节。

对于绝大多数业务场景的数字,Varint编码可以将4字节的int压缩到1-2字节,8字节的long压缩到1-4字节,大幅降低序列化后的体积。

  1. TLV存储结构Protobuf的每个字段都由TagLength(可选)、Value三部分组成:
  • Tag由字段编号(1-536870911)与字段类型组成,字段编号是字段的唯一标识,编码后仅占1个字节(编号1-15),是Protobuf兼容性的核心。
  • Length仅对变长类型字段生效,标识Value的字节长度。
  • Value是字段的实际值,采用对应类型的编码方式存储。

这种存储结构,使得Protobuf可以忽略不认识的字段,天然支持字段的新增与删除,实现向前向后兼容,同时序列化后的体积远小于JSON、XML等文本格式,序列化/反序列化速度是JSON的3-10倍。

3.2.3 网络传输层:HTTP/2核心特性

gRPC底层完全基于HTTP/2协议设计,原生继承了HTTP/2的所有高性能特性,这也是gRPC与传统RPC框架最大的区别:

  1. 二进制分帧HTTP/2将所有传输数据拆分为二进制帧,帧是最小的传输单位,分为HEADERS帧(存储请求头、响应头)、DATA帧(存储请求体、响应体)等多种类型,二进制格式的解析效率远高于HTTP/1.x的文本格式。
  2. 多路复用HTTP/2在同一个TCP连接上,可以同时开启多个双向流(Stream),每个流都有唯一的ID,流之间相互独立,互不影响。彻底解决了HTTP/1.x的队头阻塞问题,无需建立多个TCP连接即可实现并行请求,大幅降低网络连接开销。

gRPC的每一次RPC调用,都会对应HTTP/2中的一个独立流,天然支持并行调用,同时流可以动态开启与关闭,资源占用极低。

  1. 头部压缩HPACKHTTP/2采用HPACK算法对请求头、响应头进行压缩,通过静态字典与动态字典,将重复的头部字段替换为索引,大幅降低头部开销。对于RPC调用,请求头中的大量重复字段(如Content-Type、服务名、方法名)可以被极致压缩,进一步降低网络传输开销。
  2. 原生支持流式传输HTTP/2的流是双向的、持续的数据流,基于此gRPC原生支持4种调用模式:
  • 一元RPC:客户端发送一次请求,服务端返回一次响应,对应传统的RPC调用模式。
  • 服务端流式RPC:客户端发送一次请求,服务端可以持续返回多个响应,适合消息推送、日志实时同步等场景。
  • 客户端流式RPC:客户端可以持续发送多个请求,服务端最终返回一次响应,适合大文件上传、批量数据上报等场景。
  • 双向流式RPC:客户端与服务端可以同时双向持续发送数据,双方的读写完全独立,适合实时聊天、物联网设备数据交互、实时音视频信令传输等场景。
  1. 流量控制与服务器推送HTTP/2基于滑动窗口实现了端到端的流量控制,避免发送方发送数据过快导致接收方无法处理;同时原生支持服务器推送,服务端可以主动向客户端推送数据,无需客户端发起请求,进一步提升交互效率。

3.2.4 名称解析与负载均衡

gRPC设计了可扩展的名称解析与负载均衡架构,核心分为两个组件:

  • Resolver:名称解析器,负责将服务名称解析为对应的服务端地址列表,默认提供DNSResolver,支持自定义实现对接Consul、Etcd、Nacos等注册中心。
  • LoadBalancer:负载均衡器,负责从地址列表中选择合适的服务端节点发起调用,核心实现包括:
  • PickFirst:默认策略,尝试连接列表中的第一个地址,连接成功则所有请求都使用该连接,连接失败则尝试下一个地址,适合单节点服务场景。
  • RoundRobin:轮询策略,依次向所有可用的服务端节点分发请求,流量均匀分发,适合多节点集群场景。
  • WeightedRoundRobin:加权轮询,按照节点的权重分发流量,权重越高的节点处理的请求越多。
  • LeastRequest:最小请求数优先,优先选择当前正在处理请求数最少的节点,避免慢节点堆积请求。

gRPC的负载均衡是客户端侧实现的,客户端本地缓存服务端地址列表,直接发起点对点调用,无需经过中间代理,减少了网络跳转开销。

四、Dubbo与gRPC核心差异深度对比

对比维度 Dubbo 3.x gRPC
底层传输协议 原生支持TCP私有协议、HTTP/2协议,TCP协议性能更优 完全基于HTTP/2协议设计,协议通用性更强
序列化协议 多协议适配,默认Hessian2,支持Protobuf、Kryo、FST、JSON等 仅原生支持Protobuf,强绑定,序列化性能极致
服务治理能力 企业级全链路服务治理,内置注册中心、配置中心、流量管控、熔断降级、限流、链路追踪等全套能力 核心聚焦RPC调用本身,服务治理能力需通过拦截器、第三方组件扩展实现
跨语言能力 Java生态原生,其他语言支持有限,跨语言能力较弱 原生支持几乎所有主流编程语言,跨语言能力极强
流式调用支持 3.x版本基于HTTP/2支持流式调用,能力完善度一般 原生深度支持4种流式调用模式,流式场景适配性极强
云原生适配 3.x版本完成云原生升级,支持应用级服务发现、K8s、Istio适配 云原生原生设计,与K8s、云原生网关、服务网格无缝适配,是CNCF毕业项目
性能表现 Java生态内TCP协议场景下,性能优于gRPC;HTTP/2场景与gRPC持平 跨语言场景、流式场景、大报文场景下,性能优势明显
适用场景 Java生态为主的企业级微服务架构,需要强服务治理能力的业务场景 多语言微服务、云原生架构、跨平台服务调用、流式数据传输场景

五、生产级调优实战

5.1 Dubbo 3.x 核心调优策略

5.1.1 序列化调优

  • 常规业务场景,保持默认Hessian2协议即可,无需额外调整。
  • 大报文传输(超过1KB)、高并发场景,切换为Kryo/FST序列化协议,配置如下:

<dubbo:protocol name="dubbo" serialization="kryo"/>

  • 跨语言调用场景,切换为Protobuf序列化协议,配合Dubbo的Protobuf IDL生成代码使用。
  • 禁止在生产环境使用JSON序列化处理核心业务请求,仅用于调试与网关透传场景。

5.1.2 网络层调优

  • Netty线程数调优:BossGroup线程数保持默认CPU核心数即可;WorkerGroup线程数,CPU密集型场景设置为CPU核心数2,IO密集型场景设置为CPU核心数4,配置如下:

<dubbo:protocol name="dubbo" iothreads="8"/>

  • TCP参数调优:开启TCP_NODELAY禁用Nagle算法,减少小包延迟;调整SO_BACKLOG连接队列大小,应对高并发连接场景;开启SO_KEEPALIVE保活机制,清理无效连接,配置如下:

<dubbo:protocol name="dubbo" payload="8388608" accepts="1000">
   <dubbo:parameter key="tcp.nodelay" value="true"/>
   <dubbo:parameter key="so.backlog" value="1024"/>
   <dubbo:parameter key="so.keepalive" value="true"/>
</dubbo:protocol>

  • 报文大小限制:调整payload参数,默认8MB,禁止设置过大,避免大报文导致的OOM与网络阻塞。

5.1.3 线程模型调优

  • 高并发业务场景,优先使用message调度器,仅将请求响应消息派发到业务线程池,减少线程切换开销,配置如下:

<dubbo:protocol name="dubbo" dispatcher="message"/>

  • 业务逻辑耗时极短(小于1ms)的场景,使用direct调度器,极致减少线程切换。
  • 业务线程池调优:核心线程数设置为CPU核心数20,最大线程数设置为CPU核心数50,队列使用SynchronousQueue,拒绝策略使用AbortPolicy,避免队列堆积导致的请求超时,配置如下:

<dubbo:protocol name="dubbo" threadpool="cached" threads="200" queues="0"/>

  • 核心业务与非核心业务使用不同的协议端口,实现线程池隔离,避免非核心业务线程池耗尽影响核心业务。

5.1.4 集群容错调优

  • 读操作、幂等性接口,使用Failover容错策略,重试次数设置为2次,避免过多重试导致的雪崩效应,配置如下:

<dubbo:reference interface="com.jam.demo.service.UserService" retries="2"/>

  • 写操作、非幂等性接口,强制使用Failfast容错策略,禁止重试,避免重复提交。
  • 非核心链路接口,使用Failsafe容错策略,调用失败不影响主流程。
  • 对延迟敏感的读操作场景,使用ShortestResponse负载均衡策略;业务处理耗时差异大的场景,使用LeastActive负载均衡策略;分布式会话、缓存本地化场景,使用ConsistentHash负载均衡策略。

5.1.5 服务发现调优

  • 生产环境强制使用应用级服务发现,降低注册中心压力,配置如下:

<dubbo:application name="demo-provider" registry-mode="instance"/>

  • 调整服务心跳间隔,默认60s,生产环境设置为30s,加快异常节点的剔除速度;调整地址缓存刷新间隔,避免频繁拉取注册中心数据。
  • 注册中心、配置中心、元数据中心使用独立的集群部署,避免单点故障。

5.2 gRPC 核心调优策略

5.2.1 Protobuf序列化调优

  • 字段编号优化:高频使用的字段,编号设置在1-15之间,Tag仅占1个字节;不常用的字段编号设置在16以上,减少序列化体积。
  • 禁止修改已发布字段的编号与类型,避免兼容性问题;新增字段使用可选类型,删除字段保留编号,禁止复用已删除的字段编号。
  • 大字段场景,开启Protobuf的递归消息限制,避免大消息导致的OOM;复用Message.Builder对象,减少对象创建的GC开销。
  • 禁止在Protobuf中嵌套过深的消息结构,最大嵌套层数不超过10层,避免序列化/反序列化栈溢出。

5.2.2 HTTP/2层调优

  • 流控窗口调优:增大HTTP/2的初始流控窗口大小,默认64KB,大报文传输场景设置为1MB,提升大文件传输的吞吐量,配置如下:

ManagedChannel channel = ManagedChannelBuilder.forAddress("127.0.0.1", 9090)
   .flowControlWindow(1024 * 1024)
   .build();

  • 并发流数调优:调整最大并发流数,默认100,高并发场景设置为1000,避免流数限制导致的请求阻塞,配置如下:

Server server = ServerBuilder.forPort(9090)
   .maxConcurrentCallsPerConnection(1000)
   .addService(new UserServiceImpl())
   .build();

  • KeepAlive调优:开启HTTP/2 KeepAlive机制,设置心跳间隔为30s,超时时间为5s,快速清理无效连接,配置如下:

ManagedChannel channel = ManagedChannelBuilder.forAddress("127.0.0.1", 9090)
   .keepAliveTime(30, TimeUnit.SECONDS)
   .keepAliveTimeout(5, TimeUnit.SECONDS)
   .keepAliveWithoutCalls(true)
   .build();

  • 最大报文大小调优:调整最大消息大小,默认4MB,大报文场景设置为16MB,避免大消息被拦截,配置如下:

Server server = ServerBuilder.forPort(9090)
   .maxInboundMessageSize(16 * 1024 * 1024)
   .addService(new UserServiceImpl())
   .build();

5.2.3 线程模型调优

  • Netty EventLoopGroup线程数调优:客户端与服务端的EventLoopGroup线程数,CPU密集型场景设置为CPU核心数2,IO密集型场景设置为CPU核心数4,配置如下:

EventLoopGroup bossGroup = new NioEventLoopGroup(4);
EventLoopGroup workerGroup = new NioEventLoopGroup(8);
Server server = NettyServerBuilder.forPort(9090)
   .bossEventLoopGroup(bossGroup)
   .workerEventLoopGroup(workerGroup)
   .addService(new UserServiceImpl())
   .build();

  • 业务线程池隔离:禁止在IO线程中执行耗时的业务逻辑,所有业务操作都必须提交到独立的业务线程池执行,避免阻塞IO线程导致的吞吐量下降,配置如下:

ExecutorService businessExecutor = new ThreadPoolExecutor(
   40,
   200,
   60L,
   TimeUnit.SECONDS,
   new SynchronousQueue<>(),
   new ThreadFactory() {
       private final AtomicInteger threadNumber = new AtomicInteger(1);
       @Override
       public Thread newThread(Runnable r) {
           return new Thread(r, "grpc-business-thread-" + threadNumber.getAndIncrement());
       }
   },
   new ThreadPoolExecutor.AbortPolicy()
);
Server server = ServerBuilder.forPort(9090)
   .executor(businessExecutor)
   .addService(new UserServiceImpl())
   .build();

5.2.4 负载均衡与重试调优

  • 多节点集群场景,强制使用RoundRobin负载均衡策略,配置如下:

ManagedChannel channel = ManagedChannelBuilder.forAddress("127.0.0.1", 9090)
   .defaultLoadBalancingPolicy("round_robin")
   .build();

  • 幂等性接口,配置重试策略,设置最大重试次数为2次,重试超时时间与调用超时时间匹配,避免重试风暴,配置通过gRPC的ServiceConfig实现。
  • 开启健康检查机制,客户端自动剔除不可用的服务端节点,避免请求分发到故障节点。

六、完整实战示例

6.1 Dubbo 3.x Spring Boot 实战示例

6.1.1 Maven依赖配置

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">

   <modelVersion>4.0.0</modelVersion>
   <parent>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-parent</artifactId>
       <version>3.2.4</version>
       <relativePath/>
   </parent>
   <groupId>com.jam.demo</groupId>
   <artifactId>dubbo-demo</artifactId>
   <version>1.0.0</version>
   <name>dubbo-demo</name>
   <properties>
       <java.version>17</java.version>
       <dubbo.version>3.2.10</dubbo.version>
       <lombok.version>1.18.30</lombok.version>
       <fastjson2.version>2.0.49</fastjson2.version>
       <guava.version>33.1.0-jre</guava.version>
       <springdoc.version>2.5.0</springdoc.version>
   </properties>
   <dependencies>
       <dependency>
           <groupId>org.apache.dubbo</groupId>
           <artifactId>dubbo-spring-boot-starter</artifactId>
           <version>${dubbo.version}</version>
       </dependency>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-web</artifactId>
       </dependency>
       <dependency>
           <groupId>org.springdoc</groupId>
           <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
           <version>${springdoc.version}</version>
       </dependency>
       <dependency>
           <groupId>org.apache.dubbo</groupId>
           <artifactId>dubbo-registry-nacos</artifactId>
           <version>${dubbo.version}</version>
       </dependency>
       <dependency>
           <groupId>com.alibaba.nacos</groupId>
           <artifactId>nacos-client</artifactId>
           <version>2.3.2</version>
       </dependency>
       <dependency>
           <groupId>com.esotericsoftware</groupId>
           <artifactId>kryo</artifactId>
           <version>5.5.0</version>
       </dependency>
       <dependency>
           <groupId>org.projectlombok</groupId>
           <artifactId>lombok</artifactId>
           <version>${lombok.version}</version>
           <scope>provided</scope>
       </dependency>
       <dependency>
           <groupId>com.alibaba.fastjson2</groupId>
           <artifactId>fastjson2</artifactId>
           <version>${fastjson2.version}</version>
       </dependency>
       <dependency>
           <groupId>com.google.guava</groupId>
           <artifactId>guava</artifactId>
           <version>${guava.version}</version>
       </dependency>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-test</artifactId>
           <scope>test</scope>
       </dependency>
   </dependencies>
   <build>
       <plugins>
           <plugin>
               <groupId>org.springframework.boot</groupId>
               <artifactId>spring-boot-maven-plugin</artifactId>
               <configuration>
                   <excludes>
                       <exclude>
                           <groupId>org.projectlombok</groupId>
                           <artifactId>lombok</artifactId>
                       </exclude>
                   </excludes>
               </configuration>
           </plugin>
       </plugins>
   </build>
</project>

6.1.2 服务接口定义

package com.jam.demo.service;

import com.jam.demo.dto.UserDTO;
import com.jam.demo.dto.UserQueryDTO;
import com.jam.demo.common.Result;

import java.util.List;

/**
* 用户服务RPC接口
* @author ken
*/

public interface UserService {

   /**
    * 根据用户ID查询用户信息
    * @param userId 用户ID
    * @return 用户信息
    */

   Result<UserDTO> getUserById(Long userId);

   /**
    * 根据条件查询用户列表
    * @param queryDTO 查询条件
    * @return 用户列表
    */

   Result<List<UserDTO>> listUserByCondition(UserQueryDTO queryDTO);

   /**
    * 新增用户信息
    * @param userDTO 用户信息
    * @return 新增结果
    */

   Result<Long> addUser(UserDTO userDTO);
}

6.1.3 数据传输对象定义

package com.jam.demo.dto;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;

/**
* 用户数据传输对象
* @author ken
*/

@Data
@Schema(description = "用户信息DTO")
public class UserDTO implements Serializable {

   private static final long serialVersionUID = 1L;

   @Schema(description = "用户ID", example = "1")
   private Long userId;

   @Schema(description = "用户名", example = "jam")
   private String username;

   @Schema(description = "用户昵称", example = "果酱")
   private String nickname;

   @Schema(description = "邮箱", example = "jam@demo.com")
   private String email;

   @Schema(description = "手机号", example = "13800138000")
   private String phone;

   @Schema(description = "创建时间")
   private LocalDateTime createTime;

   @Schema(description = "更新时间")
   private LocalDateTime updateTime;
}

package com.jam.demo.dto;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;

/**
* 用户查询条件DTO
* @author ken
*/

@Data
@Schema(description = "用户查询条件DTO")
public class UserQueryDTO implements Serializable {

   private static final long serialVersionUID = 1L;

   @Schema(description = "用户名", example = "jam")
   private String username;

   @Schema(description = "用户昵称", example = "果酱")
   private String nickname;

   @Schema(description = "手机号", example = "13800138000")
   private String phone;

   @Schema(description = "页码", example = "1")
   private Integer pageNum = 1;

   @Schema(description = "每页条数", example = "10")
   private Integer pageSize = 10;
}

package com.jam.demo.common;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;

/**
* 统一响应结果
* @author ken
* @param <T> 响应数据类型
*/

@Data
@Schema(description = "统一响应结果")
public class Result<T> implements Serializable {

   private static final long serialVersionUID = 1L;

   @Schema(description = "响应码", example = "200")
   private Integer code;

   @Schema(description = "响应消息", example = "操作成功")
   private String message;

   @Schema(description = "响应数据")
   private T data;

   private Result(Integer code, String message, T data) {
       this.code = code;
       this.message = message;
       this.data = data;
   }

   public static <T> Result<T> success(T data) {
       return new Result<>(200, "操作成功", data);
   }

   public static <T> Result<T> success(String message, T data) {
       return new Result<>(200, message, data);
   }

   public static <T> Result<T> fail(Integer code, String message) {
       return new Result<>(code, message, null);
   }

   public static <T> Result<T> fail(String message) {
       return new Result<>(500, message, null);
   }

   public boolean isSuccess() {
       return 200 == this.code;
   }
}

6.1.4 服务提供者实现

package com.jam.demo.service.impl;

import com.jam.demo.dto.UserDTO;
import com.jam.demo.dto.UserQueryDTO;
import com.jam.demo.common.Result;
import com.jam.demo.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.util.StringUtils;
import org.springframework.util.CollectionUtils;
import com.google.common.collect.Lists;

import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

/**
* 用户服务RPC实现类
* @author ken
*/

@Slf4j
@DubboService(version = "1.0.0", group = "demo", timeout = 3000)
public class UserServiceImpl implements UserService {

   private static final List<UserDTO> USER_DATA = Lists.newArrayList();

   static {
       UserDTO user1 = new UserDTO();
       user1.setUserId(1L);
       user1.setUsername("jam");
       user1.setNickname("果酱");
       user1.setEmail("jam@demo.com");
       user1.setPhone("13800138000");
       user1.setCreateTime(LocalDateTime.now());
       user1.setUpdateTime(LocalDateTime.now());
       USER_DATA.add(user1);

       UserDTO user2 = new UserDTO();
       user2.setUserId(2L);
       user2.setUsername("ken");
       user2.setNickname("Ken");
       user2.setEmail("ken@demo.com");
       user2.setPhone("13900139000");
       user2.setCreateTime(LocalDateTime.now());
       user2.setUpdateTime(LocalDateTime.now());
       USER_DATA.add(user2);
   }

   @Override
   public Result<UserDTO> getUserById(Long userId) {
       log.info("查询用户信息,userId:{}", userId);
       if (userId == null || userId <= 0) {
           return Result.fail("用户ID不能为空");
       }
       UserDTO userDTO = USER_DATA.stream()
               .filter(user -> userId.equals(user.getUserId()))
               .findFirst()
               .orElse(null);
       return Result.success(userDTO);
   }

   @Override
   public Result<List<UserDTO>> listUserByCondition(UserQueryDTO queryDTO) {
       log.info("条件查询用户列表,queryDTO:{}", queryDTO);
       if (queryDTO == null) {
           return Result.success(USER_DATA);
       }
       List<UserDTO> resultList = USER_DATA.stream().filter(user -> {
           boolean match = true;
           if (StringUtils.hasText(queryDTO.getUsername())) {
               match = user.getUsername().contains(queryDTO.getUsername());
           }
           if (match && StringUtils.hasText(queryDTO.getNickname())) {
               match = user.getNickname().contains(queryDTO.getNickname());
           }
           if (match && StringUtils.hasText(queryDTO.getPhone())) {
               match = user.getPhone().contains(queryDTO.getPhone());
           }
           return match;
       }).collect(Collectors.toList());
       return Result.success(resultList);
   }

   @Override
   public Result<Long> addUser(UserDTO userDTO) {
       log.info("新增用户信息,userDTO:{}", userDTO);
       if (userDTO == null) {
           return Result.fail("用户信息不能为空");
       }
       if (!StringUtils.hasText(userDTO.getUsername())) {
           return Result.fail("用户名不能为空");
       }
       boolean exist = USER_DATA.stream()
               .anyMatch(user -> user.getUsername().equals(userDTO.getUsername()));
       if (exist) {
           return Result.fail("用户名已存在");
       }
       Long maxUserId = USER_DATA.stream()
               .map(UserDTO::getUserId)
               .max(Long::compareTo)
               .orElse(0L);
       userDTO.setUserId(maxUserId + 1);
       userDTO.setCreateTime(LocalDateTime.now());
       userDTO.setUpdateTime(LocalDateTime.now());
       USER_DATA.add(userDTO);
       return Result.success(userDTO.getUserId());
   }
}

6.1.5 服务提供者配置文件

spring:
 application:
   name: dubbo-demo-provider
dubbo:
 application:
   name: ${spring.application.name}
   registry-mode: instance
 registry:
   address: nacos://127.0.0.1:8848
   group: demo
 protocol:
   name: dubbo
   port: 20880
   serialization: kryo
   dispatcher: message
   threadpool: cached
   threads: 200
   iothreads: 8
   payload: 8388608
   parameters:
     tcp.nodelay: true
     so.backlog: 1024
     so.keepalive: true
 provider:
   version: 1.0.0
   group: demo
   timeout: 3000
   retries: 0

6.1.6 服务消费者Controller

package com.jam.demo.controller;

import com.jam.demo.dto.UserDTO;
import com.jam.demo.dto.UserQueryDTO;
import com.jam.demo.common.Result;
import com.jam.demo.service.UserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
* 用户服务前端控制器
* @author ken
*/

@Slf4j
@RestController
@RequestMapping("/user")
@Tag(name = "用户管理", description = "用户信息管理接口")
public class UserController {

   @DubboReference(version = "1.0.0", group = "demo", check = false, timeout = 3000, retries = 2)
   private UserService userService;

   @GetMapping("/{userId}")
   @Operation(summary = "根据用户ID查询用户信息", description = "通过用户ID获取用户详细信息")
   public Result<UserDTO> getUserById(
           @Parameter(description = "用户ID", required = true, example = "1")

           @PathVariable Long userId) {
       return userService.getUserById(userId);
   }

   @PostMapping("/list")
   @Operation(summary = "条件查询用户列表", description = "根据查询条件获取用户列表")
   public Result<List<UserDTO>> listUserByCondition(@RequestBody UserQueryDTO queryDTO) {
       return userService.listUserByCondition(queryDTO);
   }

   @PostMapping("/add")
   @Operation(summary = "新增用户", description = "新增用户信息")
   public Result<Long> addUser(@RequestBody UserDTO userDTO) {
       return userService.addUser(userDTO);
   }
}

6.1.7 服务消费者配置文件

spring:
 application:
   name: dubbo-demo-consumer
server:
 port: 8080
dubbo:
 application:
   name: ${spring.application.name}
   registry-mode: instance
 registry:
   address: nacos://127.0.0.1:8848
   group: demo
 consumer:
   version: 1.0.0
   group: demo
   timeout: 3000
   retries: 2
   check: false
springdoc:
 api-docs:
   enabled: true
 swagger-ui:
   enabled: true
   path: /swagger-ui.html

6.2 gRPC Java 实战示例

6.2.1 Maven依赖配置

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">

   <modelVersion>4.0.0</modelVersion>
   <parent>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-parent</artifactId>
       <version>3.2.4</version>
       <relativePath/>
   </parent>
   <groupId>com.jam.demo</groupId>
   <artifactId>grpc-demo</artifactId>
   <version>1.0.0</version>
   <name>grpc-demo</name>
   <properties>
       <java.version>17</java.version>
       <grpc.version>1.65.1</grpc.version>
       <protobuf.version>3.25.3</protobuf.version>
       <lombok.version>1.18.30</lombok.version>
       <guava.version>33.1.0-jre</guava.version>
   </properties>
   <dependencies>
       <dependency>
           <groupId>io.grpc</groupId>
           <artifactId>grpc-netty-shaded</artifactId>
           <version>${grpc.version}</version>
       </dependency>
       <dependency>
           <groupId>io.grpc</groupId>
           <artifactId>grpc-protobuf</artifactId>
           <version>${grpc.version}</version>
       </dependency>
       <dependency>
           <groupId>io.grpc</groupId>
           <artifactId>grpc-stub</artifactId>
           <version>${grpc.version}</version>
       </dependency>
       <dependency>
           <groupId>com.google.protobuf</groupId>
           <artifactId>protobuf-java</artifactId>
           <version>${protobuf.version}</version>
       </dependency>
       <dependency>
           <groupId>javax.annotation</groupId>
           <artifactId>javax.annotation-api</artifactId>
           <version>1.3.2</version>
       </dependency>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-web</artifactId>
       </dependency>
       <dependency>
           <groupId>org.projectlombok</groupId>
           <artifactId>lombok</artifactId>
           <version>${lombok.version}</version>
           <scope>provided</scope>
       </dependency>
       <dependency>
           <groupId>com.google.guava</groupId>
           <artifactId>guava</artifactId>
           <version>${guava.version}</version>
       </dependency>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-test</artifactId>
           <scope>test</scope>
       </dependency>
   </dependencies>
   <build>
       <extensions>
           <extension>
               <groupId>kr.motd.maven</groupId>
               <artifactId>os-maven-plugin</artifactId>
               <version>1.7.1</version>
           </extension>
       </extensions>
       <plugins>
           <plugin>
               <groupId>org.xolstice.maven.plugins</groupId>
               <artifactId>protobuf-maven-plugin</artifactId>
               <version>0.6.1</version>
               <configuration>
                   <protocArtifact>com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier}</protocArtifact>
                   <pluginId>grpc-java</pluginId>
                   <pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
                   <protoSourceRoot>src/main/proto</protoSourceRoot>
                   <outputDirectory>src/main/java</outputDirectory>
                   <clearOutputDirectory>false</clearOutputDirectory>
               </configuration>
               <executions>
                   <execution>
                       <goals>
                           <goal>compile</goal>
                           <goal>compile-custom</goal>
                       </goals>
                   </execution>
               </executions>
           </plugin>
           <plugin>
               <groupId>org.springframework.boot</groupId>
               <artifactId>spring-boot-maven-plugin</artifactId>
               <configuration>
                   <excludes>
                       <exclude>
                           <groupId>org.projectlombok</groupId>
                           <artifactId>lombok</artifactId>
                       </exclude>
                   </excludes>
               </configuration>
           </plugin>
       </plugins>
   </build>
</project>

6.2.2 Protobuf IDL定义

syntax = "proto3";

option java_multiple_files = true;
option java_package = "com.jam.demo.grpc";
option java_outer_classname = "UserServiceProto";
option objc_class_prefix = "USR";

package user;

import "google/protobuf/timestamp.proto";

// 用户信息
message User {
 int64 user_id = 1;
 string username = 2;
 string nickname = 3;
 string email = 4;
 string phone = 5;
 google.protobuf.Timestamp create_time = 6;
 google.protobuf.Timestamp update_time = 7;
}

// 用户查询请求
message UserQueryRequest {
 int64 user_id = 1;
}

// 用户列表查询请求
message UserListQueryRequest {
 string username = 1;
 string nickname = 2;
 string phone = 3;
 int32 page_num = 4;
 int32 page_size = 5;
}

// 用户新增请求
message UserAddRequest {
 string username = 1;
 string nickname = 2;
 string email = 3;
 string phone = 4;
}

// 通用响应
message CommonResponse {
 int32 code = 1;
 string message = 2;
 User data = 3;
}

// 用户列表响应
message UserListResponse {
 int32 code = 1;
 string message = 2;
 repeated User data = 3;
}

// 用户新增响应
message UserAddResponse {
 int32 code = 1;
 string message = 2;
 int64 user_id = 3;
}

// 用户服务定义
service UserService {
 // 根据用户ID查询用户信息
 rpc GetUserById(UserQueryRequest) returns (CommonResponse);
 // 条件查询用户列表
 rpc ListUserByCondition(UserListQueryRequest) returns (UserListResponse);
 // 新增用户信息
 rpc AddUser(UserAddRequest) returns (UserAddResponse);
}

6.2.3 gRPC服务实现类

package com.jam.demo.service.impl;

import com.google.protobuf.Timestamp;
import com.jam.demo.grpc.*;
import io.grpc.stub.StreamObserver;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import com.google.common.collect.Lists;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.List;
import java.util.stream.Collectors;

/**
* gRPC用户服务实现类
* @author ken
*/

@Slf4j
public class UserGrpcServiceImpl extends UserServiceGrpc.UserServiceImplBase {

   private static final List<User> USER_DATA = Lists.newArrayList();

   static {
       Timestamp now = Timestamp.newBuilder()
               .setSeconds(Instant.now().getEpochSecond())
               .setNanos(Instant.now().getNano())
               .build();
       User user1 = User.newBuilder()
               .setUserId(1L)
               .setUsername("jam")
               .setNickname("果酱")
               .setEmail("jam@demo.com")
               .setPhone("13800138000")
               .setCreateTime(now)
               .setUpdateTime(now)
               .build();
       USER_DATA.add(user1);

       User user2 = User.newBuilder()
               .setUserId(2L)
               .setUsername("ken")
               .setNickname("Ken")
               .setEmail("ken@demo.com")
               .setPhone("13900139000")
               .setCreateTime(now)
               .setUpdateTime(now)
               .build();
       USER_DATA.add(user2);
   }

   @Override
   public void getUserById(UserQueryRequest request, StreamObserver<CommonResponse> responseObserver) {
       log.info("gRPC查询用户信息,userId:{}", request.getUserId());
       CommonResponse.Builder responseBuilder = CommonResponse.newBuilder();
       try {
           long userId = request.getUserId();
           if (userId <= 0) {
               responseBuilder.setCode(500).setMessage("用户ID不能为空");
               responseObserver.onNext(responseBuilder.build());
               responseObserver.onCompleted();
               return;
           }
           User user = USER_DATA.stream()
                   .filter(u -> userId == u.getUserId())
                   .findFirst()
                   .orElse(null);
           responseBuilder.setCode(200).setMessage("操作成功");
           if (user != null) {
               responseBuilder.setData(user);
           }
       } catch (Exception e) {
           log.error("查询用户信息异常", e);
           responseBuilder.setCode(500).setMessage("系统异常");
       }
       responseObserver.onNext(responseBuilder.build());
       responseObserver.onCompleted();
   }

   @Override
   public void listUserByCondition(UserListQueryRequest request, StreamObserver<UserListResponse> responseObserver) {
       log.info("gRPC条件查询用户列表,request:{}", request);
       UserListResponse.Builder responseBuilder = UserListResponse.newBuilder();
       try {
           List<User> resultList = USER_DATA.stream().filter(user -> {
               boolean match = true;
               if (StringUtils.hasText(request.getUsername())) {
                   match = user.getUsername().contains(request.getUsername());
               }
               if (match && StringUtils.hasText(request.getNickname())) {
                   match = user.getNickname().contains(request.getNickname());
               }
               if (match && StringUtils.hasText(request.getPhone())) {
                   match = user.getPhone().contains(request.getPhone());
               }
               return match;
           }).collect(Collectors.toList());
           responseBuilder.setCode(200).setMessage("操作成功").addAllData(resultList);
       } catch (Exception e) {
           log.error("查询用户列表异常", e);
           responseBuilder.setCode(500).setMessage("系统异常");
       }
       responseObserver.onNext(responseBuilder.build());
       responseObserver.onCompleted();
   }

   @Override
   public void addUser(UserAddRequest request, StreamObserver<UserAddResponse> responseObserver) {
       log.info("gRPC新增用户信息,request:{}", request);
       UserAddResponse.Builder responseBuilder = UserAddResponse.newBuilder();
       try {
           if (!StringUtils.hasText(request.getUsername())) {
               responseBuilder.setCode(500).setMessage("用户名不能为空");
               responseObserver.onNext(responseBuilder.build());
               responseObserver.onCompleted();
               return;
           }
           boolean exist = USER_DATA.stream()
                   .anyMatch(user -> user.getUsername().equals(request.getUsername()));
           if (exist) {
               responseBuilder.setCode(500).setMessage("用户名已存在");
               responseObserver.onNext(responseBuilder.build());
               responseObserver.onCompleted();
               return;
           }
           long maxUserId = USER_DATA.stream()
                   .map(User::getUserId)
                   .max(Long::compareTo)
                   .orElse(0L);
           long newUserId = maxUserId + 1;
           Timestamp now = Timestamp.newBuilder()
                   .setSeconds(Instant.now().getEpochSecond())
                   .setNanos(Instant.now().getNano())
                   .build();
           User newUser = User.newBuilder()
                   .setUserId(newUserId)
                   .setUsername(request.getUsername())
                   .setNickname(request.getNickname())
                   .setEmail(request.getEmail())
                   .setPhone(request.getPhone())
                   .setCreateTime(now)
                   .setUpdateTime(now)
                   .build();
           USER_DATA.add(newUser);
           responseBuilder.setCode(200).setMessage("操作成功").setUserId(newUserId);
       } catch (Exception e) {
           log.error("新增用户信息异常", e);
           responseBuilder.setCode(500).setMessage("系统异常");
       }
       responseObserver.onNext(responseBuilder.build());
       responseObserver.onCompleted();
   }
}

6.2.4 gRPC服务端启动类

package com.jam.demo;

import com.jam.demo.service.impl.UserGrpcServiceImpl;
import io.grpc.Server;
import io.grpc.ServerBuilder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
* gRPC服务端启动类
* @author ken
*/

@Slf4j
@SpringBootApplication
public class GrpcServerApplication {

   private Server grpcServer;
   private static final int GRPC_PORT = 9090;

   public static void main(String[] args) {
       SpringApplication.run(GrpcServerApplication.class, args);
   }

   @PostConstruct
   public void startGrpcServer() throws Exception {
       ExecutorService businessExecutor = new ThreadPoolExecutor(
               40,
               200,
               60L,
               TimeUnit.SECONDS,
               new LinkedBlockingQueue<>(1000),
               new ThreadFactory() {
                   private final AtomicInteger threadNumber = new AtomicInteger(1);
                   @Override
                   public Thread newThread(Runnable r) {
                       return new Thread(r, "grpc-business-thread-" + threadNumber.getAndIncrement());
                   }
               },
               new ThreadPoolExecutor.AbortPolicy()
       );
       grpcServer = ServerBuilder.forPort(GRPC_PORT)
               .executor(businessExecutor)
               .maxInboundMessageSize(16 * 1024 * 1024)
               .maxConcurrentCallsPerConnection(1000)
               .addService(new UserGrpcServiceImpl())
               .build()
               .start();
       log.info("gRPC server started on port {}", GRPC_PORT);
       Runtime.getRuntime().addShutdownHook(new Thread(() -> {
           log.info("shutting down gRPC server");
           stopGrpcServer();
       }));
   }

   @PreDestroy
   public void stopGrpcServer() {
       if (grpcServer != null) {
           grpcServer.shutdown();
       }
   }
}

6.2.5 gRPC客户端Controller

package com.jam.demo.controller;

import com.jam.demo.grpc.*;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.concurrent.TimeUnit;

/**
* gRPC用户服务前端控制器
* @author ken
*/

@Slf4j
@RestController
@RequestMapping("/grpc/user")
@Tag(name = "gRPC用户管理", description = "gRPC用户信息管理接口")
public class GrpcUserController {

   private final ManagedChannel channel;
   private final UserServiceGrpc.UserServiceBlockingStub userServiceStub;

   public GrpcUserController() {
       this.channel = ManagedChannelBuilder.forAddress("127.0.0.1", 9090)
               .usePlaintext()
               .defaultLoadBalancingPolicy("round_robin")
               .flowControlWindow(1024 * 1024)
               .keepAliveTime(30, TimeUnit.SECONDS)
               .keepAliveTimeout(5, TimeUnit.SECONDS)
               .keepAliveWithoutCalls(true)
               .build();
       this.userServiceStub = UserServiceGrpc.newBlockingStub(channel);
   }

   @GetMapping("/{userId}")
   @Operation(summary = "根据用户ID查询用户信息", description = "通过gRPC调用获取用户详细信息")
   public CommonResponse getUserById(
           @Parameter(description = "用户ID", required = true, example = "1")

           @PathVariable Long userId) {
       UserQueryRequest request = UserQueryRequest.newBuilder().setUserId(userId).build();
       return userServiceStub.getUserById(request);
   }

   @PostMapping("/list")
   @Operation(summary = "条件查询用户列表", description = "通过gRPC调用获取用户列表")
   public UserListResponse listUserByCondition(@RequestBody UserListQueryRequest request) {
       return userServiceStub.listUserByCondition(request);
   }

   @PostMapping("/add")
   @Operation(summary = "新增用户", description = "通过gRPC调用新增用户信息")
   public UserAddResponse addUser(@RequestBody UserAddRequest request) {
       return userServiceStub.addUser(request);
   }
}

七、生产环境常见坑点与避坑指南

7.1 Dubbo 常见坑点

  1. 超时时间配置优先级问题Dubbo超时时间配置优先级从高到低为:方法级 > 接口级 > 消费者全局 > 提供者全局,生产环境需明确配置方法级超时时间,避免全局配置覆盖导致的超时时间不符合预期。非幂等接口必须关闭重试,避免重复提交。
  2. 序列化版本兼容问题Java序列化对象新增字段后,必须显式声明serialVersionUID,否则反序列化会出现InvalidClassException异常;Hessian2序列化不支持对象循环引用,需提前处理循环依赖的对象。
  3. 线程池隔离问题核心业务与非核心业务必须使用不同的协议端口,实现线程池隔离,避免非核心业务耗时过长耗尽线程池,导致核心业务无法处理请求。禁止在业务代码中使用ThreadLocal,Dubbo的线程池是共享的,会导致ThreadLocal数据错乱。
  4. 注册中心集群故障问题生产环境必须开启Dubbo的本地地址缓存,即使注册中心宕机,消费者仍可通过本地缓存的地址列表调用服务,避免注册中心单点故障导致整个集群不可用。

7.2 gRPC 常见坑点

  1. Protobuf字段兼容问题禁止修改已发布字段的编号与类型,否则会导致反序列化失败;删除字段必须保留编号,禁止复用已删除的字段编号,避免新旧版本服务数据错乱。字段编号1-15仅用于高频字段,避免浪费。
  2. IO线程阻塞问题禁止在gRPC的IO线程中执行耗时的业务逻辑、数据库操作、网络调用,必须提交到独立的业务线程池执行,否则会导致IO线程阻塞,吞吐量急剧下降,甚至出现请求超时。
  3. HTTP/2流控与连接管理问题大报文传输场景必须增大流控窗口大小,否则会导致传输吞吐量极低;长连接场景必须开启KeepAlive机制,避免防火墙清理空闲连接导致的请求失败。客户端必须复用Channel对象,禁止每次请求都创建新的Channel,否则会导致TCP连接耗尽。
  4. 流式调用内存泄漏问题流式调用场景下,必须正确处理StreamObserver的onError与onCompleted事件,及时释放资源,否则会导致内存泄漏;客户端与服务端的流数量必须限制,避免流数量过多导致OOM。

八、总结

RPC框架是分布式微服务架构的核心基础设施,Dubbo与gRPC作为当前最主流的两款RPC框架,各有其核心优势与适用场景。

Dubbo 3.x 深度适配Java生态,提供了企业级全链路的服务治理能力,在Java为主的微服务架构中,有着天然的优势,适合需要强服务治理、复杂业务场景的企业级应用。

gRPC基于HTTP/2与Protobuf设计,跨语言能力极强,原生支持流式调用,深度适配云原生架构,适合多语言微服务、跨平台服务调用、实时流式数据传输的场景。

在实际选型中,需根据业务的技术栈、场景需求、团队能力综合判断,同时掌握框架的底层原理与调优策略,才能充分发挥RPC框架的性能,构建稳定、高性能的分布式微服务系统。

目录
相关文章
|
10天前
|
人工智能 安全 Linux
【OpenClaw保姆级图文教程】阿里云/本地部署集成模型Ollama/Qwen3.5/百炼 API 步骤流程及避坑指南
2026年,AI代理工具的部署逻辑已从“单一云端依赖”转向“云端+本地双轨模式”。OpenClaw(曾用名Clawdbot)作为开源AI代理框架,既支持对接阿里云百炼等云端免费API,也能通过Ollama部署本地大模型,完美解决两类核心需求:一是担心云端API泄露核心数据的隐私安全诉求;二是频繁调用导致token消耗过高的成本控制需求。
5494 13
|
18天前
|
人工智能 JavaScript Ubuntu
5分钟上手龙虾AI!OpenClaw部署(阿里云+本地)+ 免费多模型配置保姆级教程(MiniMax、Claude、阿里云百炼)
OpenClaw(昵称“龙虾AI”)作为2026年热门的开源个人AI助手,由PSPDFKit创始人Peter Steinberger开发,核心优势在于“真正执行任务”——不仅能聊天互动,还能自动处理邮件、管理日程、订机票、写代码等,且所有数据本地处理,隐私完全可控。它支持接入MiniMax、Claude、GPT等多类大模型,兼容微信、Telegram、飞书等主流聊天工具,搭配100+可扩展技能,成为兼顾实用性与隐私性的AI工具首选。
21826 117
|
14天前
|
人工智能 安全 前端开发
Team 版 OpenClaw:HiClaw 开源,5 分钟完成本地安装
HiClaw 基于 OpenClaw、Higress AI Gateway、Element IM 客户端+Tuwunel IM 服务器(均基于 Matrix 实时通信协议)、MinIO 共享文件系统打造。
8302 8

热门文章

最新文章