专栏首页一杯82年的JAVA从0.5到1写个rpc框架 - 2:远程服务调用(grpc)

从0.5到1写个rpc框架 - 2:远程服务调用(grpc)


微服务要实现远程服务调用,除了直接使用如spring coud全家桶中的ribbon、feign模块,也可以试试其他优秀的框架,如谷歌的gRPC,这里基于它实现自己的服务调用模块。

gRPC是Google开源的跨语言远程服务调用(RPC)框架,通信协议用的HTTP/2,数据传输默认用的protocol buffers(一种轻便高效的结构化数据存储格式,想比json更小更快,不过没有可读性)。

需要先掌握grpc的基本用法: gRPC-Java 示例

项目结构

- acuprpc
    + acuprpc-core  //server/client核心处理逻辑
    + acuprpc-protocol-grpc  //基于grpc实现远程调用
    + acuprpc-spring-boot-starter  //server端服务扫描,client端动态代理,服务注册/发现

grpc通信

接口定义

定义服务提供者(server)和服务调用者(client)交流所用的数据结构,client需要告诉server要调用的类名、方法名以及参数(json格式的字符串,在server端再反序列化)。

syntax = "proto3";

option java_multiple_files = true;
option java_package = "com.acupt.acuprpc.protocol.grpc.proto";
option java_outer_classname = "GrpcServiceProto";

package com.acupt.acuprpc.protocol.grpc.proto;

service GrpcService {
    rpc invokeMethod (InvokeRequest) returns (InvokeResponse) {
    }
}

message InvokeRequest {
    string appName = 1;
    string serviceName = 2;
    string methodName = 3;
    repeated string orderedParameter = 4;
    map<string, string> namedParameter = 5;
}

message InvokeResponse {
    int32 code = 1;
    string message = 2;
    string result = 3;
}

grpc-service

这个类负责接收grpc-client发过来的请求,取出请求中的参数,转换成通用的结构,交给core层的RpcServer去执行对应方法,然后将返回值序列化成json返回给grpc-client。

public class GrpcService extends GrpcServiceGrpc.GrpcServiceImplBase {

    private RpcServer rpcServer;

    public GrpcService(RpcServer rpcServer) {
        this.rpcServer = rpcServer;
    }

    @Override
    public void invokeMethod(InvokeRequest request, StreamObserver<InvokeResponse> responseObserver) {
        RpcRequest rpcRequest = new RpcRequest(
                request.getAppName(),
                request.getServiceName(),
                request.getMethodName(),
                request.getOrderedParameterList(),
                request.getNamedParameterMap());
        RpcResponse rpcResponse = rpcServer.execute(rpcRequest);
        InvokeResponse response = InvokeResponse.newBuilder()
                .setCode(rpcResponse.getCode())
                .setMessage(rpcResponse.getMessage())
                .setResult(rpcResponse.getResultString())
                .build();
        responseObserver.onNext(response);
        responseObserver.onCompleted();
    }
}

grpc-server

作物服务提供者的具体实现类,只需要实现两个方法:启动服务和关闭服务,其他的交给core层的父类即可。

public class GrpcServer extends RpcServer {

    private Server server;

    public GrpcServer(RpcInstance rpcInstance) {
        super(rpcInstance);
    }

    @SneakyThrows
    @Override
    protected void startRpc() {
        server = ServerBuilder
                .forPort(getRpcInstance().getRpcConf().getPort())
                .addService(new GrpcService(this))
                .build().start();
    }

    @Override
    protected void shutdownRpc() {
        if (server != null) {
            server.shutdown();
        }
    }
}

grpc-client

作为服务调用者,需要把动态代理类传来的请求信息包装成grpc支持的结构,并调用grpc的请求方法,再把远程服务返回的结果返回给代理类。

public class GrpcClient extends RpcClient implements RpcCode {

    private AtomicReference<GrpcServiceGrpc.GrpcServiceFutureStub> stubRef;

    public GrpcClient(NodeInfo nodeInfo) {
        super(nodeInfo);
        this.stubRef = new AtomicReference<>(getStub(nodeInfo));
    }

    @Override
    protected String remoteInvoke(RpcRequest rpcRequest) {
        InvokeRequest.Builder builder = InvokeRequest.newBuilder()
                .setAppName(rpcRequest.getAppName())
                .setServiceName(rpcRequest.getServiceName())
                .setMethodName(rpcRequest.getMethodName());
        // ...
        ListenableFuture<InvokeResponse> future = stubRef.get().invokeMethod(builder.build());
        InvokeResponse response = null;
        //...
        return response.getResult();
    }

    @Override
    @SneakyThrows
    protected NodeInfo reconnectRpc(NodeInfo nodeInfo) {
        //...使用参数中的ip和端口建立新连接,并断开老的连接,可用于重新负载和异常节点重试
    }

    @Override
    @SneakyThrows
    public void shutdownRpc() {
        //...主动断开和服务端的连接
    }

    private GrpcServiceGrpc.GrpcServiceFutureStub getStub(NodeInfo nodeInfo) {
        //...和服务端建立连接,使用参数中的ip和端口
    }

本文分享自微信公众号 - 一杯82年的JAVA(acupjava),作者:acupt

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-07-14

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 从0.5到1写个rpc框架 - 1:服务注册/发现(eureka)

    原理就是利用eureka提供的客户端类来向Eureka Server发送注册请求,把自己提供服务的地址和端口(rpc服务端口,不是springboot启动的ht...

    acupt
  • JAVA中有趣的位运算

    当我们看一些源码的时候,经常会看到诸如 &、|、^、~ 的符号,这些就是位运算符。

    acupt
  • 跨域Access-Control-Allow-Origin解决方案

    本地启动了一个web服务,地址为 127.0.0.1:8882 ,然后通过一个本地静态页面去请求这个接口。虽然在同一台电脑,但依然是跨域的。

    acupt
  • C# 基础知识系列- 14 IO篇 文件的操作(2)

    除了上文提到的 GetDirectories 方法可以直接返回目录下所有子目录以外,还有一组方法也可以枚举出当前目录下的子目录:

    程序员小高
  • .NET Core的文件系统[5]:扩展文件系统构建一个简易版“云盘”

    FileProvider构建了一个抽象文件系统,作为它的两个具体实现,PhysicalFileProvider和EmbeddedFileProvider则分别为...

    蒋金楠
  • 聊聊skywalking的HTTPAccessLog

    skywalking-6.6.0/oap-server/server-core/src/main/java/org/apache/skywalking/oap/...

    codecraft
  • Jtro的技术分享:Unity解析json文件(分为json与jsonArray)

    JSON是一种取代XML的数据结构,和xml相比,它更小巧但描述能力却不差,由于它的小巧所以网络传输数据将减少更多流量从而加快速度,

    LittleU
  • 细说Golang的JSON解析

    之前一直写一些动态语言,觉得解析JOSN还是很简单的,往往只需要几行代码就能拿到解析好的JSON对象。Go语言自带的json包可以让您在程序中方便的读取和写入 ...

    mojocn
  • 基于zookeeper leader选举方式一

    一,基本介绍 Curator Framework是一个针对zookeeper做的搞层次的API,极大地简化了zookeeper的使用。它基于zookeeper构...

    Spark学习技巧
  • 实体类的变形【2】—— 行列转换

        上次说了一下在网页里面显示列表数据的情况,这个应用范围太小了,添加、修改怎么办呢?网站的后台管理、OA、CRM等怎么办?还是这样处理显然是不行的。...

    用户1174620

扫码关注云+社区

领取腾讯云代金券