「微服务远程通信一」RPC和HTTP的区别

微服务的核心之一远程通信,http请求大家很熟悉了,我们主要来看看RPC。

首先说下RPC和HTTP的区别:很多博客说的真的一言难尽

首先:http是个通讯协议,而RPC是一个远程调用方案,它通常包括了通信协议和序列化协议。当然他们都是在传输层及其以上作用。

这也是为什么我们经常说RPC也可以给予http来实现,因为我们采用了http作为我们的RPC实现的应用层通信协议。而我们平时写http请求的时候,一般会采用基于文本编码的josn的序列化协议,而RPC采用的是基于二进制编码的protobuf协议。http协议header其实有很多附带字段,这些占用了网络报文,在高并发场景下,这些字段是不需要的,使用自定义的应用层协议可以减少一些数据,别看这个不多,但是一旦数据量很多,效果是非常明显的。如果采用http来做通信协议,其实和我们普通的rest请求差别不大。当然http和http2.0又是两个差别很大的协议了。

RPC 是一种技术思想而非一种规范或协议,通常的我们需要一些框架和技术来帮助我们快速实现rpc通信,常见 RPC 技术和框架有:

  • 应用级的服务框架:阿里的 Dubbo/Dubbox、Google gRPC、Spring Boot/Spring Cloud、Facebook 的 Thrift、Twitter 的 Finagle 等。
  • 远程通信协议:RMI、Socket、SOAP(HTTP XML)、REST(HTTP JSON)。
  • 通信框架:MINA 和 Netty。
  • ps: Google gRPC 框架是基于 HTTP2 协议实现的,底层使用到了 Netty 框架的支持。

目前来看从2014年开始,grpc框架已经成为高性能微服务通信的首选。

1. RPC 框架

一个典型 RPC 的使用场景中,包含了服务发现、负载、容错、网络传输、序列化等组件,其中“RPC 协议”就指明了程序如何进行网络传输和序列化。

一个 RPC 的核心功能主要有 5 个部分组成,分别是:客户端、客户端 Stub、网络传输模块、服务端 Stub、服务端等。

下面分别介绍核心 RPC 框架的重要组成:

  1. 客户端(Client):服务调用方。
  2. 客户端存根(Client Stub):存放服务端地址信息,将客户端的请求参数数据信息打包成网络消息,再通过网络传输发送给服务端。
  3. 服务端存根(Server Stub):接收客户端发送过来的请求消息并进行解包,然后再调用本地服务进行处理。
  4. 服务端(Server):服务的真正提供者。
  5. Network Service:底层传输,可以是 TCP 或 HTTP。

一次 RPC 调用流程如下:

  1. 服务消费者(Client 客户端)通过本地调用的方式调用服务。
  2. 客户端存根(Client Stub)接收到调用请求后负责将方法、入参等信息序列化(组装)成能够进行网络传输的消息体。
  3. 客户端存根(Client Stub)找到远程的服务地址,并且将消息通过网络发送给服务端。
  4. 服务端存根(Server Stub)收到消息后进行解码(反序列化操作)。
  5. 服务端存根(Server Stub)根据解码结果调用本地的服务进行相关处理
  6. 服务端(Server)本地服务业务处理。
  7. 处理结果返回给服务端存根(Server Stub)。
  8. 服务端存根(Server Stub)序列化结果。
  9. 服务端存根(Server Stub)将结果通过网络发送至消费方。
  10. 客户端存根(Client Stub)接收到消息,并进行解码(反序列化)。
  11. 服务消费方得到最终结果。

RPC的目标就是要2~10这些步骤都封装起来,让用户对这些细节透明。

实际上写代码也非常简单:

只需要先定义接口文件:

我们添加一个proto文件:helloworld.proto

只不过以前java中,我们叫interfce,文件名是.java,当然我们需要框架帮我们生成一些java类文件,帮助我们完成序列化、反序列化、编码等琐碎的工作。

syntax = "proto3"; // 协议版本

// 选项配置
option java_package = "com.chenj.protobuf";
option java_outer_classname = "RPCDateServiceApi";
option java_multiple_files = true;

// 定义包名
package com.chenj.protobuf;

// 服务接口.定义请求参数和相应结果	
service RPCDateService {
    rpc getDate (RPCDateRequest) returns (RPCDateResponse) {
    }
}

// 定义请求体
message RPCDateRequest {
    string userName = 1;
}

// 定义响应内容
message RPCDateResponse {
    string serverDate = 1;
}

根据.proto文件生成消息体类文件和XXXGrpc类文件

使用maven命令.

在第一步修改的pom.xml的路径下,首先执行

mvn protobuf:compile 生成消息体类文件

接着执行:

