一款RPC产品应该具有的功能(一)

什么是RPC?

        RPC的全称是远程过程调用(Remote Procedure Call),由于数据不在同一个内存空间,而是在其他的服务器上,所以需要通过网络来表达语义和传达数据,通过它可以调用其他服务器上提供的服务。运用最多的地方就是在微服务里面,它能够让调用方像调用本地方法一样来调用远程服务,从而提高开发效率。

一款RPC产品应该具有哪些功能:

一、调用代理类

        代理类就是一个程序代码里的一个class,这个类提供远程调用的功能,调用方只需要在代码里new一个代理对象,通过这个代理对象提供的方法来调用远程服务获取数据,使用起来非常的方便。如下所示,userServiceProxy是用户服务的代理类,调用类的getUserInfo方法获得用户数据。

//调用用户服务获得用户信息,通过代理类
UserServiceProxy userServiceProxy = new UserServiceProxy();
User user = userServiceProxy.getUserInfo(1L);

二、组包和解包

        调用远程服务必然要走网络,走网络就要涉及到数据在网络上的传输,对于网络传输我们都是基于socket编程的,那么在socket的接收缓冲区里数据是一个一个的字节组成的字节流,服务接受方收到多个客户端传来的数据时,多个客户端的数据都是连在一起的字节流,如果不按照一定的规范组织就无法区分一个个的请求数据。为了区分一个个的请求数据需要定义一个协议来组织字节流数据,协议的数据由一个一个的数据包组成,一个数据包就代表一次客户端的请求数据,在这里列举了一个协议包的示例如下图所示:

        数据包分为包头(Header)和包体(Body)2大部分,包头的长度是固定的,包体的长度是边长的。包头用来识别整个包,而包体用来存放数据,由于包的数据有多有少,所以包体的长度是边长的。

    包头(Header)里包含有版本号(Version)、命令(Command)、魔法号(MagicNumber)、包体长度(BoydLength)。

        版本号:当协议有升级的时候,可以通过版本号来做兼容逻辑。

        命令:表示调用服务提供方的哪个服务,比如Command等于1表示获取用户服务的用户信息。

        魔法号:用户判断接受的包是合法的包,而不是其他伪造的包。

        包体长度:表示body的长度。

        以上介绍了RPC的数据包结构,我们的RPC数据就得按照协议来组织字节流,所以就要把RPC对象组装成一个字节流的数据包(组包)和把字节流的数据包解包成RPC对象(解包),如下代码所示:

package rpc.protocol;

public class RpcProtocol
{
    private int version;
    private int command;
    private int magicNumber;
    private int bodyLength;
    private byte[] body;
    final public static int HEAD_LENGTH = 16;

    public RpcProtocol setVersion(int version) {
        this.version = version;
        return this;
    }

    public int getVersion() {
        return version;
    }

    public RpcProtocol setCommand(int command) {
        this.command = command;
        return this;
    }

    public int getCommand() {
        return command;
    }

    public RpcProtocol setMagicNumber(int magicNumber) {
        this.magicNumber = magicNumber;
        return this;
    }

    public int getMagicNumber() {
        return magicNumber;
    }

    public RpcProtocol setBodyLength(int bodyLength) {
        this.bodyLength = bodyLength;
        return this;
    }

    public int getBodyLength() {
        return bodyLength;
    }

    public RpcProtocol setBody(byte[] body) {
        this.body = body;
        return this;
    }

    public byte[] getBody() {
        return body;
    }

    //把RPC对象组装成一个字节流的数据包
    public byte[] generateByteArray() {
        byte[] data = new byte[bodyLength+HEAD_LENGTH];
        int index = 0;
        System.arraycopy(ByteConverter.intToBytes(version), 0, data, index, Integer.BYTES);
        index += Integer.BYTES;
        System.arraycopy(ByteConverter.intToBytes(command), 0, data, index, Integer.BYTES);
        index += Integer.BYTES;
        System.arraycopy(ByteConverter.intToBytes(magicNumber), 0, data, index, Integer.BYTES);
        index += Integer.BYTES;
        System.arraycopy(ByteConverter.intToBytes(bodyLength), 0, data, index, Integer.BYTES);
        index += Integer.BYTES;
        System.arraycopy(body, 0, data, index, body.length);
        return data;
    }

    //把字节流的数据包解包成RPC对象
    public RpcProtocol byteArrayToRpcHeader(byte[] data) {
        if (data == null || data.length==0) {
            return null;
        }
        int index = 0;
        this.setVersion(ByteConverter.bytesToInt(data, index));
        index += Integer.BYTES;
        this.setCommand(ByteConverter.bytesToInt(data,index));
        index += Integer.BYTES;
        this.setMagicNumber(ByteConverter.bytesToInt(data,index));
        index += Integer.BYTES;
        this.setBodyLength(ByteConverter.bytesToInt(data,index));
        index += Integer.BYTES;
        this.body = new byte[bodyLength];
        System.arraycopy(data, index, this.body, 0, bodyLength);
        return this;
    }
}

三、序列化和反序列化

        除了要把数据按照数据包的格式组织外,包里面的每一个字段都要转化成字节流的形式,因为我们传送和接收数据都是按照字节的方式来处理的。比如数据包里的版本号(Version)字段,就得由int类型转化成为4字节的byte数组,Command、MagicNumber、BodyLength和Body都要转化成byte数组,这样才能在网络上传输,这就需要一个序列化的功能。有了序列化就得有反序列化把序列化的数据解析出来。

        序列化:将int类型的数据序列化为字节数组,如下所示:

public static byte[] intToBytes(int n) {
    byte[] buf = new byte[4];
    buf[0] = (byte) n;
    buf[1] = (byte) (n>>8);
    buf[2] = (byte) (n>>16);
    buf[3] = (byte) (n>>24);
    return buf;
}

        反序列化:将字节数组反序列化为int数,如下所示:

public static int bytesToInt(byte[] buf, int offset) {
    return buf[offset] & 0xff
            | (buf[offset+1] << 8) & 0xff00
            | (buf[offset+2] << 16) & 0xff0000
            | (buf[offset+3] << 24) & 0xff000000;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值