您的位置:首页 > 理论基础 > 计算机网络

一起写RPC框架(三)RPC网络模块的搭建一 网络传输模型

2016-10-19 14:27 393 查看
工欲善其事必先利其器,写一个RPC框架,最最重要的就是抓住它的灵魂,网络模块无疑就是整个RPC的Soul,如何完成一个高质量的网络通讯的底层框架,我现在还没有掌握,哈哈,没有逗你玩的意思,不管是Netty还是Mina,这些通讯框架还是离不开对协议的支持,TCP协议,一些参数配置,里面的水还是很深的,每个TCP的配置参数我们还是需要做一些了解,这样才能做到优化网络传输模型,提高所谓的QPS

现在N多RPC框架应该选用的都是Netty,基于多种原因吧:

1)它很容易上手,它封装了网络传输底层的实现,但也有Spring的开闭原则,可以根据用户的参数配置去定制属于自己的网络传输模型

2)它优化了线程模型,且基于事件驱动,基于NIO,提高IO效率

3)内存优化,直接内存的使用/池化的技术

4)第四点,我也是认为很重要的一点,也是我们写这个RPC框架的核心点,就是有很多参考,基于Netty的成熟的生产级别的代码可以参考(本RPC框架的网络模块netty应用是融合Jupiter和RocketMQ的),说白了,有点抄袭,但也是做了部分的修改

laopopo-rpc的网络框架的模型就是参考的RocketMQ,(希望大家还是写过Netty的HelloWorld的)

说到网络传输,我们不得不提,网络传输对象的问题,我们设想一个场景,假如A系统发送某个信息给B系统,为了防止粘包和拆包的问题,我们需要为这个信息量身定做一个编码器和译码器,B系统给C系统发送一个信息,我们还需要些一个编码器和译码器,这样就会大大加大我们的工作量,所以我们需要统一我们的传输对象,写一个通用的解码器和译码器,这样做的好处是:

1)统一唯一的网络传输对象方便Netty对网络对象的编码和解码

2)统一的编码和解码也解决了TCP 粘包和拆包的问题

3)方便我们编码,不需要花很多心思去处理数据的网络传输,不需要为了每一个请求都要写一个处理器handler和(解码器)decoder和(编码器)encoder,统一处理

不记得哪一天,我无意中看了RocketMQ的源码,看到了这些大神设计的思路,所以我们借而用之:

我们网络传输对象叫做RemotingTransporter,哈哈,远程传输对象

package org.laopopo.remoting.model;

import java.util.concurrent.atomic.AtomicLong;

import org.laopopo.common.protocal.LaopopoProtocol;
import org.laopopo.common.transport.body.CommonCustomBody;

/**
*
* @author BazingaLyn
* @description 网络传输的唯一对象
* @time 2016年8月10日
* @modifytime
*/
public class RemotingTransporter extends ByteHolder {

private static final AtomicLong requestId = new AtomicLong(0l);

/**
* 请求的类型
* 例如该请求是用来订阅服务的,该请求是用来发布服务的等等的
* 假设 code == 1 代表是消费者订阅服务,则接收方注册中心接到该对象的时候,就会先获取该code,判断如果该code==1 则走订阅服务的处理分支代码
* 假设 code == 2 代表是提供者发布服务,则接收发注册中心接收该对象的时候,也会先获取该code,判断如果该code==2则走发布服务的处理分支代码
*/
private byte code;

/**
* 请求的主体信息 {@link CommonCustomBody}是一个接口
* 假如code==1 则CommonCustomBody中则是一些订阅服务的具体信息
* 假如code==2 则CommonCustomBody中则是一些发布服务的具体信息
*/
private transient CommonCustomBody customHeader;

/**
* 请求的时间戳
*/
private transient long timestamp;

/**
* 请求的id
*/
private long opaque = requestId.getAndIncrement();

/**
* 定义该传输对象是请求还是响应信息
*/
private byte transporterType;

protected RemotingTransporter() {
}

/**
* 创建一个请求传输对象
* @param code 请求的类型
* @param commonCustomHeader 请求的正文
* @return
*/
public static RemotingTransporter createRequestTransporter(byte code,CommonCustomBody commonCustomHeader){
RemotingTransporter remotingTransporter = new RemotingTransporter();
remotingTransporter.setCode(code);
remotingTransporter.customHeader = commonCustomHeader;
remotingTransporter.transporterType = LaopopoProtocol.REQUEST_REMOTING;
return remotingTransporter;
}

/**
* 创建一个响应对象
* @param code 响应对象的类型
* @param commonCustomHeader 响应对象的正文
* @param opaque 此响应对象对应的请求对象的id
* @return
*/
public static RemotingTransporter createResponseTransporter(byte code,CommonCustomBody commonCustomHeader,long opaque){
RemotingTransporter remotingTransporter = new RemotingTransporter();
remotingTransporter.setCode(code);
remotingTransporter.customHeader = commonCustomHeader;
remotingTransporter.setOpaque(opaque);
remotingTransporter.transporterType = LaopopoProtocol.RESPONSE_REMOTING;
return remotingTransporter;
}

//省略Getter和Setter toString

}


