rpc netty

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

	@Override
	public 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;
	}

	@Override
	public 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;
	}

	@Override
	protected 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);
	}

	@Override
	public 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();
        }
    }

	@Override
	protected void channelRead0(ChannelHandlerContext arg0, ResponseMsg arg1) throws Exception {
		responseMsg = arg1;
		cdt.countDown();
	}

	@Override
	public 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 {
	@Autowired
	private HelloService helloService;
	
	@Test
	public void test1(){
		System.out.println(helloService.hi("world"));
	}

}


發佈了68 篇原創文章 · 獲贊 9 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章