• 基于java的Grpc实例创建及基于Nginx的Grpc服务端负载均衡


      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的原因。比较懒,效果图就不贴了。

  • 相关阅读:
    HDU3461 Code Lock 并查集应用
    记录,待总结8
    HDU1325 Is It A Tree?
    函数指针总结
    记录,待总结6
    HDU1272 小希的迷宫 并查集
    记录,待总结10
    记录,待总结9
    C# 获取radiobutton的值
    解决idea控制台tomcat输出中文乱码
  • 原文地址:https://www.cnblogs.com/8593l/p/11078061.html
Copyright © 2020-2023  润新知