基于Netty实现RPC框架

目录

项目结构

序列化类信息

序列化接口

序列化实现类

序列化工厂类

通用类信息

服务接口类

Class工具类

RPC请求解码类

RPC请求编码类

自定义异常类

RPC请求类

RPC Response类

服务端类信息

服务实现类

RPC服务维护类

服务端类

服务端处理器类

服务端测试类

客户端类信息

JDK动态代理Handler类

代理工厂类

客户端类

客户端处理器类

客户端测试类

配置文件信息

pom文件

测试结果

服务器端

客户端


项目结构

主要分为服务端, 客户端, 通用类以及序列化类

客户端:
   1. 客户端调用目标方法
   2. 使用动态代理封装Request请求对象(服务类名, 方法名称, 参数类型名称数组, 参数值数组)
   3. 向服务端发送请求, 等待服务端执行完毕返回结果
   4. 获取结果, 输出

服务端:
  1. 服务端启动时注册目标服务的实例对象
  2. 接收到客户端的请求信息后, 利用反射执行目标方法
  3. 目标方法执行完成, 封装Response对象, 返回


序列化类信息

序列化接口

作用: 定义序列化, 反序列化方法

package com.lic.demo.rpc.cust.serialize;
public interface Serializer {
   public <T> Object deserialize(byte[] bytes, Class<T> clazz) ;
   public <T> byte[] serialize(T obj);
}

序列化实现类

作用: 实现序列化逻辑

package com.lic.demo.rpc.cust.serialize;

import com.lic.demo.rpc.v1.common.RpcException;
import com.lic.demo.rpc.v1.common.RpcRequest;
import io.protostuff.LinkedBuffer;
import io.protostuff.ProtostuffIOUtil;
import io.protostuff.Schema;
import io.protostuff.runtime.RuntimeSchema;
import org.objenesis.Objenesis;
import org.objenesis.ObjenesisStd;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class ProtobufSerializer implements Serializer {
    private static Objenesis objenesis = new ObjenesisStd(true);
    private static Map<Class<?>, Schema<?>> cachedSchema = new ConcurrentHashMap<Class<?>, Schema<?>>();
    private static <T> Schema<T> getSchema(Class<T> cls) {
        @SuppressWarnings("unchecked")
      Schema<T> schema = (Schema<T>) cachedSchema.get(cls);
        if (schema == null) {
            schema = RuntimeSchema.createFrom(cls);
            if (schema != null) {
                cachedSchema.put(cls, schema);
            }
        }
        return schema;
    }
    
    @Override
   public <T> byte[] serialize(T obj) {
       @SuppressWarnings("unchecked")
      Class<T> cls = (Class<T>) obj.getClass();
        LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
        try {
            Schema<T> schema = getSchema(cls);
            return ProtostuffIOUtil.toByteArray(obj, schema, buffer);
        } catch (Exception e) {
            throw new RpcException(e.getMessage(), e);
        } finally {
            buffer.clear();
        }
   }

   @Override
   public <T> Object deserialize(byte[] bytes, Class<T> clazz) {
      try {
            T message = objenesis.newInstance(clazz);
            Schema<T> schema = getSchema(clazz);
            ProtostuffIOUtil.mergeFrom(bytes, message, schema);
            return message;
        } catch (Exception e) {
            throw new RpcException(e.getMessage(), e);
        }
   }
   
   public static void main(String[] args) {
      RpcRequest request = new RpcRequest();
      request.setMethodName("add");
      request.setParam1(1.0);
      request.setParam2(3.0); 
      ProtobufSerializer sr = new ProtobufSerializer();
      byte[] bytes = sr.serialize(request);
      System.out.println(bytes.length);
      RpcRequest rr = (RpcRequest) sr.deserialize(bytes, RpcRequest.class);
      System.out.println(rr.toString());
   }
}

序列化工厂类

