赞
踩
Spring Cloud 微服务一般采用feign调用,实际是http+json方式传输数据, 但这种方案从性能和稳定性来讲并不理想, 有没更好的微服务通讯方案?答案是有的, 如果你需要频繁的传输交互, 对性能要求较高, 且需要和各种平台(golang, c++等)对接, 那么SpringBoot + gRpc + Protobuf + Netty 这种通讯方案则更为合适。
gRPC 是Google开源的高性能、通用的RPC框架。客户端与服务端约定接口调用, 可以在各种环境中运行,具有跨语言特性, 适合构建分布式、微服务应用。
为什么使用gRpc?
gRPC 的线程模型遵循 Netty 的线程分工原则,协议层消息的接收和编解码由 Netty 的 I/O(NioEventLoop) 线程负责, 应用层的处理由应用线程负责,防止由于应用处理耗时而阻塞 Netty 的 I/O 线程。BIO线程模型采用了线程池,但是后端的应用处理线程仍然采用同步阻塞的模型,阻塞的时间取决对方I/O处理的速度和网络I/O传输的速度。
下面讲解gRpc与Spring Boot的集成使用
在grpc-demo父级工程下面增加依赖:
<dependencies>
<!-- spring boot grpc 相关依赖 -->
<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-spring-boot-starter</artifactId>
<version>2.4.0.RELEASE</version>
</dependency>
<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-server-spring-boot-starter</artifactId>
<version>2.4.0.RELEASE</version>
</dependency>
</dependencies>
这里引入的是第三方封装的gRpc组件。
syntax = "proto3"; option java_multiple_files = true; option java_package = "com.mirson.grpc.lib"; option java_outer_classname = "StockServiceProto"; // The stock service definition. service StockService { // get stock price by name rpc GetStockPrice (StockServiceRequest) returns (StockServiceReply) { } } // The request message message StockServiceRequest { string name = 1; } // The response message message StockServiceReply { string message = 1; }
<dependencies> <dependency> <groupId>com.google.protobuf</groupId> <artifactId>protobuf-java</artifactId> <version>3.8.0</version> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-netty-shaded</artifactId> <version>1.21.0</version> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-protobuf</artifactId> <version>1.21.0</version> <exclusions> <exclusion> <artifactId>protobuf-java</artifactId> <groupId>com.google.protobuf</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-stub</artifactId> <version>1.21.0</version> </dependency> </dependencies>
protoc --plugin=protoc-gen-grpc-java=d:/TestCode/protoc-grpc.exe --java_out=./ --grpc-java_out=./ StockService.proto
接下来, 我们要提供gRpc服务, 在grpc-server服务工程, 创建GrpcStockService实现类:
@GrpcService
public class GrpcStockService extends StockServiceGrpc.StockServiceImplBase {
@Override
public void getStockPrice(StockServiceRequest request, StreamObserver<StockServiceReply> responseObserver) {
String msg = "股票名称:" + request.getName() + ", 股票价格:" + (new Random().nextInt(100-20)+20);
StockServiceReply reply =StockServiceReply.newBuilder().setMessage(msg).build();
responseObserver.onNext(reply);
responseObserver.onCompleted();
}
}
重写在定义的gRpc接口, getStockPrice获取股票价格。这里做测试, 我们随机生成股票价格。
创建GrpcServiceStartup启动类:
@SpringBootApplication
@ComponentScan(basePackages = {"com.mirson"})
public class GrpcServerStartup {
public static void main(String[] args) {
SpringApplication.run(GrpcServerStartup.class, args);
}
}
工程配置信息:
spring:
application:
name: grpc-server
grpc:
server:
port: 9999
定义服务名称和端口。
有了gRpc服务, 客户端如何调用? 在grpc-client工程中, 创建GrpcClientService类:
@Service
public class GrpcClientService {
@GrpcClient("grpc-server")
private StockServiceGrpc.StockServiceBlockingStub stockServiceStub;
public String getStockPrice(final String name) {
try {
final StockServiceReply response = stockServiceStub.getStockPrice(StockServiceRequest.newBuilder().setName(name).build());
return response.getMessage();
} catch (final StatusRuntimeException e) {
return "error!";
}
}
}
看到这里, 应该可以明白, 我们引入了生成的StockServiceGrpc, 采用StockServiceBlockingStub阻塞式调用。
通过GrpcClient注解, 指定了具体的grpc服务信息, 这里名称要与配置文件中定义的名称一致。
再创建启动类:
@SpringBootApplication @RestController @ComponentScan(basePackages = {"com.mirson"}) public class GrpcClientApplication { @Autowired private GrpcClientService grpcClientService; public static void main(String[] args) { SpringApplication.run(GrpcClientApplication.class, args); } @RequestMapping("/") public String getStockPrice(@RequestParam(defaultValue = "中国平安") String name) { return grpcClientService.getStockPrice(name); } }
为了便于测试, 我们通过Spring Boot 提供了一个web接口。
接下来, 就是客户端的工程配置文件, 重点是gRpc的配置信息:
server:
port: 9000
spring:
application:
name: grpc-client
grpc:
client:
grpc-server:
address: 'static://127.0.0.1:9999'
enableKeepAlive: true
keepAliveWithoutCalls: true
negotiationType: plaintext
定义了一个名为grpc-server的远程服务,采用静态方式调用, 指定了IP和PORT, 当然, 也可以采用服务名称方式调用, 这个要结合服务注册发现组件来使用, 后面我们会讲如何与Nacos集成, 在微服务中使用。
Spring Boot集成gRpc可以正常调用, 但是这种调用方式是静态指定了IP和端口, 如果发生变化,或者要支持负载, 该如何处理? 这里我们以Spring Cloud Alibaba环境来讲解下如何使用。
Spring Cloud Alibaba 采用的是Nacos作为服务注册发现, 这里的gRpc服务是可以支持的, 但是不支持最新版本, 我们要做些修改。
为了便于微服务环境中的集成使用, 我们可以封装为自定义的启动器。
<dependencies> <!-- spring boot grpc 相关依赖 --> <dependency> <groupId>net.devh</groupId> <artifactId>grpc-server-spring-boot-starter</artifactId> <version>${grpc.version}</version> </dependency> <dependency> <groupId>net.devh</groupId> <artifactId>grpc-client-spring-boot-starter</artifactId> <version>${grpc.version}</version> </dependency> <!-- Nacos服务注册发现依赖 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> </dependencies>
@Configuration @EnableConfigurationProperties @ConditionalOnClass({NacosNamingService.class}) public class GrpcNacosConfiguration { @Autowired(required = false) private NacosRegistration nacosRegistration; @Autowired private GrpcServerProperties grpcProperties; /** * 自定义将gRpc服务信息注册至Nacos, 解决gRpc组件不支持最新Cloud Alibaba 问题 */ @PostConstruct public void init() { if (this.nacosRegistration == null) { return; } final int port = this.grpcProperties.getPort(); if (port != -1) { this.nacosRegistration.getMetadata().put(GrpcUtils.CLOUD_DISCOVERY_METADATA_PORT, Integer.toString(port)); } } }
将gRpc服务信息注册至Nacos, 解决gRpc组件不支持最新版Cloud Alibaba 问题。这样gRpc内部依赖的低版本Nacos就不用理会,直接采用我们工程当中引入的最新版的Nacos组件。
引入封装的cloud-starter-grpc工程, 会自动加入gRpc相关依赖, 接下来在工程中如何配置呢?
只需修改adress, 将ip改为服务注册的名称即可:
# grpc 配置
grpc:
server:
port: 19989
grpc:
client:
grpc-server:
# address: 'static://127.0.0.1:9999'
address: 'discovery:///grpc-server'
enableKeepAlive: true
keepAliveWithoutCalls: true
negotiationType: plaintext
基于内部微服务, 可以通过服务注册发现工具, 比如Nacos, consul等直接调用, 如何对外访问呢? 可以通过Cloud Gateway网关作一层转发, 或者自己编写一个Proxy代理服务, 通过Netty对外提供访问, Spring Boot 如何与Netty进行集成, 可以参考我写的另一篇博文: [Spring Boot + Netty+WebSocket+Protobuf 集成通讯配置]
本文由mirson创作分享,如需进一步交流,请加QQ群:19310171或访问www.softart.cn
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。