ByteHolder.java

package org.laopopo.remoting.model;

/**
*
* @author BazingaLyn
* @description
* @time 2016年8月9日
* @modifytime
*/
public class ByteHolder {

private transient byte[] bytes;

public byte[] bytes() {
return bytes;
}

public void bytes(byte[] bytes) {
this.bytes = bytes;
}

public int size() {
return bytes == null ? 0 : bytes.length;
}

}


我怕大家还是不理解上面这个类的用途,还是再解释一遍吧,其实很简单,我们学过netty的HelloWorld都知道,网络传输中,都会有一个编码器和译码器,编码器和译码器是成对的,通信的双方按照规定进行编码和译码,而不是各自编各自的,各自译各自的,那就相当于鸡同鸭讲,不知道说的是什么了

除开RPC这个大背景,假设有A 系统发送一个一些学生的信息List<Student>的信息,或者一个Teacher的信息给B系统,用我们上文规定的那个RemotingTransporter去发送,A系统的程序猿说,当RemotingTransporter中的code == 1的时候,表示我发送的学生的信息,等于二的时候就是一个Teacher的信息,这些信息数据放在ByteHolder的属性bytes中,你获取到之后,反序列化一下,就可以了,B按照A程序员的规定去做,

if(code== 1){序列化List<Student>}else{序列化Teacher} 果然没有问题

A程序员也很轻松的构建了网络传输的对象

package org.laopopo.example.netty.example_1;

import java.util.ArrayList;
import java.util.List;

import org.laopopo.common.exception.remoting.RemotingCommmonCustomException;
import org.laopopo.common.transport.body.CommonCustomBody;
import org.laopopo.remoting.model.RemotingTransporter;

public class NettyTransporterTest1 {

public static final byte STUDENTS = 1;
public static final byte TEACHER = 2;

public static void main(String[] args) {

StudentInfos infos = new StudentInfos();
//学生信息
RemotingTransporter studentsRemotingTransporter = RemotingTransporter.createRequestTransporter(STUDENTS, infos);
//学生信息
TeacherInfo info = new TeacherInfo();
RemotingTransporter teacherRemotingTransporter = RemotingTransporter.createRequestTransporter(TEACHER, info);
}

private static class StudentInfos implements CommonCustomBody {

List<String> students = new ArrayList<String>();

@Override
public void checkFields() throws RemotingCommmonCustomException {
}

public List<String> getStudents() {
return students;
}

public void setStudents(List<String> students) {
this.students = students;
}

}

private static class TeacherInfo implements CommonCustomBody {

String teacher = "";

@Override
public void checkFields() throws RemotingCommmonCustomException {
}

public String getTeacher() {
return teacher;
}

public void setTeacher(String teacher) {
this.teacher = teacher;
}

}

}


然后可以通过Netty的一些API就可以将这个网络传输模型发送给B系统,B系统也可以很方便的去反序列化成有用的信息,A系统向传输 Student,Teacher,User,Book,XXXinfo,只需要将其实现CommonCustomBody接口,而因为java可以实现多个接口,所以这样并不会影响你java类的使用,所以也很简单,好了,我们现在已经定义好了网络传输的模型了,接下来我们看看RPC中对RemotingTransporter的编码器和译码器的编写吧
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: