Java實(shí)現(xiàn)grpc框架的示例
1.grpc簡(jiǎn)介
官方地址:https://grpc.io/docs/what-is-grpc/introduction/
在gRPC中,客戶機(jī)應(yīng)用程序可以直接調(diào)用不同機(jī)器上的服務(wù)器應(yīng)用程序上的方法,就像它是本地對(duì)象一樣,使您更容易創(chuàng)建分布式應(yīng)用程序和服務(wù)。與許多RPC系統(tǒng)一樣,gRPC基于定義服務(wù)的思想,指定可以遠(yuǎn)程調(diào)用的方法及其參數(shù)和返回類型。在服務(wù)器端,服務(wù)器實(shí)現(xiàn)這個(gè)接口,并運(yùn)行g(shù)RPC服務(wù)器來(lái)處理客戶端調(diào)用。在客戶端,客戶端有一個(gè)存根(在某些語(yǔ)言中稱為客戶端),它提供與服務(wù)器相同的方法

gRPC客戶端和服務(wù)器可以在各種環(huán)境中運(yùn)行并相互通信——從Google內(nèi)部的服務(wù)器到您自己的桌面——并且可以用任何gRPC支持的語(yǔ)言編寫。因此,例如,您可以輕松地用Java創(chuàng)建gRPC服務(wù)器,用Go、Python或Ruby創(chuàng)建客戶端。
2.Protocol Buffers
官方地址:https://protobuf.dev/overview/
默認(rèn)情況下,gRPC使用Protocol Buffers,這是Google用于序列化結(jié)構(gòu)化數(shù)據(jù)的成熟開(kāi)源機(jī)制(盡管它可以與JSON等其他數(shù)據(jù)格式一起使用)
Protocol Buffers是一種語(yǔ)言無(wú)關(guān)、平臺(tái)無(wú)關(guān)的可擴(kuò)展機(jī)制,用于序列化結(jié)構(gòu)化數(shù)據(jù)。
它類似于JSON,只是更小更快,并且生成本地語(yǔ)言綁定。您只需定義一次數(shù)據(jù)的結(jié)構(gòu)化方式,然后就可以使用特殊生成的源代碼輕松地將結(jié)構(gòu)化數(shù)據(jù)寫入和讀取到各種數(shù)據(jù)流,并使用各種語(yǔ)言。
協(xié)議緩沖區(qū)是定義語(yǔ)言(在.proto文件中創(chuàng)建)、proto編譯器為與數(shù)據(jù)交互而生成的代碼、特定于語(yǔ)言的運(yùn)行時(shí)庫(kù)以及寫入文件(或通過(guò)網(wǎng)絡(luò)連接發(fā)送)的數(shù)據(jù)的序列化格式的組合
3.創(chuàng)建maven項(xiàng)目grpc-demo
3.1 編寫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">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>grpc-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!--grpc版本 -->
<grpc.version>1.60.2</grpc.version>
</properties>
<dependencies>
<!-- grpc 需要的依賴-->
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty-shaded</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>
</dependencies>
<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.5.0.Final</version>
</extension>
</extensions>
<plugins>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.5.0</version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:3.3.0:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
</configuration>
<executions>
<execution>
<goals>
<!--它將{@code .proto}文件 生成java源代碼 -->
<goal>compile</goal>
<!--它將{@code .proto}文件 生成grpc java源代碼 -->
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<!-- 解決jar 包沖突、重復(fù)依賴、無(wú)效引用 -->
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.4.1</version>
<executions>
<execution>
<id>enforce</id>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<requireUpperBoundDeps/>
</rules>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
3.2 在/src/main/目錄下創(chuàng)建 helloworld.proto文件
官方地址: https://gitcode.com/grpc/grpc-java/blob/master/examples/src/main/proto/helloworld.proto
// Copyright 2015 The gRPC Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
syntax = "proto3";
option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW";
package helloworld;
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
3.3 生成代碼
idea選中項(xiàng)目pom.xml-右鍵->run maven -> Plugins->protobuf-maven-plugin->protobuf:compile
或者進(jìn)入到項(xiàng)目目錄直接執(zhí)行mvn命令:
#生成protobuf代碼 mvn protobuf:compile #生成grpc代碼 mvn protobuf:compile-custom
生成成功的代碼如截圖,將代碼拷貝到項(xiàng)目對(duì)應(yīng)的位置:

代碼拷貝到項(xiàng)目截圖:

4.代碼編寫
4.1 HelloWordServer服務(wù)端編寫
package io.grpc.examples.helloworld2;
import io.grpc.Grpc;
import io.grpc.InsecureServerCredentials;
import io.grpc.Server;
import io.grpc.examples.helloworld.GreeterGrpc;
import io.grpc.examples.helloworld.HelloReply;
import io.grpc.examples.helloworld.HelloRequest;
import io.grpc.stub.StreamObserver;
import java.io.IOException;
import java.util.Objects;
import java.util.Random;
import java.util.concurrent.TimeUnit;
/**
* @Author tanyong
* @Version HelloWordServer v1.0.0 2024/4/2 11:21 $$
*/
public class HelloWordServer {
private static int port = 50052;
/**
* grpc服務(wù)實(shí)例
*/
private Server server;
/**
* 啟動(dòng) grpc服務(wù)
*
* @throws IOException
*/
public void start() throws IOException {
server = Grpc.newServerBuilderForPort(port, InsecureServerCredentials.create())
// 添加業(yè)務(wù)處理類
.addService(new GreeterImpl(port))
.build()
.start();
// 注冊(cè)了一個(gè)JVM關(guān)閉鉤子(Shutdown Hook),當(dāng)Java虛擬機(jī)(JVM)即將關(guān)閉時(shí)(無(wú)論是正常退出還是非正常退出,如接收到操作系統(tǒng)中斷信號(hào))當(dāng)JVM關(guān)閉時(shí),所有已注冊(cè)的關(guān)閉鉤子都將被依次調(diào)用
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
// Use stderr here since the logger may have been reset by its JVM shutdown hook.
System.err.println("*** shutting down gRPC server since JVM is shutting down");
try {
// 優(yōu)雅地停止gRPC服務(wù)器實(shí)例
HelloWordServer.this.stop();
} catch (InterruptedException e) {
e.printStackTrace(System.err);
}
System.err.println("*** server shut down");
}
});
}
/**
* 停止grpc 實(shí)例
*
* @throws InterruptedException
*/
public void stop() throws InterruptedException {
if (Objects.nonNull(server)) {
// 發(fā)起服務(wù)器的關(guān)閉流程,不再接受新的連接和請(qǐng)求,但允許現(xiàn)有連接繼續(xù)完成請(qǐng)求處理
server.shutdown()
// 給予服務(wù)器最長(zhǎng)30秒的時(shí)間去完成所有待處理的工作,超過(guò)這個(gè)時(shí)間限制,程序?qū)⒗^續(xù)執(zhí)行后續(xù)邏輯,即使服務(wù)器還有任務(wù)未完成
// 這樣設(shè)計(jì)有助于在應(yīng)用退出時(shí)確保資源得到釋放,同時(shí)也能防止因某些原因?qū)е碌拈L(zhǎng)時(shí)間無(wú)法關(guān)閉的問(wèn)題。
.awaitTermination(30, TimeUnit.SECONDS);
}
}
/**
* 確保主線程或者其他調(diào)用者線程會(huì)在服務(wù)器完全關(guān)閉之前保持等待狀態(tài)。
* 在主線程上等待終止,因?yàn)間rpc庫(kù)使用守護(hù)線程
*
* @throws InterruptedException
*/
public void blockUntilShutdown() throws InterruptedException {
if (Objects.nonNull(server)) {
server.awaitTermination();
}
}
/**
* 業(yè)務(wù)處理類
*/
public static class GreeterImpl extends GreeterGrpc
.GreeterImplBase {
private final int port;
public GreeterImpl(int port) {
this.port = port;
}
/**
* @param request
* @param responseObserver 這是gRPC提供的響應(yīng)觀察者對(duì)象,用于向客戶端發(fā)送響應(yīng)。服務(wù)端通過(guò)調(diào)用其方法將響應(yīng)數(shù)據(jù)發(fā)送給客戶端。
*/
@Override
public void sayHello(HelloRequest request, StreamObserver<HelloReply> responseObserver) {
HelloReply reply = HelloReply.newBuilder().setMessage("Hello " + request.getName() + this.port).build();
try {
Thread.sleep(500 + new Random().nextInt(1000));
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// 向客戶端發(fā)送響應(yīng)數(shù)據(jù),即將創(chuàng)建好的 reply 對(duì)象推送給客戶端。
responseObserver.onNext(reply);
// 表示響應(yīng)已經(jīng)結(jié)束,沒(méi)有更多的數(shù)據(jù)要發(fā)送給客戶端。
responseObserver.onCompleted();
}
}
public static void main(String[] args) throws IOException, InterruptedException {
final HelloWordServer helloWordServer = new HelloWordServer();
helloWordServer.start();
helloWordServer.blockUntilShutdown();
}
}
4.2 自定負(fù)載均衡解析器編寫
HelloWordConstants.java常量:
public interface HelloWordConstants {
String SCHEME = "example";
String SERVICE_NAME = "lb.example.grpc.io";
}
負(fù)載均衡解析器LoadBalanceNameResolver.java
package io.grpc.examples.helloworld2;
import io.grpc.EquivalentAddressGroup;
import io.grpc.NameResolver;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @Author tanyong
* @Version LoadBalanceNameResolver v1.0.0 2024/4/2 16:59 $$
*/
public class LoadBalanceNameResolver extends NameResolver {
private Listener2 listener;
private List<InetSocketAddress> socketAddressList;
private String serviceName;
private final Map<String, List<InetSocketAddress>> addrStore = new HashMap<>();
public LoadBalanceNameResolver(List<InetSocketAddress> socketAddressList, String serviceName) {
this.socketAddressList = socketAddressList;
this.serviceName = serviceName;
addrStore.put(serviceName, socketAddressList);
}
@Override
public void start(Listener2 listener) {
this.listener = listener;
this.resolve();
}
@Override
public void refresh() {
this.resolve();
}
/**
* 返回用于對(duì)服務(wù)器連接進(jìn)行身份驗(yàn)證的權(quán)限。它必須來(lái)自受信任的來(lái)源,因?yàn)槿绻麢?quán)限被篡改,rpc可能會(huì)被發(fā)送給攻擊者,這可能會(huì)泄露敏感的用戶數(shù)據(jù)。
* 實(shí)現(xiàn)必須在不阻塞的情況下生成它,通常是在線生成,并且必須保持它不變。從同一工廠使用相同參數(shù)創(chuàng)建的namesolvers必須返回相同的權(quán)限。
* 自:
*
* @return
*/
@Override
public String getServiceAuthority() {
return "ok";
}
@Override
public void shutdown() {
}
/**
* 解析服務(wù)器地址
*/
private void resolve() {
List<InetSocketAddress> addresses = addrStore.get(serviceName);
List<EquivalentAddressGroup> equivalentAddressGroup = addresses.stream().map(this::toSocketAddress)
.map(Arrays::asList)
.map(this::addrToEquivalentAddressGroup)
.collect(Collectors.toList());
ResolutionResult resolutionResult = ResolutionResult.newBuilder()
.setAddresses(equivalentAddressGroup)
.build();
// 處理已解析地址和屬性的更新
this.listener.onResult(resolutionResult);
}
private SocketAddress toSocketAddress(InetSocketAddress address) {
return new InetSocketAddress(address.getHostName(), address.getPort());
}
private EquivalentAddressGroup addrToEquivalentAddressGroup(List<SocketAddress> addrList) {
return new EquivalentAddressGroup(addrList);
}
}
負(fù)載均衡解析器提供者LoadBalanceNameResolverProvider.java
package io.grpc.examples.helloworld2;
import io.grpc.NameResolver;
import io.grpc.NameResolverProvider;
import java.net.InetSocketAddress;
import java.net.URI;
import java.util.List;
/**
* @Author tanyong
* @Version LoadBalanceNameResolverProvider v1.0.0 2024/4/3 9:56 $$
*/
public class LoadBalanceNameResolverProvider extends NameResolverProvider {
private final List<InetSocketAddress> socketAddressList;
public LoadBalanceNameResolverProvider(List<InetSocketAddress> socketAddressList) {
this.socketAddressList = socketAddressList;
}
@Override
protected boolean isAvailable() {
return true;
}
@Override
protected int priority() {
return 5;
}
@Override
public NameResolver newNameResolver(URI targetUri, NameResolver.Args args) {
return new LoadBalanceNameResolver(socketAddressList, HelloWordConstants.SERVICE_NAME);
}
@Override
public String getDefaultScheme() {
return HelloWordConstants.SCHEME;
}
}
4.3 客戶端HelloWordClient.java
package io.grpc.examples.helloworld2;
import io.grpc.*;
import io.grpc.examples.helloworld.GreeterGrpc;
import io.grpc.examples.helloworld.HelloReply;
import io.grpc.examples.helloworld.HelloRequest;
import java.net.InetSocketAddress;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* @Author tanyong
* @Version HellowordClient v1.0.0 2024/4/2 16:33 $$
*/
public class HelloWordClient {
/**
* 客戶端對(duì)服務(wù)Greeter進(jìn)行同步rpc調(diào)用 blockingStub
*/
private final GreeterGrpc.GreeterBlockingStub blockingStub;
public HelloWordClient(Channel channel) {
// 創(chuàng)建一個(gè)新的阻塞式Stub,支持對(duì)服務(wù)的一元和流輸出調(diào)用
blockingStub = GreeterGrpc.newBlockingStub(channel);
}
public void greet(String name) {
HelloRequest request = HelloRequest.newBuilder().setName(name).build();
HelloReply response;
try {
// 設(shè)置此次RPC調(diào)用的響應(yīng)超時(shí)時(shí)間為1秒
response = blockingStub.withDeadlineAfter(1, TimeUnit.SECONDS).sayHello(request);
System.out.println(response.getMessage());
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
// 服務(wù)段連接地址
List<InetSocketAddress> addressList = Arrays.asList(new InetSocketAddress("127.0.0.1", 50052),
new InetSocketAddress("127.0.0.1", 50053));
// 注冊(cè) NameResolverProvider
NameResolverRegistry.getDefaultRegistry().register(new LoadBalanceNameResolverProvider(addressList));
// 符合namesolver的有效URI
String target = String.format("%s:///%s", HelloWordConstants.SCHEME, HelloWordConstants.SERVICE_NAME);
// 創(chuàng)建channel
ManagedChannel channel = ManagedChannelBuilder.forTarget(target)
.defaultLoadBalancingPolicy("round_robin")
// 使用明文連接到服務(wù)器。默認(rèn)情況下,將使用安全連接機(jī)制,如TLS。
// 應(yīng)僅用于測(cè)試或API的使用或交換的數(shù)據(jù)不敏感的API。
.usePlaintext()
.disableRetry()
.build();
try {
HelloWordClient client = new HelloWordClient(channel);
long current = System.currentTimeMillis();
for (int i = 0; i < 10; i++) {
Thread.sleep(5000);
client.greet("測(cè)試");
}
System.out.println(System.currentTimeMillis() - current);
} catch (Exception e) {
e.printStackTrace();
} finally {
// 關(guān)閉channel
channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS);
}
}
}
到此這篇關(guān)于Java實(shí)現(xiàn)grpc框架的示例的文章就介紹到這了,更多相關(guān)Java grpc內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springboot+mybatis如何屏蔽掉mybatis日志
這篇文章主要介紹了springboot+mybatis如何屏蔽掉mybatis日志問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-05-05
詳解SpringBoot restful api的單元測(cè)試
本篇文章主要介紹了詳解SpringBoot restful api的單元測(cè)試,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-09-09
Ubuntu下配置Tomcat服務(wù)器以及設(shè)置自動(dòng)啟動(dòng)的方法
這篇文章主要介紹了Ubuntu下配置Tomcat服務(wù)器以及設(shè)置自動(dòng)啟動(dòng)的方法,適用于Java的web程序開(kāi)發(fā),需要的朋友可以參考下2015-10-10
java中InputStream轉(zhuǎn)為MultipartFile的解決方案
這篇文章主要介紹了java中InputStream轉(zhuǎn)為MultipartFile的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-03-03
SpringBoot集成canal實(shí)現(xiàn)示例解析
這篇文章主要為大家介紹了springboot整合canal的示例實(shí)現(xiàn)解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多多進(jìn)步,早日升職加薪2022-02-02

