不灭的焱

革命尚未成功,同志仍须努力

作者:php-note.com  发布于:2018-10-16 23:51  分类:Java基础 

工欲善其事必先利其器,写一个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的编码器和译码器的编写吧

 

 

摘自:https://blog.csdn.net/linuu/article/details/52213911