【案例实战】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实现微服务之间的远程通信。


相关文章
|
3月前
|
监控 Java API
Spring Boot 3.2 结合 Spring Cloud 微服务架构实操指南 现代分布式应用系统构建实战教程
Spring Boot 3.2 + Spring Cloud 2023.0 微服务架构实践摘要 本文基于Spring Boot 3.2.5和Spring Cloud 2023.0.1最新稳定版本,演示现代微服务架构的构建过程。主要内容包括: 技术栈选择:采用Spring Cloud Netflix Eureka 4.1.0作为服务注册中心,Resilience4j 2.1.0替代Hystrix实现熔断机制,配合OpenFeign和Gateway等组件。 核心实操步骤: 搭建Eureka注册中心服务 构建商品
660 3
|
3月前
|
JSON 自然语言处理 API
gRPC凭什么成为微服务通信首选?深度解析RPC进化史
本文深入解析了分布式系统中服务通信的核心机制,重点介绍了 RPC 与 gRPC 的原理、优势及使用场景,并详解 gRPC 所依赖的序列化协议 Protocol Buffers(Protobuf)。内容涵盖 RPC 概念、gRPC 特性、Protobuf 语法及服务定义,适合微服务架构设计与维护人员阅读,助你构建高性能、低耦合的服务通信体系。
512 73
gRPC凭什么成为微服务通信首选?深度解析RPC进化史
|
1月前
|
Cloud Native Serverless API
微服务架构实战指南:从单体应用到云原生的蜕变之路
🌟蒋星熠Jaxonic,代码为舟的星际旅人。深耕微服务架构,擅以DDD拆分服务、构建高可用通信与治理体系。分享从单体到云原生的实战经验,探索技术演进的无限可能。
微服务架构实战指南:从单体应用到云原生的蜕变之路
|
1月前
|
监控 Cloud Native Java
Spring Boot 3.x 微服务架构实战指南
🌟蒋星熠Jaxonic,技术宇宙中的星际旅人。深耕Spring Boot 3.x与微服务架构,探索云原生、性能优化与高可用系统设计。以代码为笔,在二进制星河中谱写极客诗篇。关注我,共赴技术星辰大海!(238字)
Spring Boot 3.x 微服务架构实战指南
|
2月前
|
消息中间件 Ubuntu Java
SpringBoot整合MQTT实战:基于EMQX实现双向设备通信
本教程指导在Ubuntu上部署EMQX 5.9.0并集成Spring Boot实现MQTT双向通信,涵盖服务器搭建、客户端配置及生产实践,助您快速构建企业级物联网消息系统。
1074 1
|
3月前
|
负载均衡 监控 Java
微服务稳定性三板斧:熔断、限流与负载均衡全面解析(附 Hystrix-Go 实战代码)
在微服务架构中,高可用与稳定性至关重要。本文详解熔断、限流与负载均衡三大关键技术,结合API网关与Hystrix-Go实战,帮助构建健壮、弹性的微服务系统。
453 1
微服务稳定性三板斧:熔断、限流与负载均衡全面解析(附 Hystrix-Go 实战代码)
|
5月前
|
NoSQL Java 微服务
2025 年最新 Java 面试从基础到微服务实战指南全解析
《Java面试实战指南:高并发与微服务架构解析》 本文针对Java开发者提供2025版面试技术要点,涵盖高并发电商系统设计、微服务架构实现及性能优化方案。核心内容包括:1)基于Spring Cloud和云原生技术的系统架构设计;2)JWT认证、Seata分布式事务等核心模块代码实现;3)数据库查询优化与高并发处理方案,响应时间从500ms优化至80ms;4)微服务调用可靠性保障方案。文章通过实战案例展现Java最新技术栈(Java 17/Spring Boot 3.2)的应用.
429 9
|
5月前
|
缓存 负载均衡 监控
微服务架构下的电商API接口设计:策略、方法与实战案例
本文探讨了微服务架构下的电商API接口设计,旨在打造高效、灵活与可扩展的电商系统。通过服务拆分(如商品、订单、支付等模块)和标准化设计(RESTful或GraphQL风格),确保接口一致性与易用性。同时,采用缓存策略、负载均衡及限流技术优化性能,并借助Prometheus等工具实现监控与日志管理。微服务架构的优势在于支持敏捷开发、高并发处理和独立部署,满足电商业务快速迭代需求。未来,电商API设计将向智能化与安全化方向发展。
|
1月前
|
JavaScript Java 关系型数据库
基于springboot的项目管理系统
本文探讨项目管理系统在现代企业中的应用与实现,分析其研究背景、意义及现状,阐述基于SSM、Java、MySQL和Vue等技术构建系统的关键方法,展现其在提升管理效率、协同水平与风险管控方面的价值。