zoukankan      html  css  js  c++  java
  • RPC 框架(附 dubbo & grpc 简例)



    HTTP 和 RPC

    在微服务体系结构中,独立部署在各个机器或容器上的服务之间,如何进行有效的通信,是一个很重要的问题,现在常用的主要是 RESTful HTTP 和 RPC

    HTTP 的优点

    • 通用性强,基本上所有框架,所有语言都支持 HTTP
    • 可读性高,URL 对资源的定义,Action 对操作的定义,Payload 的定义都比较清晰易懂
    • 可以通过各种防火墙、网关

    HTTP 的缺点

    • HTTP 协议的效率比较低(毕竟是网络第 7 层协议)
    • HTTP 协议的有效信息占比小
    • 客户度编写调用 HTTP 请求的代码并不易用

    RPC(Remote Procedure Call,远程过程调用)使客户端向服务端发请求就像调用本地函数一样

    比如客户端调用

    String message = service.sayHi("dubbo");
    

    服务端会有相应的函数被执行并返回结果给客户端

        public String sayHi(String name) {
            return "hi, " + name;
        }
    

    RPC 的优点

    • 使用 TCP 或 HTTP2.0 协议,通信效率高,有效信息占比大
    • 客户端调用 RPC 请求就像调用本地函数一样,代码比较简单,容易使用

    RPC 的缺点

    • 可读性没有 HTTP 强
    • 缺少通用性,和 HTTP 是统一的标准不同,RPC 框架的实现各不相同,有的仅支持单语言,有的支持跨语言,有的支持多种序列化协议,有的仅有一种固定的序列化协议,有的支持管理中心、服务发现、负载均衡、熔断降级等功能,有的不支持
    • 需要和特定的框架绑定使用,比较紧耦合
    • 不同的网关对 RPC 的支持可能会不够(对 TCP、HTTP2.0 的支持)

    可以看到,HTTP 和 RPC 各有千秋,通常暴露给外部用户的都是 HTTP,而当系统内部的微服务特别多,微服务之间的通信量特别大的时候,为了提高性能简化代码可以使用 RPC,但是如果微服务没有拆的很细,或是对性能要求不是很高,内部通信也可以使用 HTTP

    RPC 技术

    通常包含以下技术实现

    • 动态代理

    客户端实际上只定义了接口,具体的实现在服务端,所以需要有动态代理,当客户端调用函数的时候,将信息传递到服务端,在服务端调用真正的实现,接受它的返回给客户端

    • 序列化放序列化

    客户端需要将数据序列化,在服务端要做反序列化

    • 通信协议

    需要高效的通信协议以提高性能

    • 异常处理

    出现网络故障、服务端错误等各种异常的时候要怎么处理

    RPC 常用框架

    • dubbo

    阿里巴巴开发的 RPC 框架,支持 Java,2012 年开源,2014 年停止维护,2017 年又重新维护,后来捐给了 Apache

    dubbo 架构如下图

    https://dubbo.apache.org/zh/docs/v2.7/user/preface/architecture/

    Provider 向 Registry 注册服务,Consumer 通过 Registry 获取 Provider 服务的信息

    服务注册中心,服务提供者,服务消费者三者之间均为长连接

    Consumer 向 Provider (可以配多个 Provider 实例)发请求时,可以自动实现负载均衡等算法

    监控中心负责统计各服务调用次数,调用时间等,统计先在内存汇总后每分钟一次发送到监控中心服务器,并以报表展示

    注册中心和监控中心全部宕机,不影响已运行的提供者和消费者,消费者在本地缓存了提供者列表

    注册中心和监控中心都是可选的,服务消费者可以直连服务提供者

    健壮性和伸缩性

    底层协议可以是:dubbo、rest、http、hessian、redis、thrift、gRPC、memcached、rmi、webservice,默认是 dubbo(采用单一长连接和 NIO 异步通讯,适合于小数据量大并发的服务调用,不适合传送大数据量的服务)
    https://dubbo.apache.org/zh/docs/v2.7/user/references/xml/dubbo-registry/
    https://dubbo.apache.org/zh/docs/v2.7/user/references/protocol/
    https://dubbo.apache.org/zh/docs/v2.7/user/perf-test/

    注册中心可以是:dubbo, nacos, multicast, zookeeper, redis, consul, sofa, etcd,推荐使用 zookeeper
    https://dubbo.apache.org/zh/docs/v2.7/user/references/xml/dubbo-registry/

    2.7.5 引入了基于 AK/SK 机制的认证鉴权机制,并且引入了鉴权服务中心,主要原理是消费端在请求需要鉴权的服务时,会通过 SK、请求元数据、时间戳、参数等信息来生成对应的请求签名,通过 Dubbo 的 Attahcment 机制携带到对端进行验签,验签通过才进行业务逻辑处理

    dubbo 可以直接和 SpringBoot 集成

    各种用法示例
    https://dubbo.apache.org/zh/docs/v2.7/user/examples/

    • gRPC

    Google 开发的 RPC 框架

    支持多种语言,并且 Consumer 和 Provider 之间可以跨语言

    使用 proto3 定义接口后可以生成不同语言的源文件

    没有注册服务中心、负载均衡、熔断降级等功能

    默认使用 protocol buffers 作为序列化反序列化机制,protocol buffers 的压缩速度快,压缩率高

    支持 SSL/TLS、OAuth 2.0 等认证授权协议

    使用 HTTP2.0 作为通信协议,HTTP2.0 采用新的数据格式、新的 Header 压缩算法、服务端推送、链接共享等机制,性能大幅度提升,但现在支持的组件可能不多,比如如果要通过 Nginx 它可能不认 HTTP2.0 只把它当做 TCP 消息对待

    • Motan

    微博开发的 RPC 框架,用 Java 实现,可以和 SpringBoot 集成

    https://github.com/weibocom/motan

    无需多少额外代码即可实现
    支持 Consul、Zookeeper 作为服务发现中心
    支持负载均衡
    为高负载场景做了优化
    支持同步调用和异步调用
    支持 Java、Go、PHP 等多语言

    • rpcx

    微博开发的,用 Go 语言开发,参考了阿里巴巴的 dubbo 和微博的 Motan

    https://github.com/smallnest/rpcx
    https://blog.rpcx.io/posts/why-did-i-develop-rpcx/
    https://doc.rpcx.io/

    性能很好,貌似比 dubbo、grpc、motan 都要好

    支持原生 Go 函数,不需要定义 proto 文件
    支持 TCP、HTTP、KCP、QUIC 等传输协议
    支持 JSON、Protobuf、MessagePack 数据编码协议
    服务发现,支持 P2P、zookeeper、etcd、consul、mDNS 等
    容错(Fault tolerance):支持 Failover(切换)、Failfast(快速失败)、Failtry(重试)
    支持负载均衡
    支持认证授权
    支持心跳检测

    貌似还支持跨语言
    rpcx uses a binary protocol and platform-independent, which means you can develop services in other languages such as Java, python, nodejs, and you can use other prorgramming languages to invoke services developed in Go.

    • Thrift

    由 Facebook 开发的 RPC 框架,后来捐给了 Apache

    https://github.com/apache/thrift

    是一个轻量级的、跨语言的、点到点的 RPC 框架

    定义好接口后,代码生成器可以生成不同语言的代码

    从上图可以看到,Thrift 支持很多种语言、传输协议、数据协议、应用模式

    Thrift 不支持服务发现、负载均衡、熔断降级等功能

    dubbo 例子

    定义有 3 个模块的项目

        <groupId>com.example</groupId>
        <artifactId>dubbo</artifactId>
        <packaging>pom</packaging>
        <version>1.0-SNAPSHOT</version>
    
        <modules>
            <module>dubbo-example-interface</module>
            <module>dubbo-example-consumer</module>
            <module>dubbo-example-provider</module>
        </modules>
    

    interface 只定义了一个接口

    package com.example.dubbo.interfaces;
    
    public interface GreetingService {
        String sayHi(String name);
    }
    

    maven install 编译安装 interface

    provider 实现了接口

        <parent>
            <groupId>com.example</groupId>
            <artifactId>dubbo</artifactId>
            <version>1.0-SNAPSHOT</version>
            <relativePath>../pom.xml</relativePath>
        </parent>
        
        <artifactId>dubbo-example-provider</artifactId>
        <packaging>jar</packaging>
        <description>Demo project for dubbo</description>
    
        <dependencies>
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo</artifactId>
                <version>2.7.8</version>
            </dependency>
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo-dependencies-zookeeper</artifactId>
                <version>2.7.8</version>
                <type>pom</type>
            </dependency>
            
            <dependency>
                <groupId>com.example</groupId>
                <artifactId>dubbo-example-interface</artifactId>
                <version>1.0-SNAPSHOT</version>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>            
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                        <encoding>UTF-8</encoding>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    
    public class GreetingServiceImpl implements GreetingService {
        @Override
        public String sayHi(String name) {
            System.out.println("receive msg " + name);
            return "hi, " + name;
        }
    }
    
    public class Provider {
        private static String zookeeperHost = System.getProperty("zookeeper.address", "localhost");
    
        public static void main(String[] args) throws Exception {
            ServiceConfig<GreetingService> service = new ServiceConfig<>();
            service.setApplication(new ApplicationConfig("first-dubbo-provider"));
            service.setRegistry(new RegistryConfig("zookeeper://" + zookeeperHost + ":2181"));
            service.setInterface(GreetingService.class);
            service.setRef(new GreetingServiceImpl());
            service.export();
    
            System.out.println("dubbo service started");
            new CountDownLatch(1).await();
        }
    }
    

    consumer 调用接口

        <parent>
            <groupId>com.example</groupId>
            <artifactId>dubbo</artifactId>
            <version>1.0-SNAPSHOT</version>
            <relativePath>../pom.xml</relativePath>
        </parent>
        
        <artifactId>dubbo-example-consumer</artifactId>
        <packaging>jar</packaging>
        <description>Demo project for dubbo</description>
    
        <dependencies>
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo</artifactId>
                <version>2.7.8</version>
            </dependency>
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo-dependencies-zookeeper</artifactId>
                <version>2.7.8</version>
                <type>pom</type>
            </dependency>
            
            <dependency>
                <groupId>com.example</groupId>
                <artifactId>dubbo-example-interface</artifactId>
                <version>1.0-SNAPSHOT</version>
            </dependency>
        </dependencies>
        
        <build>
            <plugins>            
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                        <encoding>UTF-8</encoding>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    
    public class Consumer {
        private static String zookeeperHost = System.getProperty("zookeeper.address", "localhost");
    
        public static void main(String[] args) {
            ReferenceConfig<GreetingService> reference = new ReferenceConfig<>();
            reference.setApplication(new ApplicationConfig("first-dubbo-consumer"));
            reference.setRegistry(new RegistryConfig("zookeeper://" + zookeeperHost + ":2181"));
            reference.setInterface(GreetingService.class);
            GreetingService service = reference.get();
            String message = service.sayHi("dubbo");
            System.out.println(message);
        }
    }
    

    运行后看到 consumer 打出 hi, dubbo

    gRPC 例子

    定义有 4 个模块的项目

        <modules>
            <module>grpc-example-proto</module>
            <module>grpc-example-interfaces</module>
            <module>grpc-example-client</module>
            <module>grpc-example-server</module>
        </modules>
    
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>io.grpc</groupId>
                    <artifactId>grpc-netty-shaded</artifactId>
                    <version>1.35.0</version>
                </dependency>
                <dependency>
                    <groupId>io.grpc</groupId>
                    <artifactId>grpc-protobuf</artifactId>
                    <version>1.35.0</version>
                </dependency>
                <dependency>
                    <groupId>io.grpc</groupId>
                    <artifactId>grpc-stub</artifactId>
                    <version>1.35.0</version>
                </dependency>
                <dependency> <!-- necessary for Java 9+ -->
                    <groupId>org.apache.tomcat</groupId>
                    <artifactId>annotations-api</artifactId>
                    <version>6.0.53</version>
                    <scope>provided</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>
    

    proto 定义接口并用于生产代码

        <parent>
            <groupId>com.example</groupId>
            <artifactId>grpc</artifactId>
            <version>1.0-SNAPSHOT</version>
            <relativePath>../pom.xml</relativePath>
        </parent>
        
        <artifactId>grpc-example-proto</artifactId>
        <packaging>jar</packaging>
        <description>Demo project for grpc</description>
    
        <dependencies>
            <dependency>
                <groupId>io.grpc</groupId>
                <artifactId>grpc-netty-shaded</artifactId>
            </dependency>
            <dependency>
                <groupId>io.grpc</groupId>
                <artifactId>grpc-protobuf</artifactId>
            </dependency>
            <dependency>
                <groupId>io.grpc</groupId>
                <artifactId>grpc-stub</artifactId>
            </dependency>
        </dependencies>
    
        <build>
            <extensions>
                <extension>
                    <groupId>kr.motd.maven</groupId>
                    <artifactId>os-maven-plugin</artifactId>
                    <version>1.6.2</version>
                </extension>
            </extensions>
      
            <plugins>            
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                        <encoding>UTF-8</encoding>
                    </configuration>
                </plugin>
                
                <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.35.0:exe:${os.detected.classifier}</pluginArtifact>
                    </configuration>
                    <executions>
                        <execution>
                            <goals>
                                <goal>compile</goal>
                                <goal>compile-custom</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    

    在 src/main/proto 下创建 GreetingService.proto 文件,定义接口

    syntax = "proto3";
    
    
    option java_multiple_files = true; 
    option java_package = "com.example.grpc.interfaces"; 
    option java_outer_classname = "GreetingServiceProto"; 
    option objc_class_prefix = "HLW";
    
    
    package GreetingService;
    
    
    service GreetingService {
        rpc SayHi (GreetingRequest) returns (GreetingReply) {} 
    }
    
    message GreetingRequest { 
        string name = 1; 
    }
    
    message GreetingReply { 
        string message = 1; 
    } 
    

    然后执行 maven install 会生产源代码
    将 target/generated-sources/protobuf 下面的源文件都考到 interface 项目下面

    interface 的 pom 要引用 grpc

        <parent>
            <groupId>com.example</groupId>
            <artifactId>grpc</artifactId>
            <version>1.0-SNAPSHOT</version>
            <relativePath>../pom.xml</relativePath>
        </parent>
        
        <artifactId>grpc-example-interfaces</artifactId>
        <packaging>jar</packaging>
        <description>Demo project for grpc</description>
    
        <dependencies>
            <dependency>
                <groupId>io.grpc</groupId>
                <artifactId>grpc-netty-shaded</artifactId>
            </dependency>
            <dependency>
                <groupId>io.grpc</groupId>
                <artifactId>grpc-protobuf</artifactId>
            </dependency>
            <dependency>
                <groupId>io.grpc</groupId>
                <artifactId>grpc-stub</artifactId>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>            
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                        <encoding>UTF-8</encoding>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    

    maven install 编译安装 interface

    再编写 server 实现接口

        <parent>
            <groupId>com.example</groupId>
            <artifactId>grpc</artifactId>
            <version>1.0-SNAPSHOT</version>
            <relativePath>../pom.xml</relativePath>
        </parent>
        
        <artifactId>grpc-example-server</artifactId>
        <packaging>jar</packaging>
        <description>Demo project for grpc</description>
    
        <dependencies>
            <dependency>
                <groupId>io.grpc</groupId>
                <artifactId>grpc-netty-shaded</artifactId>
            </dependency>
            <dependency>
                <groupId>io.grpc</groupId>
                <artifactId>grpc-protobuf</artifactId>
            </dependency>
            <dependency>
                <groupId>io.grpc</groupId>
                <artifactId>grpc-stub</artifactId>
            </dependency>
            
            <dependency>
                <groupId>com.example</groupId>
                <artifactId>grpc-example-interfaces</artifactId>
                <version>1.0-SNAPSHOT</version>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>            
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                        <encoding>UTF-8</encoding>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    
    public class GreetingServiceImpl extends GreetingServiceGrpc.GreetingServiceImplBase  {
    
        @Override 
        public void sayHi(GreetingRequest req, StreamObserver<GreetingReply> responseObserver){ 
            GreetingReply reply = GreetingReply.newBuilder().setMessage(("Hi " + req.getName())).build(); 
            responseObserver.onNext(reply); 
            responseObserver.onCompleted(); 
        } 
    }
    
    public class GrpcServer {
    
        private static final Logger logger = Logger.getLogger(GrpcServer.class.getName());
    
        private int port = 50051; 
        private Server server;
    
        private void start() throws IOException {
            server = ServerBuilder.forPort(port) 
                                  .addService(new GreetingServiceImpl()) 
                                  .build() 
                                  .start();
    
            logger.info("Server started, listening on "+ port);
    
            Runtime.getRuntime().addShutdownHook(new Thread(){
                @Override 
                public void run(){
                    System.err.println("*** shutting down gRPC server since JVM is shutting down"); 
                    GrpcServer.this.stop(); 
                    System.err.println("*** server shut down"); 
                } 
            }); 
        }
    
        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 {
            final GrpcServer server = new GrpcServer(); 
            server.start(); 
            server.blockUntilShutdown(); 
        }
    } 
    

    再编写 client 调用接口

        <parent>
            <groupId>com.example</groupId>
            <artifactId>grpc</artifactId>
            <version>1.0-SNAPSHOT</version>
            <relativePath>../pom.xml</relativePath>
        </parent>
        
        <artifactId>grpc-example-client</artifactId>
        <packaging>jar</packaging>
        <description>Demo project for grpc</description>
    
        <dependencies>
            <dependency>
                <groupId>io.grpc</groupId>
                <artifactId>grpc-netty-shaded</artifactId>
            </dependency>
            <dependency>
                <groupId>io.grpc</groupId>
                <artifactId>grpc-protobuf</artifactId>
            </dependency>
            <dependency>
                <groupId>io.grpc</groupId>
                <artifactId>grpc-stub</artifactId>
            </dependency>
            
            <dependency>
                <groupId>com.example</groupId>
                <artifactId>grpc-example-interfaces</artifactId>
                <version>1.0-SNAPSHOT</version>
            </dependency>
        </dependencies>
        
        <build>
            <plugins>            
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                        <encoding>UTF-8</encoding>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    
    public class GrpcClient {
    
        private final ManagedChannel channel; 
        private final GreetingServiceGrpc.GreetingServiceBlockingStub blockingStub; 
        private static final Logger logger = Logger.getLogger(GrpcClient.class.getName());
    
        public GrpcClient(String host, int port){ 
            channel = ManagedChannelBuilder.forAddress(host, port) 
                                           .usePlaintext() 
                                           .build();
    
            blockingStub = GreetingServiceGrpc.newBlockingStub(channel); 
        }
    
        public void shutdown() throws InterruptedException { 
            channel.shutdown().awaitTermination(5, TimeUnit.SECONDS); 
        }
    
        public void greet(String name){ 
            GreetingRequest request = GreetingRequest.newBuilder().setName(name).build(); 
            GreetingReply response;
            
            try{ 
                response = blockingStub.sayHi(request); 
            } catch (StatusRuntimeException e) { 
                logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus()); 
                return;
            }
            
            logger.info("Greeting: " + response.getMessage()); 
        }
    
        public static void main(String[] args) throws InterruptedException { 
            GrpcClient client = new GrpcClient("localhost", 50051); 
            
            try{ 
                String user = "GRPC"; 
                if (args.length > 0){
                    user = args[0];
                }
                client.greet(user); 
            }finally { 
                client.shutdown(); 
            } 
        } 
    }
    


  • 相关阅读:
    .net core读取appsettings.config中文乱码问题
    vs2017错误:当前页面的脚本发生错误
    VS Code中无法识别npm命令
    Visual Studio报错/plugin.vs.js,行:1074,错误:缺少标识符、字符串或数字
    记录一次在生成数据库服务器上出现The timeout period elapsed prior to completion of the operation or the server is not responding.和Exception has been thrown by the target of an invocation的解决办法
    Java集合框架
    java hash表
    Java Dictionary 类存储键值
    java数据结构 栈stack
    java封装
  • 原文地址:https://www.cnblogs.com/moonlight-lin/p/14347010.html
Copyright © 2011-2022 走看看