Netty客户端

Netty客户端

TCPClient

public class NettyTcpClient {

    /**
     * TCP服务端IP(MD)
     */
    @Value("${mdServer.ip}")
    private String ip;

    /**
     * TCP服务端端口号(MD)
     */
    @Value("${mdServer.port}")
    private int port;

    @Resource
    private UnvChannelInitialize channelInitialize;

    private Bootstrap bootstrap = new Bootstrap();

    private List<Channel> channelList = new ArrayList<>();

    private final AtomicInteger index = new AtomicInteger();

    /**
     * 初始化TCP客户端
     */
    public void initClient() {
        int threadNum = Runtime.getRuntime().availableProcessors();
        if (threadNum <= 0) {
            threadNum = 4;
        }
        EventLoopGroup loopGroup = new NioEventLoopGroup();
        try {
            bootstrap.group(loopGroup)
                    .channel(NioSocketChannel.class)
                    .remoteAddress(new InetSocketAddress(ip, port))
                    .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
                    .option(ChannelOption.TCP_NODELAY, true)
                    .option(ChannelOption.SO_KEEPALIVE, true)
                    // 设置ChanelOutBoundBuffer缓冲区最低水位为64kb,最高水位为256kb
                    .option(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(32 * 1024, 256 * 1024))
                    .handler(channelInitialize);
            // 创建多个连接
            for (int i = 0; i < threadNum; i++) {
                ChannelFuture channelFuture = bootstrap.connect().sync();
                channelList.add(channelFuture.channel());
            }
            log.info("{} 服务端连接成功", ip + ":" + port);
        } catch (Exception e) {
            log.error("TCP客户端连接{}服务端失败:", ip + ":" + port, e);
        }
    }

    /**
     * 获取Chanel实例
     *
     * @return channel
     */
    public Channel getNextChannel() {
        return getFirstActiveChannel(0);
    }

    /**
     * 获取Chanel实例
     *
     * @param count
     * @return channel
     */
    public Channel getFirstActiveChannel(int count) {
        Channel channel = channelList.get(Math.abs(index.getAndIncrement() % channelList.size()));
        if (!channel.isActive()) {
            // 重连
            reConnect(channel);
            if (count > channelList.size()) {
                throw new RuntimeException("暂无空闲的连接通道");
            }
            return getFirstActiveChannel(count + 1);
        }
        return channel;
    }

    /**
     * 重连
     *
     * @param channel
     */
    public void reConnect(Channel channel) {
        log.info("{} 通道连接断开,正在重连...", channel);
        synchronized (channel) {
            if (channelList.indexOf(channel) == -1) {
                return;
            }
            channelList.set(channelList.indexOf(channel), bootstrap.connect().channel());
        }
    }

ChannelInitalize

public class UnvChannelInitialize extends ChannelInitializer {

    @Resource
    private UnvHeartBeatHandler heartBeatHandler;

    @Resource
    private SendMsgHandler sendMsgHandler;

    @Override
    protected void initChannel(Channel channel) throws Exception {
        channel.pipeline()
                .addLast(new StringEncoder(Charset.forName("UTF-8")))
                .addLast(new StringDecoder(Charset.forName("UTF-8")))
                //自定义解码器支持宇视协议
                .addLast("unvMsgEnCode", new UnvEncode())
                .addLast(heartBeatHandler)
                .addLast(sendMsgHandler);
    }
}

Handler


```java
public class SendMsgHandler extends SimpleChannelInboundHandler {

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        for (String pathKey : BlockingQueueConfig.queueMap.keySet()) {
            SendTask task = new SendTask(pathKey);
            InitServer.threadPoolExecutor.execute(task);
        }
        ctx.fireChannelActive();
    }

    @Override
    public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        // 不作处理
        ReferenceCountUtil.release(msg);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.error("通道连接异常:", cause);
        ctx.close();
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        log.error("{}:通道连接断开", ctx.channel());
        ctx.close();
    }

    @Override
    public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
        super.channelWritabilityChanged(ctx);
    }
}
### 发送数据

```java
public class SendTask implements Runnable {

