rpc系列-簡介2

承接rpc系列-簡介1:https://blog.csdn.net/qq_19968255/article/details/82894381

示例

1.結構

2.代碼

客戶端:

rpc-client

/**
 * 框架的RPC 客戶端(用於發送 RPC 請求)
 */
public class RpcClient extends SimpleChannelInboundHandler<RpcResponse> {

	private static final Logger logger = LoggerFactory
			.getLogger(RpcClient.class);

	private String host;
	private int port;

	private RpcResponse response;

	private final Object obj = new Object();

	public RpcClient(String host, int port) {
		this.host = host;
		this.port = port;
	}
	/**
	 * 鏈接服務端,發送消息
	 */
	public RpcResponse send(RpcRequest 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 {
							// 向pipeline中添加編碼、解碼、業務處理的handler
							channel.pipeline()
									.addLast(new RpcEncoder(RpcRequest.class))  //out-1
									.addLast(new RpcDecoder(RpcResponse.class)) //in-1
									.addLast(RpcClient.this);                   //in-2
						}
					}).option(ChannelOption.SO_KEEPALIVE, true);
			// 鏈接服務器
			ChannelFuture future = bootstrap.connect(host, port).sync();
			//將request對象寫入outbundle處理後發出(即RpcEncoder編碼器)
			future.channel().writeAndFlush(request).sync();

			// 用線程等待的方式決定是否關閉連接
			// 其意義是:先在此阻塞,等待獲取到服務端的返回後,被喚醒,從而關閉網絡連接
			synchronized (obj) {
				obj.wait();
			}
			if (response != null) {
				future.channel().closeFuture().sync();
			}
			return response;
		} finally {
			group.shutdownGracefully();
		}
	}

	/**
	 * 讀取服務端的返回結果
	 */
	@Override
	public void channelRead0(ChannelHandlerContext ctx, RpcResponse response)
			throws Exception {
		this.response = response;

		synchronized (obj) {
			obj.notifyAll();
		}
	}

	/**
	 * 異常處理
	 */
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
			throws Exception {
		logger.error("client caught exception", cause);
		ctx.close();
	}
}
/**
 * RPC 代理(用於創建 RPC 服務代理)
 */
public class RpcProxy {
	//服務地址
	private String serverAddress;
	//自動加載,查找服務
	private ServiceDiscovery serviceDiscovery;

	public RpcProxy(String serverAddress) {
		this.serverAddress = serverAddress;
	}

	public RpcProxy(ServiceDiscovery serviceDiscovery) {
		this.serviceDiscovery = serviceDiscovery;
	}

	/**
	 * 創建代理
	 * 
	 * @param interfaceClass
	 * @return
	 */
	@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 {
						//創建RpcRequest,封裝被代理類的屬性
						RpcRequest request = new RpcRequest();
						request.setRequestId(UUID.randomUUID().toString());
						//拿到聲明這個方法的業務接口名稱
						request.setClassName(method.getDeclaringClass()
								.getName());
						request.setMethodName(method.getName());
						request.setParameterTypes(method.getParameterTypes());
						request.setParameters(args);
						//查找服務
						if (serviceDiscovery != null) {
							serverAddress = serviceDiscovery.discover();
						}
						//隨機獲取服務的地址
						String[] array = serverAddress.split(":");
						String host = array[0];
						int port = Integer.parseInt(array[1]);
						//創建Netty實現的RpcClient,鏈接服務端
						RpcClient client = new RpcClient(host, port);
						//通過netty向服務端發送請求
						RpcResponse response = client.send(request);
						//返回信息
						if (response.isError()) {
							throw response.getError();
						} else {
							return response.getResult();
						}
					}
				});
	}
}

 

服務端:

rpc-server

/**
 * 處理具體的業務調用
 * 通過構造時傳入的“業務接口及實現”handlerMap,來調用客戶端所請求的業務方法
 * 並將業務方法返回值封裝成response對象寫入下一個handler(即編碼handler——RpcEncoder)
 */
public class RpcHandler extends SimpleChannelInboundHandler<RpcRequest> {

	private static final Logger logger = LoggerFactory
			.getLogger(RpcHandler.class);

	private final Map<String, Object> handlerMap;

	RpcHandler(Map<String, Object> handlerMap) {
		this.handlerMap = handlerMap;
	}

	/**
	 * 接收消息,處理消息,返回結果,
	 */
	@Override
	public void channelRead0(final ChannelHandlerContext ctx, RpcRequest request)
			throws Exception {
		RpcResponse response = new RpcResponse();
		response.setRequestId(request.getRequestId());
		try {
			//根據request來處理具體的業務調用
			Object result = handle(request);
			response.setResult(result);
		} catch (Throwable t) {
			response.setError(t);
		}
		//寫入 outbundle(即RpcEncoder)進行下一步處理(即編碼)後發送到channel中給客戶端
		ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
	}