package com.lic.demo.rpc.cust.serialize;
import com.lic.demo.rpc.v1.serialize.ProtobufSerializer;
import com.lic.demo.rpc.v1.serialize.Serializer;
public class SerializerFactory {
   public static Serializer getSerializer() {
      return new ProtobufSerializer();
   }
}

通用类信息

服务接口类

作用: 定义方法信息

package com.lic.demo.rpc.cust.common;
public interface CalculatorService {
   double add(double op1, double op2, double op3);
   double substract(double op1, double op2);  
   double multiply(double op1, double op2);
}

Class工具类

作用: 根据类型名称生成对应的Class对象

package com.lic.demo.rpc.cust.common;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class ClassUtils {
   private static final Logger logger = LoggerFactory.getLogger(ClassUtils.class);
   private static final Map<String, Class<?>> PRIMITIVE_CLASS = new HashMap<String, Class<?>>(1024);
   private static final Map<String, Class<?>> CLASS_CACHE = new HashMap<String, Class<?>>(1024);
   private static final Map<String, Method> METHOD_CACHE = new HashMap<String, Method>(1024);
   static {
      PRIMITIVE_CLASS.put("boolean", boolean.class);
      PRIMITIVE_CLASS.put("byte", byte.class);
      PRIMITIVE_CLASS.put("short", short.class);
      PRIMITIVE_CLASS.put("int", int.class);
      PRIMITIVE_CLASS.put("long", long.class);
      PRIMITIVE_CLASS.put("float", float.class);
      PRIMITIVE_CLASS.put("double", double.class);
      PRIMITIVE_CLASS.put("char", char.class);
      PRIMITIVE_CLASS.put("void", void.class);

      CLASS_CACHE.putAll(PRIMITIVE_CLASS);
   }

   public static Class<?> getClass(String className) throws ClassNotFoundException {
      Class<?> clazz = CLASS_CACHE.get(className);
      if (null != clazz) {
         return clazz;
      }
      synchronized (CLASS_CACHE) {
         if (null == CLASS_CACHE.get(className)) {
            clazz = PRIMITIVE_CLASS.get(className);
            if (null == clazz) {
               clazz = Class.forName(className);
            }
            CLASS_CACHE.put(className, clazz);
            return clazz;
         } else {
            return CLASS_CACHE.get(className);
         }
      }
   }
}

RPC请求解码类

作用: 对获取到请求信息进行反序列化

package com.lic.demo.rpc.cust.common;
import com.lic.demo.rpc.v1.serialize.Serializer;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import java.util.List;

public class RpcDecode extends ByteToMessageDecoder {
   private Class<?> genericClass;
   private Serializer serializer;
   public RpcDecode(Class<?> genericClass, Serializer serializer) {
      this.genericClass = genericClass;
      this.serializer = serializer;
   }
   @Override
   public final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
      if (in.readableBytes() < 4) {
         return;
      }
      in.markReaderIndex();
      int dataLength = in.readInt();
      if (dataLength < 0) {
         ctx.close();
      }
      if (in.readableBytes() < dataLength) {
         in.resetReaderIndex();
         return;
      }
      byte[] data = new byte[dataLength];
      in.readBytes(data);

      Object obj = serializer.deserialize(data, genericClass);
      out.add(obj);
   }
}

RPC请求编码类

作用: 对封装的请求信息进行序列化, 以便进行网络传输

package com.lic.demo.rpc.cust.common;
import com.lic.demo.rpc.v1.serialize.Serializer;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;

public class RpcEncode extends MessageToByteEncoder<Object> {
    private Class<?> genericClass;
    private Serializer serializer;
    public RpcEncode(Class<?> genericClass, Serializer serializer) {
        this.genericClass = genericClass;
        this.serializer = serializer;
    }
    @Override
    protected void encode(ChannelHandlerContext ctx, Object in, ByteBuf out) throws Exception {
        if (genericClass.isInstance(in)) {
            byte[] data = serializer.serialize(in);
            out.writeInt(data.length);
            out.writeBytes(data);
        }
    }
}

自定义异常类

作用: 封装异常信息

