rpc netty

来源:互联网 发布:mac怎么在pdf上写字 编辑:程序博客网 时间:2024/05/24 04:44
      RPC框架比如dubbo,主体实现大概是,客户端与服务端商定使用同一种传输协议,服务端接受客户端的连接,
客户端使用动态代理,根据不同的协议生成 不同的代理类,代理类中就是协议的客户端实现。服务器的地址

和端口可以使用注册中心来通知。下面使用netty来简单实现。


1、请求、响应封装类,使用jdk的序列化,序列化工具类

package com.rpc.msg;import java.io.Serializable;import java.util.Arrays;public class RequestMsg implements Serializable {/** *  */private static final long serialVersionUID = 1L;private String requestId;private String className;private String methodName;private Class<?>[] parameterTypes;private Object[] parameters;public String getRequestId() {return requestId;}public void setRequestId(String requestId) {this.requestId = requestId;}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[] getParameters() {return parameters;}public void setParameters(Object[] parameters) {this.parameters = parameters;}@Overridepublic String toString() {return "RequestMsg [requestId=" + requestId + ", className=" + className + ", methodName=" + methodName+ ", parameterTypes=" + Arrays.toString(parameterTypes) + ", parameters=" + Arrays.toString(parameters)+ "]";}}



package com.rpc.msg;import java.io.Serializable;public class ResponseMsg implements Serializable{/** *  */private static final long serialVersionUID = 1L;    private String requestId;        private Object result;public String getRequestId() {return requestId;}public void setRequestId(String requestId) {this.requestId = requestId;}public Object getResult() {return result;}public void setResult(Object result) {this.result = result;}            }


package com.rpc.util;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;public class SerializationUtil {public static <T> byte[] serialize(T obj) {if (obj == null) {return null;}ByteArrayOutputStream bo = new ByteArrayOutputStream();ObjectOutputStream oo;try {oo = new ObjectOutputStream(bo);oo.writeObject(obj);return bo.toByteArray();} catch (IOException e) {e.printStackTrace();}return null;}@SuppressWarnings("unchecked")public static <T> T deserialize(byte[] objBytes, Class<T> cls) {if (objBytes == null || objBytes.length == 0) {return null;}ByteArrayInputStream bi = new ByteArrayInputStream(objBytes);ObjectInputStream oi;try {oi = new ObjectInputStream(bi);return (T) oi.readObject();} catch (IOException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}return null;}}

2netty管道处理编码解码

package com.rpc.ende;import com.rpc.util.SerializationUtil;import io.netty.buffer.ByteBuf;import io.netty.channel.ChannelHandlerContext;import io.netty.handler.codec.MessageToByteEncoder;public class RpcEncoder extends MessageToByteEncoder<Object>{private Class<?> genericClass;    public RpcEncoder(Class<?> genericClass) {        this.genericClass = genericClass;    }    @Override    public void encode(ChannelHandlerContext ctx, Object in, ByteBuf out) throws Exception {        if (genericClass.isInstance(in)) {            byte[] data = SerializationUtil.serialize(in);            out.writeInt(data.length);            out.writeBytes(data);        }    }}

package com.rpc.ende;import java.util.List;import com.rpc.util.SerializationUtil;import io.netty.buffer.ByteBuf;import io.netty.channel.ChannelHandlerContext;import io.netty.handler.codec.ByteToMessageDecoder;public class RpcDecoder extends ByteToMessageDecoder {private Class<?> genericClass;public RpcDecoder(Class<?> genericClass) {this.genericClass = genericClass;}@Overridepublic 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 = SerializationUtil.deserialize(data, genericClass);out.add(obj);}}

3 服务端

  ProviderServer 容器初始化完成后,找到所有业务接口的实现类,启动netty服务器

  RpcService用来标识接口实现类,在spring容器中的bean

  NettyServer启动netty服务器,接受管道传来的request,使用反射处理得到结果,写回管道

  NettyServerHandler处理request的过程

