基於Netty手寫的Rpc框架

基於Netty手寫的rpc框架時序圖

本項目一共分爲三個模塊:rpcServer、rpcClient、rpcCommon

rpcCommon的依賴

  <dependencies>
    <dependency>
      <groupId>io.netty</groupId>
      <artifactId>netty-all</artifactId>
      <version>5.0.0.Alpha2</version>
    </dependency>

    <!-- 服務提供方根據調用信息反射獲取實現類時需要 -->
    <dependency>
      <groupId>org.reflections</groupId>
      <artifactId>reflections</artifactId>
      <version>0.9.11</version>
    </dependency>
  </dependencies>

rpcServer、rpcClient的依賴

<dependencies>
  <dependency>
    <groupId>rpc</groupId>
    <artifactId>rpc-common</artifactId>
    <version>1.0-SNAPSHOT</version>
  </dependency>
</dependencies>

 

rpcCommon目錄結構

 

rpcCommon--ClassInfo類

package com.hk.common;

import java.io.Serializable;

/**
 * 使用JDK的序列化技術必須實現接口Serializable
 */
public class ClassInfo implements Serializable {

    private int id;
    private String className;
    private String methodName;
    private Class[] parameterTypes;
    private Object [] paramters;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    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[] getParameterTypes() {
        return parameterTypes;
    }

    public void setParameterTypes(Class[] parameterTypes) {
        this.parameterTypes = parameterTypes;
    }

    public Object[] getParamters() {
        return paramters;
    }

    public void setParamters(Object[] paramters) {
        this.paramters = paramters;
    }
}

接口

有參接口

package com.hk.common;
public interface HasArgsHelloService {
    String hello(String msg);
}

無參接口
package com.hk.common;

public interface NoArgsHelloService {
    String hello();
}

rpcServer目錄結構

rpc服務端代碼

//利用netty創建服務ServerBootstrap,粘合兩個NioEventLoopGroup,配置端口ip,設置編碼解碼信息

package com.hk.rpc.server;

import com.hk.rpc.server_stub.RpcServerHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
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;

public class RpcServer {

    public static void main(String[] args) {
        RpcServer.start("127.0.0.1", 9999);
    }

    public static void start(String ip, Integer port) {
        //創建boss線程組(有點像負責招人的包工頭)
        NioEventLoopGroup boss = new NioEventLoopGroup();
        //創建建立鏈接的線程
        NioEventLoopGroup worker = new NioEventLoopGroup();
        ServerBootstrap bootstrap = new ServerBootstrap();
        try {
            bootstrap.group(boss, worker)
                .channel(NioServerSocketChannel.class)
                .option(ChannelOption.SO_BACKLOG, 128)
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new ObjectEncoder());
                        ch.pipeline().addLast(new ObjectDecoder(1024 * 64, ClassResolvers.cacheDisabled(null)));
                        ch.pipeline().addLast(new RpcServerHandler());
                    }
                });
            //bind初始化端口是異步的,但調用sync則會同步阻塞等待端口綁定成功
            ChannelFuture future = bootstrap.bind(ip, port).sync();
            System.out.println(ip + ":服務已啓動,端口爲:" + port);
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }
    }
}

服務端處理器代碼

//真正通過反射找到實現類,調用方法返回結果

package com.hk.rpc.server_stub;

import com.hk.common.ClassInfo;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import java.lang.reflect.Method;
import java.util.Set;
import org.reflections.Reflections;

public class RpcServerHandler extends ChannelHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        //獲取調用信息,尋找服務實現類
        ClassInfo classInfo = (ClassInfo) msg;
        String implName = getImplClassName(classInfo.getClassName());
        Class<?> clazz = Class.forName(implName);
        Method method = clazz.getMethod(classInfo.getMethodName(), classInfo.getParameterTypes());
        Object result = method.invoke(clazz.newInstance(), classInfo.getParamters());
        ctx.writeAndFlush(result);
    }

    private String getImplClassName(String interfaceName) throws ClassNotFoundException {
        Class interClass = Class.forName(interfaceName);
        String servicePath = "com.hk.rpc.service";
        Reflections reflections = new Reflections(servicePath);
        Set<Class> implClasses = reflections.getSubTypesOf(interClass);
        if (implClasses.isEmpty()) {
            System.err.println("impl class is not found!");
        } else if (implClasses.size() > 1) {
            System.err.println("there are many impl classes, not sure invoke which");
        } else {
            Class[] classes = implClasses.toArray(new Class[1]);
            return classes[0].getName();
        }
        return null;
    }
}


