承接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