package com.rpc.provider;import java.util.HashMap;import java.util.Map;import org.springframework.beans.BeansException;import org.springframework.context.ApplicationContext;import org.springframework.context.ApplicationContextAware;import com.rpc.provider.transation.NettyServer;public class ProviderServer implements ApplicationContextAware{private NettyServer nettyServer;public void setApplicationContext(ApplicationContext ctx) throws BeansException {Map<String, Object> handlerMap = new HashMap<String, Object>();Map<String, Object> serviceBeanMap = ctx.getBeansWithAnnotation(RpcService.class);         if (serviceBeanMap.values() != null) {            for (Object serviceBean : serviceBeanMap.values()) {                String interfaceName = serviceBean.getClass().getAnnotation(RpcService.class).value().getName();                handlerMap.put(interfaceName, serviceBean);            }        }nettyServer.setHandlerMap(handlerMap);try {nettyServer.start();} catch (Exception e) {e.printStackTrace();}}public void setNettyServer(NettyServer nettyServer) {this.nettyServer = nettyServer;}}

package com.rpc.provider;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;import org.springframework.stereotype.Component;@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Component public @interface RpcService {Class<?> value();}

package com.rpc.provider.transation;import java.util.Map;import com.rpc.ende.RpcDecoder;import com.rpc.ende.RpcEncoder;import com.rpc.msg.RequestMsg;import com.rpc.msg.ResponseMsg;import io.netty.bootstrap.ServerBootstrap;import io.netty.channel.ChannelFuture;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.NioServerSocketChannel;public class NettyServer {private String serverAddress;private Map<String,Object> handlerMap;public NettyServer(String serverAddress) {super();this.serverAddress = serverAddress;}public void setHandlerMap(Map<String, Object> handlerMap) {this.handlerMap = handlerMap;}public void start() throws Exception{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 RpcDecoder(RequestMsg.class))                             .addLast(new RpcEncoder(ResponseMsg.class))                             .addLast(new NettyServerHandler(handlerMap));                     }                })                .option(ChannelOption.SO_BACKLOG, 128)                .childOption(ChannelOption.SO_KEEPALIVE, true);            String[] array = serverAddress.split(":");            String host = array[0];            int port = Integer.parseInt(array[1]);            ChannelFuture future = bootstrap.bind(host, port).sync();            future.channel().closeFuture().sync();        } finally {            workerGroup.shutdownGracefully();            bossGroup.shutdownGracefully();        }}}

package com.rpc.provider.transation;import java.lang.reflect.InvocationTargetException;import java.util.Map;import org.springframework.cglib.reflect.FastClass;import org.springframework.cglib.reflect.FastMethod;import com.rpc.msg.RequestMsg;import com.rpc.msg.ResponseMsg;import io.netty.channel.ChannelFutureListener;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.SimpleChannelInboundHandler;public class NettyServerHandler extends SimpleChannelInboundHandler<RequestMsg> {private Map<String,Object> handlerMap;public NettyServerHandler(Map<String, Object> handlerMap) {super();this.handlerMap = handlerMap;}@Overrideprotected void channelRead0(ChannelHandlerContext ctx, RequestMsg request) throws Exception {ResponseMsg response = new ResponseMsg();response.setRequestId(request.getRequestId());try {Object result = handle(request);response.setResult(result);} catch (Throwable t) {response.setResult(t);}ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);}private Object handle(RequestMsg request) throws InvocationTargetException {String className = request.getClassName();Object serviceBean = handlerMap.get(className);Class<?> serviceClass = serviceBean.getClass();String methodName = request.getMethodName();Class<?>[] parameterTypes = request.getParameterTypes();Object[] parameters = request.getParameters();/* * Method method = serviceClass.getMethod(methodName, parameterTypes); * method.setAccessible(true);  * return method.invoke(serviceBean,parameters); */FastClass serviceFastClass = FastClass.create(serviceClass);FastMethod serviceFastMethod = serviceFastClass.getMethod(methodName, parameterTypes);return serviceFastMethod.invoke(serviceBean, parameters);}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {ctx.close();}}


