前置–Spring自定义对象生成并赋值
-
流程图
Netty框架在通讯过程中需要将Provider和Consumer,以及Server初始化,初始化过程中需要将对象自定好的一些值在启动的时候就添加到对象中并在容易中管理起来,其中最核心的几个类如上流程图中所示范;
-
NamespaceHandlerSupport – 将.xml中文件对应文件的名字的中所配置的值获取并注入到对象中 实现init()初始化方法
-
在初始化方法中将我们所需要的对象注册到Spring中,同时实现赋值操作,所以需要自定义一个注册对象方法也就是下面一个关键类。
public class MyNameSpaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
//MyBeanDefinitionParser 自己实现的注册方法
registerBeanDefinitionParser("consumer", new MyBeanDefinitionParser(ConsumerBean.class));
registerBeanDefinitionParser("provider", new MyBeanDefinitionParser(ProviderBean.class));
registerBeanDefinitionParser("server", new MyBeanDefinitionParser(ServerBean.class));
}
}
- 上面对应的Key名字就取下方xml文件中的名字,并获取对应的element元素值
<?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:rpc="http://rpc.ksk.cn/schema/rpc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://rpc.ksk.cn/schema/rpc http://rpc.ksk.cn/schema/rpc/rpc.xsd">
<!-- 注册中心 -->
<rpc:server id="rpc_center" host="127.0.0.1" port="6379"/>
</beans>
}
- BeanDefinitionParser – 实列化对象,并将从NamespaceHandlerSupport中初始化解析的值放入对象并交给Spring管理
- 在此我是实现自己得注册方法 需要重写parse()方法。
public class MyBeanDefinitionParser implements BeanDefinitionParser {
//需要实现装载的字节码文件
private final Class<?> beanClass;
MyBeanDefinitionParser(Class<?> beanClass) {
this.beanClass = beanClass;
}
//解析装载过程
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
RootBeanDefinition beanDefinition = new RootBeanDefinition();
//将需要解析的字节码文件放入 实例化对象需要
beanDefinition.setBeanClass(beanClass);
beanDefinition.setLazyInit(false);
//获取xml文件中定义的id定义的名字
String beanName = element.getAttribute("id");
//根据Key-value格式将对象注册到Spring容器中
parserContext.getRegistry().registerBeanDefinition(beanName, beanDefinition);
//循环获取对象中的方法 找到对象中定义的变量的get set方法 进行赋值操作
for (Method method : beanClass.getMethods()) {
if (!isProperty(method, beanClass)) continue;
String name = method.getName();
String methodName = name.substring(3, 4).toLowerCase() + name.substring(4);
String value = element.getAttribute(methodName);
//此步骤为对对应的变量进行赋值的操作
beanDefinition.getPropertyValues().addPropertyValue(methodName, value);
}
//f返回注册对象
return beanDefinition;
}
到此 咱们就将所需要初始化的对象以及属性值生成,并交给Spring管理了
前置–Netty通讯组件了解
- 流程图
- 如上要实现Neety框架核心在于客户端Client和服务端Server端,因为Netty 是一个异步框架,服务之间对话需要获取对应结果,因此需要实现Future接口,自定义一个自己得获取结果类。
-
- ChannelInboundHandlerAdapter – Channel 通道失配器,主要重写channelRead()和exceptionCaught()方法,channelRead用于Channel接受的结果进行处理的核心逻辑
public class MyClientHandler extends ChannelInboundHandlerAdapter {
/**
* 读取返回结果的逻辑
* @param ctx
* @param msg
* @throws Exception
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//转换获取对象 此Response自己定义的
Response response = (Response) msg;
//获取ID ID唯一
String requestId = response.getRequestId();
//根据ID获取对应写入发送请求时哪个线程处理的 获取到是哪个Future处理的 并将结果放入对应的对象中 为后续监听放入返会值
SyncWriteFuture future = (SyncWriteFuture) SyncWriteMap.syncKey.get(requestId);
if (future != null){
future.setResponse(response);
}
}
/**
* 异常处理逻辑
* @param ctx
* @param cause
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
-
- ServerSocket – 服务端实现。
package cn.ksk.design.rpc.network.server;
public class ServerSocket implements Runnable {
private ChannelFuture f;
@Override
public void run() {
//服务端要管理接受和发送 因此需要两个轮询事件
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
//服务端配置
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
//通道一定要是NioServerSocketChannel字节码 这里新手容易写成别的 要注意
.channel(NioServerSocketChannel.class)
//定义连接规则 这里定义了可用连接队列最多为128哥
.option(ChannelOption.SO_BACKLOG, 128)
//定义Channel通道属性
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch){
ch.pipeline().addLast(
//解码编码对象 因为通讯是要序列化的
new RpcDecoder(Request.class),
new RpcEncoder(Response.class),
//对接受的结果进行处理
new MyServerHandler());
}
});
ChannelFuture f = null;
//设置访问端口 这里要注意 用锁 因为端口设置是异步的 如果没加锁 主线程走完 端口
//还没设置好会导致异常
f = b.bind(8083).sync();
//将Channel通道线程创建完成后关闭,这样就不会走入finally方法,为了保证需要同步关闭
// 异步可能会导致走入finally方法 就会将轮询事件终止 导致无法进行通话
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
}
-
- ClientSocket – 客户端实现同理,主要是要将实现的Channel保存下来,用于后续和服务端建立连接使用代码如下
public class ClientSocket implements Runnable {
private ChannelFuture future;
@Override
public void run() {
//创建Nio异步通道
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
//创建工厂所需模块集合
Bootstrap bootstrap = new Bootstrap();
//轮询事件
bootstrap.group(workerGroup);
//线程下的处理通道对象 这里不同于服务端 小伙伴们要注意奥
bootstrap.channel(NioSocketChannel.class);
//处理设置参数
bootstrap.option(ChannelOption.AUTO_READ , true);
bootstrap.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(
new RpcDecoder(Response.class),
new RpcEncoder(Request.class),
new MyClientHandler());
}
});
/**
* 连接对象
*/
ChannelFuture f = null;
//因为是异步 线程连接异步所以要用上锁保证连接完成
f = bootstrap.connect("127.0.0.1", 8083).sync();
//设置线程对象
this.future = f;
//将Channel通道线程创建完成后关闭,这样就不会走入finally方法,为了保证需要同步关闭
// 异步可能会导致走入finally方法 就会将轮询事件终止 导致无法进行通话
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//最后关闭这个轮询事件
workerGroup.shutdownGracefully();
}
}
public ChannelFuture getFuture() {
return future;
}
public void setFuture(ChannelFuture future) {
this.future = future;
}
}
以上就是实现Netty通讯框架需要了解的前置知识啦,下篇我会详细讲解Netty-RPC框架核心如何实现
Future讲解参考:Future讲解参考
ChannelFuture参考:ChannelFuture参考
ChannelOptionc参数了解:ChannelOptionc参数了解