day05.轻量级RPC框架【大数据教程】
轻量级RPC框架开发
1. RPC原理学习
1.1. 什么是RPC
RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。
RPC采用客户机/服务器模式。请求程序就是一个客户机,而服务提供程序就是一个服务器。首先,客户机调用进程发送一个有进程参数的调用信息到服务进程,然后等待应答信息。在服务器端,进程保持睡眠状态直到调用信息到达为止。当一个调用信息到达,服务器获得进程参数,计算结果,发送答复信息,然后等待下一个调用信息,最后,客户端调用进程接收答复信息,获得进程结果,然后调用执行继续进行。
1.2. RPC原理
运行时,一次客户机对服务器的RPC调用,其内部操作大致有如下十步:
1.调用客户端句柄;执行传送参数
2.调用本地系统内核发送网络消息
3.消息传送到远程主机
4.服务器句柄得到消息并取得参数
5.执行远程过程
6.执行的过程将结果返回服务器句柄
7.服务器句柄返回结果,调用远程系统内核
8.消息传回本地主机
9.客户句柄由内核接收消息
10.客户接收句柄返回的数据
1.3. hadoopRPC演示
见代码(教程和代码在www.javahelp.com.cn)
2.nio原理学习(nio的优势不在于数据传送的速度)
2.1. 简介
nio是New IO的简称,在jdk1.4里提供的新api。Sun官方标榜的特性如下: 为所有的原始类型提供(Buffer)缓存支持。字符集编码解码解决方案。Channel:一个新的原始I/O抽象。 支持锁和内存映射文件的文件访问接口。 提供多路(non-bloking)非阻塞式的高伸缩性网络I/O。
2.2. 传统socket和socket nio代码
见代码(教程和代码在www.javahelp.com.cn)
2.3. socket nio原理
2.3.1.传统的I/O
使用传统的I/O程序读取文件内容,并写入到另一个文件(或Socket),如下程序:
File.read(fileDesc, buf, len);
Socket.send(socket, buf, len);
会有较大的性能开销,主要表现在一下两方面:
1.上下文切换(context switch),此处有4次用户态和内核态的切换
2. Buffer内存开销,一个是应用程序buffer,另一个是系统读取buffer以及socket buffer
其运行示意图如下
1)先将文件内容从磁盘中拷贝到操作系统buffer
2)再从操作系统buffer拷贝到程序应用buffer
3)从程序buffer拷贝到socket buffer
4)从socket buffer拷贝到协议引擎.
2.3.2. NIO
NIO技术省去了将操作系统的read buffer拷贝到程序的buffer,以及从程序buffer拷贝到socket buffer的步骤,直接将read buffer拷贝到socket buffer. java的FileChannel.transferTo()方法就是这样的实现,这个实现是依赖于操作系统底层的sendFile()实现的.
publicvoid transferTo(long position, long count, WritableByteChannel target);
他的底层调用的是系统调用sendFile()方法
sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
如下图
3.netty常用API学习
3.1. netty简介
Netty是基于Java NIO的网络应用框架.
Netty是一个NIO client-server(客户端服务器)框架,使用Netty可以快速开发网络应用,例如服务器和客户端协议。Netty提供了一种新的方式来使开发网络应用程序,这种新的方式使得它很容易使用和有很强的扩展性。Netty的内部实现时很复杂的,但是Netty提供了简单易用的api从网络处理代码中解耦业务逻辑。Netty是完全基于NIO实现的,所以整个Netty都是异步的。
网络应用程序通常需要有较高的可扩展性,无论是Netty还是其他的基于Java NIO的框架,都会提供可扩展性的解决方案。Netty中一个关键组成部分是它的异步特性.
3.2. netty的helloworld
3.2.1.下载netty包
•下载netty包,下载地址http://netty.io/
3.2.2. 服务端启动类
3.2.3. 服务端回调方法
3.2.4. 客户端启动类
3.2.5. 客户端回调方法
3.3. netty中handler的执行顺序
3.3.1. 简介
Handler在netty中,无疑占据着非常重要的地位。Handler与Servlet中的filter很像,通过Handler可以完成通讯报文的解码编码、拦截指定的报文、统一对日志错误进行处理、统一对请求进行计数、控制Handler执行与否。一句话,没有它做不到的只有你想不到的。
Netty中的所有handler都实现自ChannelHandler接口。按照输出输出来分,分为ChannelInboundHandler、ChannelOutboundHandler两大类。ChannelInboundHandler对从客户端发往服务器的报文进行处理,一般用来执行解码、读取客户端数据、进行业务处理等;ChannelOutboundHandler对从服务器发往客户端的报文进行处理,一般用来进行编码、发送报文到客户端。
Netty中,可以注册多个handler。ChannelInboundHandler按照注册的先后顺序执行;ChannelOutboundHandler按照注册的先后顺序逆序执行,如下图所示,按照注册的先后顺序对Handler进行排序,request进入Netty后的执行顺序为:
3.3.2. 代码
见代码
3.3.3. 总结
在使用Handler的过程中,需要注意:
1、ChannelInboundHandler之间的传递,通过调用ctx.fireChannelRead(msg)实现;调用ctx.write(msg)将传递到ChannelOutboundHandler。
2、ctx.write()方法执行后,需要调用flush()方法才能令它立即执行。
3、流水线pipeline中outhandler不能放在最后,否则不生效
4、Handler的消费处理放在最后一个处理。
3.4. netty发送对象
3.4.1. 简介
Netty中,通讯的双方建立连接后,会把数据按照ByteBuf的方式进行传输,例如http协议中,就是通过HttpRequestDecoder对ByteBuf数据流进行处理,转换成http的对象。基于这个思路,我自定义一种通讯协议:Server和客户端直接传输java对象。
实现的原理是通过Encoder把java对象转换成ByteBuf流进行传输,通过Decoder把ByteBuf转换成java对象进行处理,处理逻辑如下图所示:
3.4.2. 代码
见代码
4.Spring(IOC/AOP)注解学习
4.1. spring的初始化顺序
在spring的配置文件中配置bean,如下
在One类和Two类中,分别实现一个参数的构造如下
加载spring配置文件,初始化bean如下
那么。结果如何呢?
结论:spring会按照bean的顺序依次初始化xml中配置的所有bean
4.1.1. 通过ApplicationContextAware加载Spring上下文环境
在One中实现ApplicationContextAware接口会出现如何的变换呢?
结果
4.1.2. InitializingBean的作用
在One中实现InitializingBean接口呢?
结果:
4.1.3. 如果使用注解@Component
使用@Component注入类,那么它的顺序是如何呢?
4.1.4. 结论
1.spring先检查注解注入的bean,并将它们实例化
2.然后spring初始化bean的顺序是按照xml中配置的顺序依次执行构造
3.如果某个类实现了ApplicationContextAware接口,会在类初始化完成后调用setApplicationContext()方法进行操作
4.如果某个类实现了InitializingBean接口,会在类初始化完成后,并在setApplicationContext()方法执行完毕后,调用afterPropertiesSet()方法进行操作
4.2. 注解使用回顾
1、在spring中,用注解来向Spring容器注册Bean。需要在applicationContext.xml中注册。
2、如果某个类的头上带有特定的注解@Component/@Repository/@Service/@Controller,就会将这个对象作为Bean注册进Spring容器
3、在使用spring管理的bean时,无需在对调用的对象进行new的过程,只需使用@Autowired将需要的bean注入本类即可
4.3. 自定义注解
4.3.1.解释
1.自定义注解的作用:在反射中获取注解,以取得注解修饰的“类、方法、属性”的相关解释。
2.java内置注解
@Target表示该注解用于什么地方,可能的ElemenetType参数包括:
ElemenetType.CONSTRUCTOR构造器声明
ElemenetType.FIELD域声明(包括enum实例)
ElemenetType.LOCAL_VARIABLE局部变量声明
ElemenetType.METHOD方法声明
ElemenetType.PACKAGE包声明
ElemenetType.PARAMETER参数声明
ElemenetType.TYPE类,接口(包括注解类型)或enum声明
@Retention表示在什么级别保存该注解信息。可选的RetentionPolicy参数包括:
RetentionPolicy.SOURCE注解将被编译器丢弃
RetentionPolicy.CLASS注解在class文件中可用,但会被VM丢弃
RetentionPolicy.RUNTIME JVM将在运行期也保留注释,因此可以通过反射机制读取注解的信息。
4.3.2. 实现
定义自定义注解
2.将直接类加到需要使用的类上,我们可以通过获取注解,来得到这个类
3.类实现的接口
4.通过ApplicationContext获取所有标记这个注解的类
5.结合spring实现junit测试
5.轻量级RPC框架开发
5.1.轻量级RPC框架需求分析及原理分析
5.1.1. netty实现的RPC的缺点
在我们平常使用的RPC中,例如webservice,使用的习惯类似于下图
但是netty的实现过于底层,我们不能够像以前一样只关心方法的调用,而是要关心数据的传输,对于不熟悉netty的开发者,需要了解很多netty的概念和逻辑,才能实现RPC的调用。
应上面的需求,我们需要基于netty实现一个我们熟悉的RPC框架。逻辑如下:
5.2. 轻量级RPC框架开发
见代码(教程和代码在www.javahelp.com.cn)
5.3. zookeeper API简单使用及框架介绍
5.3.1. zk简介和单机版搭建
见文档(教程和代码在www.javahelp.com.cn)
5.3.2. zk简单api使用
见代码(教程和代码在www.javahelp.com.cn)
5.3.3. zk在框架中的实现
在上面的框架中,server端存在着一个问题,就是单点问题,也就是说,当服务端“挂了”之后,框架的使用就造成了单点屏障。
我们可以通过zookeeper来实现服务端的负载均衡
领取专属 10元无门槛券
私享最新 技术干货