简单的netty实现的rpc

 

一,自定义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("serverclient发送数据");

        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);

        }

    }

}

就写到这吧,后期完善…


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

aqiu12316

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值