手写RPC基于netty

什么是rpc?
RPC,即 Remote Procedure Call(远程过程调用),调用远程计算机上的服务,就像调用本地服务一
样,相当于你new对象,然后对象点方法 object.tostring()一样一样滴,jvm帮你调用 ,帮你取指译码执行等,rpc其实是本地创建代理,代理分为静态与动态(jdk与cglib,dubbo是另一种字节码)。

rpc实现步骤

  1. 服务消费方(client)调用以本地调用方式调用服务;
  2. client stub 接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体;
  3. client stub 找到服务地址,并将消息发送到服务端;
  4. server stub 收到消息后进行解码;
  5. server stub 根据解码结果调用本地的服务;
  6. 本地服务执行并将结果返回给 server stub;
  7. server stub 将返回结果打包成消息并发送至消费方;
  8. client stub 接收到消息,并进行解码;
  9. 服务消费方得到最终结果。

与其他rpc框架相比 ,我们框架缺少什么?
例如 我们缺少路由(标签路由,脚本路由,条件路由),重试(根据配置重试次数,然后for循环等),负载均衡(随机,hash一致性,轮训),注册中心(我们采用相当与直连)等。
关于netty
文章给出详细注解,算是入门级别,可以详细看下,与我们传统编程的区别,编码解码都是netty默认的
我们给出代码
github地址:https://github.com/ylha/rpcdemo.git
https://github.com/ylha/rpcclient1
server端

package com.test;

import java.io.Serializable;

/**
 * serversub
 */
public class ClassInfo implements Serializable {
    /**
     * 序列化id
     */
    /**
     * 服务端存根就是根据收到的请求信息执行相应的业务逻辑调用并把结果返回客户端
     */
    private static final long serialVersionUID = -7821682294197810003L;

    private String className;//类名

    private String methodName;//返回值

    private Class<?>[] types; //参数类型

    private Object[] objects; //参数列表

    public static long getSerialVersionUID() {
        return serialVersionUID;
    }

    public String getClassName() {
        return className;
    }

    public void setClassName(String className) {
        this.className = className;
    }

    public String getMethodName() {
        return methodName;
    }

    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }

    public Class<?>[] getTypes() {
        return types;
    }

    public void setTypes(Class<?>[] types) {
        this.types = types;
    }

    public Object[] getObjects() {
        return objects;
    }

    public void setObjects(Object[] objects) {
        this.objects = objects;
    }
}

服务端接口

package com.test;
/**
 * 接口
 */
public interface HelloRPC {
    String hello(String name);
}

服务端实现类

 package com.test;


/**
 * 接口实现类
 * @author admin
 */
public class HelloRpcImpl implements HelloRPC {
    @Override
    public String hello(String name) {
        return "hello,myGirlFriend" + "=====>>>>" + name;
    }
}

处理器 InvokeHandler

package com.test;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import org.reflections.Reflections;

import java.lang.reflect.Method;
import java.util.Set;

public class InvokeHandler extends ChannelInboundHandlerAdapter {
    /**
     * 触发读事件,重写读取消息的方法,
     *
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ClassInfo classInfo = (ClassInfo) msg;

        Object clazz = Class.forName(getImplClassName(classInfo)).newInstance();
        Method method = clazz.getClass().getMethod(classInfo.getMethodName(), classInfo.getTypes());
        //通过反射调用实现类的方法
        Object result = method.invoke(clazz, classInfo.getObjects());
        ctx.writeAndFlush(result);
    }

    /**
     * 获取实现类,调用实现类方法, 写回response
     */
    private String getImplClassName(ClassInfo classInfo) throws ClassNotFoundException {

        String interfacePath = "com.test";

        int lastDot = classInfo.getClassName().lastIndexOf(".");
        String interfaceName = classInfo.getClassName().substring(lastDot);


        Class<?> aClass = Class.forName(interfacePath + interfaceName);

        Reflections reflections = new Reflections(interfacePath);

        //得到某接口下的所有实现类
        Set<Class<?>> ImplClassSet = (Set<Class<?>>) reflections.getSubTypesOf(aClass);
        if (ImplClassSet.size() == 0) {
            System.out.println("未找到实现类");
            return null;
        } else if (ImplClassSet.size() > 1) {
            System.out.println("找到多个实现类,未明确使用哪一个");
            return null;
        } else {
            //把集合转换为数组
            Class[] classes = ImplClassSet.toArray(new Class[0]);
            return classes[0].getName();//得到实现类的名字
        }

    }
}