package com.lic.demo.rpc.cust.common;
public class RpcException extends RuntimeException {
   public RpcException(String msg) {
      super(msg);
   }
   public RpcException(String message, Exception e) {
      super(message, e);
   }
}

RPC请求类

作用: 封装请求信息(请求唯一标识, 服务类名称, 目标方法名称, 参数类型名称数组, 参数值数组)

package com.lic.demo.rpc.cust.common;
import java.util.Arrays;

public class RpcRequest {
   private String id;
   private String serviceName;
   private String methodName;
   private transient  Class<?>[] parameterTypes; //add: {double.class, double.class}   
   private String[] parameterTypeNames; //add: {"double", "double"}   
   private Object[] args;

   public RpcRequest() {
      super();
   }
   public String getId() {
      return id;
   }
   public void setId(String id) {
      this.id = id;
   }
   public String getServiceName() {
      return serviceName;
   }
   public void setServiceName(String serviceName) {
      this.serviceName = serviceName;
   }
   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 String[] getParameterTypeNames() {
      return parameterTypeNames;
   }
   public void setParameterTypeNames(String[] parameterTypeNames) {
      this.parameterTypeNames = parameterTypeNames;
   }
   public Object[] getArgs() {
      return args;
   }
   public void setArgs(Object[] args) {
      this.args = args;
   }
   @Override
   public int hashCode() {
      final int prime = 31;
      int result = 1;
      result = prime * result + Arrays.hashCode(args);
      result = prime * result + ((id == null) ? 0 : id.hashCode());
      result = prime * result + ((methodName == null) ? 0 : methodName.hashCode());
      result = prime * result + Arrays.hashCode(parameterTypeNames);
      result = prime * result + ((serviceName == null) ? 0 : serviceName.hashCode());
      return result;
   }

   @Override
   public boolean equals(Object obj) {
      if (this == obj)
         return true;
      if (obj == null)
         return false;
      if (getClass() != obj.getClass())
         return false;
      RpcRequest other = (RpcRequest) obj;
      if (!Arrays.equals(args, other.args))
         return false;
      if (id == null) {
         if (other.id != null)
            return false;
      } else if (!id.equals(other.id))
         return false;
      if (methodName == null) {
         if (other.methodName != null)
            return false;
      } else if (!methodName.equals(other.methodName))
         return false;
      if (!Arrays.equals(parameterTypeNames, other.parameterTypeNames))
         return false;
      if (serviceName == null) {
         if (other.serviceName != null)
            return false;
      } else if (!serviceName.equals(other.serviceName))
         return false;
      return true;
   }
   @Override
   public String toString() {
      return "RpcRequestv1_1 [id=" + id + ", serviceName=" + serviceName + ", methodName=" + methodName
            + ", parameterTypeNames=" + Arrays.toString(parameterTypeNames) + ", args=" + Arrays.toString(args)
            + "]";
   }  
}

RPC Response类

作用: 封装服务端返回的结果信息(唯一标识, 方法名称, 结果)

package com.lic.demo.rpc.cust.common;
public class RpcResponse {
   private String id;
   private String methodName;
   private String cause;
   private double result;

