首页 > 其他分享 >【案例实战】SpringBoot整合GRPC微服务远程通信

【案例实战】SpringBoot整合GRPC微服务远程通信

时间:2023-01-18 17:31:40浏览次数:55  
标签:实战 优惠券 SpringBoot GRPC private server grpc new public


文章目录

  • ​​1.什么是GRPC​​
  • ​​2.GRPC特性以及应用场景​​
  • ​​3.GRPC大致请求流程​​
  • ​​4.GRPC的优点和缺点​​
  • ​​5.SpringBoot整合GRPC环境准备​​
  • ​​6.SpringBoot整合GRPC业务开发​​

1.什么是GRPC

【案例实战】SpringBoot整合GRPC微服务远程通信_maven

GRPC是RPC框架中的一种,是一个高性能,开源和通用的RPC框架,基于Protobuf序列化协议开发,且支持众多开发语言。

面向服务端和协议端,基于http/2设计,带来诸如双向流,流控,头部压缩,单TCP连接上的多路复用请求等特性。这些特性使得其在移动设备上表现的更好,更省电和节省空间。

在GRPC里客户端可以向调用本地对象一样直接调用另一台不同机器上服务端医用的方法,使得您能够更容易地创建分布式应用和服务。

与许多RPC系统类似,GRPC也是基于以下理念:定义一个服务,指定其能够被远程调用的方法。在服务端实现这个接口。并运行一个GRPC服务器来处理客户端调用。在客户端拥有一个存根能够向服务端一样的方法。

2.GRPC特性以及应用场景

(1)特性:

  • grpc可以跨语言使用。支持多种语言 支持C++、Java、Go、Python、Ruby、C#、Node.js、Android Java、Objective-C、PHP等编程语言。
  • 基于 IDL ( 接口定义语言)文件定义服务,通过 proto3 工具生成指定语言的数据结构、服务端接口以及客户端 Stub。
  • 通信协议基于标准的 HTTP/2 设计,支持双向流、消息头压缩、单 TCP 的多路复用、服务端推送等特性,这些特性使得 gRPC 在移动端设备上更加省电和节省网络流量。
  • 序列化支持 PB(Protocol Buffer)和 JSON,PB 是一种语言无关的高性能序列化框架,基于 HTTP/2 + PB, 保障了 RPC 调用的高性能。
  • 安装简单,扩展方便(用该框架每秒可达到百万个RPC)

(2)使用场景:

  • 微服务:gRPC 设计用于低延迟和高吞吐量通信。 gRPC 对于效率至关重要的轻量级微服务非常有用。
  • 点对点实时通信:gRPC 对双向流式传输提供出色的支持。 gRPC 服务可以实时推送消息而无需轮询。
  • 多语言环境:gRPC 工具支持所有常用的开发语言,因此,gRPC 是多语言环境的理想选择。
  • 网络受限环境:gRPC 消息使用 Protobuf(一种轻量级消息格式)进行序列化。 gRPC 消息始终小于等效的 JSON 消息。

3.GRPC大致请求流程

【案例实战】SpringBoot整合GRPC微服务远程通信_spring boot_02

  • 客户端(gRPC Stub)调用 A 方法,发起 RPC 调用。
  • 对请求信息使用 Protobuf 进行对象序列化压缩(IDL)。
  • 服务端(gRPC Server)接收到请求后,解码请求体,进行业务逻辑处理并返回。
  • 对响应结果使用 Protobuf 进行对象序列化压缩(IDL)。
  • 客户端接受到服务端响应,解码请求体。回调被调用的 A 方法,唤醒正在等待响应(阻塞)的客户端调用并返回响应结果。

4.GRPC的优点和缺点

  • 优点:
  • protobuf二进制消息,性能好/效率高
  • proto文件生成目标代码,简单易用
  • 序列化反序列化直接对应程序中的数据类,不需要解析后在进行映射
  • 支持向前兼容和向后兼容
  • 支持多种语言,底层采用Netty实现
  • 缺点:
  • GRPC尚未提供链接池,需要自己实现。
  • 尚未提供服务发现、负载均衡机制
  • Protobuf二进制可读性差

5.SpringBoot整合GRPC环境准备

(1)案例背景