netty服务端

 package com.test;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;

/**
 * 参考博文 https://www.jianshu.com/p/ce5a73093916
 */
public class NettyServerTest {
    private int port;

    /**
     * 端口号
     *
     * @param port
     */
    public NettyServerTest(int port) {
        this.port = port;
    }

    public void start() {
        /**
         * 1.基于netty写的rpc,首先我们应该了解  jdk1.4中的nio ,然后我们看netty 会相对容易一些,nio中涉及的组件有 buffer,channel,selector
         * 2.bossGroup中的每个eventLoop(事件循环)会维护一个selector和taskQueue,负责处理客户端请求和内部任务,如ServerSocketChannel注册和ServerSocket绑定等。
         */
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        //真正干活的eventloop
        EventLoopGroup workGrop = new NioEventLoopGroup();
        ServerBootstrap b = new ServerBootstrap();
        //Java NIO中的 ServerSocketChannel 是一个可以监听新进来的TCP连接的通道, 就像标准IO中的ServerSocket一样。ServerSocketChannel类在 java.nio.channels包中。
        //设置 EventLoopGroup,其提供了用
        //于处理Channel 事件的EventLoop
        b.group(bossGroup, workGrop).channel(NioServerSocketChannel.class).
                // ChannelOption.SO_BACKLOG对应的是tcp/ip协议listen函数中的backlog参数,函数listen(int socketfd,int backlog)用来初始化服务端可连接队列
                        option(ChannelOption.SO_BACKLOG, 128).childOption(ChannelOption.SO_KEEPALIVE, true)
                //绑定端口号
                /*handler() 方法和 childHandler() 方法之间的区别是:前者所
                添加的 ChannelHandler 由接受子 Channel 的 ServerChannel 处理,而
                childHandler() 方法所添加的 ChannelHandler 将由已被接受的子 Channel
                处理,其代表一个绑定到远程节点的套接字
                */
                .localAddress(port).childHandler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel socketChannel) throws Exception {
                /*
                 *
                 * <pre>
                 *                                                 I/O Request
                 *                                            via {@link Channel} or
                 *                                        {@link ChannelHandlerContext}
                 *                                                      |
                 *  +---------------------------------------------------+---------------+
                 *  |                           ChannelPipeline         |               |
                 *  |                                                  \|/              |
                 *  |    +---------------------+            +-----------+----------+    |
                 *  |    | Inbound Handler  N  |            | Outbound Handler  1  |    |
                 *  |    +----------+----------+            +-----------+----------+    |
                 *  |              /|\                                  |               |
                 *  |               |                                  \|/              |
                 *  |    +----------+----------+            +-----------+----------+    |
                 *  |    | Inbound Handler N-1 |            | Outbound Handler  2  |    |
                 *  |    +----------+----------+            +-----------+----------+    |
                 *  |              /|\                                  .               |
                 *  |               .                                   .               |
                 *  | ChannelHandlerContext.fireIN_EVT() ChannelHandlerContext.OUT_EVT()|
                 *  |        [ method call]                       [method call]         |
                 *  |               .                                   .               |
                 *  |               .                                  \|/              |
                 *  |    +----------+----------+            +-----------+----------+    |
                 *  |    | Inbound Handler  2  |            | Outbound Handler M-1 |    |
                 *  |    +----------+----------+            +-----------+----------+    |
                 *  |              /|\                                  |               |
                 *  |               |                                  \|/              |
                 *  |    +----------+----------+            +-----------+----------+    |
                 *  |    | Inbound Handler  1  |            | Outbound Handler  M  |    |
                 *  |    +----------+----------+            +-----------+----------+    |
                 *  |              /|\                                  |               |
                 *  +---------------+-----------------------------------+---------------+
                 *                  |                                  \|/
                 *  +---------------+-----------------------------------+---------------+
                 *  |               |                                   |               |
                 *  |       [ Socket.read() ]                    [ Socket.write() ]     |
                 *  |                                                                   |
                 *  |  Netty Internal I/O Threads (Transport Implementation)            |
                 *  +-------------------------------------------------------------------+
                 * </pre>
                 */
                ChannelPipeline channelPipeline = socketChannel.pipeline();
                //编码器
                channelPipeline.addLast("encoder", new ObjectEncoder())
                        //解码器
                        .addLast("decoder", new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.cacheDisabled(null)))