   public RpcResponse() {
      super();
   }
   public String getId() {
      return id;
   }
   public void setId(String id) {
      this.id = id;
   }
   public String getMethodName() {
      return methodName;
   }
   public void setMethodName(String methodName) {
      this.methodName = methodName;
   }
   public String getCause() {
      return cause;
   }
   public void setCause(String cause) {
      this.cause = cause;
   }
   public double getResult() {
      return result;
   }
   public void setResult(double result) {
      this.result = result;
   }
   @Override
   public int hashCode() {
      final int prime = 31;
      int result = 1;
      result = prime * result + ((cause == null) ? 0 : cause.hashCode());
      result = prime * result + ((id == null) ? 0 : id.hashCode());
      result = prime * result + ((methodName == null) ? 0 : methodName.hashCode());
      long temp;
      temp = Double.doubleToLongBits(this.result);
      result = prime * result + (int) (temp ^ (temp >>> 32));
      return result;
   }
   @Override
   public boolean equals(Object obj) {
      if (this == obj)
         return true;
      if (obj == null)
         return false;
      if (getClass() != obj.getClass())
         return false;
      RpcResponse other = (RpcResponse) obj;
      if (cause == null) {
         if (other.cause != null)
            return false;
      } else if (!cause.equals(other.cause))
         return false;
      if (id == null) {
         if (other.id != null)
            return false;
      } else if (!id.equals(other.id))
         return false;
      if (methodName == null) {
         if (other.methodName != null)
            return false;
      } else if (!methodName.equals(other.methodName))
         return false;
      if (Double.doubleToLongBits(result) != Double.doubleToLongBits(other.result))
         return false;
      return true;
   }
   @Override
   public String toString() {
      return "RpcResponse [id=" + id + ", methodName=" + methodName + ", cause=" + cause + ", result=" + result + "]";
   }
}

服务端类信息

服务实现类

作用: 业务逻辑实现

package com.lic.demo.rpc.cust.server;
import com.lic.demo.rpc.v1_2.CalculatorService;
public class CalculatorServiceImpl implements CalculatorService {
   @Override
   public double add(double op1, double op2) {
      return op1 + op2;
   }
   @Override
   public double substract(double op1, double op2) {
      return op1 - op2;
   }
   @Override
   public double multiply(double op1, double op2) {
      return op1 * op2;
   }
}

RPC服务维护类

作用: 在服务端发布服务, 维护服务类

package com.lic.demo.rpc.cust.server;
import com.lic.demo.rpc.cust.common.ClassUtils;
import com.lic.demo.rpc.cust.common.RpcException;
import com.lic.demo.rpc.cust.common.RpcRequest;
import com.lic.demo.rpc.cust.common.RpcResponse;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class RpcBuilder {
   Map<String, Object> services = new ConcurrentHashMap();
   Map<String, Class<?>> interfaces = new ConcurrentHashMap();
   /**
    * 发布服务
    * @param serviceInterface 服务接口
    * @param service  服务实例
    */
   public void publishService(Class<?> serviceInterface, Object service) {
      if (services.containsKey(serviceInterface.getName()) || interfaces.containsKey(serviceInterface.getName())) {
         throw new RpcException("serviceInterface has been already registered......");
      }
      if (!(serviceInterface.isInstance(service))) {
         throw new RpcException("service object must implement the service Interface .......");
      }
      services.put(serviceInterface.getName(), service);
      interfaces.put(serviceInterface.getName(), serviceInterface);
   }

   public RpcResponse invokeService(RpcRequest rpcRequest) {
      String serviceName = rpcRequest.getServiceName(); //获取服务类名
      String methodName = rpcRequest.getMethodName();  //获取方法名称
      Class<?> serviceInterface = interfaces.get(serviceName); //根据服务名称获取目标接口
      String[] parameterTypeNames = rpcRequest.getParameterTypeNames(); //获取参数类型名称

      Method m;
      Object result = null;
        /**
         * 根据参数类型名称生成参数类型数组, 后面根据此数组可以选择指定的方法
       */
      Class<?>[] parameterClasses = new Class<?>[parameterTypeNames.length];
      try {
         for (int i = 0; i < parameterClasses.length; i++) {
            parameterClasses[i] = ClassUtils.getClass(parameterTypeNames[i]);
         }
      } catch (ClassNotFoundException e) {
         e.printStackTrace();
      }
      try {
         /**
          * 根据方法名称以及参数类型数组选择目标方法
          */
         m = serviceInterface.getMethod(methodName, parameterClasses);
         /**
          * 执行目标方法
          */
         result = m.invoke(services.get(serviceName), rpcRequest.getArgs());

      } catch (Exception e) {
         e.printStackTrace();
      }
        /**
         * 封装response对象
       */
      RpcResponse response = new RpcResponse();
      response.setId(rpcRequest.getId());
      response.setMethodName(methodName);
      response.setResult((Double) result);
      return response;
   }
}

