gRPC在Spring Cloud中的應(yīng)用
作者:xcbeyond
瘋狂源自夢(mèng)想,技術(shù)成就輝煌!微信公眾號(hào):《程序猿技術(shù)大咖》號(hào)主,專注后端開發(fā)多年,擁有豐富的研發(fā)經(jīng)驗(yàn),樂于技術(shù)輸出、分享,現(xiàn)階段從事微服務(wù)架構(gòu)項(xiàng)目的研發(fā)工作,涉及架構(gòu)設(shè)計(jì)、技術(shù)選型、業(yè)務(wù)研發(fā)等工作。對(duì)于Java、微服務(wù)、數(shù)據(jù)庫、Docker有深入了解,并有大量的調(diào)優(yōu)經(jīng)驗(yàn)。
1、前言
在微服務(wù)開發(fā)中,服務(wù)間的調(diào)用一般有兩種方式:Feign、RestTemplate,但在實(shí)際使用過程中,尤其是Feign,存在各種限制及局限性,如:HTTP請(qǐng)求方式、返回類型等限制,有時(shí)會(huì)讓你覺得那那都別扭。在微服務(wù)項(xiàng)目中,服務(wù)間的調(diào)用,是非常普遍頻繁的,其性能也不是很理想。
為了解決上述問題,進(jìn)行反復(fù)對(duì)比,最終服務(wù)間的調(diào)用方式采取了gRPC方式,其顯著特點(diǎn)就是性能之高(通信采用Netty),通過proto文件定義的接口也是非常清晰而又靈活。本文主要就gRPC在Spring Cloud項(xiàng)目中的使用進(jìn)行說明實(shí)戰(zhàn)。
關(guān)于gRPC相關(guān)基礎(chǔ)知識(shí)可以參考上一篇文章gRPC的使用。
2、gRPC在Spring Cloud中的使用
看過上一篇文章gRPC的使用的話,你就清楚如果直接使用gRPC,顯得有些吃力,因此借助一些開源的框架變得尤為必要。gRPC在Spring Cloud中使用開源項(xiàng)目grpc-spring-boot-starter,便于在Spring Cloud項(xiàng)目中開發(fā)應(yīng)用。
(grpc-spring-boot-starter雖然存在一些問題,但集成Sping Cloud項(xiàng)目已經(jīng)相當(dāng)高了,還是不錯(cuò)之選。如果你有時(shí)間,精力,還是又必要在源碼基礎(chǔ)上進(jìn)行開發(fā)。)
下面以實(shí)際demo來說明grpc-spring-boot-starter的應(yīng)用。
2.1 特點(diǎn)
使用@ GrpcService自動(dòng)創(chuàng)建并運(yùn)行一個(gè) gRPC 服務(wù),內(nèi)嵌在 spring-boot 應(yīng)用中
使用@ GrpcClient自動(dòng)創(chuàng)建和管理你的客戶端
支持Spring Cloud(向Consul或Eureka注冊(cè)服務(wù)并獲取gRPC服務(wù)器信息)
支持Spring Sleuth 進(jìn)行鏈路跟蹤
支持對(duì)于server、client 分別設(shè)置全局?jǐn)r截器或單個(gè)的攔截器
支持Spring-Security
支持metric (micrometer / actuator)
(看了上面這些特點(diǎn),就知道為啥選擇這個(gè)開源項(xiàng)目了)
2.2 使用DEMO
2.2.1 定義gRPC接口
基于protobuf來聲明數(shù)據(jù)模型和RPC接口服務(wù)。
創(chuàng)建一個(gè)公共字模塊項(xiàng)目spring-boot-grpc-common,用于定義存放gRPC接口(proto),便于gRPC服務(wù)端和客戶端使用。以helloworld.proto(src\main\proto\helloworld.proto)為例:
syntax = "proto3";
option java_multiple_files = true;
option java_package = "com.xcbeyond.springboot.grpc.lib";
option java_outer_classname = "HelloWorldProto";
// The greeting service definition.
service Simple {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {
}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
根據(jù)proto的命令可以轉(zhuǎn)換成對(duì)應(yīng)的語言的代碼,生成java代碼,也可以借助maven插件,在編譯時(shí)自動(dòng)生成。這里通過mavent插件,可以在pom.xml中增加如下依賴:
<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>${os.plugin.version}</version>
</extension>
</extensions>
<plugins>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>${protobuf.plugin.version}</version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:${protoc.version}:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
在maven中進(jìn)行package編譯打包,將會(huì)在target中看見根據(jù)proto自動(dòng)生成的Java類。(編譯過程中可能會(huì)報(bào)錯(cuò),此時(shí)可以忽略)
2.2.2 gRPC服務(wù)端
maven依賴:
<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-server-spring-boot-starter</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
實(shí)現(xiàn) gRPC server 的業(yè)務(wù)邏輯,使用注解@GrpcService定義gRPC服務(wù)端,如下所示:
package com.xcbeyond.springboot.grpc.server.service;
import com.xcbeyond.springboot.grpc.lib.HelloReply;
import com.xcbeyond.springboot.grpc.lib.HelloRequest;
import com.xcbeyond.springboot.grpc.lib.SimpleGrpc;
import io.grpc.stub.StreamObserver;
import net.devh.boot.grpc.server.service.GrpcService;
/**
* @Auther: xcbeyond
* @Date: 2019/3/6 18:15
*/
@GrpcService
public class GrpcServerService extends SimpleGrpc.SimpleImplBase {
@Override
public void sayHello(HelloRequest request, StreamObserver<HelloReply> responseObserver) {
System.out.println("GrpcServerService...");
HelloReply reply = HelloReply.newBuilder().setMessage("Hello ==> " + request.getName()).build();
responseObserver.onNext(reply);
responseObserver.onCompleted();
}
}
application.yml配置:
gRPC 的 host 跟 port ,默認(rèn)的監(jiān)聽的 host 是 0.0.0.0,默認(rèn)的 port 是 9090,配置為0將會(huì)自動(dòng)分配未使用的端口。
grpc:
server:
port: 0
2.2.3 gRPC客戶端
maven依賴:
<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-client-spring-boot-starter</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
使用注解@GrpcClient來調(diào)用服務(wù)端接口,通過
HelloReply response = simpleBlockingStub.sayHello(HelloRequest.newBuilder().setName(name).build());
直接向服務(wù)端發(fā)起請(qǐng)求,和調(diào)用本地接口一樣。
package com.xcbeyond.springboot.grpc.client.service;
import com.xcbeyond.springboot.grpc.lib.HelloReply;
import com.xcbeyond.springboot.grpc.lib.HelloRequest;
import com.xcbeyond.springboot.grpc.lib.SimpleGrpc.SimpleBlockingStub;
import io.grpc.StatusRuntimeException;
import net.devh.boot.grpc.client.inject.GrpcClient;
import org.springframework.stereotype.Service;
/**
* @Auther: xcbeyond
* @Date: 2019/3/7 09:10
*/
@Service
public class GrpcClientService {
@GrpcClient("spring-boot-grpc-server")
private SimpleBlockingStub simpleBlockingStub;
public String sendMessage(String name) {
try {
HelloReply response = simpleBlockingStub.sayHello(HelloRequest.newBuilder().setName(name).build());
return response.getMessage();
} catch (final StatusRuntimeException e) {
return "FAILED with " + e.getStatus().getCode();
}
}
}
application.yml配置:
spring-boot-grpc-server,即:服務(wù)端應(yīng)用名,結(jié)合spring cloud Eureka注冊(cè)中心,通過服務(wù)名將會(huì)找到服務(wù)端的ip,進(jìn)行通信,實(shí)際上是netty通信。
grpc:
client:
spring-boot-grpc-server:
enableKeepAlive: true
keepAliveWithoutCalls: true
negotiationType: plaintext
本demo完整代碼請(qǐng)參考https://github.com/xcbeyond/spring-boot-grpc.git