	/**
	 * 根據request來處理具體的業務調用
	 * 調用是通過反射的方式來完成
	 */
	private Object handle(RpcRequest request) throws Throwable {
		String className = request.getClassName();

		//拿到實現類對象
		Object serviceBean = handlerMap.get(className);
		
		//拿到要調用的方法名、參數類型、參數值
		String methodName = request.getMethodName();
		Class<?>[] parameterTypes = request.getParameterTypes();
		Object[] parameters = request.getParameters();
		
		//拿到接口類
		Class<?> forName = Class.forName(className);

		System.out.println(serviceBean.toString()+"  "+ Arrays.toString(parameters));
		//調用實現類對象的指定方法並返回結果
		Method method = forName.getMethod(methodName, parameterTypes);
		return method.invoke(serviceBean, parameters);
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
		logger.error(cause.getMessage());
		ctx.close();
	}
}
/**
 * 框架的RPC 服務器(用於將用戶系統的業務類發佈爲 RPC 服務)
 * 使用時可由用戶通過spring-bean的方式注入到用戶的業務系統中
 * 由於本類實現了ApplicationContextAware InitializingBean
 * spring構造本對象時會調用setApplicationContext()方法,從而可以在方法中通過自定義註解獲得用戶的業務接口和實現
 * 還會調用afterPropertiesSet()方法,在方法中啓動netty服務器
 * 順序
 * 1.setApplicationContext 報錯會中間截斷
 * 2.afterPropertiesSet
 */
public class RpcServer implements ApplicationContextAware, InitializingBean {

	private static final Logger logger = LoggerFactory
			.getLogger(RpcServer.class);

	private String serverAddress;
	private ServiceRegistry serviceRegistry;

	//用於存儲業務接口和實現類的實例對象(由spring所構造)
	private Map<String, Object> handlerMap = new HashMap<String, Object>();

	public RpcServer(String serverAddress) {
		this.serverAddress = serverAddress;
	}

	//服務器綁定的地址和端口由spring在構造本類時從配置文件中傳入
	public RpcServer(String serverAddress, ServiceRegistry serviceRegistry) {
		this.serverAddress = serverAddress;
		//用於向zookeeper註冊名稱服務的工具類
		this.serviceRegistry = serviceRegistry;
	}

	/**
	 * 通過註解,獲取標註了rpc服務註解的業務類的----接口及impl對象,將它放到handlerMap中
	 */
	public void setApplicationContext(ApplicationContext ctx)
			throws BeansException {
		Map<String, Object> serviceBeanMap = ctx
				.getBeansWithAnnotation(RpcService.class);
		if (MapUtils.isNotEmpty(serviceBeanMap)) {
			for (Object serviceBean : serviceBeanMap.values()) {
				//從業務實現類上的自定義註解中獲取到value,從來獲取到業務接口的全名
				String interfaceName = serviceBean.getClass()
						.getAnnotation(RpcService.class).value().getName();
				handlerMap.put(interfaceName, serviceBean);
			}
		}
	}

	/**
	 * 在此啓動netty服務,綁定handle流水線:
	 * 1、接收請求數據進行反序列化得到request對象
	 * 2、根據request中的參數,讓RpcHandler從handlerMap中找到對應的業務imple,調用指定方法,獲取返回結果
	 * 3、將業務調用結果封裝到response並序列化後發往客戶端
	 */
	public void afterPropertiesSet() 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(RpcRequest.class))// in-1
									.addLast(new RpcEncoder(RpcResponse.class))// out-1
									.addLast(new RpcHandler(handlerMap));// in-2
						}
					}).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();
			logger.debug("server started on port {}", port);

			if (serviceRegistry != null) {
				serviceRegistry.register(serverAddress);
			}

			future.channel().closeFuture().sync();
		} finally {
			workerGroup.shutdownGracefully();
			bossGroup.shutdownGracefully();
		}
	}
}
/**
 * RPC 請求註解(標註在服務實現類上)
 */
@Target({ ElementType.TYPE })//註解用在接口上
@Retention(RetentionPolicy.RUNTIME)//VM將在運行期也保留註釋,因此可以通過反射機制讀取註解的信息
@Component
public @interface RpcService {

	Class<?> value();
}

運行結果:

zkCli數據:

服務端

sample—server調用服務端接口實現:
Hello! World

客戶端

服務端返回結果:
Hello! World

代碼運行路徑:

 

代碼下載

地址:https://download.csdn.net/download/qq_19968255/10696211

 

 

 

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