一,自定义rpc框架的思路
RPC(RemoteProcedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。
RPC采用客户机/服务器模式。请求程序就是一个客户机,而服务提供程序就是一个服务器。首先,客户机调用进程发送一个有进程参数的调用信息到服务进程,然后等待应答信息。在服务器端,进程保持睡眠状态直到调用信息到达为止。当一个调用信息到达,服务器获得进程参数,计算结果,发送答复信息,然后等待下一个调用信息,最后,客户端调用进程接收答复信息,获得进程结果,然后调用执行继续进行。
目的实现在控制层调用业务层方法
设计思路:
代码实现
二,socket流阻塞
1) 首先来看一段代码
Server.java
MyThread.java
Client.java
2)代码分析
每一个客户端在连接服务端的时候,在服务端都会产生一个线程。每个线程都会产生一个Socket来接收客户端发来的消息,若客户端停止发送消息,服务端线程仍在等待接收,占用资源,那么若客户端较多,线程池会占满,内存资源会溢出,导致服务器系统瘫痪。
若把代码做如下改变
这样的代码不仅非常怪异,而且你要知道客户端最后发来的内容是什么。即使这样做可以跳出循环,但是输入流仍然没有关闭,流仍然占用资源
三,nio原理
nio是NewIO 的简称,在jdk1.4里提供的新api。Sun官方标榜的特性如下: 为所有的原始类型提供(Buffer)缓存支持。字符集编码解码解决方案。Channel :一个新的原始I/O 抽象。 支持锁和内存映射文件的文件访问接口。 提供多路(non-bloking)非阻塞式的高伸缩性网络I/O。
1, 传统io
1)先将文件内容从磁盘中拷贝到操作系统buffer
2)再从操作系统buffer拷贝到程序应用buffer
3)从程序buffer拷贝到socketbuffer
4)从socketbuffer拷贝到协议引擎.
2.nio
NIO技术省去了将操作系统的readbuffer拷贝到程序的buffer, 以及从程序buffer拷贝到socketbuffer的步骤, 直接将 read buffer 拷贝到socket buffer. java 的 FileChannel.transferTo() 方法就是这样的实现,这个实现是依赖于操作系统底层的sendFile()实现的.
publicvoidtransferTo(long position, long count, WritableByteChannel target);
他的底层调用的是系统调用sendFile()方法
sendfile(intout_fd, int in_fd, off_t *offset, size_t count);
3.传统io与nio
IO | NIO |
面向流 | 面向缓冲 |
阻塞IO | 非阻塞IO |
无 | 选择器 |
面向流与面向缓冲
Java NIO和IO之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的。 JavaIO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。此外,它不能前后移动流中的数据。如果需要前后移动从流中读取的数据,需要先将它缓存到一个缓冲区。 Java NIO的缓冲导向方法略有不同。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。但是,还需要检查是否该缓冲区中包含所有您需要处理的数据。而且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里尚未处理的数据。
阻塞与非阻塞IO
Java IO的各种流是阻塞的。这意味着,当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。 Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。线程通常将非阻塞IO的空闲时间用于在其它通道上执行IO操作,所以一个单独的线程现在可以管理多个输入和输出通道(channel)。
选择器(Selectors)
Java NIO的选择器允许一个单独的线程来监视多个输入通道,你可以注册多个通道使用一个选择器,然后使用一个单独的线程来“选择”通道:这些通道里已经有可以处理的输入,或者选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道。
四,高性能nio框架netty
Netty是基于Java NIO的网络应用框架.
Netty是一个NIO client-server(客户端服务器)框架,使用Netty可以快速开发网络应用,例如服务器和客户端协议。Netty提供了一种新的方式来使开发网络应用程序,这种新的方式使得它很容易使用和有很强的扩展性。Netty的内部实现时很复杂的,但是Netty提供了简单易用的api从网络处理代码中解耦业务逻辑。Netty是完全基于NIO实现的,所以整个Netty都是异步的。
网络应用程序通常需要有较高的可扩展性,无论是Netty还是其他的基于JavaNIO的框架,都会提供可扩展性的解决方案。Netty中一个关键组成部分是它的异步特性.
1, netty的helloword
1) 下载netty包,下载地址http://netty.io/
package com.oracle.netty; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel;
/** * * * @author aqiu * */ publicclass EchoServer {
privatefinalintport;
public EchoServer(intport) { this.port = port; }
publicvoid start() throws Exception { EventLoopGroup eventLoopGroup = null; try { //创建ServerBootstrap实例来引导绑定和启动服务器 ServerBootstrap serverBootstrap = new ServerBootstrap(); //创建NioEventLoopGroup对象来处理事件,如接受新连接、接收数据、写数据等等 eventLoopGroup = new NioEventLoopGroup(); //指定通道类型为NioServerSocketChannel,设置InetSocketAddress让服务器监听某个端口已等待客户端连接。 serverBootstrap.group(eventLoopGroup).channel(NioServerSocketChannel.class).localAddress("localhost",port).childHandler(new ChannelInitializer<Channel>() { //设置childHandler执行所有的连接请求 @Override protectedvoid initChannel(Channel ch) throws Exception { ch.pipeline().addLast(new EchoServerHandler()); } }); // 最后绑定服务器等待直到绑定完成,调用sync()方法会阻塞直到服务器完成绑定,然后服务器等待通道关闭,因为使用sync(),所以关闭操作也会被阻塞。 ChannelFuture channelFuture = serverBootstrap.bind().sync(); System.out.println("开始监听,端口为:" + channelFuture.channel().localAddress()); channelFuture.channel().closeFuture().sync(); } finally { eventLoopGroup.shutdownGracefully().sync(); } }
publicstaticvoid main(String[] args) throws Exception { new EchoServer(20000).start(); } }
|
package com.oracle.netty;
import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter;
import java.util.Date;
publicclass EchoServerHandler extends ChannelInboundHandlerAdapter {
@Override publicvoid channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { System.out.println("server 读取数据……"); //读取数据 ByteBuf buf = (ByteBuf) msg; byte[] req = newbyte[buf.readableBytes()]; buf.readBytes(req); String body = new String(req, "UTF-8"); System.out.println("接收客户端数据:" + body); //向客户端写数据 System.out.println("server向client发送数据"); String currentTime = new Date(System.currentTimeMillis()).toString(); ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes()); ctx.write(resp); }
@Override publicvoid channelReadComplete(ChannelHandlerContext ctx) throws Exception { System.out.println("server 读取数据完毕.."); ctx.flush();//刷新后才将数据发出到SocketChannel }
@Override publicvoid exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); }
}
|
package com.oracle.netty;
import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel;
import java.net.InetSocketAddress;
/** * * * @author aqiu * */ publicclass EchoClient {
privatefinal String host; privatefinalintport;
public EchoClient(String host, intport) { this.host = host; this.port = port; }
publicvoid start() throws Exception { EventLoopGroup nioEventLoopGroup = null; try { //创建Bootstrap对象用来引导启动客户端 Bootstrap bootstrap = new Bootstrap(); //创建EventLoopGroup对象并设置到Bootstrap中,EventLoopGroup可以理解为是一个线程池,这个线程池用来处理连接、接受数据、发送数据 nioEventLoopGroup = new NioEventLoopGroup(); //创建InetSocketAddress并设置到Bootstrap中,InetSocketAddress是指定连接的服务器地址 bootstrap.group(nioEventLoopGroup).channel(NioSocketChannel.class).remoteAddress(new InetSocketAddress(host, port)) .handler(new ChannelInitializer<SocketChannel>() { //添加一个ChannelHandler,客户端成功连接服务器后就会被执行 @Override protectedvoid initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new EchoClientHandler()); } }); // 调用Bootstrap.connect()来连接服务器 ChannelFuture f = bootstrap.connect().sync(); // 最后关闭EventLoopGroup来释放资源 f.channel().closeFuture().sync(); } finally { nioEventLoopGroup.shutdownGracefully().sync(); } }
publicstaticvoid main(String[] args) throws Exception { new EchoClient("localhost", 20000).start(); } }
|
package com.oracle.netty; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler;
publicclass EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> { //客户端连接服务器后被调用 @Override publicvoid channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println("客户端连接服务器,开始发送数据……"); byte[] req = "QUERY TIME ORDER".getBytes(); ByteBuf firstMessage = Unpooled.buffer(req.length); firstMessage.writeBytes(req); ctx.writeAndFlush(firstMessage); } //从服务器接收到数据后调用 @Override protectedvoid channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { System.out.println("client 读取server数据.."); //服务端返回消息后 ByteBuf buf = (ByteBuf) msg; byte[] req = newbyte[buf.readableBytes()]; buf.readBytes(req); String body = new String(req, "UTF-8"); System.out.println("服务端数据为 :" + body); } //发生异常时被调用 @Override publicvoid exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { System.out.println("client exceptionCaught.."); // 释放资源 ctx.close(); } }
|
五,基于netty的rpc框架的实现
服务端
package com.oracle.service; publicinterface Service { public String s(String str); } |
package com.oracle.service.impl; import com.oracle.myrpc.RPCService; import com.oracle.service.Service; @RPCService("service") public class ServiceImpl implements Service{ public String s(String str) {
return str; } } |
package com.oracle.myrpc;
import com.oracle.service.impl.ServiceImpl;
import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel;
public class DiscardServer {
private static final int portNumber = 6666;
public static void main(String[] args) throws Exception { MyRPCAnnotation.annotationHandler(ServiceImpl.class); EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup); b.channel(NioServerSocketChannel.class); b.childHandler(new ChannelInitializer<SocketChannel>() {
@Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new DiscardServerHandler());
} });
// 服务器绑定端口监听 ChannelFuture f = b.bind(portNumber).sync(); // 监听服务器关闭监听 f.channel().closeFuture().sync();
// 可以简写为 /* b.bind(portNumber).sync().channel().closeFuture().sync(); */ } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } } |
package com.oracle.myrpc;
import java.lang.reflect.Method; import java.net.InetAddress;
import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter;
public class DiscardServerHandler extends ChannelInboundHandlerAdapter{
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { // 收到消息直接打印输出 System.out.println("server 读取数据……"); //读取数据 ByteBuf buf = (ByteBuf) msg; byte[] req = new byte[buf.readableBytes()]; buf.readBytes(req); String body = new String(req, "UTF-8"); String[] strs = body.split("_"); String objectName = strs[0]; String methodName = strs[1]; //应该解析参数类型以及值... String param = strs[2]; String result = execute(objectName,methodName,param); System.out.println(result+"454545"); // 返回客户端消息 - 方法执行的结果 byte[] resp = result.getBytes("utf-8"); ByteBuf firstMessage = Unpooled.buffer(resp.length); firstMessage.writeBytes(resp); ctx.writeAndFlush(firstMessage); } // private String execute(String objectName,String methodName,String param){ Object obj = MyRPCAnnotation.getMap().get(objectName); Class clazz = obj.getClass(); String result = null; try { Method method = clazz.getMethod(methodName, String.class); result = (String)method.invoke(obj, param); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return result; }
/* * * 覆盖 channelActive 方法在channel被启用的时候触发 (在建立连接的时候) * * channelActive 和 channelInActive 在后面的内容中讲述,这里先不做详细的描述 * */ @Override public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("RamoteAddress : " + ctx.channel().remoteAddress() + " active !");
ctx.writeAndFlush( "Welcome to " + InetAddress.getLocalHost().getHostName() + " service!\n");
super.channelActive(ctx); } } |
package com.oracle.myrpc;
import java.util.HashMap; import java.util.Map;
/** * * @author aqiu * @content 注解处理器 */ public class MyRPCAnnotation {
private static Map<String, Object> map = new HashMap<>();
public static void annotationHandler(Class<?> clazz) throws Exception{ //解析注解目的 //获取注解value值 实例化对象 RPCService nclass = clazz.getAnnotation(RPCService.class); String key = nclass.value(); map.put(key, clazz.newInstance()); } public static Map<String, Object> getMap(){ return map; } } |
package com.oracle.myrpc;
import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;
@Target(value={ElementType.TYPE}) @Documented @Retention(RetentionPolicy.RUNTIME) public @interface RPCService {
String value() default ""; } |
客户端
package com.oracle.controller;
import com.oracle.myrpc.Autowire; import com.oracle.service.Service;
public class Controller {
@Autowire private Service service;
public void setService(Service service) { this.service = service; }
public void test(){ String s = service.s("我自己做的rpc成功了!"); System.out.println(s); } // public static void main(String[] args) throws Exception { // Controller c = new Controller(); // MyRPCAnnotation1.annotationHandler(c); // c.test(); // // } } |
package com.oracle.service;
public interface Service {
public String s(String str); }
|
package com.oracle.myrpc;
import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) @Target(value={ElementType.FIELD}) @Documented public @interface Autowire {
} |
package com.oracle.myrpc;
import java.net.InetSocketAddress;
import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel;
public class DiscardClient {
public static String host = "127.0.0.1"; public static int port = 6666;
/** * @param args * @throws InterruptedException * @throws IOException */ public static void start() throws Exception { EventLoopGroup nioEventLoopGroup = null; try { //创建Bootstrap对象用来引导启动客户端 Bootstrap bootstrap = new Bootstrap(); //创建EventLoopGroup对象并设置到Bootstrap中,EventLoopGroup可以理解为是一个线程池,这个线程池用来处理连接、接受数据、发送数据 nioEventLoopGroup = new NioEventLoopGroup(); //创建InetSocketAddress并设置到Bootstrap中,InetSocketAddress是指定连接的服务器地址 bootstrap.group(nioEventLoopGroup).channel(NioSocketChannel.class).remoteAddress(new InetSocketAddress(host, port)) .handler(new ChannelInitializer<SocketChannel>() { //添加一个ChannelHandler,客户端成功连接服务器后就会被执行 @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new DiscardClientHandler()); } }); // 调用Bootstrap.connect()来连接服务器 ChannelFuture f = bootstrap.connect().sync(); // 最后关闭EventLoopGroup来释放资源 f.channel().closeFuture().sync(); } finally { nioEventLoopGroup.shutdownGracefully().sync(); } } public static void main(String[] args) throws Exception { DiscardClient.start(); } } |
package com.oracle.myrpc;
import com.oracle.controller.Controller;
import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler;
public class DiscardClientHandler extends SimpleChannelInboundHandler<ByteBuf>{
//客户端连接服务器后被调用 @Override public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("客户端开始写消息...");
Controller c = new Controller(); MyRPCAnnotation1.annotationHandler(c,ctx); c.test();
} //从服务器接收到数据后调用 @Override protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { // 收到消息直接打印输出 System.out.println("client 读取数据……"); //读取数据 ByteBuf buf = (ByteBuf) msg; byte[] req = new byte[buf.readableBytes()]; buf.readBytes(req); String body = new String(req, "UTF-8"); System.out.println(body); } //发生异常时被调用 @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { System.out.println("client exceptionCaught.."); // 释放资源 ctx.close(); }
} |
package com.oracle.myrpc;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy;
import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext;
public class MyHandler implements InvocationHandler {
private static ChannelHandlerContext ctx;
/** * 在invoke方法中实现对真实角色的控制 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String objectName = "service"; String methodName = method.getName(); String param = String.valueOf(args[0]); String str = objectName+"_"+methodName+"_"+param; byte[] req = str.getBytes("utf-8"); ByteBuf firstMessage = Unpooled.buffer(req.length); firstMessage.writeBytes(req);
ctx.writeAndFlush(firstMessage); return "123"; }
public static <T> T newMapperProxy(Class<T> mapperInterface,ChannelHandlerContext ctx) { MyHandler.ctx = ctx; ClassLoader classLoader = mapperInterface.getClassLoader(); Class<?>[] interfaces = new Class[]{mapperInterface}; MyHandler proxy = new MyHandler(); return (T) Proxy.newProxyInstance(classLoader, interfaces, proxy); } } |
package com.oracle.myrpc;
import java.lang.reflect.Field;
import com.oracle.service.Service;
import io.netty.channel.ChannelHandlerContext;
public class MyRPCAnnotation1 {
public static void annotationHandler(Object obj,ChannelHandlerContext ctx) throws Exception{ Field field = obj.getClass().getDeclaredField("service"); Autowire autowire = field.getAnnotation(Autowire.class); if(autowire!=null){ Service service = MyHandler.newMapperProxy(Service.class,ctx); obj.getClass().getMethod("setService", Service.class).invoke(obj,service); } } } |
就写到这吧,后期完善…