博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
SpringBoot集成gRPC微服务工程搭建实践
阅读量:6574 次
发布时间:2019-06-24

本文共 9855 字,大约阅读时间需要 32 分钟。

前言

本文将使用MavengRPCProtocol buffersDockerEnvoy等工具构建一个简单微服务工程,笔者所使用的示例工程是以前写的一个Java后端工程,因为最近都在 学习微服务相关的知识,所以利用起来慢慢的把这个工程做成微服务化应用。在实践过程踩过很多坑,主要是经验不足对微服务还是停留在萌新阶段,通过本文 记录创建微服务工程碰到一些问题,此次实践主要是解决以下问题:

  • 如何解决、统一服务工程依赖管理
  • SpringBoot集成gRPC
  • 管理Protocol buffers文件
  • 使用Envoy代理访问gRPC
  • 部署到Docker

本文假设读者已经了解以下相关知识:

  • Maven
  • Envoy
  • gRPC
  • Protocol buffers
  • SpringBoot
  • Docker

由于是初步实现微服务,不会考虑过多的细节,现阶段只需要能够使用gRPC正常通信,后续计划会发布到k8s中,使用istio实现来服务网格。

使用Maven

现在比较流行的构建工具有MavenGradle,现阶段后端开发大多数都是用的Maven所以本工程也使用Maven来构建项目,当然使用Gradle也可以两者概念大都想通,不同的地方大多是实现和配置方式不一致。

使用项目继承

根据Maven的POM文件继承特性,将工程分不同的模块,所有的模块都继承父pom.xml依赖插件等内容,这样就可以实现统一管理,并方便以后管理、维护。先看一下大概的项目结构:

AppBubbleBackend            (1)├── AppBubbleCommon├── AppBubbleSmsService     (2)├── AppBubbleUserService├── docker-compose.yaml     (3)├── pom.xml├── protos                  (4)│   ├── sms│   └── user└── scripts                 (5)    ├── docker    ├── envoy    ├── gateway    └── sql复制代码

以下是各个目录的用处简述,详细的用处文章后面都会提到,先在这里列出个大概:

  1. 工程主目录
  2. 单个服务工程目录(模块)
  3. docker-compose发布文件
  4. 存放.proto文件
  5. 发布、编译时用到的脚本文件

知道大概的项目工程结构后我们创建一个父pom.xml文件,放在AppBubbleBackend目录下面:

4.0.0
org.springframework.boot
spring-boot-starter-parent
2.1.2.RELEASE
com.bubble
bubble
0.0.1-SNAPSHOT
pom
AppBubbleSmsService
AppBubbleCommon
AppBubbleUserService
复制代码

因为使用SpringBoot构架,所以主pom.xml文件继承自SpringBoot的POM文件。 有了主pom.xml后然后使每个模块的pom.xml都继承自 主pom.xml文件:

4.0.0
com.bubble
bubble
0.0.1-SNAPSHOT
sms
0.0.1-SNAPSHOT
复制代码

经过上面的配置后,所有的模块都会继承AppBubbleBackend中的pom.xml文件,这样可以很方便的更改依赖、配置等信息。

依赖管理

Maven提供依赖中心化的管理机制,通过项目继承特性所有对AppBubbleBackend/pom.xml所做的更改都会对其他模块产生影响,详细的 内容可查看官方文档。

io.grpc
grpc-netty-shaded
${grpc.version}
复制代码

通过dependencyManagement标签来配置依赖,这样可以就可以实现统一依赖的管理,并且还可以添加公共依赖。

插件管理

使用pluginManagement可以非常方便的配置插件,因为项目中使用了Protocol buffers需要集成相应的插件来生成Java源文件:

org.xolstice.maven.plugins
protobuf-maven-plugin
0.5.1
compile
compile-custom
复制代码

Protocol buffers插件的完整配置参数,可以这找到。

Profile