服务端类

作用: 启动网络服务, 等待客户端请求信息

package com.lic.demo.rpc.cust.server;

import com.lic.demo.rpc.cust.common.RpcDecode;
import com.lic.demo.rpc.cust.common.RpcEncode;
import com.lic.demo.rpc.cust.common.RpcRequest;
import com.lic.demo.rpc.cust.common.RpcResponse;
import com.lic.demo.rpc.cust.serialize.SerializerFactory;
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class NettyServer   {
   private static final Logger logger = LoggerFactory.getLogger(NettyServer.class);
   private Thread thread; 
   private final int port;
   public NettyServer(int port) {
      this.port = port;
   }

   public void start(final RpcBuilder rpcBuilder) throws Exception {
      thread = new Thread(new Runnable() {
         @Override
         public void run() {
            EventLoopGroup bossGroup = new NioEventLoopGroup();
            EventLoopGroup workerGroup = new NioEventLoopGroup();
            try {
               ServerBootstrap bootstrap = new ServerBootstrap();
               bootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
                     .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel channel) throws Exception {
                           channel.pipeline()
                                 .addLast(new RpcDecode(RpcRequest.class, SerializerFactory.getSerializer()))
                                 .addLast(new RpcEncode(RpcResponse.class, SerializerFactory.getSerializer()))
                                 .addLast(new NettyServerHandler(rpcBuilder));
                        }
                     })
                     .option(ChannelOption.SO_TIMEOUT, 100)
                     .option(ChannelOption.SO_BACKLOG, 128)
                     .option(ChannelOption.TCP_NODELAY, true)
                     .option(ChannelOption.SO_REUSEADDR, true)
                     .childOption(ChannelOption.SO_KEEPALIVE, true);
               ChannelFuture future = bootstrap.bind(port).sync();

                    logger.info("接口服务器端启动成功......");
 
               Channel serviceChannel = future.channel().closeFuture().sync().channel();
            } catch (InterruptedException e) {
               logger.error(e.getMessage(), e);
            } finally {
               workerGroup.shutdownGracefully();
               bossGroup.shutdownGracefully();
            }
         }
      });
      thread.start();

   }
   public Thread getThread() {
      return this.thread;
   }
   public void stop() throws Exception {
        if (thread!=null && thread.isAlive()) {
            thread.interrupt();
        }
         logger.info("接口服务器端停止......");
   }
}

服务端处理器类

作用: 当客户端请求到达时调用调用服务维护类解析请求

package com.lic.demo.rpc.cust.server;
import com.lic.demo.rpc.cust.common.RpcRequest;
import com.lic.demo.rpc.cust.common.RpcResponse;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class NettyServerHandler extends SimpleChannelInboundHandler<RpcRequest> {
   private static final Logger logger = LoggerFactory.getLogger(NettyServerHandler.class);
   private RpcBuilder rpcBuilder;
   public NettyServerHandler(RpcBuilder rpcBuilder) {
       this.rpcBuilder = rpcBuilder;
   }
   @Override
   public void channelRead0(final ChannelHandlerContext ctx, RpcRequest rpcRequest) throws Exception {
       /**
        * 解析Request请求信息, 执行目标方法
        */
       RpcResponse rpcResponse = rpcBuilder.invokeService(rpcRequest);
       //返回执行结果
       ctx.writeAndFlush(rpcResponse);
   }
   @Override
   public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        ctx.close();
   }
}

服务端测试类

作用: 开启服务

package com.lic.demo.rpc.cust.server;
import com.lic.demo.rpc.v1_1.server.NettyServer;
import com.lic.demo.rpc.v1_1.server.RpcBuilder;
import com.lic.demo.rpc.v1_2.CalculatorService;
import com.lic.demo.rpc.v1_2.CalculatorServiceImpl;

