赞
踩
目录
之所以会说grpc是高性能框架,默认情况下,gRPC基于Netty进行服务端和客户端互通,使用Protocol Buffers进行传输,这是Google用于序列化结构化数据的成熟开源机制,基于proto3情况下它还是一个跨语言的RPC框架(目前支持Java、c++、Dart、Python、Objective-C、c#、lite-runtime (Android Java)、Ruby和JavaScript(来自协议缓冲区GitHub repo),以及来自golang/protobuf官方包的Go语言生成器)
如果对grpc不够了解可以参考Introduction to gRPC | gRPC。
1、PROTOC下载及安装
2、准备两个springboot项目,作为服务端和客户端
3、下载Apifox软件,用于直连服务端(可选)
1、服务端server
- <!--部分配置-->
- <dependencies>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter</artifactId>
- </dependency>
- <dependency>
- <groupId>io.grpc</groupId>
- <artifactId>grpc-protobuf</artifactId>
- <version>${grpc.version}</version>
- </dependency>
-
- <dependency>
- <groupId>io.grpc</groupId>
- <artifactId>grpc-stub</artifactId>
- <version>${grpc.version}</version>
- </dependency>
- <!--gRPC服务端-->
- <dependency>
- <groupId>net.devh</groupId>
- <artifactId>grpc-server-spring-boot-starter</artifactId>
- <version>2.13.1.RELEASE</version>
- </dependency>
-
-
- </dependencies>
- <properties>
- <grpc.version>1.42.1</grpc.version>
- <protobuf.version>3.7.1</protobuf.version>
- </properties>
- <build>
- <finalName>${project.artifactId}-${project.version}</finalName>
- <extensions>
- <extension>
- <groupId>kr.motd.maven</groupId>
- <artifactId>os-maven-plugin</artifactId>
- <version>1.6.2</version>
- </extension>
- </extensions>
-
- <plugins>
- <!-- Grpc coding plug-in-->
- <plugin>
- <groupId>org.xolstice.maven.plugins</groupId>
- <artifactId>protobuf-maven-plugin</artifactId>
- <version>0.6.1</version>
- <configuration>
- <!--suppress UnresolvedMavenProperty -->
- <!--<!– ${os.detected.classifier} 变量由${os.detected.name} 和 ${os.detected.arch} 组成-->
- <protocArtifact>com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier}</protocArtifact>
- <pluginId>grpc-java</pluginId>
- <pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
- <!-- protoSourceRoot 默认src/main/proto-->
- <!-- <protoSourceRoot>src/java/main/proto</protoSourceRoot>-->
- </configuration>
- <executions>
- <execution>
- <goals>
- <goal>compile</goal>
- <goal>compile-custom</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
- </plugins>
- </build>
注意:proto文件需要创建在src/main/proto下,因为pom使用的是protoSourceRoot默认路径
创建Simple.proto
- syntax = "proto3"; // 协议版本
-
- // 选项配置
- option java_multiple_files = true;
- //生成位置
- option java_package = "com.na.model.proto";
- option java_outer_classname = "SimpleProto";
-
- service Simple {
- // 简单gRPC
- rpc OneToOne (MyRequest) returns (MyResponse) {
- }
- }
-
- message MyRequest {
- string name = 1;
-
- int32 value = 2;
- }
-
- message MyResponse {
- string message = 1;
-
- int64 result = 2;
- }
生成方式有两种一种是通过命令,这里使用maven插件生成compile和compile-custom
编译后可在target下看到生成的相应的java类
这里只贴出关键代码,其他配置根据实际情况来
- # gRPC有关的配置,这里只需要配置服务端口号默认9090
- grpc:
- server:
- port: 19898
- spring:
- application:
- name: nacos-grpc
- package com.na.grpc.server;
- import com.na.model.proto.MyRequest;
- import com.na.model.proto.MyResponse;
- import com.na.model.proto.SimpleGrpc;
- import io.grpc.stub.StreamObserver;
- import lombok.extern.slf4j.Slf4j;
- import net.devh.boot.grpc.server.service.GrpcService;
-
-
- /**
- * GrpcServerService.java中有几处需要注意:
- *
- * 是使用@GrpcService注解,再继承SimpleImplBase,这样就可以借助grpc-server-spring-boot-starter库将oneToOne暴露为gRPC服务;
- *
- * SimpleImplBase是前文中根据maven compile编译 proto文件自动生成的java代码,
- *
- * oneToOne方法中处理完毕业务逻辑后,调用responseObserver.onNext方法填入返回内容;
- *
- * 调用responseObserver.onCompleted方法表示本次gRPC服务完成;
- */
- @GrpcService
- @Slf4j
- public class GrpcServerService extends SimpleGrpc.SimpleImplBase {
-
- @Override
- public void oneToOne(MyRequest request, StreamObserver<MyResponse> responseObserver) {
- log.info("接收客户端数据{}", request);
- MyResponse response = MyResponse.newBuilder().setMessage( request.getName()).build();
- responseObserver.onNext(response);
- responseObserver.onCompleted();
- }
-
- }
2、客户端client
- <!--部分配置-->
- <dependencies>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-web</artifactId>
- </dependency>
-
- <dependency>
- <groupId>io.grpc</groupId>
- <artifactId>grpc-protobuf</artifactId>
- <version>${grpc.version}</version>
- </dependency>
- <dependency>
- <groupId>io.grpc</groupId>
- <artifactId>grpc-stub</artifactId>
- <version>${grpc.version}</version>
- </dependency>
- <!--gRPC客户端-->
- <dependency>
- <groupId>net.devh</groupId>
- <artifactId>grpc-client-spring-boot-starter</artifactId>
- <version>2.14.0.RELEASE</version>
- </dependency>
- </dependencies>
-
- <properties>
- <grpc.version>1.42.1</grpc.version>
- <protobuf.version>3.7.1</protobuf.version>
- </properties>
- <build>
- <finalName>${project.artifactId}-${project.version}</finalName>
-
- <extensions>
- <extension>
- <groupId>kr.motd.maven</groupId>
- <artifactId>os-maven-plugin</artifactId>
- <version>1.6.2</version>
- </extension>
- </extensions>
-
- <plugins>
- <!-- Grpc coding plug-in-->
- <plugin>
- <groupId>org.xolstice.maven.plugins</groupId>
- <artifactId>protobuf-maven-plugin</artifactId>
- <version>0.6.1</version>
- <configuration>
- <!--suppress UnresolvedMavenProperty -->
- <!--<!– ${os.detected.classifier} 变量由${os.detected.name} 和 ${os.detected.arch} 组成-->
- <protocArtifact>com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier}</protocArtifact>
- <pluginId>grpc-java</pluginId>
- <pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
- <!-- protoSourceRoot 默认src/main/proto-->
- <!-- <protoSourceRoot>src/java/main/proto</protoSourceRoot>-->
- </configuration>
- <executions>
- <execution>
- <goals>
- <goal>compile</goal>
- <goal>compile-custom</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
- </plugins>
- </build>
与服务端操作一样,这里省略。。。。。。
这里只贴出关键代码,其他配置根据实际情况来
- server:
- port: 11278
- servlet:
- context-path: /
- # grpc配置
- grpc:
- # grpc clienT相关配置
- client:
- # 服务名(不同服务名可对应不同配置)
- # nacos-grpc是服务端配置的名字,GrpcClient注解会用到
- nacos-grpc:
- # gRPC服务端地址
- # address: 'dns://127.0.0.1:19898'
- address: 'static://127.0.0.1:19898'
- # 是否开启保持连接(长连接)
- enableKeepAlive: true
- # 保持连接时长(默认20s)
- keepAliveTimeout: 20s
- # 没有RPC调用时是否保持连接(默认false,可禁用避免额外消耗CPU)
- keepAliveWithoutCalls: false
- # 客户端负载均衡策略(round_robin(默认), pick_first)
- defaultLoadBalancingPolicy: round_robin
- # 通信类型
- # plaintext | plaintext_upgrade | tls
- # 明文通信且http/2 | 明文通信且升级http/1.1为http/2 | 使用TLS(ALPN/NPN)通信
- negotiationType: plaintext
- package com.na.grpc.client;
-
- import com.na.model.proto.MyRequest;
- import com.na.model.proto.MyResponse;
- import com.na.model.proto.SimpleGrpc;
- import io.grpc.StatusRuntimeException;
- import lombok.extern.slf4j.Slf4j;
- import net.devh.boot.grpc.client.inject.GrpcClient;
- import org.springframework.stereotype.Service;
-
- /**
- * GrpcClientService类有几处要注意的地方:
- * <p>
- * 用@Service将GrpcClientService注册为spring的普通bean实例;
- * <p>
- * 用@GrpcClient修饰SimpleBlockingStub,这样就可以通过grpc-client-spring-boot-starter库发起gRPC调用,被调用的服务端信息来自名为nacos-grpc服务端配置;
- * <p>
- * SimpleBlockingStub来自前文中根据helloworld.proto生成的java代码;
- * <p>
- * SimpleBlockingStub.oneToOne方法会远程调用nacos-grpc应用的gRPC服务;
- */
- @Service
- @Slf4j
- public class GrpcClientService {
-
- @GrpcClient("nacos-grpc")
- private SimpleGrpc.SimpleBlockingStub simpleStub;
-
- public String oneToOne(final String name) {
- try {
- final MyResponse response = this.simpleStub.oneToOne(MyRequest.newBuilder().setName(name).build());
- return response.getMessage();
- } catch (final StatusRuntimeException e) {
- log.error("FAILED with " + e.getStatus().getCode().name() + ",and e:{}", e.getMessage());
- return "FAILED with " + e.getStatus().getCode().name() + ",and e:" + e.getMessage();
- }
- }
- }
-
定义了两种连接方法getOneToOne和testForAddress
- package com.na.controller;
-
- import com.na.base.BaseResponse;
- import com.na.grpc.client.GrpcClientService;
- import com.na.model.proto.MyRequest;
- import com.na.model.proto.SimpleGrpc;
- import io.grpc.ManagedChannel;
- import io.grpc.ManagedChannelBuilder;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.web.bind.annotation.GetMapping;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RestController;
-
- /**
- * @Description 测试grpc接口
- * @Author kele
- * @Data 2023/9/6 15:35
- */
- @RestController
- @RequestMapping("grpc")
- public class SimpleGrpcController {
- @Autowired
- private GrpcClientService service;
-
- @GetMapping("getOneToOne")
- public BaseResponse getOneToOne() {
- return new BaseResponse(service.oneToOne("客户端kele连接"));
- }
-
-
- @GetMapping("testForAddress")
- public BaseResponse testForAddress() {
- ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 19898)
- .usePlaintext()
- .build();
- MyRequest request = MyRequest.newBuilder().setName("kele的访问").build();
- SimpleGrpc.SimpleBlockingStub stub = SimpleGrpc.newBlockingStub(channel);
- return new BaseResponse(stub.oneToOne(request));
- }
-
- }
1)先启动服务端启动的netty端口为19898
2)使用Apifox连接服务端
可以看到,在Apifox中导入了Simple.proto帮我自动生成了客户端连接操作,自己只需要改一下19898端口和请求的参数
1)启动客户端web端口为11278
2)直接通过web访问
这里访问的是getOneToOne方法, 地址http://localhost:11278/grpc/getOneToOne
另外的testForAddress方法可以自己玩一下生产一般不会那样写
1、出现无法访问com.google.protobuf.GeneratedMessageV3 找不到com.google.protobuf.GeneratedMessageV,在pom中添加以下依赖。
- <dependency>
- <groupId>com.google.protobuf</groupId>
- <artifactId>protobuf-java</artifactId>
- <version>${protobuf.version}</version>
- </dependency>
- <dependency>
- <groupId>com.google.protobuf</groupId>
- <artifactId>protobuf-java-util</artifactId>
- <version>${protobuf.version}</version>
- </dependency>
2、出现:io.grpc.StatusRuntimeException: UNAVAILABLE
1、检查下IP是否能ping通,IP、端口 是否正确
2、Server是否打开
3、连接中如果有证书,证书是否有效
4、无证书的,是否写了明文连接,例如:
5、以上都没问题可以查看服务端是否添加了这个依赖,grpc-netty-shaded包中可能会存在core的冲突
- <dependency>
- <groupId>io.grpc</groupId>
- <artifactId>grpc-netty-shaded</artifactId>
- <version>${grpc.version}</version>
- </dependency>
1、客户端序列化,可以查看MyRequest
2、在客户端调用SimpleGrpc.SimpleBlockingStub.oneToOne方法时,已经将name序列化传输了
3、再往下可以看到客户端ClientCalls中,采用的是异步的方式进行发送二进制数据,等结束执行后再进行判断是否执行结束,
waitAndDrain()即为等待,直到有一个Runnable,然后执行它和所有在它之后排队的Runnables。一次只能由一个线程调用。poll()方法用于从队列中取出并返回头部的元素,如果队列为空,则返回null。之后又将当前线程赋给了waiter,
用的是线程池,在服务端断点的情况下,后续请求会进入等待队列。
待更新。。。。。。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。