统一下单业务,下单时会选择对应的商品和优惠券,那么根据选择的商品ID和优惠券ID分别去商品微服务和优惠券微服务进行调用。

【案例实战】SpringBoot整合GRPC微服务远程通信_spring boot_03

(2)创建MAVEN聚合项目

【案例实战】SpringBoot整合GRPC微服务远程通信_spring_04

(3)父级工程pom.xml引入依赖,锁定版本

<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<!-- grpc版本-->
<grpc-version>1.42.2</grpc-version>
<!-- service和client要使用的lib版本-->
<common-version>1.0-SNAPSHOT</common-version>
<!-- netty版本-->
<netty-version>4.1.65.Final</netty-version>
<!-- Springboot版本-->
<spring-boot.version>2.6.4</spring-boot.version>
<!-- Springboot-grpc版本,用于server服务注解使用-->
<grpc-spring-boot-starter.version>2.13.1.RELEASE</grpc-spring-boot-starter.version>
<!-- maven构建工具版本-->
<maven-plugin-version>3.8.1</maven-plugin-version>
<!-- lombok-->
<lombok-version>1.18.16</lombok-version>
<!--fastjson-->
<fastjson.version>1.2.83</fastjson.version>
</properties>

<!--使用dependencyManagement声明得到依赖子模块不需要再进行版本指定,直接通过父模块指定即可,以此实现依赖的统一管理,防止出现依赖冲突-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.lixiang</groupId>
<artifactId>common</artifactId>
<version>${common-version}</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty</artifactId>
<version>${grpc-version}</version>
</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>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-common</artifactId>
<version>${netty-version}</version>
</dependency>
<!-- spring boot grpc 依赖 -->
<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-client-spring-boot-starter</artifactId>
<version>${grpc-spring-boot-starter.version}</version>
</dependency>
<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-server-spring-boot-starter</artifactId>
<version>${grpc-spring-boot-starter.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok-version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

(4)创建common模块

common中引入依赖

<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<os.detected.classifier>windows-x86_64</os.detected.classifier>
</properties>

<dependencies>
<!-- Spring-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>

<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty</artifactId>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-common</artifactId>
</dependency>
<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-client-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-server-spring-boot-starter</artifactId>
</dependency>
<!-- other-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
</dependencies>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<build>
<plugins>
<!--配置protobuf插件 可参阅https://github.com/grpc/grpc-java-->
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.5.0</version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:3.19.1:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:1.43.1:exe:${os.detected.classifier}</pluginArtifact>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-plugin-version}</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>

这块注意这个值os.detected.classifier,获取当前系统信息,根据你们自己的电脑或者运行环境去填写,获取信息的主类如下:

public class GetClassifier {
public static void main(String[] args) {
System.out.println(System.getProperty("os.name"));
System.out.println(System.getProperty("os.arch"));
}
}

【案例实战】SpringBoot整合GRPC微服务远程通信_maven_05

common模块中创建一个统一返回工具类

【案例实战】SpringBoot整合GRPC微服务远程通信_java_06

/**
* @description 统一返回格式工具类
* @author lixiang
* @Version 1.0
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class BaseResponse {

/**
* 状态码 0 表示成功
*/

private Integer code;
/**
* 数据
*/
private Object data;
/**
* 描述
*/
private String msg;


/**
* 获取远程调用数据
* 注意事项:支持多单词下划线专驼峰(序列化和反序列化)
* @param typeReference
* @param <T>
* @return
*/
public <T> T getData(TypeReference<T> typeReference){
return JSON.parseObject(JSON.toJSONString(data),typeReference);
}

/**
* 成功,不传入数据
* @return
*/
public static BaseResponse buildSuccess() {
return new BaseResponse(0, null, null);
}

/**
* 成功,传入数据
* @param data
* @return
*/
public static BaseResponse buildSuccess(Object data) {
return new BaseResponse(0, data, null);
}

/**
* 失败,传入描述信息
* @param msg
* @return
*/
public static BaseResponse buildError(String msg) {
return new BaseResponse(-1, null, msg);
}

/**
* 自定义状态码和错误信息
* @param code
* @param msg
* @return
*/
public static BaseResponse buildCodeAndMsg(int code, String msg) {
return new BaseResponse(code, null, msg);
}

}