public class RpcDemoServer {
   public static void main(String[] args) {
      /**
       * 创建RpcBuilder对象
       * 该对象用于发布服务信息以及通过反射调用服务方法
       */
      RpcBuilder builder = new RpcBuilder();
      //发布服务
      builder.publishService(CalculatorService.class, new CalculatorServiceImpl());
      //创建服务端, 启动
      NettyServer server = new NettyServer(8080);
      try {
         server.start(builder);
      } catch (Exception e) {
         e.printStackTrace();
      }
   }
}

客户端类信息

JDK动态代理Handler类

作用: 生成代理类逻辑, 包装请求信息, 发送请求

package com.lic.demo.rpc.cust.client;
import com.lic.demo.rpc.v1_1.client.NettyClient;
import com.lic.demo.rpc.v1_1.common.RpcRequest;
import com.lic.demo.rpc.v1_1.common.RpcResponse;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.SynchronousQueue;

public class JDKInvocationHandler implements InvocationHandler {
   private NettyClient client;
   private Class<?> proxyClass;
   public JDKInvocationHandler(Class<?> proxyClass, NettyClient client) {
      this.proxyClass = proxyClass;
      this.client = client;
   }
   @Override
   public Object invoke(Object proxy, Method method, Object[] paramValues) throws Throwable {
      String methodName = method.getName();
      Class<?>[] paramTypes = method.getParameterTypes();
      /**
         * 如果执行的是toString(), hashCode(), equals()方法,则调用原方法
       */
      if ("toString".equals(methodName) && paramTypes.length == 0) {
         return client.toString();
      } else if ("hashCode".equals(methodName) && paramTypes.length == 0) {
         return client.hashCode();
      } else if ("equals".equals(methodName) && paramTypes.length == 1) {
         Object another = paramValues[0];
         return proxy == another || (proxy.getClass().isInstance(another) && client.equals(parseInvoker(another)));
      }
        /**
         * 根据目标方法信息封装Request请求信息
       */
      RpcRequest request = buildRequest(proxyClass.getName(), method, paramValues);
      /**
         * 发送RPC请求, 让服务端执行目标方法
       */
      RpcResponse response = sendRPCRequest(request);
      return response.getResult();
   }

   private RpcResponse sendRPCRequest(RpcRequest request) {
      //创建同步队列, 等待返回数据
      SynchronousQueue<RpcResponse> queue = new SynchronousQueue();
      NettyClient.putSunchronousQuee(request.getId(), queue);
      RpcResponse response = null;
      try {
         /**
             * 客户端发送请求
          */
         client.sendRpcRequest(request);
         /**
          * 等待服务器端返回数据, 然后客户端处理器会将返回的response对象放入同步对列中
          */
         response = queue.take();
      } catch (Exception e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
      }
      return response;
   }
   
    public static NettyClient parseInvoker(Object proxyObject) {
        InvocationHandler handler = java.lang.reflect.Proxy.getInvocationHandler(proxyObject);
        if (handler instanceof JDKInvocationHandler) {
            return ((JDKInvocationHandler) handler).getProxyInvoker();
        }
        return null;
    }
   public Class<?> getProxyClass() {
      return proxyClass;
   }
   public NettyClient getProxyInvoker() {
        return client;
    }

   private RpcRequest buildRequest(String serviceName, Method method, Object[] args) {
      /**
       * 包装Request
       */
      String id = UUID.randomUUID().toString();

      RpcRequest request = new RpcRequest();
      request.setServiceName(serviceName);
      request.setId(id);
      request.setMethodName(method.getName());
      request.setArgs(args); //设置参数值
        //遍历所有参数, 获取参数类型集合
      List<String> parameterTypes = new ArrayList<String>();
      for (Class<?> parameterType : method.getParameterTypes()) {
         parameterTypes.add(parameterType.getName());
      }
        //参数类型信息
      request.setParameterTypeNames(parameterTypes.toArray(new String[0]));
      return request;
   }
}

代理工厂类

作用: 生成代理类

