在前面的文章之中我們介紹了基于Kubernetes及Istio如何一步一步把Service Mesh微服務(wù)架構(gòu)玩起來(lái)!在該文章中,我們演示了一個(gè)非常貼近實(shí)戰(zhàn)的案例,這里回顧下該案例的結(jié)構(gòu),如下圖所示:
該案例所演示的就是我們?nèi)粘J褂梦⒎?wù)架構(gòu)開(kāi)發(fā)時(shí),服務(wù)間最普遍的通信場(chǎng)景。在Spring Cloud微服務(wù)體系中,服務(wù)間可以通過(guò)Fegin+Ribbon組合的方式,實(shí)現(xiàn)服務(wù)間負(fù)載均衡方式的Http接口調(diào)用;但在Service Mesh架構(gòu)中,服務(wù)發(fā)現(xiàn)及負(fù)載均衡等治理邏輯已經(jīng)由SideCar代理,如果還希望延續(xù)Spring Cloud場(chǎng)景下服務(wù)間接口調(diào)用的代碼體驗(yàn),一般可以通過(guò)改寫(xiě)Feign組件,去掉其中關(guān)于服務(wù)治理的邏輯,只保留簡(jiǎn)單的接口聲明式調(diào)用邏輯來(lái)實(shí)現(xiàn)。
上述案例中“micro-api->micro-order”之間的服務(wù)通信調(diào)用,就是基于該方式實(shí)現(xiàn)的(可參考之前的文章)。但在微服務(wù)架構(gòu)中除了采用Http協(xié)議通信外,對(duì)于某些對(duì)性能有著更高要求的系統(tǒng)來(lái)說(shuō),采用通信效率更高的RPC協(xié)議往往是更合適的選擇!
在基于Spring Cloud框架的微服務(wù)體系中,服務(wù)之間也可以通過(guò)RPC協(xié)議通信,但由于服務(wù)治理的需要,也需要一套類似于Fegin+Ribbon組合的SDK支持。例如gRPC框架就有針對(duì)Spring Boot框架的“grpc-client-spring-boot-starter”依賴支持!該項(xiàng)目是一個(gè) gRPC 的 Spring Boot 模塊,可以在 Spring Boot 中內(nèi)嵌一個(gè) gRPC Server 對(duì)外提供服務(wù),并支持 Spring Cloud 的服務(wù)發(fā)現(xiàn)、注冊(cè)、鏈路跟蹤等等。
那么在Service Mesh微服務(wù)體系下,服務(wù)間基于gRPC框架的通信應(yīng)該怎么實(shí)現(xiàn)呢?接下來(lái),我將以案例中"micro-order->micro-pay"之間的服務(wù)調(diào)用為例,演示在Service Mesh微服務(wù)架構(gòu)下實(shí)現(xiàn)服務(wù)間的gRPC通信調(diào)用,并將案例中Http+gRPC服務(wù)間通信的完整場(chǎng)景串起來(lái)!
gRPC概述
在演示Service Mesh微服務(wù)架構(gòu)下的gRPC通信場(chǎng)景之前,我們先簡(jiǎn)單介紹下RPC協(xié)議及gRPC框架的基本知識(shí)。
RPC(Remote Procedure Call),又稱遠(yuǎn)程過(guò)程調(diào)用,是一種通過(guò)掩藏底層網(wǎng)絡(luò)通信復(fù)雜性,從而屏蔽遠(yuǎn)程和本地調(diào)用區(qū)別的通信方式。相比于Http協(xié)議,RPC協(xié)議屬于一種自定義的TCP協(xié)議,從而在實(shí)現(xiàn)時(shí)避免了一些Http協(xié)議信息的臃腫問(wèn)題,實(shí)現(xiàn)了更高效率的通信。
在主流實(shí)現(xiàn)RPC協(xié)議的框架中,比較著名的有Dubbo、Thrift及gRPC等。因?yàn)槟壳爸髁鞯娜萜靼l(fā)布平臺(tái)Kubernetes,以及Service Mesh開(kāi)源平臺(tái)Istio都是通過(guò)gRPC協(xié)議來(lái)實(shí)現(xiàn)內(nèi)部組件之間的交互,所以在Service Mesh微服務(wù)架構(gòu)中,服務(wù)間通信采用gRPC協(xié)議,從某種角度上說(shuō)會(huì)更具有原生優(yōu)勢(shì)。況且在此之前,gRPC框架已經(jīng)在分布式、多語(yǔ)言服務(wù)場(chǎng)景中得到了大量應(yīng)用,因此可以預(yù)測(cè)在Service Mesh微服務(wù)架構(gòu)場(chǎng)景下,基于gRPC框架的微服務(wù)通信方式會(huì)逐步成為主流。
gRPC是Google發(fā)布的基于HTTP/2.0傳輸層協(xié)議承載的高性能開(kāi)源軟件框架,提供了支持多種編程語(yǔ)言的、對(duì)網(wǎng)絡(luò)設(shè)備進(jìn)行配置和納管的方法。由于是開(kāi)源框架,通信的雙方可以進(jìn)行二次開(kāi)發(fā),所以客戶端和服務(wù)器端之間的通信會(huì)更加專注于業(yè)務(wù)層面的內(nèi)容,減少了對(duì)由gRPC框架實(shí)現(xiàn)的底層通信的關(guān)注。
接下來(lái)的內(nèi)容就具體演示在Service Mesh微服務(wù)架構(gòu)下,實(shí)現(xiàn)微服務(wù)“micro-order->micro-pay”的gRPC通信調(diào)用!
構(gòu)建gRPC服務(wù)端程序(micro-pay)
首先從gRPC服務(wù)端的角度,在微服務(wù)micro-pay項(xiàng)目中集成gRPC-Java,并實(shí)現(xiàn)一個(gè)gRPC服務(wù)端程序。具體如下:
1、構(gòu)建Spring Boot基本工程(micro-pay/micro-pay-client)
使用Spring Boot框架構(gòu)建基本的Maven工程,為了工程代碼的復(fù)用,這里單獨(dú)抽象一個(gè)micro-pay-client工程,并定義micro-pay微服務(wù)gRPC服務(wù)接口的protobuf文件(*/proto/paycore.proto),代碼如下:
syntax = "proto3";
package com.wudimanong.pay.client;
option java_multiple_files = true;
option java_package = "com.wudimanong.micro.pay.proto";
service PayService {
//定義支付rpc方法
rpc doPay (PayRequest) returns (PayResponse);
}
message PayRequest {
string orderId = 1;
int32 amount=2;
}
message PayResponse {
int32 status = 1;
}
如上所示,創(chuàng)建了一個(gè)基于protobuf協(xié)議的支付接口定義文件,其中定義了支付服務(wù)PayService及其中的doPay支付rpc方法,并定義了其請(qǐng)求和返回參數(shù)對(duì)象,具體的語(yǔ)法遵循“proto3”協(xié)議。
為了能夠正常編譯和生成protobuf文件所定義服務(wù)接口的代碼,需要在項(xiàng)目pom.xml文件中引入jar包依賴及Maven編譯插件配置,代碼如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
....
<dependencies>
....
<!--gRPC通信類庫(kù)(截止目前的最新版本)-->
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-all</artifactId>
<version>1.36.1</version>
</dependency>
</dependencies>
<build>
<!--引入gRpc框架proto文件編譯生產(chǎn)插件-->
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.6.2</version>
</extension>
</extensions>
<plugins>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.6.1</version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:3.12.0:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:1.36.0:exe:${os.detected.classifier}</pluginArtifact>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
這是單獨(dú)關(guān)于gRPC接口proto文件定義的工程,定義后編譯工程,maven就會(huì)根據(jù)前面定義的paycore.proto文件生成gRPC服務(wù)端/客戶端相關(guān)代碼。
完成后,繼續(xù)構(gòu)建micro-pay微服務(wù)的spring boot工程代碼,并在其pom.xml文件中引入上述gRPC協(xié)議文件定義的依賴,例如:
<!--引入支付服務(wù)gRPC ProtoBuf定義依賴-->
<dependency>
<groupId>com.wudimanong</groupId>
<artifactId>micro-pay-client</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
在micro-pay-client工程中所引入的gRPC相關(guān)的依賴及插件配置會(huì)自動(dòng)繼承至micro-pay工程!
2、編寫(xiě)gRPC支付服務(wù)代碼
在micro-pay代碼工程中創(chuàng)建一個(gè)PayCoreProvider接口代碼,用于表示支付gRPC服務(wù)的入口(類似于Controller),其代碼如下:
package com.wudimanong.micro.pay.provider;
import com.wudimanong.micro.pay.proto.PayRequest;
import com.wudimanong.micro.pay.proto.PayResponse;
import com.wudimanong.micro.pay.proto.PayServiceGrpc;
import io.grpc.stub.StreamObserver;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class PayCoreProvider extends PayServiceGrpc.PayServiceImplBase {
/**
* 實(shí)現(xiàn)ProtoBuf中定義的服務(wù)方法
*
* @param request
* @param responseStreamObserver
*/
@Override
public void doPay(PayRequest request, StreamObserver<PayResponse> responseStreamObserver) {
//邏輯處理(簡(jiǎn)單模擬打印日志)
log.info("處理gRPC支付處理請(qǐng)求,orderId->{};payAmount{}", request.getOrderId(), request.getAmount());
//構(gòu)建返回對(duì)象(構(gòu)建處理狀態(tài))
PayResponse response = PayResponse.newBuilder().setStatus(2).build();
//設(shè)置數(shù)據(jù)響應(yīng)
responseStreamObserver.onNext(response);
responseStreamObserver.onCompleted();
}
}
上述代碼所引入的一些依賴代碼如PayServiceGrpc等,就是前面定義paycore.proto文件所生成的樁文件代碼!由于只是簡(jiǎn)單測(cè)試,這里僅僅打印了下日志就返回了,如果涉及復(fù)雜業(yè)務(wù)還是可以按照MVC分層架構(gòu)思想進(jìn)行代碼拆分!
3、編寫(xiě)gRPC與Spring Boot框架集成配置代碼
在Spring Cloud微服務(wù)中集成gRPC可以通過(guò)前面提到的“grpc-client-spring-boot-starter”來(lái)實(shí)現(xiàn),但目前還沒(méi)有現(xiàn)成的支持Service Mesh架構(gòu)下的集成SDK,所以這里通過(guò)手工配置定義的方式實(shí)現(xiàn)集成。先創(chuàng)建一個(gè)配置類,代碼如下:
package com.wudimanong.micro.pay.config;
import com.wudimanong.micro.pay.provider.PayCoreProvider;
import io.grpc.Server;
import io.grpc.ServerBuilder;
import java.io.IOException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class GrpcServerConfiguration {
@Autowired
PayCoreProvider service;
/**
* 注入配置文件中的端口信息
*/
@Value("${grpc.server-port}")
private int port;
private Server server;
public void start() throws IOException {
// 構(gòu)建服務(wù)端
log.info("Starting gRPC on port {}.", port);
server = ServerBuilder.forPort(port).addService(service).build().start();
log.info("gRPC server started, listening on {}.", port);
// 添加服務(wù)端關(guān)閉的邏輯
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
log.info("Shutting down gRPC server.");
GrpcServerConfiguration.this.stop();
log.info("gRPC server shut down successfully.");
}));
}
private void stop() {
if (server != null) {
// 關(guān)閉服務(wù)端
server.shutdown();
}
}
public void block() throws InterruptedException {
if (server != null) {
// 服務(wù)端啟動(dòng)后直到應(yīng)用關(guān)閉都處于阻塞狀態(tài),方便接收請(qǐng)求
server.awaitTermination();
}
}
}
如上所示,在該配置代碼中,通過(guò)gRPC-Java依賴所提供的Server對(duì)象構(gòu)建了gRPC服務(wù)端啟動(dòng)、停止、阻塞的方法,并在啟動(dòng)時(shí)將前面定義的服務(wù)端類通過(guò)“.addService()”方法進(jìn)行了加入(可考慮封裝更優(yōu)雅的方式)!
為了讓該配置類與Spring Boot集成,再定義一個(gè)集成類,代碼如下:
package com.wudimanong.micro.pay.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class GrpcCommandLineRunner implements CommandLineRunner {
@Autowired
GrpcServerConfiguration configuration;
@Override
public void run(String... args) throws Exception {
configuration.start();
configuration.block();
}
}
上述代碼會(huì)在Spring Boot應(yīng)用啟動(dòng)時(shí)自動(dòng)加載,其中的邏輯就是啟動(dòng)gRPC服務(wù),并阻塞等待連接!
接下來(lái)在配置文件中定義服務(wù)所開(kāi)啟的gRPC端口,配置如下:
spring:
application:
name: micro-pay
server:
port: 9092
#定義gRPC服務(wù)開(kāi)放的端口
grpc:
server-port: 18888
該配置所定義的參數(shù)在前面的服務(wù)配置類中引用,表示gRPC服務(wù)開(kāi)啟的端口,這里定義的是18888!
到這里gRPC服務(wù)端工程代碼就構(gòu)建完成了,從整體上看就是Spring Boot+gRPC的集成與整合,這其中沒(méi)有引入Spring Boot定制的gRPC集成SDK,目的在于避免其中所涉及的客戶端服務(wù)治理邏輯(與前面Http調(diào)用不直接引入Open Feign一樣)。
構(gòu)建gRPC客戶端程序(micro-order)
接下來(lái)我們改造micro-order微服務(wù),使其成為調(diào)用micro-pay微服務(wù)的gRPC客戶端程序!
1、引入gRPC客戶端依賴包
引入前面定義micro-pay gRPC服務(wù)時(shí)構(gòu)建的micro-pay-client protobuf工程依賴,代碼如下:
<!--引入支付服務(wù)gRPC ProtoBuf定義依賴-->
<dependency>
<groupId>com.wudimanong</groupId>
<artifactId>micro-pay-client</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
2、業(yè)務(wù)邏輯中實(shí)現(xiàn)gRPC服務(wù)調(diào)用
接下來(lái)在micro-order邏輯中調(diào)用gRPC支付服務(wù),代碼示例如下:
@Slf4j
@Service
public class OrderServiceImpl implements OrderService {
/**
* 引入gRPC客戶端配置依賴
*/
@Autowired
GrpcClientConfiguration gRpcClent;
@Override
public CreateOrderBO create(CreateOrderDTO createOrderDTO) {
log.info("現(xiàn)在開(kāi)始處理下單請(qǐng)求.....");
//生成訂單號(hào)
String orderId = String.valueOf(new Random(100).nextInt(100000) + System.currentTimeMillis());
//構(gòu)建支付請(qǐng)求(gRPC調(diào)用)
PayRequest payRequest = PayRequest.newBuilder().setOrderId(orderId).setAmount(createOrderDTO.getAmount())
.build();
//使用stub發(fā)送請(qǐng)求到服務(wù)端
PayResponse payResponse = gRpcClent.getStub().doPay(payRequest);
log.info("pay gRpc response->" + payResponse.toString());
return CreateOrderBO.builder().orderId(orderId).status(payResponse.getStatus()).build();
}
}
如上所示,該業(yè)務(wù)邏輯在接收micro-api通過(guò)Http調(diào)用的請(qǐng)求后,會(huì)在邏輯實(shí)現(xiàn)過(guò)程中通過(guò)gRPC協(xié)議訪問(wèn)支付服務(wù),其中涉及的接口定義代碼,由protobuf文件所定義!
3、gRPC客戶端配置
上述邏輯是通過(guò)定義“GrpcClientConfiguration”gRPC客戶端配置類來(lái)實(shí)現(xiàn)gRPC服務(wù)調(diào)用的,該配置類代碼如下:
@Slf4j
@Component
public class GrpcClientConfiguration {
/**
* 支付gRPC Server的地址
*/
@Value("${server-host}")
private String host;
/**
* 支付gRPC Server的端口
*/
@Value("${server-port}")
private int port;
private ManagedChannel channel;
/**
* 支付服務(wù)stub對(duì)象
*/
private PayServiceGrpc.PayServiceBlockingStub stub;
public void start() {
//開(kāi)啟channel
channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext().build();
//通過(guò)channel獲取到服務(wù)端的stub
stub = PayServiceGrpc.newBlockingStub(channel);
log.info("gRPC client started, server address: {}:{}", host, port);
}
public void shutdown() throws InterruptedException {
//調(diào)用shutdown方法后等待1秒關(guān)閉channel
channel.shutdown().awaitTermination(1, TimeUnit.SECONDS);
log.info("gRPC client shut down successfully.");
}
public PayServiceGrpc.PayServiceBlockingStub getStub() {
return this.stub;
}
}
如上所示配置代碼,通過(guò)依服務(wù)配置文件指定的gRPC服務(wù)端地址+端口,實(shí)現(xiàn)對(duì)gRPC客戶端的配置,其中主要包括啟動(dòng)和停止方法,并在啟動(dòng)的過(guò)程中初始化gRPC服務(wù)客戶端的樁代碼的實(shí)例(可考慮更優(yōu)雅地實(shí)現(xiàn))。
在該配置類中所依賴的gRPC服務(wù)端地址+端口配置,依賴于服務(wù)配置文件的定義,代碼如下:
spring:
application:
name: micro-order
server:
port: 9091
#支付微服務(wù)Grpc服務(wù)地址、端口配置
server-host: ${grpc_server_host}
server-port: ${grpc_server_port}
如果是本地測(cè)試可以直接指定grpc_server_host及端口的值,但在Service Mesh微服務(wù)架構(gòu)中,直接在應(yīng)用的配置文件中指定其他微服務(wù)的地址及端口可能并不是很靈活,這個(gè)配置信息將在發(fā)布Kubernetes集群時(shí),通過(guò)Kubernetes發(fā)布文件注入!
為了讓gRPC客戶端配置與Spring Boot集成,這里也需要定義一個(gè)Spring Boot加載類,代碼如下:
@Component
@Slf4j
public class GrpcClientCommandLineRunner implements CommandLineRunner {
@Autowired
GrpcClientConfiguration configuration;
@Override
public void run(String... args) throws Exception {
//開(kāi)啟gRPC客戶端
configuration.start();
//添加客戶端關(guān)閉的邏輯
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
try {
configuration.shutdown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}));
}
}
該代碼將在Spring Boot應(yīng)用自動(dòng)時(shí)自動(dòng)加載!到這里micro-order gRPC客戶端配置就完成了!
將部署服務(wù)至Service Mesh架構(gòu)環(huán)境
前面基于“micro-order->micro-pay”微服務(wù)間的gRPC調(diào)用場(chǎng)景,分別將兩個(gè)微服務(wù)改造成了gRPC服務(wù)端/客戶端。但此時(shí)從代碼上是很難看出來(lái)它們二者之間應(yīng)該怎么實(shí)現(xiàn)調(diào)用!而這也恰恰就印證了Service Mesh架構(gòu)的優(yōu)勢(shì),服務(wù)的發(fā)現(xiàn)、及負(fù)載均衡調(diào)用之類的服務(wù)治理邏輯,已經(jīng)完全不用微服務(wù)自己管了!
在Istio中,它們是基于Kubernetes的Service發(fā)現(xiàn)機(jī)制+Istio-proxy(SideCar代理)來(lái)實(shí)現(xiàn)的。而具體的操作就是通過(guò)微服務(wù)Kubernetes服務(wù)發(fā)布文件的定義,接下來(lái)分別定義micro-order及micro-pay的Kubernetes發(fā)布文件。
先看下作為gRPC服務(wù)端的micro-pay的發(fā)布文件(micro-pay.yaml),代碼如下:
apiVersion: v1
kind: Service
metadata:
name: micro-pay
labels:
app: micro-pay
service: micro-pay
spec:
type: ClusterIP
ports:
- name: http
#容器暴露端口
port: 19092
#目標(biāo)應(yīng)用端口
targetPort: 9092
#設(shè)置gRPC端口
- name: grpc
port: 18888
targetPort: 18888
selector:
app: micro-pay
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: micro-pay-v1
labels:
app: micro-pay
version: v1
spec:
replicas: 2
selector:
matchLabels:
app: micro-pay
version: v1
template:
metadata:
labels:
app: micro-pay
version: v1
spec:
containers:
- name: micro-pay
image: 10.211.55.2:8080/micro-service/micro-pay:1.0-SNAPSHOT
imagePullPolicy: Always
tty: true
ports:
- name: http
protocol: TCP
containerPort: 19092
#指定服務(wù)gRPC端口
- name: grpc
protocol: TCP
containerPort: 18888
如上所示k8s發(fā)布文件,主要是定義了Service服務(wù)訪問(wèn)資源及Deployment容器編排資源,這兩種資源都是Kubernetes的資源類型,在容器編排資源和服務(wù)資源中分別定義了gRPC的訪問(wèn)端口,通過(guò)這種設(shè)置,后續(xù)gRPC客戶端通過(guò)Service資源訪問(wèn)服務(wù)時(shí),就能夠進(jìn)行端口映射了!
而其他配置則是基本的Kubernetes發(fā)布部署邏輯,其中涉及的鏡像,需要在發(fā)布之前,通過(guò)構(gòu)建的方式對(duì)項(xiàng)目進(jìn)行Docker鏡像打包并上傳私有鏡像倉(cāng)庫(kù)(如果有疑問(wèn),可以參考本號(hào)之前的文章)。
接下來(lái)繼續(xù)看看作為gRPC客戶端的micro-order微服務(wù)的k8s發(fā)布文件(micro-order.yaml),代碼如下:
apiVersion: v1
kind: Service
metadata:
name: micro-order
labels:
app: micro-order
service: micro-order
spec:
type: ClusterIP
ports:
- name: http
#此處設(shè)置80端口的原因在于改造的Mock FeignClient代碼默認(rèn)是基于80端口進(jìn)行服務(wù)調(diào)用
port: 80
targetPort: 9091
selector:
app: micro-order
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: micro-order-v1
labels:
app: micro-order
version: v1
spec:
replicas: 2
selector:
matchLabels:
app: micro-order
version: v1
template:
metadata:
labels:
app: micro-order
version: v1
spec:
containers:
- name: micro-order
image: 10.211.55.2:8080/micro-service/micro-order:1.0-SNAPSHOT
imagePullPolicy: Always
tty: true
ports:
- name: http
protocol: TCP
containerPort: 19091
#環(huán)境參數(shù)設(shè)置(設(shè)置微服務(wù)返回gRPC服務(wù)端的地址+端口)
env:
- name: GRPC_SERVER_HOST
value: micro-pay
- name: GRPC_SERVER_PORT
value: "18888"
在該發(fā)布文件中,需要說(shuō)明的主要就是通過(guò)容器env環(huán)境參數(shù)的設(shè)置,指定了之前gRPC客戶端服務(wù)配置中所依賴的參數(shù)變量“GRPC_SERVER_HOST及GRPC_SERVER_PORT”,其中服務(wù)地址就是micro-pay微服務(wù)在Kubernetes中Service資源定義的名稱,端口則是gRPC服務(wù)端所開(kāi)啟的端口。
這樣在gRPC客戶端在Kubernetes集群中根據(jù)Service名稱發(fā)起微服務(wù)調(diào)用時(shí),Kubernetes集群自身的服務(wù)發(fā)現(xiàn)邏輯就能自動(dòng)將請(qǐng)求映射到相應(yīng)的Pod資源了!這其實(shí)就是Service Mesh微服務(wù)架構(gòu)服務(wù)發(fā)現(xiàn)的基本邏輯!
接下來(lái)將微服務(wù)進(jìn)行發(fā)布,這里假設(shè)你已經(jīng)部署了一套Kubernetes集群并安裝了基于Istio的Service Mesh微服務(wù)架構(gòu)環(huán)境,最終的部署效果如下所示:
root@kubernetes:/opt/istio/istio-1.8.4# kubectl get pods
NAME READY STATUS RESTARTS AGE
micro-api-6455654996-9lsxr 2/2 Running 2 43m
micro-order-v1-744d469d84-rnqq8 2/2 Running 0 6m28s
micro-order-v1-744d469d84-vsn5m 2/2 Running 0 6m28s
micro-pay-v1-7fd5dd4768-txq9d 2/2 Running 0 43s
micro-pay-v1-7fd5dd4768-wqw6b 2/2 Running 0 43s
如上所示,可以看到案例所涉及的微服務(wù)都被部署了,并且對(duì)應(yīng)的SideCar代理(istio-proxy)也被正常啟動(dòng)了!為了演示負(fù)載均衡效果,這里micro-order及micro-pay都分別被部署了兩個(gè)副本!
微服務(wù)多副本負(fù)載均衡調(diào)用演示
如果環(huán)境都沒(méi)啥問(wèn)題,此時(shí)可以通過(guò)調(diào)用Istio Gateway來(lái)訪問(wèn)micro-api服務(wù),然后micro-api服務(wù)會(huì)通過(guò)Http的方式訪問(wèn)micro-order服務(wù),之后micro-order服務(wù)通過(guò)gRPC協(xié)議調(diào)用micro-pay服務(wù)。
通過(guò)curl命令訪問(wèn)Istio Gateway網(wǎng)關(guān)服務(wù),效果如下:
curl -H "Content-Type:application/json" -H "Data_Type:msg" -X POST --data '{"businessId": "202012102", "amount": 100, "channel": 2}' http://10.211.55.12:30844/api/order/create
如果正常返回響應(yīng)結(jié)果,則說(shuō)明上述調(diào)用鏈路走通了!此時(shí)分別通過(guò)觀察服務(wù)的業(yè)務(wù)日志和istio-proxy代理日志來(lái)加以觀測(cè)!
其中micro-pay兩個(gè)實(shí)例(PodA~PodB)業(yè)務(wù)日志信息:
//支付微服務(wù)接口訪問(wèn)日志(POD-A)
root@kubernetes:~# kubectl logs micro-pay-v1-7fd5dd4768-txq9d micro-pay
....
2021-04-01 14:46:15.818 INFO 1 --- [ main] c.w.m.p.config.GrpcServerConfiguration : Starting gRPC on port 18888.
2021-04-01 14:46:18.859 INFO 1 --- [ main] c.w.m.p.config.GrpcServerConfiguration : gRPC server started, listening on 18888.
2021-04-01 15:07:36.709 INFO 1 --- [ault-executor-0] c.w.micro.pay.provider.PayCoreProvider : 處理gRPC支付處理請(qǐng)求,orderId->1617289656289;payAmount100
//支付微服務(wù)接口訪問(wèn)日志(POD-B)
root@kubernetes:~# kubectl logs micro-pay-v1-7fd5dd4768-wqw6b micro-pay
...
2021-04-01 15:34:59.673 INFO 1 --- [ main] c.w.m.p.config.GrpcServerConfiguration : Starting gRPC on port 18888.
2021-04-01 15:35:06.175 INFO 1 --- [ main] c.w.m.p.config.GrpcServerConfiguration : gRPC server started, listening on 18888.
2021-04-01 15:40:22.019 INFO 1 --- [ault-executor-0] c.w.micro.pay.provider.PayCoreProvider : 處理gRPC支付處理請(qǐng)求,orderId->1617291624127;payAmount100
2021-04-01 15:44:31.630 INFO 1 --- [ault-executor-2] c.w.micro.pay.provider.PayCoreProvider : 處理gRPC支付處理請(qǐng)求,orderId->1617291867537;payAmount100
可以看到,多次訪問(wèn)接口,基于gRPC的微服務(wù)調(diào)用也實(shí)現(xiàn)了負(fù)載均衡調(diào)用!接下來(lái)分別看下這兩個(gè)微服務(wù)的istio-proxy(SideCar代理)的日志,具體如下:
--istio-proxy代理日志(POD-A)
root@kubernetes:~# kubectl logs micro-pay-v1-7fd5dd4768-txq9d istio-proxy
...
2021-04-01T15:34:48.009972Z info Envoy proxy is ready
[2021-04-01T15:40:26.240Z] "POST /com.wudimanong.pay.client.PayService/doPay HTTP/2" 200 - "-" 22 7 498 477 "-" "grpc-java-netty/1.36.1" "8eb318e5-ac09-922d-9ca7-603a5c14bdd5" "micro-pay:18888" "127.0.0.1:18888" inbound|18888|| 127.0.0.1:57506 10.32.0.10:18888 10.32.0.12:36844 outbound_.18888_._.micro-pay.default.svc.cluster.local default
2021-04-01T15:45:18.377555Z info xdsproxy disconnected
...
[2021-04-01T15:45:34.885Z] "POST /com.wudimanong.pay.client.PayService/doPay HTTP/2" 200 - "-" 22 7 1200 171 "-" "grpc-java-netty/1.36.1" "c08d540e-db46-9228-b381-0808ac08377e" "micro-pay:18888" "127.0.0.1:18888" inbound|18888|| 127.0.0.1:33218 10.32.0.10:18888 10.32.0.2:42646 outbound_.18888_._.micro-pay.default.svc.cluster.local default
...
2021-04-01T15:52:49.825955Z info xdsproxy connecting to upstream XDS server: istiod.istio-system.svc:15012
如上所示,可以看到istio-proxy代理日志中顯示了通過(guò)post方式轉(zhuǎn)發(fā)gRPC服務(wù)的情況,而且可以看出gRRPC是采用Http/2實(shí)現(xiàn)的!
后記
本文通過(guò)實(shí)戰(zhàn)案例,演示了在Service Mesh微服務(wù)架構(gòu)下,服務(wù)間通過(guò)gRPC協(xié)議實(shí)現(xiàn)通信調(diào)用的場(chǎng)景!
歡迎大家關(guān)注我的公眾號(hào)【風(fēng)平浪靜如碼】,海量Java相關(guān)文章,學(xué)習(xí)資料都會(huì)在里面更新,整理的資料也會(huì)放在里面。
覺(jué)得寫(xiě)的還不錯(cuò)的就點(diǎn)個(gè)贊,加個(gè)關(guān)注唄!點(diǎn)關(guān)注,不迷路,持續(xù)更新!!!