zoukankan      html  css  js  c++  java
  • gRPC 本地服务搭建

    RPC

    RPC 原理

    RPC 框架的目标就是让远程服务调用更加简单、透明,RPC 框架负责屏蔽底层的传输方式(TCP 或者 UDP)、序列化方式(XML/Json/二进制)和通信细节。服务调用者可以像调用本地接口一样调用远程的服务提供者,而不需要关心底层通信细节和调用过程。

    RPC 框架的调用原理图:

    主流 RPC 框架

    1. 支持多语言的 RPC 框架,如 Google 的 grpc、Apache 的 Thrift
    2. 只支持特定语言的 RPC 框架,例如新浪微博的 Motan
    3. 支持服务治理等服务化特性的分布式服务框架,其底层内核仍然是 RPC 框架,例如阿里的 Doubble

    随着微服务的发展,基于语言中立性原则构建微服务,逐渐成为一种主流模式,例如对于后端并发处理要求高的微服务,比较适合采用 Go 语言构建,而对于前端的 Web 界面,更适合 Java 和 JavaScript。

    因此,基于多语言的 RPC 框架来构建微服务,是一种比较好的技术选择。例如 Netflix,API 服务编编排层和后端的微服务之间就采用 grpc 进行通信。

    gRPC

    概述

    gRPC 是由 Google 开发并开源的一种语言中立的 RPC 框架,当前支持 C、Java 和 Go 语言。

    gRPC的调用示例如下所示:

    特点

    1. 语言中立,支持多种语言;
    2. 基于 IDL 文件定义服务,通过 proto3 工具生成指定语言的数据结构、服务端接口以及客户端 Stub;
    3. 通信协议基于标准的 HTTP/2 设计,支持双向流、消息头压缩、单 TCP 的多路复用、服务端推送等特性,这些特性使得 gRPC 在移动端设备上更加省电和节省网络流量;
    4. 序列化支持 PB (Protocol Buffer)和 JSON,PB 是一种语言无关的高性能序列化框架,基于 HTTP/2 + PB,保障了 RPC 调用的高性能。

    服务端创建

    定义服务

    一个 RPC 服务通过参数返回值类型来指定可以远程调用的方法。在这里我们使用 protocal buffers 接口定义语言来定义服务方法。客户端和服务端均使用服务定义生成的接口代码。

    创建 helloworld.proto 文件:

    在这里我们定义一个服务。Greeter 服务有一个方法 SayHello ,可以让服务端从远程客户端接收一个包含用户名的 HelloRequest 消息后,在一个 HelloReply 里发送回一个Greeter。

    syntax = "proto3";
    
    option java_package = "io.grpc.examples";
    
    package helloworld;
    
    // The greeter 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;
    }
    

    生成 gRPC 代码

    添加依赖:

    <properties>
            <grpc.version>1.0.3</grpc.version>
        </properties>
        <!--grpc所依赖的包-->
        <dependencies>
            <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>org.apache.commons</groupId>
                <artifactId>commons-pool2</artifactId>
                <version>2.4.2</version>
            </dependency>
        </dependencies>
    
        <build>
            <!--解析proto的工具-->
            <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.1.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>
                                <goal>compile</goal>
                                <goal>compile-custom</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    

    添加完依赖后使用 IDEA Maven 的 compile :

    生成代码,在如下图目录中:

    以下类包含所有我们需要创建这个例子的所有代码:

    • HelloRequest.java,HelloResponse.java 和其它文件所包含的所有 protocol buffer 用来填充、序列化和提取 HelloRequestHelloReply 类型的代码。

    • GreeterGrpc.java, 包含 (还有其他有用的代码):

      Greeter 服务端需要实现的接口

      public static interface Greeter {
      	public void sayHello(Helloworld.HelloRequest request,
                 StreamObserver&lt;Helloworld.HelloReply> responseObserver);
      }
      

      客户端用来与 Greeter 服务端进行对话的 存根 类。就像你所看到的,异步存根也实现了 Greeter 接口。

      public static class GreeterStub extends AbstractStub&lt;GreeterStub>
          implements Greeter {
            ...
      }
      

    服务端实现

    public class HelloWorldServer {
        private int port = 50051;
        private Server server;
    
        /**
         * 启动服务
         * @throws IOException
         */
        private void start() throws IOException {
            server = ServerBuilder.forPort(port)
                    .addService(new GreeterImpl())
                    .build()
                    .start();
    
            System.out.println("service start...");
    
            Runtime.getRuntime().addShutdownHook(new Thread() {
    
                @Override
                public void run() {
                    System.err.println("*** shutting down gRPC server" +
                                       "since JVM is shutting down");
                    HelloWorldServer.this.stop();
                    System.err.println("*** server shut down");
                }
            });
        }
    
        private void stop() {
            if (server != null) {
                server.shutdown();
            }
        }
    
        // block 一直到退出程序
        private void blockUntilShutdown() throws InterruptedException {
            if (server != null) {
                server.awaitTermination();
            }
        }
    
    
        public static void main(String[] args) throws IOException
            , InterruptedException {
            final HelloWorldServer server = new HelloWorldServer();
            server.start();
            server.blockUntilShutdown();
        }
    
    
        // 实现 定义一个实现服务接口的类
        private class GreeterImpl extends GreeterGrpc.GreeterImplBase {
            public void sayHello(HelloRequest req,
                                 StreamObserver<HelloReply> responseObserver) {
                //获取参数
                System.out.println("收到的信息:" + req.getName());
    
                //这里可以放置具体业务处理代码 start
    
                //这里可以放置具体业务处理代码 end
    
                //构造返回
                HelloReply reply = HelloReply.newBuilder()
                    .setMessage(("Hello: " + req.getName())).build();
                responseObserver.onNext(reply);
                responseObserver.onCompleted();
            }
        }
    }
    

    客户端实现

    public class HelloWorldClient {
    
        private final ManagedChannel channel; //一个gRPC信道
        private final GreeterGrpc.GreeterBlockingStub blockingStub;//阻塞/同步 存根
    
        //初始化信道和存根
        public HelloWorldClient(String host,int port){
            this(ManagedChannelBuilder.forAddress(host, port)
                    // Channels are secure by default (via SSL/TLS). For the example we disable TLS to avoid
                    // needing certificates.
                    .usePlaintext(true));
        }
    
        /** Construct client for accessing RouteGuide server using the existing channel. */
        private HelloWorldClient(ManagedChannelBuilder<?> channelBuilder) {
            channel = channelBuilder.build();
            blockingStub = GreeterGrpc.newBlockingStub(channel);
        }
    
        public void shutdown() throws InterruptedException {
            channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
        }
    
        //客户端方法
        public  void greet(String name){
            HelloRequest request = HelloRequest
                .newBuilder().setName(name).build();
            HelloReply response;
            try {
                response = blockingStub.sayHello(request);
            } catch (StatusRuntimeException e) {
                System.out.println("RPC调用失败:"+e.getMessage());
                return;
            }
            System.out.println("服务器返回信息:"+response.getMessage());
        }
    
        public static void main(String[] args) throws InterruptedException {
            HelloWorldClient client = new HelloWorldClient("127.0.0.1",50051);
            try {
                for(int i=0;i<5;i++){
                    client.greet("world:"+i);
                }
            }finally {
                client.shutdown();
            }
        }
    }
    

    先运行服务端再与运行客户端,结果:

    客户端结果:
    服务器返回信息:Hello: world:0
    服务器返回信息:Hello: world:1
    服务器返回信息:Hello: world:2
    服务器返回信息:Hello: world:3
    服务器返回信息:Hello: world:4
    服务端结果:
    service start...
    收到的信息:world:0
    收到的信息:world:1
    收到的信息:world:2
    收到的信息:world:3
    收到的信息:world:4
    

    自此,一个简单的本地 RPC 调用就完成了。

    踩坑记录

    proto 文件必须放在 main 目录下,否则 compile 的时候不会生成 target 里面的代码。

    源码

    源码

    GitHub 学习仓库

  • 相关阅读:
    10 个你需要了解的 Linux 网络和监控命令
    U盘安装 bt5
    SpringCloud RabbitMQ 使用
    两个大数相乘笔试题目
    activemq 话题模式(三)
    activemq 队列模式(二)
    activemq 安装 (一)
    安装mysql5.7时缺少my.ini文件
    linux 远程rsa 登录配置 文件 /etc/ssh/sshd_config
    java -jar 解决占用终端问题
  • 原文地址:https://www.cnblogs.com/czsy/p/11215486.html
Copyright © 2011-2022 走看看