package com.lic.demo.rpc.cust.client;
import com.lic.demo.rpc.v1_1.client.NettyClient;
import com.lic.demo.rpc.v1_2.JDKInvocationHandler;
import java.lang.reflect.InvocationHandler;
public final class ProxyFactory {
    /**
     * 使用JDK动态代理创建代理对象
     */
    public static <T> T getProxy(Class<T> interfaceClass, NettyClient proxyInvoker) {
        /**
         * 1. 创建Handler, 增强逻辑
         */
        InvocationHandler handler = new JDKInvocationHandler(interfaceClass, proxyInvoker);
        /**
         * 2. 获取类加载器
         */
        ClassLoader classLoader = ProxyFactory.class.getClassLoader();
        /**
         * 3. 获取代理对象
         */
        T proxy = (T) java.lang.reflect.Proxy.newProxyInstance(classLoader,new Class[] { interfaceClass }, handler);
        return proxy;
    }
}


客户端类

package com.lic.demo.rpc.cust.client;

import com.lic.demo.rpc.v1.common.RpcDecode;
import com.lic.demo.rpc.v1.common.RpcEncode;
import com.lic.demo.rpc.v1.serialize.Serializer;
import com.lic.demo.rpc.v1_1.common.RpcRequest;
import com.lic.demo.rpc.v1_1.common.RpcResponse;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
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.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.SynchronousQueue;

public class NettyClient {

   private static ConcurrentHashMap<String, SynchronousQueue<RpcResponse>> mapInfo = new ConcurrentHashMap();

   public static void putSunchronousQuee(String id, SynchronousQueue<RpcResponse> queue) {
      mapInfo.put(id, queue);
   }

   public static SynchronousQueue<RpcResponse> getSynchronousQueue(String id) {
      return mapInfo.get(id);
   }

   public static void removeById(String id) {
      mapInfo.remove(id);
   }

   public void sendRpcRequest(RpcRequest rpcRequest) throws Exception {

      try {
         this.channel.writeAndFlush(rpcRequest).sync();
      } catch (Exception e) {
         throw e;
      }
   }

   private Channel channel;

   public void init(String host, int port, final Serializer serializer) throws Exception {
      EventLoopGroup group = new NioEventLoopGroup();
      Bootstrap bootstrap = new Bootstrap();
      bootstrap.group(group)
            .channel(NioSocketChannel.class)
            .handler(new ChannelInitializer<SocketChannel>() {
                  @Override
                  public void initChannel(SocketChannel channel) throws Exception {
                     channel.pipeline().addLast(new RpcEncode(RpcRequest.class, serializer))
                           .addLast(new RpcDecode(RpcResponse.class, serializer))
                           .addLast(new NettyClientHandler());
                  }
            })
            .option(ChannelOption.TCP_NODELAY, true)
            .option(ChannelOption.SO_REUSEADDR, true)
            .option(ChannelOption.SO_KEEPALIVE, true);

      this.channel = bootstrap.connect(host, port).sync().channel();
      System.out.println("接口服务端连接成功......");
   }

   public boolean isValidate() {
      if (this.channel != null) {
         return this.channel.isActive();
      }
      return false;
   }

   public void close() {
      if (this.channel != null) {
         if (this.channel.isOpen()) {
            this.channel.close();
         }
      }
   }
}

客户端处理器类

作用: 接收服务器端返回的结果

package com.lic.demo.rpc.cust.client;
import com.lic.demo.rpc.v1_1.common.RpcResponse;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.SynchronousQueue;
public class NettyClientHandler extends SimpleChannelInboundHandler<RpcResponse> {
   private static final Logger logger = LoggerFactory.getLogger(NettyClientHandler.class);
   @Override
   public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
      logger.error("远程连接异常:", cause);
      ctx.close();
   }

   @Override
   protected void channelRead0(ChannelHandlerContext ctx, RpcResponse rpcResponse) throws Exception {
      String id = rpcResponse.getId();
      /**
       * 将从接收到服务器返回的结果放入同步对列中
       */
      SynchronousQueue<RpcResponse> synchronousQueue = NettyClient.getSynchronousQueue(id);
      synchronousQueue.put(rpcResponse);

      NettyClient.removeById(id);
   }
}