(5)创建coupon-server模块

添加依赖

<dependencies>
<dependency>
<groupId>com.lixiang</groupId>
<artifactId>common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-plugin-version}</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.3.7.RELEASE</version>
<configuration>
<mainClass>com.lixiang.CouponApplication</mainClass>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

创建SpringBoot运行主类

@SpringBootApplication
public class CouponApplication {
public static void main(String[] objArgs)
{
SpringApplication.run(CouponApplication.class, objArgs);
}
}

创建application.properties

# 定义服务名
spring.application.name=coupon-server
# 定义服务端口
server.port=8081
# 定义GRPC端口
grpc.server.port=8071

测试启动

【案例实战】SpringBoot整合GRPC微服务远程通信_spring boot_07

(6)创建product-server模块

添加依赖

<dependencies>
<dependency>
<groupId>com.lixiang</groupId>
<artifactId>common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-plugin-version}</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.3.7.RELEASE</version>
<configuration>
<mainClass>com.lixiang.ProductApplication</mainClass>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

创建SpringBoot运行主类

@SpringBootApplication
public class ProductApplication {
public static void main(String[] objArgs)
{
SpringApplication.run(ProductApplication.class, objArgs);
}
}

创建application.properties

# 定义服务名
spring.application.name=product-server
# 定义服务端口
server.port=8083
# 定义GRPC端口
grpc.server.port=8073

测试运行

【案例实战】SpringBoot整合GRPC微服务远程通信_spring_08

这里我们需要注意一点,我们在优惠券服务和商品服务都定义了一个grpc.server.port,这个是用来提供给其他服务调用是写的端口,不要和springboot本身的server.port重复,否则会报错。

(7)创建order-server模块

添加maven依赖

<dependencies>
<dependency>
<groupId>com.lixiang</groupId>
<artifactId>common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-plugin-version}</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.3.7.RELEASE</version>
<configuration>
<mainClass>com.lixiang.OrderApplication</mainClass>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

创建SpringBoot运行主类

@SpringBootApplication
public class OrderApplication {
public static void main(String[] objArgs)
{
SpringApplication.run(OrderApplication.class, objArgs);
}
}

创建application.properties

# 定义服务名
spring.application.name=order-server
# 定义服务端口
server.port=8082
# 定义GRPC端口
grpc.server.port=8072

# 定义调用商品服务的GRPC
product.server.address=localhost:8073
# 定义调用优惠券服务的GRPC
coupon.server.address=localhost:8071

【案例实战】SpringBoot整合GRPC微服务远程通信_maven_09

6.SpringBoot整合GRPC业务开发

ok,到目前我们所有的环境准备已经完成了,那么下面我们就开始进入到业务的开发,首先我们在下单的时候,会传入一个商品ID和一个优惠券ID,那么我们要通过这两个ID去对应的服务查出具体的详细信息。

(1)开发coupon-server根据ID获取优惠卷详情信息

准备CouponServer.proto文件,写在common模块中

【案例实战】SpringBoot整合GRPC微服务远程通信_spring boot_10

CouponServer.proto内容如下:

/**
* 编译工具版本
*/
syntax = "proto3";
/**
* 指定生成实体
*/
option java_multiple_files = true;
/**
* 指定生成接口
*/
option java_generic_services = true;
/**
* 声明包
*/
package com.lixiang.grpc.server;

/**
* 商品服务proto文件
*/
option java_outer_classname = "CouponServer";

/**
* 统一返回实体
*/
message CouponServerResponse {
string message = 1;
int32 code = 2;
string data=3;
}

/**
* 声明接口
*/
service CouponService {
rpc deductProductInventory (DeductCouponRequest) returns (CouponServerResponse);
}

/**
* 声明扣减商品库存实体
*/
message DeductCouponRequest {
int32 couponId = 1;
}

编写好proto文件点开maven运行protobuf插件

【案例实战】SpringBoot整合GRPC微服务远程通信_后端_11

运行好之后,会发现target下多了一个这个包

【案例实战】SpringBoot整合GRPC微服务远程通信_maven_12

然后在coupon-server中开始编写获取优惠券的方法,这块我们先模拟一些优惠券的数据。