使用Profile的目的是为了区分生成Docker镜像时的一些特殊配置,示例工程只配置了一个docker-build的profile:

docker-build
app
${project.artifactId}-${project.version}
${jarName}
复制代码

如果使用mvn package -P docker-build命令生成jar包时,相应的输出文件名是app.jar这样可以方便在Dockerfile中引用文件,而不需要使用${project.artifactId}-${project.version}的形式来查找输出的jar这样可以省去了解析pom.xml文件。如果还需要特殊的参数可以或者不同的行为,可以添加多个Profile,这样配置起来非常灵活。

Protocol buffers文件管理

因为是使用微服务开发,而且RPC通信框架是使用的gRPC,所以每个服务工程都会使用.proto文件。服务工程之间又会有使用同一份.proto文件的需求,比如在进行RPC通信时服务提供方返回的消息Test定义在a.proto文件中,那么在使用方在解析消息时也同样需要a.proto文件来将接收到的消息转换成Test消息,因此管理.proto文件也有一些小麻烦。关于Protocol buffers的使用可参考 。

Protocol buffers文件管理规约

在我们的示例项目中使用集中管理的方式,即将所有的.proto文件放置在同一个目录(AppBubbleBackend/protos)下并按服务名称来划分:

├── sms│   ├── SmsMessage.proto│   └── SmsService.proto└── user    └── UserMessage.proto复制代码

还可以将整个目录放置在一个单独的git仓库中,然后在项目中使用git subtree来管理文件。

Protocol buffers 插件配置

有了上面的目录结构后,就需要配置一下Protocol buffers的编译插件来支持这种.proto文件的组织结构。在讲解如何配置插件解决.proto文件的编译问题之前,推荐读者了解一下插件的配置文档: 。在我们的工程中使用如下配置:

org.xolstice.maven.plugins
protobuf-maven-plugin
0.5.1
com.google.protobuf:protoc:3.5.1-1:exe:${os.detected.classifier}
grpc-java
io.grpc:protoc-gen-grpc-java:1.17.1:exe:${os.detected.classifier}
${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis
${GOPATH}/src
${protos.basedir}
true
true
复制代码

首先上面的插件配置使用protoSourceRoot标签将Protocol buffers的源文件目录更改成AppBubbleBackend/protos目录,因为工程中使用了googleapis来定义服务接口,所以需要使用添加additionalProtoPathElement标签添加额外的依赖文件。注意这个插件的配置是在AppBubbleBackend/pom.xml文件中的,服务工程都是继承此文件的。在父POM文件配置好以后,再看一下服务工程的插件配置:

org.xolstice.maven.plugins
protobuf-maven-plugin
${project.artifactId}/*.proto
user/*.proto
复制代码

服务工程主要使用includes标签,将需要的.proto文件包含在编译脚本中,includes标签中的include只是一个指定匹配.proto文件的匹配模式,<include>${project.artifactId}/*.proto</include>意思是AppBubbleBackend/protos/${project.artifactId}目录下的所有以.proto文件结尾的文件,如果服务工程有多个依赖可以将需要依赖的文件也添加到编译服务中,如上面的<include>user/*.proto</include>就将AppBubbleBackend/protos/user中的.proto文件添加进来,然后进行整体的编译。

gRPC

gRPC是由Google开源的RPC通信框架,gRPC使用Protocol buffers定义服务接口并自动生成gRPC相关代码,有了这些代码后就可以非常方便的实现gRPC服务端和gPRC客户端,过多的细节就不细说了先看一下如何使用在SpringBoot中使用gRPC。

运行gRPC服务

利用ApplicationRunner接口,在SprintBoot中运行gRPC服非常方便,只需要像下面代码一样就可以运行一个简单的gRPC服务。

package com.bubble.sms.grpc;@Componentpublic class GrpcServerInitializer implements ApplicationRunner {    @Autowired    private List
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服务运行起来后就需要进行调试了,比如使用curlchrome等工具向gRPC服务发起Restful请求,实际上gRPC的调试并没有那么简单。一开始的方案是使用了gRPC-gateway,为每个服务都启动一个网关将Http 1.x请求转换并发送到gRPC服务。然而gRPC-gateway只有go语言的版本,并没有Java语言的版本,所有在编译和使用中比较困难,后来发现了Envoy提供了envoy.grpc_json_transcoder这个http过滤器,可以很方便的将RESTful JSON API转换成gRPC请求并发送给gRPC服务器。

envoy的相关配置都放置在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的文件,将此文件设置到envoy.grpc_json_transcoder就可大致完成了envoy的代理配置。

使用Docker发布

经过上面的一系统准备工作之后,我们就可以将服务发布到docker中了,Docker相关的文件都放置中AppBubbleBackend/scripts/docker和一个AppBubbleBackend/docker-compose.yaml文件。在发布时使用单个Dockerfile文件来制作服务镜像:

FROM rcntech/ubuntu-grpc:v0.0.5EXPOSE 8080EXPOSE 8090#将当前目录添加文件到/bubbleARG APP_PROJECT_NAME#复制父pom.xmlADD /pom.xml /app/pom.xmlADD /protos /app/protosADD $APP_PROJECT_NAME /app/$APP_PROJECT_NAMEADD scripts/gateway /app/gatewayADD scripts/docker/entrypoint.sh /app/entrypoint.shRUN chmod u+x /app/entrypoint.shENTRYPOINT ["/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/bashexport GOPATH=${HOME}/goexport PATH=$PATH:/usr/local/go/bin:$GOPATH/binrootProjectDir="/app"projectDir="${rootProjectDir}/${APP_PROJECT_NAME}"cd ${rootProjectDir}/AppBubbleCommon./mvnw installcd $projectDir#打包app.jar./mvnw package -DskipTests -P docker-build#编译proto文件./mvnw protobuf:compile protobuf:compile-custom -P docker-build# Run servicejava -jar ${projectDir}/target/app.jar复制代码

entrypoint.sh脚本中将服务工程编译成app.jar包再运行服务。还有envoy代理也要启动起来这样我们就可以使用curl或其他工具直接进行测试了。

总结

搭建这个工程大概摸索了一周的时间,主要的时间是花在了Protocol buffers文件的管理与使用Envoy作为代理调试gRPC服务上。文章中的示例工程已经传到了GitHub: 后面会打算慢慢的完善这个应用,这是个简单的短视屏应用除了服务器还包含了AndroidiOS端,等到将后端微服务化为开源出来供学习交流使用。

参考引用

转载地址:http://hogjo.baihongyu.com/

你可能感兴趣的文章
博客园博客美化相关文章目录
查看>>
root用户重置其他密码
查看>>
关于查询扩展版ESI高被引论文的说明
查看>>
Oracle推断值为非数字
查看>>
多年前写的一个ASP.NET网站管理系统,到现在有些公司在用
查看>>
vue-cli中理不清的assetsSubDirectory 和 assetsPublicPath
查看>>
从JDK源码角度看Short
查看>>
解密Angular WebWorker Renderer (二)
查看>>
parceljs 中文文档24小时诞生记
查看>>
五年 Web 开发者 star 的 github 整理说明
查看>>
Docker 部署 SpringBoot 项目整合 Redis 镜像做访问计数Demo
查看>>
ReactNative字体大小不随系统字体大小变化而变化
查看>>
中台之上(五):业务架构和中台的难点,都是需要反复锤炼出标准模型
查看>>
为什么中台是传统企业数字化转型的关键?
查看>>
使用模板将Web服务的结果转换为标记语言
查看>>
inno setup 打包脚本学习
查看>>
php 并发控制中的独占锁
查看>>
从pandas到geopandas
查看>>
用express搭建网站
查看>>
如何在 Swift 中进行错误处理
查看>>