4 客户端

RpcProxy生成接口代理类方法,具体实现需要根据传输协议,使用jdk动态代理

NettyClient 客户端连接服务器,发送请求,得到响应,使用CountDownLatch阻塞线程

ConsumerService为了使用方便,将配置的接口,使用RpcProxy生成代理类,注册到spring容器中

package com.rpc.consumer;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.util.UUID;import com.rpc.consumer.transation.NettyClient;import com.rpc.msg.RequestMsg;import com.rpc.msg.ResponseMsg;public class RpcProxy {private String serverAddress;public RpcProxy(String serverAddress) {        this.serverAddress = serverAddress;    }@SuppressWarnings("unchecked")    public <T> T create(Class<?> interfaceClass) {        return (T) Proxy.newProxyInstance(            interfaceClass.getClassLoader(),            new Class<?>[]{interfaceClass},            new InvocationHandler() {                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {                    RequestMsg request = new RequestMsg();                     request.setRequestId(UUID.randomUUID().toString());                    request.setClassName(method.getDeclaringClass().getName());                    request.setMethodName(method.getName());                    request.setParameterTypes(method.getParameterTypes());                    request.setParameters(args);                    String[] array = serverAddress.split(":");                    String host = array[0];                    int port = Integer.parseInt(array[1]);                    NettyClient client = new NettyClient(host, port);                     ResponseMsg response = client.send(request);                                         return response.getResult();                                   }            }        );    }}

package com.rpc.consumer.transation;import java.util.concurrent.CountDownLatch;import com.rpc.ende.RpcDecoder;import com.rpc.ende.RpcEncoder;import com.rpc.msg.RequestMsg;import com.rpc.msg.ResponseMsg;import io.netty.bootstrap.Bootstrap;import io.netty.channel.ChannelFuture;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.ChannelInitializer;import io.netty.channel.ChannelOption;import io.netty.channel.EventLoopGroup;import io.netty.channel.SimpleChannelInboundHandler;import io.netty.channel.nio.NioEventLoopGroup;import io.netty.channel.socket.SocketChannel;import io.netty.channel.socket.nio.NioSocketChannel;public class NettyClient extends SimpleChannelInboundHandler<ResponseMsg>{private String host;    private int port;public NettyClient(String host, int port) {super();this.host = host;this.port = port;}private CountDownLatch cdt = new CountDownLatch(1);private ResponseMsg responseMsg;public ResponseMsg send(RequestMsg request) throws Exception {        EventLoopGroup group = new NioEventLoopGroup();        try {            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 RpcEncoder(RequestMsg.class))                             .addLast(new RpcDecoder(ResponseMsg.class))                             .addLast(NettyClient.this);                     }                })                .option(ChannelOption.SO_KEEPALIVE, true);            ChannelFuture future = bootstrap.connect(host, port).sync();            future.channel().writeAndFlush(request).sync();            cdt.await();                        if (responseMsg != null) {                future.channel().closeFuture().sync();            }            return responseMsg;        } finally {            group.shutdownGracefully();        }    }@Overrideprotected void channelRead0(ChannelHandlerContext arg0, ResponseMsg arg1) throws Exception {responseMsg = arg1;cdt.countDown();}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {ctx.close();}}


package com.rpc.consumer;import java.util.List;import org.springframework.beans.BeansException;import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;import org.springframework.context.ApplicationContext;import org.springframework.context.ApplicationContextAware;import org.springframework.context.ConfigurableApplicationContext;public class ConsumerService implements ApplicationContextAware{private RpcProxy rpcProxy;public void setRpcProxy(RpcProxy rpcProxy) {this.rpcProxy = rpcProxy;}private List<String> clsNames;public ConsumerService(List<String> clsNames) {super();this.clsNames = clsNames;}public void setApplicationContext(ApplicationContext arg0) throws BeansException {ConfigurableApplicationContext cac = (ConfigurableApplicationContext) arg0;ConfigurableListableBeanFactory beanFactory = cac.getBeanFactory();for (String string : clsNames) {try {Class<?> interfaceClass = Class.forName(string);Object singletonObject = rpcProxy.create(interfaceClass);beanFactory.registerSingleton(string, singletonObject);} catch (ClassNotFoundException e) {e.printStackTrace();continue;}}}}