客户端测试类

作用: 连接服务端, 执行目标方法 

package com.lic.demo.rpc.cust.client;
import com.lic.demo.rpc.v1.common.SerializerFactory;
import com.lic.demo.rpc.v1_1.client.NettyClient;
import com.lic.demo.rpc.v1_2.CalculatorService;
import com.lic.demo.rpc.v1_2.ProxyFactory;
public class RpcDemoClient {

   public static void main(String[] args) throws Exception {
      // 创建客户端
      NettyClient client = new NettyClient();
      // 初始化客户端
      client.init("127.0.0.1", 8080, SerializerFactory.getSerializer());
      /**
       * 获取代理类
       */
      CalculatorService service = ProxyFactory.getProxy(CalculatorService.class, client);
      /**
         * 执行目标方法
       */
      System.out.println(service.add(1.0, 156.0));
      
      client.close();
   }
}



配置文件信息

pom文件

<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <artifactId>netty</artifactId>
    <name>learning-netty</name>
    <groupId>com.turingschool.demo</groupId>
    <version>1.0.0</version>
    <packaging>jar</packaging>

    <url>http://www.waylau.com</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <slf4j-api.version>1.7.25</slf4j-api.version>
        <protobuf.version>3.6.1</protobuf.version>
        <os-maven-plugin.version>1.5.0.Final</os-maven-plugin.version>
        <protobuf-maven-plugin.version>0.5.0</protobuf-maven-plugin.version>
        <junit.version>4.12</junit.version>

    </properties>
    <build>
        <extensions>
            <extension>
                <groupId>kr.motd.maven</groupId>
                <artifactId>os-maven-plugin</artifactId>
                <version>${os-maven-plugin.version}</version>
            </extension>
        </extensions>

        <plugins>
            <plugin>
                <groupId>org.xolstice.maven.plugins</groupId>
                <artifactId>protobuf-maven-plugin</artifactId>
                <version>${protobuf-maven-plugin.version}</version>
                <configuration>
                    <protocArtifact>
                        com.google.protobuf:protoc:3.6.1:exe:${os.detected.classifier}
                    </protocArtifact>
                    <pluginId>protoc-java</pluginId>
                    <protoSourceRoot>${project.basedir}/src/main/resources</protoSourceRoot>

                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.2</version>
                <configuration>
                    <optimize>true</optimize>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>

        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.jboss.marshalling/jboss-marshalling -->
        <dependency>
            <groupId>org.jboss.marshalling</groupId>
            <artifactId>jboss-marshalling</artifactId>
            <version>2.0.6.Final</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.jboss.marshalling/jboss-marshalling-serial -->
        <dependency>
            <groupId>org.jboss.marshalling</groupId>
            <artifactId>jboss-marshalling-serial</artifactId>
            <version>2.0.6.Final</version>
        </dependency>
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.30.Final</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/io.protostuff/protostuff-core -->
        <dependency>
            <groupId>io.protostuff</groupId>
            <artifactId>protostuff-core</artifactId>
            <version>1.6.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/io.protostuff/protostuff-runtime -->
        <dependency>
            <groupId>io.protostuff</groupId>
            <artifactId>protostuff-runtime</artifactId>
            <version>1.6.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/io.protostuff/protostuff-api -->
        <dependency>
            <groupId>io.protostuff</groupId>
            <artifactId>protostuff-api</artifactId>
            <version>1.6.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.objenesis/objenesis -->
        <dependency>
            <groupId>org.objenesis</groupId>
            <artifactId>objenesis</artifactId>
            <version>3.0.1</version>
        </dependency>
        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java-util</artifactId>
            <version>${protobuf.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>com.google.guava</groupId>
                    <artifactId>guava</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- slf4j -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j-api.version}</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.slf4j/jul-to-slf4j -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jul-to-slf4j</artifactId>
            <version>1.7.25</version>
        </dependency>
    </dependencies>

</project>

测试结果

服务器端

客户端

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值