Netty即是服务端又是客户端,服务端和客户端相互对应。
具体功能细节是:
上游有一个服务,会主动发送消息给我中间件平台。中间件平台既有服务端也有客户端。通过下游客户端连接进来的客户端和中间件传下去的通道id是一样的,实现互相对应的一种通道,保证知道传输的消息是走的同一条路。那么就要自己具备同时是一个服务端和一个客户端,下游客户端连接服务端。也就是达到一种透传的功能,中间件平台只充当消息转换的平台,不做任何转换。
针对上面的功能细节实现代码思路是:将上游下发的数据和自己本身的连接数据以及下游客户端连接数据都使用全局map存储,在map中获取对应通道id和数据。
第一步:监听是否有下游客户端连入,创建客户端连接
public class NettyClientListener implements ApplicationRunner {
@Autowired
NettyClient NettyClient;
@Override
public void run(ApplicationArguments args) {
log.info("Netty Client connection is starting ........................................");
//自己本身作为客户端创建的一个客户端连接
Channel channel = cGSNettyClient.newClient();
CommonDataContext.putChannel(CommonDataContext.CGS_CHANNEL_KEY, channel);
log.info("Netty Client channel id = {}", channel.id());
}
}
第二步:初始化新的连接
public class NettyClient {
public void init() {
client = new Bootstrap();
EventLoopGroup group = new NioEventLoopGroup();
//绑定客户端通道
client.channel(NioSocketChannel.class);
client.option(ChannelOption.SO_KEEPALIVE, true).option(ChannelOption.TCP_NODELAY, true);
client.group(group);
client.remoteAddress(host, port);
//给NioSocketChannel初始化handler,处理读写事件
client.handler(new ChannelInitializer<NioSocketChannel>() { // 通道是NioSocketChannel
@Override
protected void initChannel(NioSocketChannel ch) {
ch.pipeline().addLast(new IdleStateHandler(5, 5, 0));
ch.pipeline().addLast(new ByteArrayDecoder());
// 增加handler
ch.pipeline().addLast(new ClientHandler(applicationContext));
}
});
}
public Channel newClient() {
try {
if (client == null) {
init();
}
// 连接服务器
ChannelFuture channelFuture = client.connect(host, port).sync();
channelFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture arg0) throws Exception {
if (channelFuture.isSuccess()) {
log.info("Netty client connection is successful ........................................");
} else {
log.info("Netty client connection is failed ........................................");
channelFuture.cause().printStackTrace();
}
}
});
return channelFuture.channel();
} catch (Exception e) {
log.error("Netty client connection is failed ........................................, because: {}", e.getMessage());
return null;
}
}
}
第三部:通道连接,读写,销毁
public class YJServerHandler extends ChannelInboundHandlerAdapter {
/***
* 通道下线(连接断开)
* @param ctx
*/
@Override
public void channelInactive(ChannelHandlerContext ctx) {
Channel hardwareChannel = ctx.channel();
//客户端通道ID
String hardwareChannelId = ctx.channel().id().asLongText();
//中间件的客户端也需要关闭
Channel ccChannel = CommonDataContext.getChannel(hardwareChannelId);
//客户端ID查询对应的中间件通道ID
String ccChannelId = ccChannel.id().asLongText();
CommonDataContext.removeChannel(hardwareChannelId);
CommonDataContext.removeChannel(ccChannelId);
ccChannel.disconnect();
hardwareChannel.disconnect();
}
/**
* 通道激活,客户端通道激活的同时,中间件也创建和激活一个客户端通道;
* @param ctx
*/
@Override
public void channelActive(ChannelHandlerContext ctx) {
NettyClient NettyClient = SpringContextUtils.getBean(NettyClient.class);
Channel channel = ctx.channel();
//客户端通道激活的同时,(中间件)也创建一个客户端;
Channel cccClientChannel = ccNettyClient.newClient();
CommonDataContext.putChannel(channel.id().asLongText(), ccClientChannel);
CommonDataContext.putChannel(ccClientChannel.id().asLongText(), channel);
log.info("logictis channelActive ChannelMap = {}, 客户端地址及端口号:{}", CommonDataContext.getChannelMap(), ctx.channel().remoteAddress());
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//获取中间件的通道
Channel channel = CommonDataContext.getChannel(ctx.channel().id().asLongText());
//log.info("SocketHandler channel = {}", channel);
channel.writeAndFlush(msg);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
super.userEventTriggered(ctx, evt);
if (evt instanceof IdleStateEvent) {
IdleStateEvent event = (IdleStateEvent) evt;
if (IdleState.READER_IDLE.equals(event.state())) {
log.warn("暂时没有接收到数据");
// 可以选择重新连接
} else if (IdleState.WRITER_IDLE.equals(event.state())) {
log.warn("暂时没有发送数据");
// 发送心跳包
// ctx.writeAndFlush(MessageProto.Message.newBuilder().setType(1));
} else if (event.state().equals(IdleState.ALL_IDLE)) {
// 重新连接和
log.warn("暂时没有接收或发送数据");
}
}
}
}
第四部:监测
public class XYServerPipeline extends ChannelInitializer<SocketChannel> {
@Override
public void initChannel(SocketChannel ch) {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new ByteArrayEncoder());
// 空闲状态处理器,检测通信Channel的读写状态是否超时,实现心跳检测
pipeline.addLast("idleStateHandler", new IdleStateHandler(180, 180, 180, TimeUnit.SECONDS));
pipeline.addLast(new XYServerHandler());
}
}
第五步:实现业务读取并下发到下游客户端
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//向客户端发送消息,并获取到消息结果,在返回给上游;
Channel channel = CommonDataContext.getChannel(ctx.channel().id().asLongText());
log.info("ClientHandler channel = {}", channel);
channel.writeAndFlush(msg);
return;
}