粗汉手撕RPC核心原理-Netty版
文章目录
源码地址: https://gitee.com/kylin1991_admin/mini-tomcat/tree/master/nio-tomcat
环境准备
1、maven
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/io.netty/netty-all -->
<!-- 5 版本已经被丢弃了-->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.48.Final</version>
</dependency>
2、插件:Lombok
1、protocol协议包
- 直接用一个协议类就好了,用于数据传输过程的包装类
@Data
public class InvokeProtocol implements Serializable {
private String className;
private String methodName;
private Class<?>[] params;
private Object[] values;
}
2、registry注册中心
a、RpcRegistry注册中心类
- 主要就是服务开启。用netty来开启服务
public class RpcRegistry {
private static final int PORT = 8080;
public static void main(String[] args) {
new RpcRegistry().start();
}
private void start() {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap registry = new ServerBootstrap()
.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel serverSocketChannel) throws Exception {
ChannelPipeline pipeline = serverSocketChannel.pipeline();
pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4));
pipeline.addLast(new LengthFieldPrepender(4));
pipeline.addLast("decoder", new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.cacheDisabled(null)));
pipeline.addLast("encoder", new ObjectEncoder());
pipeline.addLast(new RegistryHandler());
}
});
try {
ChannelFuture f = registry.bind(this.PORT).sync();
System.out.println("start rpc registry success , prot : " + this.PORT);
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
b、RegistryHandler初始化逻辑类
- 逻辑处理的,主要是初始化容器,然后用于回调。其实这里也可以用Proxy来远程调用。
- 所以真正的情况就是consumer远程调用。provider远程调用。简化简化哈哈
public class RegistryHandler extends ChannelInboundHandlerAdapter {
private static final List<String> classNames = new ArrayList<>();
private static final Map<String, Object> serverMap = new HashMap<>();
// 指定扫描的实现路径,正常的情况是去读取配置文件,为了方便
private static final String PACKAGE_NAME = "org.example.provider";
public RegistryHandler() {
scannerClassName(PACKAGE_NAME);
doRegistry();
}
private void doRegistry() {
if (classNames == null) return;
for (String className : classNames) {
try {
Class<?> clazz = Class.forName(className);
Class<?> server = clazz.getInterfaces()[0];
serverMap.put(server.getName(), clazz.newInstance());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
private void scannerClassName(String packageName) {
URL url = this.getClass().getResource("/" + packageName.replaceAll("\\.", "/"));
File files = new File(url.getFile());
for (File file : files.listFiles()) {
if (file.isDirectory()) {
scannerClassName(file.getName());
} else {
classNames.add(packageName + "." + file.getName().replace(".class", ""));
}
}
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
Object result = null;
if (msg instanceof InvokeProtocol) {
InvokeProtocol request = (InvokeProtocol) msg;
if (serverMap.containsKey(request.getClassName())) {
Object service = serverMap.get(request.getClassName());
Method method = service.getClass().getMethod(request.getMethodName(), request.getParams());
result = method.invoke(service, request.getValues());
}
}
ctx.writeAndFlush(result);
ctx.close();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.out.println("registry exception ");
throw new RuntimeException(cause.getMessage());
}
}
3、api
- 用于提供接口使用的jar包
public interface IHelloService {
String sayHello(String content);
}
4、provider服务提供方
public class HelloServiceImpl implements IHelloService {
@Override
public String sayHello(String content) {
System.out.println("request in :" + content);
return "SUCCESS";
}
}
5、Consumer客户调用方
还是要用AOP代理的思想
a、Consumer调用类
public class Consumer {
public static void main(String[] args) {
IHelloService helloService = ProxyClient.create(IHelloService.class);
System.out.println(helloService.sayHello("七楼"));
}
}
b、RpcProxyClient代理类
- 利用的jdk的动态代理,然后代理时采用netty来做客户端请求
public class RpcProxyClient {
public static <T> T create(Class<?> interfacesCls) {
return (T) Proxy.newProxyInstance(interfacesCls.getClassLoader(), new Class<?>[]{interfacesCls}, new ProxyHandler());
}
private static class ProxyHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}
InvokeProtocol request = new InvokeProtocol();
request.setClassName(method.getDeclaringClass().getName());
request.setMethodName(method.getName());
request.setParams(method.getParameterTypes());
request.setValues(args);
RemoteHandler remoteHandler = new RemoteHandler();
EventLoopGroup workerGroup = new NioEventLoopGroup();
Bootstrap client = new Bootstrap()
.group(workerGroup)
.channel(NioSocketChannel.class)
.option(ChannelOption.SO_KEEPALIVE, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4));
pipeline.addLast(new LengthFieldPrepender(4));
pipeline.addLast("decoder", new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.cacheDisabled(null)));
pipeline.addLast("encoder", new ObjectEncoder());
pipeline.addLast(remoteHandler);
}
});
ChannelFuture f = client.connect("localhost", 8080).sync();
f.channel().writeAndFlush(request).sync();
f.channel().closeFuture().sync();
workerGroup.shutdownGracefully();
return remoteHandler.getResult();
}
}
}
c、RemoteHandler远程调用逻辑类
- 接受返回结果
public class RemoteHandler extends ChannelInboundHandlerAdapter {
private Object result;
public Object getResult() {
return result;
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
result = msg;
super.channelRead(ctx, msg);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.out.println("rpc registry exception");
throw new RuntimeException(cause.getMessage());
}
}
6、测试服务类
-
启动RpcRegistry
-
直接consumer上操作就好了
好了。至此。直接启动Netty版的RPC就完成了就可以访问了。