Java Grpc实例创建负载均衡详解
作者:风缱云流 发布时间:2022-03-07 17:50:15
Grpc是googe开发的,是一款语言中立、平台中立、开源的远程过程调用(RPC)系统。新公司的项目服务之间的调用使用的Grpc来实现服务间的调用,这边一开始接到的工作内容是基于Nginx实现Grpc服务端的负载均衡。Nginx的1.13及以上版本是支持grpc的反向代理和负载均衡的。但是公司的nginx服务器的版本是1.10的,所以没办法直接使用grpc的代理。只能使用更底层的tcp层的负载均衡。最终服务跑起来是感觉挺简单的,但是nginx的基础太差,所以过程有点曲折。还是记录下吧。
文章分两部分,一个是创建简单的Grpc客户端和服务端的例子(其实也是用的网上的demo,这边就贴一下源码,讲下更细的实现步骤),然后对比下Nginx的Grpc负载均衡和Tcp的负载均衡。
一、Java创建Grpc客户端和服务端的例子(创建的配置信息相关的代码基本网上博客的,忘记是哪篇文章了,所以暂时没法给出转载链接。)
1、在开发工具ide上创建一个maven project。打包方式选择jar。
2、在POM.xml上增加grpc相关的依赖及maven的打包插件
<dependencies>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty</artifactId>
<version>1.17.1</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
<version>1.17.1</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
<version>1.17.1</version>
</dependency>
</dependencies>
<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.4.1.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.0.0:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:1.0.0: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>2.3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
3、在项目下的路径src/main下面创建proto文件夹,并在里面创建一个hello.proto文件。具体如下截图。
4、在hello.proto文件上输入,相应的配置信息,用来映射生成java代码。里面的内容就是生成一个MyRPC的服务提供一个sayHi的接口,接口需要传递一个request类的实例,该request实例只有一个name的字段。然后进行相应的业务代码处理之后,返回一个response类的实例,也是只有一个name的字段。
如果进行到这边,看到第2步添加依赖上面的<execution>标签可能报错,先暂时不要管他。直接进行第5步。
syntax = "proto3";
option java_package = "com.qidai.proto";
option java_outer_classname = "MyThing";
message Request {
string name = 1;
}
message Response {
string name = 2;
}
service MyRPC {
rpc sayHi(Request) returns(Response);
}
5、运行项目,右击项目Run as -->maven build....->protobuf:compile以及protobuf:compile-custom,这样就编译生成了相应的代码了。不过存放的路径不对,需要自己拷贝到相应的项目目录下。
6、grpc的客户端和服务端代码需要自己编写。不过这一块的demo已经很全了。c+v然后改成自己的自己需要的就行了。
服务端demo:
package server;
import com.qidai.proto.MyRPCGrpc;
import com.qidai.proto.MyThing;
import io.grpc.ServerBuilder;
import io.grpc.stub.StreamObserver;
import service.RequestImpl;
import java.io.IOException;
public class Server {
private static final int PORT = 2222;
private final io.grpc.Server server;
public Server() throws IOException {
//这个部分启动server
this.server = ServerBuilder.forPort(PORT)
.addService(new RequestImpl())
.build()
.start();
System.out.println("Server1 Started ...");
}
private void stop() {
if (server != null) {
server.shutdown();
}
}
private void blockUntilShutdown() throws InterruptedException {
if (server != null) {
server.awaitTermination();
}
}
public static void main(String[] args) throws IOException, InterruptedException {
Server server = new Server();
//block Server防止关闭
server.blockUntilShutdown();
}
}
客户端demo
package client;
import com.qidai.proto.MyRPCGrpc;
import com.qidai.proto.MyRPCGrpc.MyRPCBlockingStub;
import com.qidai.proto.MyThing;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import java.util.concurrent.TimeUnit;
public class Client {
private final ManagedChannelBuilder<?> managedChannelBuilder;
private final MyRPCBlockingStub blockingStub;
private final ManagedChannel channel;
public Client(String name, int port) {
managedChannelBuilder = ManagedChannelBuilder.forAddress(name, port);
channel = managedChannelBuilder.usePlaintext().build();
blockingStub = MyRPCGrpc.newBlockingStub(channel);
}
public void shutdown() throws InterruptedException {
channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
}
public void sayHi(String name){
MyThing.Request request = MyThing.Request.newBuilder().setName(name).build();
MyThing.Response response = blockingStub.sayHi(request);
System.out.println(response.getName());
}
public static void main(String[] args) throws Exception{
Client client = new Client("localhost", 5005);
for (int i = 0; i < 10; i++) {
Thread.sleep(1000);
//进行rpc调用的真正逻辑
client.sayHi("Hello Server1111 ->5005 " + i);
}
client.shutdown();
Client client2 = new Client("localhost", 5005);
for (int i = 0; i < 10; i++) {
Thread.sleep(1000);
//进行rpc调用的真正逻辑
client2.sayHi("Hello Server2222 ->5005 " + i);
}
client2.shutdown();
}
}
7、接下来就是才是比较关键的一步,实现自己的grpc服务端的业务代码。主要的关键步骤就是继承grpc自动映射出来的抽象类。是不是很熟悉,没错就是proto文件里面配置的服务。然后重写服务里面配置的方法即可。最后放心大胆的去根据传递的request参数去做相关的业务逻辑的处理。并用response封装需要返回的接口。(此处的request与response均是grcp根据proto配置文件映射出来的相关实体类。)
package service;
import com.qidai.proto.MyRPCGrpc.MyRPCImplBase;
import com.qidai.proto.MyThing.Response;
public class RequestImpl extends MyRPCImplBase {
@Override
public void sayHi(com.qidai.proto.MyThing.Request request,
io.grpc.stub.StreamObserver<com.qidai.proto.MyThing.Response> responseObserver) {
//proto文件上定义的response返回信息
Response response;
System.out.println("Request>>>say::" + request.getName());
//AccountQryResponse response = QryAccountProto.AccountQryResponse.newBuilder().setRc(1).setAmount(666).build();
response = Response.newBuilder().setName("Response11111>>>say:::hello_client"+request.getName()).build();
responseObserver.onNext(response);
responseObserver.onCompleted();
}
}
二、Grpc服务基于nginx(1.12.2)实现负载均衡。下面直接贴nginx相关的配置,服务端和客户端的代码改动都很小。只需调整ip和port的值即可。其他的不需要改动。
TCP层负载均衡配置
stream {
log_format proxy '$remote_addr [$time_local] '
'$protocol $status $bytes_sent $bytes_received '
'$session_time "$upstream_addr" '
'"$upstream_bytes_sent" "$upstream_bytes_received" "$upstream_connect_time"';
include ./conf.d/*.tcpstream;
upstream grpc {
server 127.0.0.1:2223;
server 127.0.0.1:2222;
}
server {
error_log logs/device5001_error.log;
access_log logs/device5001_access.log proxy;
listen 5005;
proxy_pass grpc;
}
}
grpc的负载均衡配置(grpc的支持在nginx1.13之后才有,所以这里是1.17.0)
http {
include mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log logs/access.log main;
sendfile on;
keepalive_timeout 65;
gzip on;
upstream grpcservers {
server 127.0.0.1:2222;
server 127.0.0.1:2223;
}
server {
listen 8080 http2;
server_name localhost;
location / {
grpc_pass grpc://grpcservers;
}
}
}
最后分别启动nginx1.12.2和nginx1.17.0,并在ide上启动服务端和客户端,更改相应的客户端端口。就可以看到控制台打印不同的信息了。tcp和grcp的负载均衡的效果是不一样的。这也是我客户端new 了一个client,然后又new 了一个client2的原因。比较懒,效果图就不贴了。
来源:https://www.cnblogs.com/8593l/p/11078061.html


猜你喜欢
- 摘要: 前面介绍了MapStrut简单用法,MapStrut的最重要的特点就是处理Java中实体与模型间不匹配属性的转换。实体模型有一个Us
- 从Microsoft .Net 2.0版本以后,就默认提供了System.IO.Ports.SerialPort类,用户可以非常简单地编写少
- Lambda,希腊字母λ,在C#编程语言中,被引入为Lambda表达式,表示为匿名函数(匿名方法)。编程时离不开函数,
- 一、在学习枚举之前,首先来听听枚举的优点。1、枚举能够使代码更加清晰,它允许使用描述性的名称表示整数值。2、枚举使代码更易于维护,有助于确保
- Java为什么不浪(long)学而时习之不亦说乎,继续温习Java。今天使用switch时,不小心写了如下代码,报错如下。 public s
- 多线程@Async的使用体验场景导入:可以将大批量的数据insert操作采用多线程的方式并行执行第三方服务的接口调用:由于存在个别第三方服务
- 本文实例讲述了Java 8新增的方法参数反射。分享给大家供大家参考,具体如下:一 点睛Java 8在java.lang.reflect包下新
- 智能指针(smart pointer)是存储指向动态分配(堆)对象指针的类,用于生存期控制
- String 对象是不可改变的。每次使用 System.String 类中的方法之一时,都要在内存中创建一个新的字符串对象,这就需要为该新对
- 前言Android 从 4.0 开始就提供了手机录屏方法,但是需要 root 权限,比较麻烦不容易实现。但是从 5.0 开始,系统提供给了
- 1.介绍说明: 其实@Valid 与 @Validated都是做数据校验的,只不过注解位置与用法有点不同。不同点:(1)@Valid是使用H
- Android提供了Invalidate方法实现界面刷新,但是Invalidate不能直接在线程中调用,因为他是违背了单线程模型:Andro
- 实现控件拖动的基本原理是对鼠标位置的捕获,同时根据鼠标按键的按下、释放确定控件移动的幅度和时机。 简单示例: 在Grid中有一个Button
- 1、环境搭建创建一个SpringBoot项目,普通的web项目就可以了,我这里使用的是start.aliyun引入依赖:(1)老演员了不多说
- 1. 前言我在Spring Security 实战干货:内置 Filter 全解析对Spring Security的内置过滤器进行了罗列,但
- 使用方法 首先在Github或者Gitee上面新建一个仓库复制仓库的链接用idea在本地新建一个demo项目点击菜单栏的VCS,按
- 本文实例讲述了C#实现泛型List分组输出元素的方法。分享给大家供大家参考,具体如下:背景:在输出列表时,往往需要按照某一字段进行分组,比如
- 本文研究的主要是Flask实现异步非阻塞请求功能,具体实现如下。最近做物联网项目的时候需要搭建一个异步非阻塞的HTTP服务器,经过查找资料,
- 前言前天工作中遇到了这样一个问题,我在接口的参数封装了一个pojo,这是很常见的,当参数一多,惯性的思维就是封装一个pojo.那么在参数前有
- 本文介绍了如何使用Spring Security OAuth2构建一个授权服务器来验证用户身份以提供access_token,并使用这个ac