5 测试

   测试接口及实现

App服务器端启动类

application-provider.xml服务器配置文件

package test;public interface HelloService {      String hi(String name);}

package test.server;import com.rpc.provider.RpcService;import test.HelloService;@RpcService(HelloService.class)public class HelloServiceImpl implements HelloService {public String hi(String name) {return "hi " + name;}}

package test.server;import org.springframework.context.support.ClassPathXmlApplicationContext;public class App {@SuppressWarnings("resource")public static void main(String[] args) {new ClassPathXmlApplicationContext("application-provider.xml");}}


application-provider.xml
<?xml version="1.0" encoding="UTF-8"?><beans  xmlns="http://www.springframework.org/schema/beans"             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"             xmlns:context="http://www.springframework.org/schema/context"        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd          http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop--4.3.xsd       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx--4.3.xsd         http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">    <context:component-scan base-package="test"/>    <bean id="nettyServer" class="com.rpc.provider.transation.NettyServer">        <constructor-arg name="serverAddress" value="127.0.0.1:8000"/>    </bean>        <bean id="providerServer" class="com.rpc.provider.ProviderServer">       <property name="nettyServer" ref="nettyServer"></property>    </bean></beans>

客户端

application-consumer.xml

<?xml version="1.0" encoding="UTF-8"?><beans  xmlns="http://www.springframework.org/schema/beans"             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"             xmlns:context="http://www.springframework.org/schema/context"        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd          http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop--4.3.xsd       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx--4.3.xsd         http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">        <bean id="rpcProxy" class="com.rpc.consumer.RpcProxy">        <constructor-arg name="serverAddress" value="127.0.0.1:8000"/>    </bean>        <bean id="providerServer" class="com.rpc.consumer.ConsumerService">       <constructor-arg name="clsNames">         <list>            <value>test.HelloService</value>         </list>       </constructor-arg>       <property name="rpcProxy" ref="rpcProxy"></property>    </bean></beans>

package test.client;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import test.HelloService;@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(locations = "application-consumer.xml")public class HelloServiceTest {@Autowiredprivate HelloService helloService;@Testpublic void test1(){System.out.println(helloService.hi("world"));}}


0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 安装ps打不开安装包怎么办 安装好的软件打不开怎么办? win10系统语言修改不了怎么办 一个月婴儿吵夜怎么办 玩游戏一直闪退怎么办 钱站一直闪退怎么办 win7重装连不上网怎么办 笔记本屏幕横过来了怎么办 3D贴图丢了怎么办 百度文库安装后手机打不开怎么办 win7系统不带usb驱动怎么办 手机网页上的pdf打不开怎么办 网页下载pdf后缀是.do怎么办 ps界面太小怎么办win10 ps软件打不开程序错误怎么办 ps打开后 未响应怎么办 ps图层无法解锁怎么办 ie8浏览器电脑不能用怎么办 系统要ie6.0才能打开怎么办 2g手机内存不够怎么办 2g运行内存不够怎么办 手机运行内存2g不够怎么办 手机无法加载程序秒退怎么办 电脑账户密码忘记了怎么办 玩绝地求生卡顿怎么办 地下城总运行时间错误怎么办 逆战更新太慢怎么办 win7我的电脑没了怎么办 剑灵启动游戏慢怎么办 网页页面结束进程也关不掉怎么办 开机就启动微信怎么办 微信突然无法启动怎么办 微信发送太频繁怎么办 微信在电脑上打不开文件怎么办 微信照片电脑上打不开怎么办 换一部手机微信怎么办 微信支付宝停止运行怎么办 剑三重制版卡顿怎么办 剑三客户端更新不动了怎么办 安装包安装失败怎么办有内存 qq飞车换手机了怎么办