    private FilePathKeyProperties pathKeyProperties;

    private NettyTcpClient tcpClient;

    private Map<String, FilePathKeyProperties.PathProperties> branchPath;

    private String pathKey;

    /**
     * 宇视物联协议报文命令字
     */
    private int command;

    public SendTask(String pathKey) {
        this.pathKeyProperties = ApplicationContextUtil.getBean(FilePathKeyProperties.class);
        this.tcpClient = ApplicationContextUtil.getBean(NettyTcpClient.class);
        this.branchPath = pathKeyProperties.getBranchPath();
        this.pathKey = pathKey;
    }

    @Override
    public void run() {
        File file = null;
        while (true) {
            try {
                BlockingQueue<File> fileQueue = BlockingQueueConfig.queueMap.get(pathKey);
                log.info("{}:阻塞队列大小:{}", pathKey, fileQueue.size());
                file = fileQueue.take();
                if (null == file || !file.exists()) {
                    continue;
                }
                String fileName = file.getName();
                // 读取文件内容
                byte[] fileData = FileUtils.readFileToByteArray(file);
                // 实际文件中数据长度
                String length = fileName.split("\\.")[0].split(ConstantUtil.SPLITTER)[4];
                int fileDataLength = Integer.parseInt(length);
                // 文件内容为空或者长度为0,不发送
                if (null == fileData || fileData.length == 0) {
                    file.delete();
                    continue;
                }
                if (fileData.length < fileDataLength) {
                    log.info("读取到的数据大小:{}, 实际文件数据大小:{}", fileData.length, fileDataLength);
                    renameFile(file);
                    continue;
                }

                command = branchPath.get(pathKey).getCommand();
                byte[] sendData = UnvProtocolUtil.buildUnvMsg(fileData, command);
                log.info("{}:数据大小:{}", branchPath.get(pathKey), sendData.length);
                Channel channel = tcpClient.getNextChannel();
                if (channel.isWritable()) {
                    File tempFile = file;
                    channel.writeAndFlush(sendData).addListener(future -> {
                        if (!future.isSuccess()) {
                            log.error("{}文件数据发送数据失败,重命名为.fail文件");
                            renameFile(tempFile);
                        } else {
                            // 数据发送成功后,删除文件
                            log.debug("{}:数据发送成功", tempFile.getName());
                            if (!tempFile.delete()) {
                                log.error("{}:文件删除失败", tempFile.getPath());
                            }
                        }
                    });
                } else {
                    renameFile(file);
                }
            } catch (InterruptedException e) {
                log.error("数据出队异常:", e);
            } catch (IOException e) {
                log.error("读取文件内容异常:", e);
                renameFile(file);
            } catch (Exception e) {
                log.error("数据发送异常:", e);
                renameFile(file);
            }
        }
    }

    /**
     * 将.temp文件重命名为.fail文件
     *
     * @param file 文件
     */
    private void renameFile(File file) {

        Runnable renameFailFile = () -> {
            try {
                int times = 0;
                String filePath = file.getPath();
                filePath = filePath.replace(ConstantUtil.TEMP_SUFFIX, ConstantUtil.FAIL_SUFFIX);
                File failFile = new File(filePath);
                while (true) {
                    // 文件重命名成功,跳出循环
                    if (file.renameTo(failFile)) {
                        break;
                    }
                    // 文件重命名失败,延时0.5秒重试,失败三次后,文件删除
                    if (times >= 2) {
                        log.info("{}文件三次重命名失败,删除", filePath);
                        file.delete();
                        break;
                    }
                    times++;
                    TimeUnit.MILLISECONDS.sleep(500);
                }
                log.info("将.temp文件重命名为{}", filePath);
            } catch (Exception e) {
                log.error("{}文件重命名为fail文件异常:", e);
            }
        };
        InitServer.failFileThreadPoolExecutor.execute(renameFailFile);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值