springcloud 集成nacos与GRPC,需要做如下准备
1. server端
2. client端
3. proto文件
4. 相应的pom
5. nacos服务器
springcloud 集成nacos与GRPC,其它就是普通的springcloud的项目,使用nacos作为注册中心与配置中心,然后引入GRPC协议。本身没什么难度,上手速度也比较快。
步骤1.
创建java maven工程,使用maven对Jar包进行管理,工程目录如下
步骤2.
在springcloud-nacos-grpc的pom中引入要应的jar,同样也需要注意版本问题。
需要引入的jar大概分为两部分,一部分是parent,一部分是dependencyManagement,引入本项目的module,就不进行代码展示。从这里也可以看出,GRPC只有两个包,其它的都是springCloud所需要。
parent:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.6.RELEASE</version>
<relativePath/>
</parent>
dependencyManagement:
<properties>
<alibaba.version>2021.1</alibaba.version>
<cloud.version>2020.0.4</cloud.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-dependencies -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR8</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-client-spring-boot-starter</artifactId>
<version>2.10.1.RELEASE</version>
</dependency>
<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-server-spring-boot-starter</artifactId>
<version>2.10.1.RELEASE</version>
</dependency>
</dependencies>
</dependencyManagement>
步骤3.
编写proto文件,我这里是在api中编写,跟java直接使用grpc一样,也需要注意文件存放的位置。
步骤4.
在api的pom.xml中添加编译依赖,这里需要注意版本(protoc:3.12.0),其它的根据这个进行适配,这里没什么变化,java直接使用grpc一致
<dependencies>
<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-server-spring-boot-starter</artifactId>
</dependency>
</dependencies>
<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.6.2</version>
</extension>
</extensions>
<plugins>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.6.1</version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:3.12.0:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:1.34.1:exe:${os.detected.classifier}</pluginArtifact>
<!--设置grpc生成代码到指定路径-->
<outputDirectory>${project.basedir}/src/main/java</outputDirectory>
<!--生成代码前是否清空目录-->
<clearOutputDirectory>false</clearOutputDirectory>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- 设置多个源文件夹 -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>3.0.0</version>
<executions>
<!-- 添加主源码目录 -->
<execution>
<id>add-source</id>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>${project.basedir}/src/main/gen</source>
<source>${project.basedir}/src/main/java</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
步骤5.
生成GRPC代码,使用ideal,直接在maven操作区,点击compile,如果出现错误,可能需要安装protobuf插件,点击compile之后,会在相应的目录生成代码,目录的设置是根据步骤4,以及proto的设置决定。
步骤6.
编写server端代码 ,需要引入api的pom,这里GRPC需要的包只有一个,server端用grpc-server-spring-boot-starter,但实际项目中,一般都是双向通信,这点要注意。
<dependencies>
<!-- GRPC 服务端引入包 -->
<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-server-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.example</groupId>
<artifactId>api</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>
步骤6.1
编写bootstrap.yml,这里直接使用nacos的配置中心去读取,命名空间为cc,配置文件名为springcloud-nacos-grpc-server,这里面也就只配置了一个grpc.server.port
spring:
application:
name: springcloud-nacos-grpc-server
profiles:
active: cc
cloud:
nacos:
discovery:
server-addr: nacos:8848
namespace: ${spring.profiles.active}
config:
file-extension: yml
server-addr: ${spring.cloud.nacos.discovery.server-addr}
namespace: ${spring.cloud.nacos.discovery.namespace}
logging:
file:
path: logs
level:
root: WARN
步骤6.2.
编写启动类,这里使用的是nacos的配置,所以把必要信息打印出来
@SpringBootApplication
public class ServerApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(ServerApplication.class, args);
String prot = context.getEnvironment().getProperty("server.port");
String profile = StringUtils.join(context.getEnvironment().getActiveProfiles(), ",");
String path = context.getEnvironment().getProperty("server.servlet.context-path") == null ? "/" : context.getEnvironment().getProperty("server.servlet.context-path");
System.out.println("======================================");
System.out.println("服务启动成功,运行端口:"+ prot + " bootstrap 启用配置: "+profile + " 访问路径:" + path );
System.out.println("======================================");
}
}
步骤6.3
编写接收处理方法,UserInfoServiceGrpcImpl,用于处理接收client 端GRPC的请求;该类直接继承UserInfoServiceGrpc.UserInfoServiceImplBase,实现其方法即可。由于是springcloud项目,类是交由spring进行管理,所以需要加上特定注解@GrpcService
@Slf4j
@GrpcService
public class UserInfoServiceGrpcImpl extends UserInfoServiceGrpc.UserInfoServiceImplBase {
@Override
public void queryUserInfo2(UserInfoReq request, StreamObserver<UserInfoResponse> responseObserver) {
super.queryUserInfo2(request, responseObserver);
}
@Override
public void queryUserInfo3(UserStr request, StreamObserver<UserStr> responseObserver) {
System.out.println("queryUserInfo3 =======> " + request.getStr());
responseObserver.onNext(UserStr.newBuilder().setStr("msg : success").build());
responseObserver.onCompleted();
}
@Override
public void queryUserInfo(UserInfoReq request, StreamObserver<UserInfoResponse> responseObserver) {
log.info("request id {}", request.getId());
UserInfoResponse.Builder userInfoResp = UserInfoResponse.newBuilder();
userInfoResp.setCode(0).setMsg("success").setSuccess(true);
UserInfo.Builder userInfo = UserInfo.newBuilder();
userInfo.setId(request.getId());
userInfo.setName(request.getName());
userInfoResp.setData(UserInfoResponse.Data.newBuilder().setUserInfo(userInfo));
responseObserver.onNext(userInfoResp.build());
responseObserver.onCompleted();
}
}
步骤7
编写client端代码,需要引入api的pom,这里GRPC需要的包只有一个,client端用grpc-client-spring-boot-starter,这里与server端相反,但实际项目中,一般都是双向通信,这点要注意。
<dependencies>
<!-- GRPC 客户端引入包 -->
<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-client-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.example</groupId>
<artifactId>api</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>
步骤7.1
编写bootstrap.yml,这里直接使用nacos的配置中心去读取,命名空间为cc,配置文件名为springcloud-nacos-grpc-client,这里与springboot项目集成是有区别的,不需要引入服务端地址,让nacos帮我们去识别。
spring:
application:
name: springcloud-nacos-grpc-client
profiles:
active: cc
cloud:
nacos:
discovery:
server-addr: nacos:8848
namespace: ${spring.profiles.active}
config:
file-extension: yml
server-addr: ${spring.cloud.nacos.discovery.server-addr}
namespace: ${spring.cloud.nacos.discovery.namespace}
logging:
file:
path: logs
level:
root: WARN
步骤7.2.
编写启动类,这里使用的是nacos的配置,所以把必要信息打印出来
@SpringBootApplication
public class ClientApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(ClientApplication.class, args);
String prot = context.getEnvironment().getProperty("server.port");
String profile = StringUtils.join(context.getEnvironment().getActiveProfiles(), ",");
String path = context.getEnvironment().getProperty("server.servlet.context-path") == null ? "/" : context.getEnvironment().getProperty("server.servlet.context-path");
System.out.println("======================================");
System.out.println("服务启动成功,运行端口:"+ prot + " bootstrap 启用配置: "+profile + " 访问路径:" + path );
System.out.println("======================================");
}
}
步骤7.3
编写Controller,这个controller就是rest请求的controller,用url调用,就是这个
@GrpcClient中的value值,就是server端的名称(spring.application.name )
@RestController
@RequestMapping("/userInfo")
@Slf4j
public class UserInfoController {
@GrpcClient("springcloud-nacos-grpc-server")
UserInfoServiceGrpc.UserInfoServiceFutureStub userInfoServiceStub;
@RequestMapping(value="/query/{id}")
public String queryUser(@PathVariable Integer id, String str) {
UserInfoResponse userResp = null;
try {
log.info("id: {} ,str : {}",id,str);
userResp = userInfoServiceStub.queryUserInfo(
UserInfoReq.newBuilder().setName("王五").setId(id).build()
).get();
return userResp.toString();
} catch (InterruptedException e) {
} catch (ExecutionException e) {
}
return userResp.toString();
}
}
步骤8
启动server与client
测试