SpringBoot集成gRPC微服務(wù)工程搭建實(shí)踐的方法
前言
本文將使用Maven、gRPC、Protocol buffers、Docker、Envoy等工具構(gòu)建一個(gè)簡(jiǎn)單微服務(wù)工程,筆者所使用的示例工程是以前寫的一個(gè)Java后端工程,因?yàn)樽罱荚?學(xué)習(xí)微服務(wù)相關(guān)的知識(shí),所以利用起來慢慢的把這個(gè)工程做成微服務(wù)化應(yīng)用。在實(shí)踐過程踩過很多坑,主要是經(jīng)驗(yàn)不足對(duì)微服務(wù)還是停留在萌新階段,通過本文 記錄創(chuàng)建微服務(wù)工程碰到一些問題,此次實(shí)踐主要是解決以下問題:
- 如何解決、統(tǒng)一服務(wù)工程依賴管理
- SpringBoot集成gRPC
- 管理Protocol buffers文件
- 使用Envoy代理訪問gRPC
- 部署到Docker
本文假設(shè)讀者已經(jīng)了解以下相關(guān)知識(shí):
- Maven
- Envoy
- gRPC
- Protocol buffers
- SpringBoot
- Docker
由于是初步實(shí)現(xiàn)微服務(wù),不會(huì)考慮過多的細(xì)節(jié),現(xiàn)階段只需要能夠使用gRPC正常通信,后續(xù)計(jì)劃會(huì)發(fā)布到k8s中,使用istio實(shí)現(xiàn)來服務(wù)網(wǎng)格。
使用Maven
現(xiàn)在比較流行的構(gòu)建工具有Maven和Gradle,現(xiàn)階段后端開發(fā)大多數(shù)都是用的Maven所以本工程也使用Maven來構(gòu)建項(xiàng)目,當(dāng)然使用Gradle也可以兩者概念大都想通,不同的地方大多是實(shí)現(xiàn)和配置方式不一致。
使用項(xiàng)目繼承
根據(jù)Maven的POM文件繼承特性,將工程分不同的模塊,所有的模塊都繼承父pom.xml的依賴、插件等內(nèi)容,這樣就可以實(shí)現(xiàn)統(tǒng)一管理,并方便以后管理、維護(hù)。先看一下大概的項(xiàng)目結(jié)構(gòu):
AppBubbleBackend (1) ├── AppBubbleCommon ├── AppBubbleSmsService (2) ├── AppBubbleUserService ├── docker-compose.yaml (3) ├── pom.xml ├── protos (4) │ ├── sms │ └── user └── scripts (5) ├── docker ├── envoy ├── gateway └── sql
以下是各個(gè)目錄的用處簡(jiǎn)述,詳細(xì)的用處文章后面都會(huì)提到,先在這里列出個(gè)大概:
- 工程主目錄
- 單個(gè)服務(wù)工程目錄(模塊)
- docker-compose發(fā)布文件
- 存放.proto文件
- 發(fā)布、編譯時(shí)用到的腳本文件
知道大概的項(xiàng)目工程結(jié)構(gòu)后我們創(chuàng)建一個(gè)父pom.xml文件,放在AppBubbleBackend目錄下面:
<?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 http://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>2.1.2.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.bubble</groupId> <artifactId>bubble</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>pom</packaging> <modules> <module>AppBubbleSmsService</module> <module>AppBubbleCommon</module> <module>AppBubbleUserService</module> </modules> <!-- 省略其他部分 --> </project>
因?yàn)槭褂肧pringBoot構(gòu)架,所以主pom.xml文件繼承自SpringBoot的POM文件。 有了主pom.xml后然后使每個(gè)模塊的pom.xml都繼承自 主pom.xml文件:
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.bubble</groupId> <artifactId>bubble</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <artifactId>sms</artifactId> <version>0.0.1-SNAPSHOT</version> <!-- 省略其他部分 --> </project>
經(jīng)過上面的配置后,所有的模塊都會(huì)繼承AppBubbleBackend中的pom.xml文件,這樣可以很方便的更改依賴、配置等信息。
依賴管理
Maven提供依賴中心化的管理機(jī)制,通過項(xiàng)目繼承特性所有對(duì)AppBubbleBackend/pom.xml所做的更改都會(huì)對(duì)其他模塊產(chǎn)生影響,詳細(xì)的依賴管理 內(nèi)容可查看官方文檔。
<dependencyManagement>
<dependencies>
<!-- gRPC -->
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty-shaded</artifactId>
<version>${grpc.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
通過dependencyManagement標(biāo)簽來配置依賴,這樣可以就可以實(shí)現(xiàn)統(tǒng)一依賴的管理,并且還可以添加公共依賴。
插件管理
使用pluginManagement可以非常方便的配置插件,因?yàn)轫?xiàng)目中使用了Protocol buffers需要集成相應(yīng)的插件來生成Java源文件:
<pluginManagement>
<plugins>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.5.1</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
Protocol buffers插件的完整配置參數(shù),可以這這里找到。
Profile
使用Profile的目的是為了區(qū)分生成Docker鏡像時(shí)的一些特殊配置,示例工程只配置了一個(gè)docker-build的profile:
<profiles>
<profile>
<id>docker-build</id>
<properties>
<jarName>app</jarName>
</properties>
</profile>
</profiles>
<properties>
<jarName>${project.artifactId}-${project.version}</jarName>
</properties>
<build>
<finalName>${jarName}</finalName>
</build>
如果使用mvn package -P docker-build命令生成jar包時(shí),相應(yīng)的輸出文件名是app.jar這樣可以方便在Dockerfile中引用文件,而不需要使用${project.artifactId}-${project.version}的形式來查找輸出的jar這樣可以省去了解析pom.xml文件。如果還需要特殊的參數(shù)可以或者不同的行為,可以添加多個(gè)Profile,這樣配置起來非常靈活。
Protocol buffers文件管理
因?yàn)槭鞘褂梦⒎?wù)開發(fā),而且RPC通信框架是使用的gRPC,所以每個(gè)服務(wù)工程都會(huì)使用.proto文件。服務(wù)工程之間又會(huì)有使用同一份.proto文件的需求,比如在進(jìn)行RPC通信時(shí)服務(wù)提供方返回的消息Test定義在a.proto文件中,那么在使用方在解析消息時(shí)也同樣需要a.proto文件來將接收到的消息轉(zhuǎn)換成Test消息,因此管理.proto文件也有一些小麻煩。關(guān)于Protocol buffers的使用可參考 官方文檔。
Protocol buffers文件管理規(guī)約
在我們的示例項(xiàng)目中使用集中管理的方式,即將所有的.proto文件放置在同一個(gè)目錄(AppBubbleBackend/protos)下并按服務(wù)名稱來劃分:
├── sms │ ├── SmsMessage.proto │ └── SmsService.proto └── user └── UserMessage.proto
還可以將整個(gè)目錄放置在一個(gè)單獨(dú)的git倉(cāng)庫(kù)中,然后在項(xiàng)目中使用git subtree來管理文件。
Protocol buffers 插件配置
有了上面的目錄結(jié)構(gòu)后,就需要配置一下Protocol buffers的編譯插件來支持這種.proto文件的組織結(jié)構(gòu)。在講解如何配置插件解決.proto文件的編譯問題之前,推薦讀者了解一下插件的配置文檔:Xolstice Maven Plugins。在我們的工程中使用如下配置:
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.5.1</version>
<configuration >
<protocArtifact>com.google.protobuf:protoc:3.5.1-1:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:1.17.1:exe:${os.detected.classifier}</pluginArtifact>
<additionalProtoPathElements combine.children="append" combine.self="append">
<additionalProtoPathElement>${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis</additionalProtoPathElement>
<additionalProtoPathElement>${GOPATH}/src</additionalProtoPathElement>
</additionalProtoPathElements>
<protoSourceRoot>${protos.basedir}</protoSourceRoot>
<writeDescriptorSet>true</writeDescriptorSet>
<includeDependenciesInDescriptorSet>true</includeDependenciesInDescriptorSet>
</configuration>
<!-- ... -->
</plugin>
首先上面的插件配置使用protoSourceRoot標(biāo)簽將Protocol buffers的源文件目錄更改成AppBubbleBackend/protos目錄,因?yàn)楣こ讨惺褂昧薵oogleapis來定義服務(wù)接口,所以需要使用添加additionalProtoPathElement標(biāo)簽添加額外的依賴文件。注意這個(gè)插件的配置是在AppBubbleBackend/pom.xml文件中的,服務(wù)工程都是繼承此文件的。在父POM文件配置好以后,再看一下服務(wù)工程的插件配置:
<plugins>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<configuration>
<includes>
<include>${project.artifactId}/*.proto</include>
<include>user/*.proto</include>
</includes>
</configuration>
</plugin>
</plugins>
服務(wù)工程主要使用includes標(biāo)簽,將需要的.proto文件包含在編譯腳本中,includes標(biāo)簽中的include只是一個(gè)指定匹配.proto文件的匹配模式,<include>${project.artifactId}/*.proto</include>意思是AppBubbleBackend/protos/${project.artifactId}目錄下的所有以.proto文件結(jié)尾的文件,如果服務(wù)工程有多個(gè)依賴可以將需要依賴的文件也添加到編譯服務(wù)中,如上面的<include>user/*.proto</include>就將AppBubbleBackend/protos/user中的.proto文件添加進(jìn)來,然后進(jìn)行整體的編譯。
gRPC
gRPC是由Google開源的RPC通信框架,gRPC使用Protocol buffers定義服務(wù)接口并自動(dòng)生成gRPC相關(guān)代碼,有了這些代碼后就可以非常方便的實(shí)現(xiàn)gRPC服務(wù)端和gPRC客戶端,過多的細(xì)節(jié)就不細(xì)說了先看一下如何使用在SpringBoot中使用gRPC。
運(yùn)行g(shù)RPC服務(wù)
利用ApplicationRunner接口,在SprintBoot中運(yùn)行g(shù)RPC服非常方便,只需要像下面代碼一樣就可以運(yùn)行一個(gè)簡(jiǎn)單的gRPC服務(wù)。
package com.bubble.sms.grpc;
@Component
public class GrpcServerInitializer implements ApplicationRunner {
@Autowired
private List<BindableService> services;
@Value("${grpc.server.port:8090}")
private int port;
@Override
public void run(ApplicationArguments args) throws Exception {
ServerBuilder serverBuilder = ServerBuilder
.forPort(port);
if (services != null && !services.isEmpty()) {
for (BindableService bindableService : services) {
serverBuilder.addService(bindableService);
}
}
Server server = serverBuilder.build();
serverBuilder.intercept(TransmitStatusRuntimeExceptionInterceptor.instance());
server.start();
startDaemonAwaitThread(server);
}
private void startDaemonAwaitThread(Server server) {
Thread awaitThread = new Thread(() -> {
try {
server.awaitTermination();
} catch (InterruptedException ignore) {
}
});
awaitThread.setDaemon(false);
awaitThread.start();
}
}
Envoy代理
gRPC服務(wù)運(yùn)行起來后就需要進(jìn)行調(diào)試了,比如使用curl、chrome等工具向gRPC服務(wù)發(fā)起Restful請(qǐng)求,實(shí)際上gRPC的調(diào)試并沒有那么簡(jiǎn)單。一開始的方案是使用了gRPC-gateway,為每個(gè)服務(wù)都啟動(dòng)一個(gè)網(wǎng)關(guān)將Http 1.x請(qǐng)求轉(zhuǎn)換并發(fā)送到gRPC服務(wù)。然而gRPC-gateway只有g(shù)o語(yǔ)言的版本,并沒有Java語(yǔ)言的版本,所有在編譯和使用中比較困難,后來發(fā)現(xiàn)了Envoy提供了envoy.grpc_json_transcoder這個(gè)http過濾器,可以很方便的將RESTful JSON API轉(zhuǎn)換成gRPC請(qǐng)求并發(fā)送給gRPC服務(wù)器。
envoy的相關(guān)配置都放置在AppBubbleBackend/scripts/envoy目錄中,里面的envoy.yaml是一份簡(jiǎn)單的配置文件:
static_resources:
listeners:
- name: grpc-8090
address:
socket_address: { address: 0.0.0.0, port_value: 8090 }
filter_chains:
- filters:
- name: envoy.http_connection_manager
config:
stat_prefix: sms_http
codec_type: AUTO
# 省略部分配置
http_filters:
- name: envoy.grpc_json_transcoder
config:
proto_descriptor: "/app/app.protobin"
services: ["sms.SmsService"]
match_incoming_request_route: true
print_options:
add_whitespace: true
always_print_primitive_fields: true
always_print_enums_as_ints: false
preserve_proto_field_names: false
# 省略部分配置
使用envoy.grpc_json_transcoder過濾器的主要配置是proto_descriptor選項(xiàng),該選項(xiàng)指向一個(gè)proto descriptor set文件。AppBubbleBackend/scripts/envoy/compile-descriptor.sh是編譯proto descriptor set的腳本文件, 運(yùn)行腳本文件會(huì)在腳本目錄下生成一個(gè)app.protobin的文件,將此文件設(shè)置到envoy.grpc_json_transcoder就可大致完成了envoy的代理配置。
使用Docker發(fā)布
經(jīng)過上面的一系統(tǒng)準(zhǔn)備工作之后,我們就可以將服務(wù)發(fā)布到docker中了,Docker相關(guān)的文件都放置中AppBubbleBackend/scripts/docker和一個(gè)AppBubbleBackend/docker-compose.yaml文件。在發(fā)布時(shí)使用單個(gè)Dockerfile文件來制作服務(wù)鏡像:
FROM rcntech/ubuntu-grpc:v0.0.5 EXPOSE 8080 EXPOSE 8090 #將當(dāng)前目錄添加文件到/bubble ARG APP_PROJECT_NAME #復(fù)制父pom.xml ADD /pom.xml /app/pom.xml ADD /protos /app/protos ADD $APP_PROJECT_NAME /app/$APP_PROJECT_NAME ADD scripts/gateway /app/gateway ADD scripts/docker/entrypoint.sh /app/entrypoint.sh RUN chmod u+x /app/entrypoint.sh ENTRYPOINT ["/app/entrypoint.sh"]
有了Dockerfile文件后,在docker-compose.yaml里面做一些配置就能將服務(wù)打包成鏡像:
sms: build: context: ./ dockerfile: scripts/docker/Dockerfile args: APP_PROJECT_NAME: "AppBubbleSmsService" environment: APOLLO_META: "http://apollo-configservice-dev:8080" APP_PROJECT_NAME: "AppBubbleSmsService" ENV: dev
同時(shí)編寫了一個(gè)通用的entrypoint.sh腳本文件來啟動(dòng)服務(wù)器:
#!/bin/bash
export GOPATH=${HOME}/go
export PATH=$PATH:/usr/local/go/bin:$GOPATH/bin
rootProjectDir="/app"
projectDir="${rootProjectDir}/${APP_PROJECT_NAME}"
cd ${rootProjectDir}/AppBubbleCommon
./mvnw install
cd $projectDir
#打包app.jar
./mvnw package -DskipTests -P docker-build
#編譯proto文件
./mvnw protobuf:compile protobuf:compile-custom -P docker-build
# Run service
java -jar ${projectDir}/target/app.jar
entrypoint.sh腳本中將服務(wù)工程編譯成app.jar包再運(yùn)行服務(wù)。還有envoy代理也要啟動(dòng)起來這樣我們就可以使用curl或其他工具直接進(jìn)行測(cè)試了。
總結(jié)
搭建這個(gè)工程大概摸索了一周的時(shí)間,主要的時(shí)間是花在了Protocol buffers文件的管理與使用Envoy作為代理調(diào)試gRPC服務(wù)上。文章中的示例工程已經(jīng)傳到了GitHub: AppBubbleBackend 后面會(huì)打算慢慢的完善這個(gè)應(yīng)用,這是個(gè)簡(jiǎn)單的短視屏應(yīng)用除了服務(wù)器還包含了Android和iOS端,等到將后端微服務(wù)化為開源出來供學(xué)習(xí)交流使用。
參考引用
gRPC官方文檔
Protocol buffers Maven 插件文檔
Protocol buffers官方文檔
gRPC 官方文檔中文版
gRPC-JSON transcoder
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Windows環(huán)境使用bat腳本啟動(dòng)Java服務(wù)的過程
Java項(xiàng)目一般會(huì)被打包成jar后啟動(dòng),在windows系統(tǒng)中可以通過終端窗口cmd啟動(dòng)jar包,即在jar包所在的目錄中打開cmd,或在cmd中進(jìn)入到j(luò)ar包目錄,這篇文章主要介紹了Windows環(huán)境使用bat腳本啟動(dòng)Java服務(wù),需要的朋友可以參考下2023-08-08
JNDI簡(jiǎn)介_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要介紹了JNDI簡(jiǎn)介,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-08-08
帶你了解Java數(shù)據(jù)結(jié)構(gòu)和算法之無(wú)權(quán)無(wú)向圖
這篇文章主要為大家介紹了Java數(shù)據(jù)結(jié)構(gòu)和算法之無(wú)權(quán)無(wú)向圖?,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助2022-01-01
SpringCloud集成MybatisPlus實(shí)現(xiàn)MySQL多數(shù)據(jù)源配置方法
本文詳細(xì)介紹了SpringCloud集成MybatisPlus實(shí)現(xiàn)MySQL多數(shù)據(jù)源配置的方法,包括在application.properties中配置多數(shù)據(jù)源,配置MybatisPlus,創(chuàng)建Mapper接口和使用多數(shù)據(jù)源等步驟,此外,還解釋了每一個(gè)配置項(xiàng)目的含義,以便讀者更好地理解和應(yīng)用2024-10-10
學(xué)習(xí)spring事務(wù)與消息隊(duì)列
這篇文章主要為大家詳細(xì)介紹了spring事務(wù)與消息隊(duì)列,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-10-10
SpringBoot對(duì)靜態(tài)資源的映射規(guī)則詳解
在Web應(yīng)用中會(huì)涉及到大量的靜態(tài)資源,例如 JS、CSS和HTML等,我們知道,Spring MVC 導(dǎo)入靜態(tài)資源文件時(shí),需要配置靜態(tài)資源的映射,但在 SpringBoot 中則不再需要進(jìn)行此項(xiàng)配置,因?yàn)镾pringBoot已經(jīng)默認(rèn)完成了這一工作,本文給大家介紹了SpringBoot對(duì)靜態(tài)資源的映射規(guī)則詳2024-12-12
Spring session 獲取當(dāng)前賬戶登錄數(shù)的實(shí)例代碼
這篇文章主要介紹了Spring session 獲取當(dāng)前賬戶登錄數(shù),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-04-04
利用java反射機(jī)制調(diào)用類的私有方法(推薦)
下面小編就為大家?guī)硪黄胘ava反射機(jī)制調(diào)用類的私有方法(推薦)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-08-08