                        /**
                         *  //ChannelInboundHandler——处理入站数据以及各种状态变化;
                         * //ChannelOutboundHandler——处理出站数据并且允许拦截所有的操作。
                         * 业务逻辑处理
                         */
                        .addLast(new InvokeHandler());
            }
        });

        ChannelFuture future = null;
        try {
            //绑定端口
            future = b.bind(port).sync();
            System.out.println("......准备好了么?......");
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            //优雅关闭
            bossGroup.shutdownGracefully();
            workGrop.shutdownGracefully();
        }

    }

    public static void main(String[] args) {
        new NettyServerTest(9999).start();
    }
}

客户端

package com.test;

import java.io.Serializable;

/**
 * serversub
 */
public  class  ClassInfo implements Serializable {
    /**
     * 序列化id
     */
    /**
     * 客户端存根的作用是把请求的参数以约定的通讯协议打包好发送给服务端然后解析服务端返回消息
     */
    private String className;//类名

    private String methodName;//返回值

    private Class<?>[] types; //参数类型

    private Object[] objects; //参数列表


    public String getClassName() {
        return className;
    }

    public void setClassName(String className) {
        this.className = className;
    }

    public String getMethodName() {
        return methodName;
    }

    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }

    public Class<?>[] getTypes() {
        return types;
    }

    public void setTypes(Class<?>[] types) {
        this.types = types;
    }

    public Object[] getObjects() {
        return objects;
    }

    public void setObjects(Object[] objects) {
        this.objects = objects;
    }
}

客户端接口

package com.test;

public interface HelloRPC {
    String hello(String s);
}

客户端代理类

package com.test;


import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
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 io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class NettyRpcProxy {
    public static Object createProxy(final Class target) {
        return Proxy.newProxyInstance(target.getClassLoader(),new Class[]{target}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                //封装ClassInfo
                ClassInfo classInfo = new ClassInfo();
                classInfo.setClassName(target.getName());
                classInfo.setMethodName(method.getName());
                classInfo.setObjects(args);
                classInfo.setTypes(method.getParameterTypes());
                //开始用Netty发送数据
                EventLoopGroup workerGroup = new NioEventLoopGroup();
                final ResultHandler resultHandler = new ResultHandler();
                Bootstrap b = new Bootstrap();
                b.group(workerGroup).channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        ChannelPipeline pipeline = socketChannel.pipeline();
                        //编码器
                        pipeline.addLast("encoder", new ObjectEncoder())
                                //解码器,构造方法第一个参数设置二进制的最大字节数,第二个参数设置具体使用哪个类解析器
                                .addLast("decoder", new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.cacheDisabled(null)))
                                //客户端业务处理类
                                .addLast("handler", resultHandler);
                    }
                });
                ChannelFuture future = b.connect("127.0.0.1", 9999).sync();
                //发送请求数据
                future.channel().writeAndFlush(classInfo).sync();
                future.channel().closeFuture().sync();
                return resultHandler.getResponse();
            }
        });
    }

    public static void main(String[] args) {
        HelloRPC helloRPC = (HelloRPC) NettyRpcProxy.createProxy(HelloRPC.class);
        System.out.println(helloRPC.hello("RPC"));
    }
}

结果处理器

package com.test;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

public class ResultHandler extends ChannelInboundHandlerAdapter {

    private Object response;

    public Object getResponse() {
        return response;
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        response = msg;
        ctx.close();
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值