/**
* 优惠券实体类
*/
@Data
public class Coupon {

/**
* 优惠券ID
*/
private Integer id;

/**
* 优惠券金额
*/
private BigDecimal amount;

/**
* 满减额度
*/
private BigDecimal fullReduction;

/**
* 优惠券名称
*/
private String name;

/**
* 优惠券可用开始时间
*/
private LocalDateTime startTime;

/**
* 优惠券结束时间
*/
private LocalDateTime endTime;

}
public class CouponUtil {

private final static List<Coupon> couponList = new ArrayList<>();

static {
Coupon coupon1 = new Coupon();
coupon1.setAmount(new BigDecimal(50));
coupon1.setFullReduction(new BigDecimal(300));
coupon1.setId(1);
coupon1.setName("满300减50券");
coupon1.setStartTime(LocalDateTime.parse("2022-10-20 16:25:49", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
coupon1.setEndTime(LocalDateTime.parse("2023-10-20 16:25:49", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));

Coupon coupon2 = new Coupon();
coupon2.setAmount(new BigDecimal(100));
coupon1.setFullReduction(new BigDecimal(500));
coupon2.setId(2);
coupon2.setName("满500减100券");
coupon2.setStartTime(LocalDateTime.parse("2022-10-20 16:25:49", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
coupon2.setEndTime(LocalDateTime.parse("2023-10-20 16:25:49", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));

Coupon coupon3 = new Coupon();
coupon3.setAmount(new BigDecimal(200));
coupon1.setFullReduction(new BigDecimal(800));
coupon3.setId(3);
coupon3.setName("满800减100券");
coupon3.setStartTime(LocalDateTime.parse("2022-10-20 16:25:49", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
coupon3.setEndTime(LocalDateTime.parse("2023-10-20 16:25:49", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));

couponList.add(coupon3);
couponList.add(coupon2);
couponList.add(coupon1);
}

/**
* 获取具体某个优惠券
* @param couponId
* @return
*/
public static Coupon getCouponById(Integer couponId){
return couponList.stream().filter(obj-> Objects.equals(obj.getId(), couponId)).collect(Collectors.toList()).get(0);
}

}

创建CouponGrpcServer,继承CouponServiceGrpc.CouponServiceImplBase,CouponServiceGrpc.CouponServiceImplBase这个类是刚刚运行插件自动生成的。

@GrpcService
public class CouponGrpcServer extends CouponServiceGrpc.CouponServiceImplBase {

@Override
public void deductProductInventory(DeductCouponRequest request, StreamObserver<CouponServerResponse> responseObserver) {
int couponId = request.getCouponId();

//查找优惠券详细信息
Coupon coupon = CouponUtil.getCouponById(couponId);
String jsonData = JSON.toJSONString(coupon);
CouponServerResponse couponServerResponse = CouponServerResponse.newBuilder()
.setCode(200)
.setMessage("")
.setData(jsonData)
.build();
responseObserver.onNext(couponServerResponse);
responseObserver.onCompleted();
}

ok,这样我们一个根据ID查询优惠券的信息就定义好了。

整体的目录结构:

【案例实战】SpringBoot整合GRPC微服务远程通信_java_13

(2)开发product-server根据ID获取优惠卷详情信息

product-server和coupon-server是一样的,这里就不一一细说了,直接上代码。

/**
* 商品实体类
*/
@Data
public class Product {

/**
* 商品ID
*/
private Integer id;

/**
* 商品名称
*/
private String name;

/**
* 商品价格
*/
private BigDecimal price;

}
public class ProductUtil {

private final static List<Product> productList = new ArrayList<>();

static {
Product product1 = new Product();
product1.setId(1);
product1.setName("互联网JAVA架构师养成零基础到精通");
product1.setPrice(new BigDecimal(12999));

Product product2 = new Product();
product2.setId(2);
product2.setName("Python+大数据零基础到精通");
product2.setPrice(new BigDecimal(15999));

Product product3 = new Product();
product3.setId(3);
product3.setName("5G云计算运维架构零基础到精通");
product3.setPrice(new BigDecimal(10999));

productList.add(product1);
productList.add(product2);
productList.add(product3);
}

/**
* 根据商品ID获取商品详情
* @param productId
* @return
*/
public static Product getProductById(Integer productId){
return productList.stream().filter(obj-> Objects.equals(obj.getId(), productId)).collect(Collectors.toList()).get(0);
}

}
@GrpcService
public class ProductGrpcServer extends ProductServiceGrpc.ProductServiceImplBase {

@Override
public void deductProductInventory(DeductInventoryRequest request, StreamObserver<ProductServerResponse> responseObserver) {

int productId = request.getProductId();
Product product = ProductUtil.getProductById(productId);
String jsonData = JSON.toJSONString(product);

ProductServerResponse productServerResponse = ProductServerResponse.newBuilder()
.setCode(200)
.setMessage("")
.setData(jsonData)
.build();
responseObserver.onNext(productServerResponse);
responseObserver.onCompleted();
}
}

目录结构:

【案例实战】SpringBoot整合GRPC微服务远程通信_java_14

(3)开发order-server模块统一下单接口

首先我们要先配置一下GRPC的地址

【案例实战】SpringBoot整合GRPC微服务远程通信_spring boot_15

配置GrpcClientConfig

/**
* @description Grpc Client 配置类
* @author lixiang
*/
@Configuration
public class GrpcClientConfig {

/**
* 商品服务地址
*/
@Value("${product.server.address}")
private String productServerAddress;

/**
* 优惠券服务地址
*/
@Value("${coupon.server.address}")
private String couponServerAddress;

/**
* 商品服务grpc-client
* @return
*/
@Bean
public ProductServiceGrpc.ProductServiceBlockingStub getProductServerClient() {
return ProductServiceGrpc.newBlockingStub(ManagedChannelBuilder.forTarget(productServerAddress).usePlaintext().build());
}

/**
* 优惠卷服务grpc-client
* @return
*/
@Bean
public CouponServiceGrpc.CouponServiceBlockingStub getCouponServerClient() {
return CouponServiceGrpc.newBlockingStub(ManagedChannelBuilder.forTarget(couponServerAddress).usePlaintext().build());
}

}

编写统一下单接口请求类

@Data
public class OrderConfirmRequest {

/**
* 支付价格
*/
private BigDecimal totalAmount;

/**
* 支付类型
*/
private String payType;

/**
* 支付商品的ID
*/
private Integer productId;

/**
* 支付时用的优惠券ID
*/
private Integer couponRecordId;
}

编写统一下单接口

@RestController
@RequestMapping(value = "/order")
public class OrderController {

@Autowired
private OrderService orderService;

@PostMapping("/confirm")
public BaseResponse confirm(@RequestBody OrderConfirmRequest orderConfirmRequest) {
orderService.confirm(orderConfirmRequest);
return BaseResponse.buildSuccess();
}

}

编写统一下单业务类

public interface OrderService {

/**
* 统一下单接口
* @param orderConfirmRequest
*/
void confirm(OrderConfirmRequest orderConfirmRequest);

}
@Service
@Slf4j
public class OrderServiceImpl implements OrderService{

@Autowired
private ProductServiceGrpc.ProductServiceBlockingStub productService;

@Autowired
private CouponServiceGrpc.CouponServiceBlockingStub couponService;

@Override
public void confirm(OrderConfirmRequest orderConfirmRequest) {
//1.调用优惠券服务获取优惠券详情
DeductCouponRequest deductCouponRequest = DeductCouponRequest.newBuilder()
.setCouponId(orderConfirmRequest.getCouponRecordId())
.build();
CouponServerResponse couponServerResponse = couponService.deductProductInventory(deductCouponRequest);
String couponDataStr = couponServerResponse.getData();
JSONObject couponData = JSON.parseObject(couponDataStr);
log.info("调用优惠卷服务获取的优惠券信息:{}",couponData);

//2.调用商品服务获取商品详细信息
DeductInventoryRequest deductInventoryRequest = DeductInventoryRequest.newBuilder()
.setProductId(orderConfirmRequest.getProductId())
.build();
ProductServerResponse productServerResponse = productService.deductProductInventory(deductInventoryRequest);
String productDataStr = productServerResponse.getData();
JSONObject productData = JSON.parseObject(productDataStr);
log.info("调用商品服务获取的商品信息:{}",productData);

//3.判断优惠券是否在使用时间范围内
long today = new Date().getTime();
long startTime = couponData.getDate("startTime").getTime();
long endTime = couponData.getDate("endTime").getTime();

if(startTime>today || endTime<today){
throw new RuntimeException("当前优惠券不在可用范围内");
}

//4.验证价格
BigDecimal amount = couponData.getBigDecimal("amount");
BigDecimal price = productData.getBigDecimal("price");

if(!price.subtract(amount).equals(orderConfirmRequest.getTotalAmount())){
throw new RuntimeException("订单验价失败");
}

//5.生成订单
log.info("当前订单购买的商品为:{},原价为:{},本次消耗优惠券:{},实际支付金额:{}",
productData.getString("name"),productData.getBigDecimal("price"),couponData.getString("name"),orderConfirmRequest.getTotalAmount());

}
}

测试全流程,启动三个微服务

【案例实战】SpringBoot整合GRPC微服务远程通信_maven_16


【案例实战】SpringBoot整合GRPC微服务远程通信_java_17

至此,由GRPC整合微服务,实现远程通信就已经完成了,当然下单业务不止这么简单,中间还有防重提交,调用三方支付,延时关单等等一些列复杂的业务,这里只是给大家演示一下怎末用GRPC代替feign实现微服务之间的远程通信。


标签:实战,优惠券,SpringBoot,GRPC,private,server,grpc,new,public
From: https://blog.51cto.com/u_15646271/6019336

相关文章

  • springboot项目集成xxl-job
    一、xxl-job简介xxl-job是一个开源的分布式定时任务框架,它可以与其他微服务组件一起构成微服务集群。它的调度中心(xxl-job)和执行器(自己的springboot项目中有@XxlJob("......
  • 【集成-Jedis】SpringBoot集成Jedis
    将jedis的依赖放进Maven<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>3.7.......
  • MySQL实战宝典 索引调优篇 14 分区表:哪些场景不建议用分区表
    MySQL实战宝典索引调优篇14分区表:哪些场景不建议用分区表 前面几节,通过索引的原理,索引覆盖的使用,结合复杂SQL的调优,学习了索引设计的各个方面,接下来聊聊分区表的设......
  • Springboot:aop注解实现
    概念Aop原指面向切面编程。但在spring中对aop的实现指的是:对方法执行前后加入其它逻辑代码,达到增强方法的目的spring-aop的底层实现一般实现aop,有两种方案:JDK动态......
  • SpringBoot2注解:@Configuration
    @configuration@Configuration这个注解作用就是告诉springboot这是一个配置类。配置类以及类里的方法都可以作为bean。用@Bean标记@Configuration包含 proxyBeanMeth......
  • Calcite SQL 解析、语法扩展、元数据验证原理与实战(上)
    引言ApacheCalcite是一个动态数据管理框架,其中包含了许多组件,例如SQL解析器、SQL验证器、SQL优化器、SQL生成器等。因为Calcite的体系结构并不支持数据的存储和处......
  • 使用Dapr和.NET 6.0进行微服务实战:Dapr简介
    大家好,我是张飞洪,感谢您的阅读,我会不定期和你分享学习心得,希望我的文章能成为你成长路上的垫脚石,让我们一起精进。本文是《使用Dapr和.NET6.0进行微服务实战》的第2篇D......
  • Web安全入门与靶场实战(9)- 利用Burpsuite拦截HTTP数据
    在Web安全中经常需要对HTTP请求和响应的数据进行拦截,从而来对这些数据做进一步的分析或处理,这就要用到一个非常重要的工具Burpsuite。Burpsuite可谓Web安全的神器,要学习Web......
  • 《RPC实战与核心原理》学习笔记Day1
    开篇词|别老想着怎么用好RPC框架,你得多花时间琢磨原理为什么要学习RPC?RPC是解决分布式系统通信问题的一大利器。RPC对网络通信的整个过程做了完整包装,在搭建分布式系......
  • PyTorch图像分类实战task1——构建图像分类数据集
    构建图像分类数据集视频链接:https://www.bilibili.com/video/BV1Jd4y1T7rw/?vd_source=ec0dfe3d40081b44c0160eacc0f39d0f脚本文件:https://github.com/TommyZihao/Train_......