mvn protobuf:compile-custom 生成XXXGrpc类文件

使用maven插件, 编译.

第一个命令执行完. 在 target目录里找就行了. 第二个命令也是找就行了. 然后将生成的Java文件拷贝到你的目录里.就可以了

编写接口实现类

package com.chenj;

import com.chenj.grpc.api.RPCDateRequest;
import com.chenj.grpc.api.RPCDateResponse;
import com.chenj.grpc.api.RPCDateServiceGrpc;
import io.grpc.stub.StreamObserver;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

// RPCDateServiceGrpc.RPCDateServiceImplBase 这个就是接口.
// RPCDateServiceImpl 我们需要继承他的,实现方法回调
public class RPCDateServiceImpl extends RPCDateServiceGrpc.RPCDateServiceImplBase {
    @Override
    public void getDate(RPCDateRequest request, StreamObserver<RPCDateResponse> responseObserver) {
        //请求结果,我们定义的
        RPCDateResponse rpcDateResponse = null;
        //
        String userName = request.getUserName();
        String response = String.format("你好:%s,今天是%s.", userName,LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        try {
            // 定义响应,是一个builder构造器.
            rpcDateResponse = RPCDateResponse.newBuilder()
                    .setServerDate(response)
                    .build();
            //int i = 10/0;
        } catch (Exception e) {
            responseObserver.onError(e);
        } finally {
            
            responseObserver.onNext(rpcDateResponse);
        }

        responseObserver.onCompleted();

    }
}

定义服务端

package com.chenj;

import io.grpc.Server;
import io.grpc.ServerBuilder;

import java.io.IOException;

public class GRPCServer {
    private static final int port = 9999;

    public static void main(String[] args) throws IOException, InterruptedException {
        //设置service端口
        Server server = ServerBuilder.forPort(port)
                .addService(new RPCDateServiceImpl())
                .build().start();
        System.out.println(String.format("GRpc服务端启动成功, 端口号: %d.", port));

        server.awaitTermination();


    }
}

定义客户端

package com.chenj;

import com.chenj.grpc.api.RPCDateRequest;
import com.chenj.grpc.api.RPCDateResponse;
import com.chenj.grpc.api.RPCDateServiceGrpc;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;

public class GRPCClient {
    private static final String host = "localhost";
    private static final int serverPort = 9999;
    public static void main(String[] args) {
        //1,拿到一个通信channel
        ManagedChannel channel = ManagedChannelBuilder.forAddress(host, serverPort).
                usePlaintext()//无需加密或认证
                .build();
        try {
            //2.拿到stub对象
            RPCDateServiceGrpc.RPCDateServiceBlockingStub rpcDateService  = RPCDateServiceGrpc.newBlockingStub(channel);
            RPCDateRequest rpcDateRequest = RPCDateRequest.newBuilder()
                    .setUserName("JACK")
                    .build();
            //3,请求
            RPCDateResponse rpcDateResponse = rpcDateService.getDate(rpcDateRequest);
            //4,输出结果
            System.out.println(rpcDateResponse.getServerDate());
        } finally {
            // 5.关闭channel, 释放资源.
            channel.shutdown();
        }

    }
}

然后先启动Server:

再启动Client:

可以看到执行成功。一个简单的gRPC helloworld工程就搭建好了。当然具体到我们实际使用中,

还要搭配注册中心来完成rpc调用,不用自己写死域名、ip和端口

参考文献:3小时快速入门Java版gRPC系列(一)-了解RPC - 知乎

看这个绝大多数3小时学会gRPC - 知乎

java grpc 简单易懂 ---1 - 骨头酥 - 博客园简介: grpc是谷歌的一个开源的rpc(远程服务调用)框架,可以让各个语言按照指定的规则通过http2协议相互调用,这个规则是用Protocol Buffer(谷歌的一个数据描述语言)写的一个.prhttps://www.cnblogs.com/gutousu/p/9951956.html

 需要先生存

gRPC初探——概念介绍以及如何构建一个简单的gRPC服务 - takumiCX - 博客园[TOC] 引言 对于分布式系统而言,不同的服务分布在不同的节点上,一个服务要完成自己的功能经常需要调用其他服务的接口,比如典型的微服务架构。通常这种服务调用方式有两种,一种是发送HTTP请求的方式,https://www.cnblogs.com/takumicx/p/10059448.html

grpc服务注册与发现

gRPC服务发现与服务治理技术选型 - 掘金一. nginx + consul + consul-template 当有节点上线或下线时, 需要nginx reload. 有一定风险 (微服务在运行时难免会触发隐藏Bug或者panic, 如果每次都要nginx reload来确保健康的路由,我认为代价太大) 因为Envo…https://juejin.cn/post/6844903888298983432

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值