首先,我们的provider
是需要提供rpc
服务的,而我们 rpc
底层是需要依赖netty的,因此我们需要依赖我们已经编写好的rpc-protocol
模块。
rpc-provider
模块 pom.xml
文件添加
<dependency>
<groupId>com.info</groupId>
<artifactId>rpc-protocol</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
启动netty
服务端
package com.info.provider;
import com.info.protocol.netty.server.NettyServer;
public class ProviderApplication {
public static void main(String[] args) {
new NettyServer("localhost", 9000).startServer();
}
}
接下来编写rpc-consumer
模块,因为rpc-consumer
需要使用netty
客户端发送请求,同样需要依赖rpc-protocol
。
其实,在客户端我们可以直接生成protocol
对象设置相应的参数,然后使用netty
客户端把这个对象传输出去即可,然后考虑到通用性,这里考虑使用代理的方式生成被调用对象代理对象的方法来完成。
我们知道jdk
提供了一个java.lang.reflect.Proxy
来方便我们生成代理对象
Proxy provides static methods for creating dynamic proxy classes and
instances, and it is also the superclass of all dynamic proxy classes
created by those methods. To create a proxy for some interface Foo:InvocationHandler handler = new MyInvocationHandler(...); Class<?> proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), Foo.class); Foo f = (Foo) proxyClass.getConstructor(InvocationHandler.class). newInstance(handler);
or more simply:
Foo f = (44Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), new Class<?>[] { Foo.class }, handler);
根据jdk
的代码示例,生成一个代理类需要三个参数,分别是类加载器,代理类需要实现的接口集合,以及一个InvocationHandler
,因此我们需要实现一个自定义InvocationHandler
,这也是实现动态代理对原方法增强的实现。
package com.info.consumer;
import com.info.protocol.constants.CommonConstant;
import com.info.protocol.enums.MessageTypeEnum;
import com.info.protocol.enums.SerializeTypeEnum;
import com.info.protocol.netty.client.NettyClient;
import com.info.protocol.netty.core.*;
import io.netty.channel.DefaultEventLoop;
import io.netty.util.concurrent.DefaultPromise;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
@Slf4j
public class CustomInvocationHandler implements InvocationHandler {
private final String serviceAddress;
private final int servicePort;
public CustomInvocationHandler(String serviceAddress, int servicePort) {
this.serviceAddress = serviceAddress;
this.servicePort = servicePort;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log.info("begin invoke target server");
//组装参数
Protocol<Request> protocol = new Protocol<>();
long requestId = RequestHolder.REQUEST_ID.incrementAndGet();
// 魔数 16bit|协议版本 8bit|序列化方式 8bit| 消息长度 32bit |消息类型(请求还是响应)2bit|messageId 64bit
Header header = new Header(CommonConstant.MAGIC, CommonConstant.PROTOCOL_VERSION,
SerializeTypeEnum.JDK.getCode(), 0, MessageTypeEnum.REQUEST.getCode(), requestId);
protocol.setHeader(header);
Request request = new Request();
request.setClassName(method.getDeclaringClass().getName());
request.setMethodName(method.getName());
request.setParameterTypes(method.getParameterTypes());
request.setParams(args);
protocol.setContent(request);
//发送请求
NettyClient nettyClient = new NettyClient(serviceAddress, servicePort);
//构建异步数据处理
CustomFuture<Response> future = new CustomFuture<>(new DefaultPromise<>(new DefaultEventLoop()));
RequestHolder.REQUEST_MAP.put(requestId, future);
nettyClient.sendRequest(protocol);
return future.getPromise().get().getData();
}
}
封装一个生成代理对象的方法
package com.info.proxy;
import java.lang.reflect.Proxy;
public class ClientProxy {
public <T> T clientProxy(final Class<T> interfaceCls, final String host, final int port) {
return (T) Proxy.newProxyInstance
(interfaceCls.getClassLoader(),
new Class<?>[]{
interfaceCls},
new CustomInvocationHandler(host, port));
}
}
至此,完美所有的代码都已经编写完毕,第一次编写一个rpc
框架的你是不是稍稍膨胀了一下?当时的我也是这样子的心态!
下节完美开始编写测试代码进行测试。
系列文章传送门如下:
手写RPC(一) 絮絮叨叨
手写RPC(二) 碎碎念
手写RPC(三) 基础结构搭建
手写RPC(四) 核心模块网络协议模块编写 ---- netty服务端
手写RPC(五) 核心模块网络协议模块编写 ---- 自定义协议
手写RPC(六) 核心模块网络协议模块编写 ---- 实现编解码器
手写RPC(七) 核心模块网络协议模块编写 ---- 实现客户端
手写RPC(九) 测试
手写RPC(十) 优化
关于 LengthFieldBasedFrameDecoder 不得不说的事