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

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

1.什么是GRPC9f3711a5dd81422d8b15ac28b9b58c5b.jpg


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大致请求流程

795d05c2895247c0b34d453a1694b215.jpg

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

4.GRPC的优点和缺点

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

5.SpringBoot整合GRPC环境准备

(1)案例背景

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


41dcb64e089b4c7191d63a4c9ca4fa8f.jpg

(2)创建MAVEN聚合项目cf670b34814345f2bb99d3cf6d7abf36.jpg


(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"));
    }
}

e17408f5092c4968805af222b64a0c50.jpg

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


b282f5cdcf7943bf814df6ed643bbd2b.jpg

/**
 * @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);
    }
}

添加依赖

    <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

测试启动


d966fe4faad5413f84453624d9933754.jpg

(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

测试运行

0e3eddc0753a40338b353bd5d7f7c042.jpg

这里我们需要注意一点,我们在优惠券服务和商品服务都定义了一个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

5246c9953da745c1861795f462f0d267.jpg

6.SpringBoot整合GRPC业务开发

ok,到目前我们所有的环境准备已经完成了,那么下面我们就开始进入到业务的开发,首先我们在下单的时候,会传入一个商品ID和一个优惠券ID,

那么我们要通过这两个ID去对应的服务查出具体的详细信息。

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

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

d181bbcfafe8489b908586e50cd7a48c.jpg

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插件

742d1dd679de4b0d84eae8cf99501d87.jpg

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

e87e061d3bb14d56b940e7e0a2e6a385.jpg

然后在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查询优惠券的信息就定义好了。

整体的目录结构:d4bd24266e7d447c95120b725b730a8c.jpg


(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();
    }
}

目录结构:

466776cea35e480387d3292975b22447.jpg

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

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

3aebf9ac9c4e4979b62d835a851b950f.jpg

配置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());
    }
}

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

3a5fdff0d15c46848221f099a347144b.jpg


260e4800490540eca9e14b06d7562597.jpg

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


相关文章
|
1月前
|
Dubbo Java 应用服务中间件
Spring Cloud Dubbo:微服务通信的高效解决方案
【10月更文挑战第15天】随着信息技术的发展,微服务架构成为企业应用开发的主流。Spring Cloud Dubbo结合了Dubbo的高性能RPC和Spring Cloud的生态系统,提供高效、稳定的微服务通信解决方案。它支持多种通信协议,具备服务注册与发现、负载均衡及容错机制,简化了服务调用的复杂性,使开发者能更专注于业务逻辑的实现。
58 2
|
22天前
|
运维 NoSQL Java
后端架构演进:微服务架构的优缺点与实战案例分析
【10月更文挑战第28天】本文探讨了微服务架构与单体架构的优缺点,并通过实战案例分析了微服务架构在实际应用中的表现。微服务架构具有高内聚、低耦合、独立部署等优势,但也面临分布式系统的复杂性和较高的运维成本。通过某电商平台的实际案例,展示了微服务架构在提升系统性能和团队协作效率方面的显著效果,同时也指出了其带来的挑战。
60 4
|
22天前
|
消息中间件 NoSQL Java
springboot整合常用中间件框架案例
该项目是Spring Boot集成整合案例,涵盖多种中间件的使用示例,每个案例项目使用最小依赖,便于直接应用到自己的项目中。包括MyBatis、Redis、MongoDB、MQ、ES等的整合示例。
80 1
|
1月前
|
前端开发 Java Apache
Springboot整合shiro,带你学会shiro,入门级别教程,由浅入深,完整代码案例,各位项目想加这个模块的人也可以看这个,又或者不会mybatis-plus的也可以看这个
本文详细讲解了如何整合Apache Shiro与Spring Boot项目,包括数据库准备、项目配置、实体类、Mapper、Service、Controller的创建和配置,以及Shiro的配置和使用。
351 1
Springboot整合shiro,带你学会shiro,入门级别教程,由浅入深,完整代码案例,各位项目想加这个模块的人也可以看这个,又或者不会mybatis-plus的也可以看这个
|
1月前
|
Web App开发 JavaScript Java
elasticsearch学习五:springboot整合 rest 操作elasticsearch的 实际案例操作,编写搜索的前后端,爬取京东数据到elasticsearch中。
这篇文章是关于如何使用Spring Boot整合Elasticsearch,并通过REST客户端操作Elasticsearch,实现一个简单的搜索前后端,以及如何爬取京东数据到Elasticsearch的案例教程。
197 0
elasticsearch学习五:springboot整合 rest 操作elasticsearch的 实际案例操作,编写搜索的前后端,爬取京东数据到elasticsearch中。
|
2月前
|
Dubbo 应用服务中间件 Apache
Star 4w+,Apache Dubbo 3.3 全新发布,Triple X 领衔,开启微服务通信新时代
在 Apache Dubbo 突破 4w Star 之际,Apache Dubbo 团队正式宣布,Dubbo 3.3 正式发布!作为全球领先的开源微服务框架,Dubbo 一直致力于为开发者提供高性能、可扩展且灵活的分布式服务解决方案。此次发布的 Dubbo 3.3,通过 Triple X 的全新升级,突破了以往局限,实现了对南北向与东西向流量的全面支持,并提升了对云原生架构的友好性。
144 9
|
2月前
|
消息中间件 Java Kafka
springboot项目启动报错-案例情景介绍
springboot项目启动报错-案例情景介绍
80 2
|
2月前
|
存储 安全 API
微服务之间的安全通信
在微服务架构中,服务之间的通信是系统的核心部分。然而,由于服务的分布式和独立性,确保它们之间的通信安全至关重要。
49 3
|
2月前
|
存储 安全 API
微服务之间的安全通信
在微服务架构中,服务之间的通信是系统的核心部分。然而,由于服务的分布式和独立性,确保它们之间的通信安全至关重要。如果没有适当的安全机制,微服务系统可能会暴露在各种网络攻击和安全漏洞中。
43 4
|
2月前
|
缓存 负载均衡 数据管理
深入探索微服务架构的核心要素与实践策略在当今软件开发领域,微服务架构以其独特的优势和灵活性,已成为众多企业和开发者的首选。本文将深入探讨微服务架构的核心要素,包括服务拆分、通信机制、数据管理等,并结合实际案例分析其在不同场景下的应用策略,旨在为读者提供一套全面、深入的微服务架构实践指南。**
**微服务架构作为软件开发领域的热门话题,正引领着一场技术革新。本文从微服务架构的核心要素出发,详细阐述了服务拆分的原则与方法、通信机制的选择与优化、数据管理的策略与挑战等内容。同时,结合具体案例,分析了微服务架构在不同场景下的应用策略,为读者提供了实用的指导和建议。
下一篇
无影云桌面