您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關(guān)SpringBoot集成gRPC微服務工程搭建的示例,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。
前言
將使用Maven、gRPC、Protocol buffers、Docker、Envoy等工具構(gòu)建一個簡單微服務工程,筆者所使用的示例工程是以前寫的一個Java后端工程,因為最近都在 學習微服務相關(guān)的知識,所以利用起來慢慢的把這個工程做成微服務化應用。在實踐過程踩過很多坑,主要是經(jīng)驗不足對微服務還是停留在萌新階段,通過本文 記錄創(chuàng)建微服務工程碰到一些問題,此次實踐主要是解決以下問題:
如何解決、統(tǒng)一服務工程依賴管理
SpringBoot集成gRPC
管理Protocol buffers文件
使用Envoy代理訪問gRPC
部署到Docker
本文假設(shè)讀者已經(jīng)了解以下相關(guān)知識:
Maven
Envoy
gRPC
Protocol buffers
SpringBoot
Docker
由于是初步實現(xiàn)微服務,不會考慮過多的細節(jié),現(xiàn)階段只需要能夠使用gRPC正常通信,后續(xù)計劃會發(fā)布到k8s中,使用istio實現(xiàn)來服務網(wǎng)格。
使用Maven
現(xiàn)在比較流行的構(gòu)建工具有Maven和Gradle,現(xiàn)階段后端開發(fā)大多數(shù)都是用的Maven所以本工程也使用Maven來構(gòu)建項目,當然使用Gradle也可以兩者概念大都想通,不同的地方大多是實現(xiàn)和配置方式不一致。
使用項目繼承
根據(jù)Maven的POM文件繼承特性,將工程分不同的模塊,所有的模塊都繼承父pom.xml的依賴、插件等內(nèi)容,這樣就可以實現(xiàn)統(tǒ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
以下是各個目錄的用處簡述,詳細的用處文章后面都會提到,先在這里列出個大概:
工程主目錄
單個服務工程目錄(模塊)
docker-compose發(fā)布文件
存放.proto文件
發(fā)布、編譯時用到的腳本文件
知道大概的項目工程結(jié)構(gòu)后我們創(chuàng)建一個父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>
因為使用SpringBoot構(gòu)架,所以主pom.xml文件繼承自SpringBoot的POM文件。 有了主pom.xml后然后使每個模塊的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)過上面的配置后,所有的模塊都會繼承AppBubbleBackend中的pom.xml文件,這樣可以很方便的更改依賴、配置等信息。
依賴管理
Maven提供依賴中心化的管理機制,通過項目繼承特性所有對AppBubbleBackend/pom.xml所做的更改都會對其他模塊產(chǎn)生影響,詳細的依賴管理 內(nèi)容可查看官方文檔。
<dependencyManagement> <dependencies> <!-- gRPC --> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-netty-shaded</artifactId> <version>${grpc.version}</version> </dependency> </dependencies> </dependencyManagement>
通過dependencyManagement標簽來配置依賴,這樣可以就可以實現(xiàn)統(tǒng)一依賴的管理,并且還可以添加公共依賴。
插件管理
使用pluginManagement可以非常方便的配置插件,因為項目中使用了Protocol buffers需要集成相應的插件來生成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鏡像時的一些特殊配置,示例工程只配置了一個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包時,相應的輸出文件名是app.jar這樣可以方便在Dockerfile中引用文件,而不需要使用${project.artifactId}-${project.version}的形式來查找輸出的jar這樣可以省去了解析pom.xml文件。如果還需要特殊的參數(shù)可以或者不同的行為,可以添加多個Profile,這樣配置起來非常靈活。
Protocol buffers文件管理
因為是使用微服務開發(fā),而且RPC通信框架是使用的gRPC,所以每個服務工程都會使用.proto文件。服務工程之間又會有使用同一份.proto文件的需求,比如在進行RPC通信時服務提供方返回的消息Test定義在a.proto文件中,那么在使用方在解析消息時也同樣需要a.proto文件來將接收到的消息轉(zhuǎn)換成Test消息,因此管理.proto文件也有一些小麻煩。關(guān)于Protocol buffers的使用可參考 官方文檔。
Protocol buffers文件管理規(guī)約
在我們的示例項目中使用集中管理的方式,即將所有的.proto文件放置在同一個目錄(AppBubbleBackend/protos)下并按服務名稱來劃分:
├── sms │ ├── SmsMessage.proto │ └── SmsService.proto └── user └── UserMessage.proto
還可以將整個目錄放置在一個單獨的git倉庫中,然后在項目中使用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標簽將Protocol buffers的源文件目錄更改成AppBubbleBackend/protos目錄,因為工程中使用了googleapis來定義服務接口,所以需要使用添加additionalProtoPathElement標簽添加額外的依賴文件。注意這個插件的配置是在AppBubbleBackend/pom.xml文件中的,服務工程都是繼承此文件的。在父POM文件配置好以后,再看一下服務工程的插件配置:
<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>
服務工程主要使用includes標簽,將需要的.proto文件包含在編譯腳本中,includes標簽中的include只是一個指定匹配.proto文件的匹配模式,<include>${project.artifactId}/*.proto</include>意思是AppBubbleBackend/protos/${project.artifactId}目錄下的所有以.proto文件結(jié)尾的文件,如果服務工程有多個依賴可以將需要依賴的文件也添加到編譯服務中,如上面的<include>user/*.proto</include>就將AppBubbleBackend/protos/user中的.proto文件添加進來,然后進行整體的編譯。
gRPC
gRPC是由Google開源的RPC通信框架,gRPC使用Protocol buffers定義服務接口并自動生成gRPC相關(guān)代碼,有了這些代碼后就可以非常方便的實現(xiàn)gRPC服務端和gPRC客戶端,過多的細節(jié)就不細說了先看一下如何使用在SpringBoot中使用gRPC。
運行g(shù)RPC服務
利用ApplicationRunner接口,在SprintBoot中運行g(shù)RPC服非常方便,只需要像下面代碼一樣就可以運行一個簡單的gRPC服務。
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服務運行起來后就需要進行調(diào)試了,比如使用curl、chrome等工具向gRPC服務發(fā)起Restful請求,實際上gRPC的調(diào)試并沒有那么簡單。一開始的方案是使用了gRPC-gateway,為每個服務都啟動一個網(wǎng)關(guān)將Http 1.x請求轉(zhuǎn)換并發(fā)送到gRPC服務。然而gRPC-gateway只有g(shù)o語言的版本,并沒有Java語言的版本,所有在編譯和使用中比較困難,后來發(fā)現(xiàn)了Envoy提供了envoy.grpc_json_transcoder這個http過濾器,可以很方便的將RESTful JSON API轉(zhuǎn)換成gRPC請求并發(fā)送給gRPC服務器。
envoy的相關(guān)配置都放置在AppBubbleBackend/scripts/envoy目錄中,里面的envoy.yaml是一份簡單的配置文件:
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選項,該選項指向一個proto descriptor set文件。AppBubbleBackend/scripts/envoy/compile-descriptor.sh是編譯proto descriptor set的腳本文件, 運行腳本文件會在腳本目錄下生成一個app.protobin的文件,將此文件設(shè)置到envoy.grpc_json_transcoder就可大致完成了envoy的代理配置。
使用Docker發(fā)布
經(jīng)過上面的一系統(tǒng)準備工作之后,我們就可以將服務發(fā)布到docker中了,Docker相關(guān)的文件都放置中AppBubbleBackend/scripts/docker和一個AppBubbleBackend/docker-compose.yaml文件。在發(fā)布時使用單個Dockerfile文件來制作服務鏡像:
FROM rcntech/ubuntu-grpc:v0.0.5 EXPOSE 8080 EXPOSE 8090 #將當前目錄添加文件到/bubble ARG APP_PROJECT_NAME #復制父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里面做一些配置就能將服務打包成鏡像:
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
同時編寫了一個通用的entrypoint.sh腳本文件來啟動服務器:
#!/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腳本中將服務工程編譯成app.jar包再運行服務。還有envoy代理也要啟動起來這樣我們就可以使用curl或其他工具直接進行測試了。
關(guān)于“SpringBoot集成gRPC微服務工程搭建的示例”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。