SpringBoot整合GRPC微服務(wù)遠(yuǎn)程通信的實(shí)現(xiàn)示例
1.什么是GRPC
GRPC是RPC框架中的一種,是一個(gè)高性能,開源和通用的RPC框架,基于Protobuf序列化協(xié)議開發(fā),且支持眾多開發(fā)語(yǔ)言。
面向服務(wù)端和協(xié)議端,基于http/2設(shè)計(jì),帶來諸如雙向流,流控,頭部壓縮,單TCP連接上的多路復(fù)用請(qǐng)求等特性。這些特性使得其在移動(dòng)設(shè)備上表現(xiàn)的更好,更省電和節(jié)省空間。
在GRPC里客戶端可以向調(diào)用本地對(duì)象一樣直接調(diào)用另一臺(tái)不同機(jī)器上服務(wù)端醫(yī)用的方法,使得您能夠更容易地創(chuàng)建分布式應(yīng)用和服務(wù)。
與許多RPC系統(tǒng)類似,GRPC也是基于以下理念:定義一個(gè)服務(wù),指定其能夠被遠(yuǎn)程調(diào)用的方法。在服務(wù)端實(shí)現(xiàn)這個(gè)接口。并運(yùn)行一個(gè)GRPC服務(wù)器來處理客戶端調(diào)用。在客戶端擁有一個(gè)存根能夠向服務(wù)端一樣的方法。
2.GRPC特性以及應(yīng)用場(chǎng)景
(1)特性:
- grpc可以跨語(yǔ)言使用。支持多種語(yǔ)言 支持C++、Java、Go、Python、Ruby、C#、Node.js、Android Java、Objective-C、PHP等編程語(yǔ)言。
- 基于 IDL ( 接口定義語(yǔ)言)文件定義服務(wù),通過 proto3 工具生成指定語(yǔ)言的數(shù)據(jù)結(jié)構(gòu)、服務(wù)端接口以及客戶端 Stub。
- 通信協(xié)議基于標(biāo)準(zhǔn)的 HTTP/2 設(shè)計(jì),支持雙向流、消息頭壓縮、單 TCP 的多路復(fù)用、服務(wù)端推送等特性,這些特性使得 gRPC 在移動(dòng)端設(shè)備上更加省電和節(jié)省網(wǎng)絡(luò)流量。
- 序列化支持 PB(Protocol Buffer)和 JSON,PB 是一種語(yǔ)言無(wú)關(guān)的高性能序列化框架,基于 HTTP/2 + PB, 保障了 RPC 調(diào)用的高性能。
- 安裝簡(jiǎn)單,擴(kuò)展方便(用該框架每秒可達(dá)到百萬(wàn)個(gè)RPC)
(2)使用場(chǎng)景:
- 微服務(wù):gRPC 設(shè)計(jì)用于低延遲和高吞吐量通信。 gRPC 對(duì)于效率至關(guān)重要的輕量級(jí)微服務(wù)非常有用。
- 點(diǎn)對(duì)點(diǎn)實(shí)時(shí)通信:gRPC 對(duì)雙向流式傳輸提供出色的支持。 gRPC 服務(wù)可以實(shí)時(shí)推送消息而無(wú)需輪詢。
- 多語(yǔ)言環(huán)境:gRPC 工具支持所有常用的開發(fā)語(yǔ)言,因此,gRPC 是多語(yǔ)言環(huán)境的理想選擇。
- 網(wǎng)絡(luò)受限環(huán)境:gRPC 消息使用 Protobuf(一種輕量級(jí)消息格式)進(jìn)行序列化。 gRPC 消息始終小于等效的 JSON 消息。
3.GRPC大致請(qǐng)求流程
- 客戶端(gRPC Stub)調(diào)用 A 方法,發(fā)起 RPC 調(diào)用。
- 對(duì)請(qǐng)求信息使用 Protobuf 進(jìn)行對(duì)象序列化壓縮(IDL)。
- 服務(wù)端(gRPC Server)接收到請(qǐng)求后,解碼請(qǐng)求體,進(jìn)行業(yè)務(wù)邏輯處理并返回。
- 對(duì)響應(yīng)結(jié)果使用 Protobuf 進(jìn)行對(duì)象序列化壓縮(IDL)。
- 客戶端接受到服務(wù)端響應(yīng),解碼請(qǐng)求體?;卣{(diào)被調(diào)用的 A 方法,喚醒正在等待響應(yīng)(阻塞)的客戶端調(diào)用并返回響應(yīng)結(jié)果。
4.GRPC的優(yōu)點(diǎn)和缺點(diǎn)
- 優(yōu)點(diǎn):
- protobuf二進(jìn)制消息,性能好/效率高
- proto文件生成目標(biāo)代碼,簡(jiǎn)單易用
- 序列化反序列化直接對(duì)應(yīng)程序中的數(shù)據(jù)類,不需要解析后在進(jìn)行映射
- 支持向前兼容和向后兼容
- 支持多種語(yǔ)言,底層采用Netty實(shí)現(xiàn)
- 缺點(diǎn):
- GRPC尚未提供鏈接池,需要自己實(shí)現(xiàn)。
- 尚未提供服務(wù)發(fā)現(xiàn)、負(fù)載均衡機(jī)制
- Protobuf二進(jìn)制可讀性差
5.SpringBoot整合GRPC環(huán)境準(zhǔn)備
(1)案例背景
統(tǒng)一下單業(yè)務(wù),下單時(shí)會(huì)選擇對(duì)應(yīng)的商品和優(yōu)惠券,那么根據(jù)選擇的商品ID和優(yōu)惠券ID分別去商品微服務(wù)和優(yōu)惠券微服務(wù)進(jìn)行調(diào)用。
(2)創(chuàng)建MAVEN聚合項(xiàng)目
(3)父級(jí)工程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服務(wù)注解使用--> <grpc-spring-boot-starter.version>2.13.1.RELEASE</grpc-spring-boot-starter.version> <!-- maven構(gòu)建工具版本--> <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聲明得到依賴子模塊不需要再進(jìn)行版本指定,直接通過父模塊指定即可,以此實(shí)現(xiàn)依賴的統(tǒng)一管理,防止出現(xiàn)依賴沖突--> <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)創(chuàng)建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>
? 這塊注意這個(gè)值os.detected.classifier,獲取當(dāng)前系統(tǒng)信息,根據(jù)你們自己的電腦或者運(yùn)行環(huán)境去填寫,獲取信息的主類如下:
public class GetClassifier { public static void main(String[] args) { System.out.println(System.getProperty("os.name")); System.out.println(System.getProperty("os.arch")); } }
common模塊中創(chuàng)建一個(gè)統(tǒng)一返回工具類
/** * @description 統(tǒng)一返回格式工具類 * @author lixiang * @Version 1.0 */ @Data @AllArgsConstructor @NoArgsConstructor public class BaseResponse { /** * 狀態(tài)碼 0 表示成功 */ private Integer code; /** * 數(shù)據(jù) */ private Object data; /** * 描述 */ private String msg; /** * 獲取遠(yuǎn)程調(diào)用數(shù)據(jù) * 注意事項(xiàng):支持多單詞下劃線專駝峰(序列化和反序列化) * @param typeReference * @param <T> * @return */ public <T> T getData(TypeReference<T> typeReference){ return JSON.parseObject(JSON.toJSONString(data),typeReference); } /** * 成功,不傳入數(shù)據(jù) * @return */ public static BaseResponse buildSuccess() { return new BaseResponse(0, null, null); } /** * 成功,傳入數(shù)據(jù) * @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); } /** * 自定義狀態(tài)碼和錯(cuò)誤信息 * @param code * @param msg * @return */ public static BaseResponse buildCodeAndMsg(int code, String msg) { return new BaseResponse(code, null, msg); } }
(5)創(chuàng)建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>
? 創(chuàng)建SpringBoot運(yùn)行主類
@SpringBootApplication public class CouponApplication { public static void main(String[] objArgs) { SpringApplication.run(CouponApplication.class, objArgs); } }
? 創(chuàng)建application.properties
# 定義服務(wù)名 spring.application.name=coupon-server # 定義服務(wù)端口 server.port=8081 # 定義GRPC端口 grpc.server.port=8071
? 測(cè)試啟動(dòng)
(6)創(chuàng)建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>
? 創(chuàng)建SpringBoot運(yùn)行主類
@SpringBootApplication public class ProductApplication { public static void main(String[] objArgs) { SpringApplication.run(ProductApplication.class, objArgs); } }
? 創(chuàng)建application.properties
# 定義服務(wù)名 spring.application.name=product-server # 定義服務(wù)端口 server.port=8083 # 定義GRPC端口 grpc.server.port=8073
? 測(cè)試運(yùn)行
? 這里我們需要注意一點(diǎn),我們?cè)趦?yōu)惠券服務(wù)和商品服務(wù)都定義了一個(gè)grpc.server.port,這個(gè)是用來提供給其他服務(wù)調(diào)用是寫的端口,不要和springboot本身的server.port重復(fù),否則會(huì)報(bào)錯(cuò)。
(7)創(chuàng)建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>
? 創(chuàng)建SpringBoot運(yùn)行主類
@SpringBootApplication public class OrderApplication { public static void main(String[] objArgs) { SpringApplication.run(OrderApplication.class, objArgs); } }
? 創(chuàng)建application.properties
# 定義服務(wù)名 spring.application.name=order-server # 定義服務(wù)端口 server.port=8082 # 定義GRPC端口 grpc.server.port=8072 # 定義調(diào)用商品服務(wù)的GRPC product.server.address=localhost:8073 # 定義調(diào)用優(yōu)惠券服務(wù)的GRPC coupon.server.address=localhost:8071
6.SpringBoot整合GRPC業(yè)務(wù)開發(fā)
? ok,到目前我們所有的環(huán)境準(zhǔn)備已經(jīng)完成了,那么下面我們就開始進(jìn)入到業(yè)務(wù)的開發(fā),首先我們?cè)谙聠蔚臅r(shí)候,會(huì)傳入一個(gè)商品ID和一個(gè)優(yōu)惠券ID,那么我們要通過這兩個(gè)ID去對(duì)應(yīng)的服務(wù)查出具體的詳細(xì)信息。
(1)開發(fā)coupon-server根據(jù)ID獲取優(yōu)惠卷詳情信息
? 準(zhǔn)備CouponServer.proto文件,寫在common模塊中
? CouponServer.proto內(nèi)容如下:
/** * 編譯工具版本 */ syntax = "proto3"; /** * 指定生成實(shí)體 */ option java_multiple_files = true; /** * 指定生成接口 */ option java_generic_services = true; /** * 聲明包 */ package com.lixiang.grpc.server; /** * 商品服務(wù)proto文件 */ option java_outer_classname = "CouponServer"; /** * 統(tǒng)一返回實(shí)體 */ message CouponServerResponse { string message = 1; int32 code = 2; string data=3; } /** * 聲明接口 */ service CouponService { rpc deductProductInventory (DeductCouponRequest) returns (CouponServerResponse); } /** * 聲明扣減商品庫(kù)存實(shí)體 */ message DeductCouponRequest { int32 couponId = 1; }
? 編寫好proto文件點(diǎn)開maven運(yùn)行protobuf插件
? 運(yùn)行好之后,會(huì)發(fā)現(xiàn)target下多了一個(gè)這個(gè)包
? 然后在coupon-server中開始編寫獲取優(yōu)惠券的方法,這塊我們先模擬一些優(yōu)惠券的數(shù)據(jù)。
/** * 優(yōu)惠券實(shí)體類 */ @Data public class Coupon { /** * 優(yōu)惠券ID */ private Integer id; /** * 優(yōu)惠券金額 */ private BigDecimal amount; /** * 滿減額度 */ private BigDecimal fullReduction; /** * 優(yōu)惠券名稱 */ private String name; /** * 優(yōu)惠券可用開始時(shí)間 */ private LocalDateTime startTime; /** * 優(yōu)惠券結(jié)束時(shí)間 */ 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); } /** * 獲取具體某個(gè)優(yōu)惠券 * @param couponId * @return */ public static Coupon getCouponById(Integer couponId){ return couponList.stream().filter(obj-> Objects.equals(obj.getId(), couponId)).collect(Collectors.toList()).get(0); } }
? 創(chuàng)建CouponGrpcServer,繼承CouponServiceGrpc.CouponServiceImplBase,CouponServiceGrpc.CouponServiceImplBase這個(gè)類是剛剛運(yùn)行插件自動(dòng)生成的。
@GrpcService public class CouponGrpcServer extends CouponServiceGrpc.CouponServiceImplBase { @Override public void deductProductInventory(DeductCouponRequest request, StreamObserver<CouponServerResponse> responseObserver) { int couponId = request.getCouponId(); //查找優(yōu)惠券詳細(xì)信息 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,這樣我們一個(gè)根據(jù)ID查詢優(yōu)惠券的信息就定義好了。
? 整體的目錄結(jié)構(gòu):
(2)開發(fā)product-server根據(jù)ID獲取優(yōu)惠卷詳情信息
? product-server和coupon-server是一樣的,這里就不一一細(xì)說了,直接上代碼。
/** * 商品實(shí)體類 */ @Data public class Product { /** * 商品ID */ private Integer id; /** * 商品名稱 */ private String name; /** * 商品價(jià)格 */ private BigDecimal price; }
public class ProductUtil { private final static List<Product> productList = new ArrayList<>(); static { Product product1 = new Product(); product1.setId(1); product1.setName("互聯(lián)網(wǎng)JAVA架構(gòu)師養(yǎng)成零基礎(chǔ)到精通"); product1.setPrice(new BigDecimal(12999)); Product product2 = new Product(); product2.setId(2); product2.setName("Python+大數(shù)據(jù)零基礎(chǔ)到精通"); product2.setPrice(new BigDecimal(15999)); Product product3 = new Product(); product3.setId(3); product3.setName("5G云計(jì)算運(yùn)維架構(gòu)零基礎(chǔ)到精通"); product3.setPrice(new BigDecimal(10999)); productList.add(product1); productList.add(product2); productList.add(product3); } /** * 根據(jù)商品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(); } }
? 目錄結(jié)構(gòu):
(3)開發(fā)order-server模塊統(tǒng)一下單接口
? 首先我們要先配置一下GRPC的地址
? 配置GrpcClientConfig
/** * @description Grpc Client 配置類 * @author lixiang */ @Configuration public class GrpcClientConfig { /** * 商品服務(wù)地址 */ @Value("${product.server.address}") private String productServerAddress; /** * 優(yōu)惠券服務(wù)地址 */ @Value("${coupon.server.address}") private String couponServerAddress; /** * 商品服務(wù)grpc-client * @return */ @Bean public ProductServiceGrpc.ProductServiceBlockingStub getProductServerClient() { return ProductServiceGrpc.newBlockingStub(ManagedChannelBuilder.forTarget(productServerAddress).usePlaintext().build()); } /** * 優(yōu)惠卷服務(wù)grpc-client * @return */ @Bean public CouponServiceGrpc.CouponServiceBlockingStub getCouponServerClient() { return CouponServiceGrpc.newBlockingStub(ManagedChannelBuilder.forTarget(couponServerAddress).usePlaintext().build()); } }
? 編寫統(tǒng)一下單接口請(qǐng)求類
@Data public class OrderConfirmRequest { /** * 支付價(jià)格 */ private BigDecimal totalAmount; /** * 支付類型 */ private String payType; /** * 支付商品的ID */ private Integer productId; /** * 支付時(shí)用的優(yōu)惠券ID */ private Integer couponRecordId; }
? 編寫統(tǒng)一下單接口
@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(); } }
? 編寫統(tǒng)一下單業(yè)務(wù)類
public interface OrderService { /** * 統(tǒng)一下單接口 * @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.調(diào)用優(yōu)惠券服務(wù)獲取優(yōu)惠券詳情 DeductCouponRequest deductCouponRequest = DeductCouponRequest.newBuilder() .setCouponId(orderConfirmRequest.getCouponRecordId()) .build(); CouponServerResponse couponServerResponse = couponService.deductProductInventory(deductCouponRequest); String couponDataStr = couponServerResponse.getData(); JSONObject couponData = JSON.parseObject(couponDataStr); log.info("調(diào)用優(yōu)惠卷服務(wù)獲取的優(yōu)惠券信息:{}",couponData); //2.調(diào)用商品服務(wù)獲取商品詳細(xì)信息 DeductInventoryRequest deductInventoryRequest = DeductInventoryRequest.newBuilder() .setProductId(orderConfirmRequest.getProductId()) .build(); ProductServerResponse productServerResponse = productService.deductProductInventory(deductInventoryRequest); String productDataStr = productServerResponse.getData(); JSONObject productData = JSON.parseObject(productDataStr); log.info("調(diào)用商品服務(wù)獲取的商品信息:{}",productData); //3.判斷優(yōu)惠券是否在使用時(shí)間范圍內(nèi) 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("當(dāng)前優(yōu)惠券不在可用范圍內(nèi)"); } //4.驗(yàn)證價(jià)格 BigDecimal amount = couponData.getBigDecimal("amount"); BigDecimal price = productData.getBigDecimal("price"); if(!price.subtract(amount).equals(orderConfirmRequest.getTotalAmount())){ throw new RuntimeException("訂單驗(yàn)價(jià)失敗"); } //5.生成訂單 log.info("當(dāng)前訂單購(gòu)買的商品為:{},原價(jià)為:{},本次消耗優(yōu)惠券:{},實(shí)際支付金額:{}", productData.getString("name"),productData.getBigDecimal("price"),couponData.getString("name"),orderConfirmRequest.getTotalAmount()); } }
? 測(cè)試全流程,啟動(dòng)三個(gè)微服務(wù)
? 至此,由GRPC整合微服務(wù),實(shí)現(xiàn)遠(yuǎn)程通信就已經(jīng)完成了,當(dāng)然下單業(yè)務(wù)不止這么簡(jiǎn)單,中間還有防重提交,調(diào)用三方支付,延時(shí)關(guān)單等等一些列復(fù)雜的業(yè)務(wù),這里只是給大家演示一下怎末用GRPC代替feign實(shí)現(xiàn)微服務(wù)之間的遠(yuǎn)程通信。
到此這篇關(guān)于SpringBoot整合GRPC微服務(wù)遠(yuǎn)程通信的實(shí)現(xiàn)示例的文章就介紹到這了,更多相關(guān)SpringBoot GRPC遠(yuǎn)程通信內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot整合WebSocket實(shí)現(xiàn)后端向前端發(fā)送消息的實(shí)例代碼
WebSocket使得客戶端和服務(wù)器之間的數(shù)據(jù)交換變得更加簡(jiǎn)單,允許服務(wù)端主動(dòng)向客戶端推送數(shù)據(jù),下面這篇文章主要給大家介紹了關(guān)于SpringBoot整合WebSocket實(shí)現(xiàn)后端向前端發(fā)送消息的相關(guān)資料,需要的朋友可以參考下2023-03-03Struts攔截器實(shí)現(xiàn)攔截未登陸用戶實(shí)例解析
這篇文章主要介紹了Struts攔截器實(shí)現(xiàn)攔截未登陸用戶實(shí)例解析,分享了相關(guān)代碼示例,小編覺得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下2018-02-02springMVC使用ajaxFailUpload上傳圖片的方法
這篇文章主要介紹了springMVC使用ajaxFailUpload上傳圖片的相關(guān)知識(shí),代碼簡(jiǎn)單易懂,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-06-06Java語(yǔ)法基礎(chǔ)之for語(yǔ)句練習(xí)
以下是對(duì)Java語(yǔ)法基礎(chǔ)中的for語(yǔ)句進(jìn)行了詳細(xì)介紹,需要的朋友可以過來參考下2013-07-07org.apache.ibatis.annotations不存在的問題
這篇文章主要介紹了org.apache.ibatis.annotations不存在的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10java數(shù)據(jù)結(jié)構(gòu)基礎(chǔ):線性表
這篇文章主要介紹了Java的數(shù)據(jù)解構(gòu)基礎(chǔ),希望對(duì)廣大的程序愛好者有所幫助,同時(shí)祝大家有一個(gè)好成績(jī),需要的朋友可以參考下,希望能給你帶來幫助2021-07-07Java 中文字符按Unicode排序的實(shí)現(xiàn)方法
這篇文章主要介紹了Java 中文字符按Unicode排序的實(shí)現(xiàn)方法,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-10-10Springboot參數(shù)校驗(yàn)之分組校驗(yàn)、嵌套校驗(yàn)的實(shí)現(xiàn)
日常開發(fā)中,免不了需要對(duì)請(qǐng)求參數(shù)進(jìn)行校驗(yàn),諸如判空,長(zhǎng)度,正則,集合等,復(fù)雜一點(diǎn)的請(qǐng)求參數(shù)可能會(huì)包含嵌套,分組校驗(yàn),本文就詳細(xì)的介紹一下,感興趣的可以了解一下2023-08-08