0
点赞
收藏
分享

微信扫一扫

SpringCloud接入GRPC

一、背景

在小型项目中,并发不高,使用基于Restful接口即可满足需求。 在并发搞得场景,请求响应时间就决定并发量,如果还是阻塞模型,那么对线程占用还是很大。

二、RPC选型

市面上的RPC:

  1. Http + Feign/RestTemplate
  2. Dubbo2.*
  3. SOFA
  4. Motan
  5. GRPC
  6. Thrift(性能好,2倍于GRPC)
    ...

本文不考虑性能,可扩展性因素,仅仅入门GRPC

可能有人认为rest不算RPC,个人认为远程调用都应该算是RPC,不关心底层实现

三、使用

3.1 模块拆分

  • boot-grpc-api
  • boot-grpc-provider
  • boot-grpc-consumer

3.2 依赖

3.2.1 ​​boot-grpc-api​​配置

依赖

<dependencies>
<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-client-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>
<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>

</plugins>
</build>

编写proto

syntax = "proto3";

option java_multiple_files = true;
option java_package = "com.teddy.rpc.user";

service UserService {
rpc findByName(FindUserByNameRequest) returns (ApiResponse) {}
}

message UserDetail {
int64 id = 1;
string name = 2;
string nick = 3;
int32 gender = 4;
int32 age = 5;
string email = 6;
string phone = 7;
}

message FindUserByNameRequest {
string name = 1;
}

message ApiResponse {
string code = 1;
string msg = 2;
bool success = 3;
UserDetail data = 4;
}

执行mvn clean complie生成代码

SpringCloud接入GRPC_java

3.2.2 ​​boot-grpc-provider​​配置

pom.xml

<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</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-discovery</artifactId>
</dependency>

<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-server-spring-boot-starter</artifactId>
</dependency>

<dependency>
<groupId>com.teddy</groupId>
<artifactId>boot-grpc-api</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>

启动类

@EnableDiscoveryClient
@SpringBootApplication
public class BootGrpcProvider {
public static void main(String[] args) {
SpringApplication.run(BootGrpcProvider.class, args);
}

}

RPC实现类

@Slf4j
@GrpcService
public class UserServiceImpl extends UserServiceGrpc.UserServiceImplBase{

@Override
public void findByName(FindUserByNameRequest request, StreamObserver<ApiResponse> responseObserver) {
log.info("request[name:{}]", request.getName());
UserDetail userDetail = UserDetail.newBuilder()
.setId(1L)
.setName("zhangsan")
.setNick("张三")
.setAge(18)
.setEmail("zhangsan@gmail.com")
.setGender(1)
.setPhone("13000000000")
.build();

ApiResponse response = ApiResponse
.newBuilder()
.setCode("200")
.setMsg("success")
.setData(userDetail)
.build();

responseObserver.onNext(response);
responseObserver.onCompleted();
}
}

配置文件

bootstrap.yml

server:
port: 8101
spring:
application:
name: @artifactId@
cloud:
nacos:
discovery:
server-addr: ${NACOS_HOST:127.0.0.1}:${NACOS_PORT:8848}
metadata:
version: v100
context-path: ${server.servlet.context-path}
config:
server-addr: ${spring.cloud.nacos.discovery.server-addr}
file-extension: yml
shared-configs:
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
profiles:
active: @profiles.active@

application.yml

spring:

profiles:
active:
dev
mvc:
throw-exception-if-no-handler-found: true

jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
default-property-inclusion: NON-NULL
deserialization:
accept_empty_string_as_null_object: true
thymeleaf:
cache: false

grpc:
client:
GLOBAL:
negotiation-type: plaintext
enable-keep-alive: true
keep-alive-without-calls: true

3.2.3 ​​boot-grpc-consumer​​配置

pom.xml

<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</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-discovery</artifactId>
</dependency>

<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
</dependency>

<!--swagger2 api在线文档-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
</dependency>

<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
</dependency>

<dependency>
<groupId>com.teddy</groupId>
<artifactId>boot-grpc-api</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>

启动类

@EnableDiscoveryClient
@SpringBootApplication
public class BootGrpcConsumer {

public static void main(String[] args) {
SpringApplication.run(BootGrpcConsumer.class, args);
}

}

GRPC调用类

@Slf4j
@Service
public class UserServiceImpl {

private static final String SUCCESS_CODE = "200";

@GrpcClient("boot-grpc-provider")
private UserServiceGrpc.UserServiceBlockingStub stub;

public UserController.UserDetailVO findByName(String name) {
FindUserByNameRequest request = FindUserByNameRequest.newBuilder()
.setName(name)
.build();


ApiResponse response = stub.findByName(request);
checkResponse(response);

return convert2VO(response.getData());
}

private UserController.UserDetailVO convert2VO(UserDetail userDetail) {
return new UserController.UserDetailVO()
.setId(userDetail.getId())
.setName(userDetail.getName())
.setNick(userDetail.getNick())
.setAge(userDetail.getAge())
.setGender(userDetail.getGender())
.setPhone(userDetail.getPhone())
.setEmail(userDetail.getEmail());
}

private void checkResponse(ApiResponse response) {
if (!SUCCESS_CODE.equals(response.getCode())) {
throw new BizException(response.getCode(), response.getMsg());
}
}

}

门面类

@Api(value = "用户前端控制器", tags = "用户前端控制器")
@RestController
@RequestMapping("/user")
@RequiredArgsConstructor
public class UserController {

private final UserServiceImpl userService;

@ApiOperation("查找用户")
@PostMapping("/findByName")
public ApiResponse<UserDetailVO> sayHello(@Validated @RequestBody FindUserRequest request) {
return ApiResponseBuilderUtil.success(userService.findByName(request.getName()));
}

@Getter
@Setter
@ApiModel("查找用户请求参数")
private static class FindUserRequest {
@ApiModelProperty(value = "账户名")
private String name;
}

@ApiModel("用户信息")
@Getter
@Setter
@Accessors(chain = true)
public static class UserDetailVO {

private Long id;
private String name;
private String nick;
private Integer age;
private Integer gender;
private String phone;
private String email;

}

}

配置文件

bootstrap.yml

server:
port: 8111
spring:
application:
name: @artifactId@
cloud:
nacos:
discovery:
server-addr: ${NACOS_HOST:127.0.0.1}:${NACOS_PORT:8848}
metadata:
version: v100
context-path: ${server.servlet.context-path}
config:
server-addr: ${spring.cloud.nacos.discovery.server-addr}
file-extension: yml
shared-configs:
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
profiles:
active: @profiles.active@

application.yml

spring:

profiles:
active:
dev
mvc:
throw-exception-if-no-handler-found: true

jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
default-property-inclusion: NON-NULL
deserialization:
accept_empty_string_as_null_object: true
thymeleaf:
cache: false

grpc:
client:
GLOBAL:
negotiation-type: plaintext
enable-keep-alive: true
keep-alive-without-calls: true

四、测试工具

​​grpc-swagger​​

五、优化

可以考虑将​​grpc​​​的请求和响应都抽象成通用对象和方法,​​proxy​​​代理客户端和服务端,使用​​Java IDL​​​,一般提供接口都是SDK,通过​​Java IDL​​​反向生成​​proto​

举报

相关推荐

GRPC

grpc

gRPC

Linux(gRPC):Ubuntu22.04安装gRPC

what is grpc

grpc protobuf

0 条评论