兩個實現類代碼

package com.hk.rpc.service;

import com.hk.common.HasArgsHelloService;

public class HasArgsHelloServiceImpl implements HasArgsHelloService {

    public String hello(String msg) {
        return "hello "+msg;
    }
}

package com.hk.rpc.service;

import com.hk.common.NoArgsHelloService;

public class NoArgsHelloServiceImpl implements NoArgsHelloService {

    public String hello() {
        return "hello";
    }
}

項目結構:rpcClient

客戶端代碼

//利用代理類來測試流程

package com.hk.rpc.server;

import com.hk.common.HasArgsHelloService;
import com.hk.common.NoArgsHelloService;
import com.hk.rpc.client_stub.RpcProxy;

public class RPCClient {

    public static void main(String[] args){
        NoArgsHelloService noArgsHelloService = (NoArgsHelloService) RpcProxy.create(NoArgsHelloService.class);
        System.out.println(noArgsHelloService.hello());

        HasArgsHelloService hasArgsHelloService = (HasArgsHelloService) RpcProxy.create(HasArgsHelloService.class);
        System.out.println(hasArgsHelloService.hello("hello 黃凱!"));
    }

}

客戶端處理器代碼

//接收到服務端的請求,並賦值以及關閉連接

package com.hk.rpc.client_stub;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;


public class RpcClientHandler extends ChannelHandlerAdapter {

    /**
     * RPC調用返回的結果
     */
    private Object rpcResult;

    public Object getRpcResult() {
        return rpcResult;
    }

    public void setRpcResult(Object rpcResult) {
        this.rpcResult = rpcResult;
    }

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

客戶端代碼類

//根據接口類信息通過jdk代理來調用實現類,在Bootstrap建立連接後,添加客戶端處理器,並返回信息

package com.hk.rpc.client_stub;

import com.hk.common.ClassInfo;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
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 RpcProxy {

    public static Object create(final Class clazz) {
        return Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //構造調用信息
                ClassInfo classInfo = new ClassInfo();
                classInfo.setClassName(clazz.getName());
                classInfo.setMethodName(method.getName());
                classInfo.setParameterTypes(method.getParameterTypes());
                classInfo.setParamters(args);

                //使用netty發送調用信息給服務提供方
                NioEventLoopGroup group = new NioEventLoopGroup();
                //
                Bootstrap bootstrap = new Bootstrap();
                final RpcClientHandler rpcClientHandler = new RpcClientHandler();
                try {
                    bootstrap.group(group)
                        .channel(NioSocketChannel.class)
                        .option(ChannelOption.SO_KEEPALIVE, true)
                        .option(ChannelOption.TCP_NODELAY, true)
                        .handler(new ChannelInitializer<SocketChannel>() {
                            @Override
                            protected void initChannel(SocketChannel ch) throws Exception {
                                ch.pipeline().addLast(new ObjectEncoder());
                                //反序列化對象時指定類解析器,null表示使用默認的類加載器
                                ch.pipeline().addLast(new ObjectDecoder(1024 * 64, ClassResolvers.cacheDisabled(null)));
                                ch.pipeline().addLast(rpcClientHandler);

                            }
                        });
                    //connect是異步的,但調用其future的sync則是同步等待連接成功
                    ChannelFuture future = bootstrap.connect("127.0.0.1", 9999).sync();
                    //同步等待調用信息發送成功
                    future.channel().writeAndFlush(classInfo).sync();
                    //同步等待RPCClientHandler的channelRead被觸發後(意味着收到了調用結果)
                    future.channel().closeFuture().sync();
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    group.shutdownGracefully();
                }

                //返回調用結果
                return rpcClientHandler.getRpcResult();
            }
        });
    }
}

最後測試,先啓動RpcServer服務類,打印了信息之後,再啓動客戶端RPCClient進行測試

測試結果如下:

hello
hello hello 黃凱!

測試成功!!!

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章