Netty TCP 客户端/服务端 通用模板

本文介绍了Nettyx库中提供的TCP通信模板,包括单channel和多channel客户端的创建,以及服务端模板的设计。通过这些模板,作者试图统一Netty的使用范式以提高开发效率和减少协调成本。
摘要由CSDN通过智能技术生成

  我参与过多个基于netty的项目, 他们对原生Netty组件的使用方式五花八门,风格迥异, 各种曲线救国,各种妥协, 各种凌乱, 并不是说这样不正确, 只是如果无法建立统一的使用范式, 将会浪费很多沟通,协调,学习, 百度的时间, 为此Nettyx提供了基础的TCP通信模板, 并分成客户端(client)和服务端(server), 意欲为netty的使用建立一种范式,有范式总比没有好, 说不定没有各位大佬手中的模板好,手动狗头保命

直接上依赖:

请从maven中央仓获取{lastest.version},最新版本号
<dependency>
    <groupId>io.github.fbbzl</groupId>
    <artifactId>nettyx</artifactId>
    <version>{lastest.version}</version>
</dependency>

一. 客户端模板

  Nettyx根据channel载量提供了两种tcp客户端,一种是单channel的客户端, 一种是多channel的客户端. 单channel顾名思义, 一个client组件中只保存了一个channel, 而多channel客户端通常是用来保存一组相同功能的channel.

1. SingleTcpChannelTemplate

  Nettyx提供的单通道客户端基类SingleTcpChannelTemplate, 提供了一些通用的抽象方法,以下展示了一个简单的单通道客户端实现


 @Slf4j
public class TestSingleTcpClient extends SingleTcpChannelTemplate {

   // 为了演示, 此处定义了一个定时器 , 用来在连接成功之后发送消息给对端
    static ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
    
		// 保证初始化远端地址即可
    public TestSingleTcp(InetSocketAddress address) {
        super(address);
    }
    	
    // channel初始化器, 各位根据自己业务自行创建 
   @Override
    protected ChannelInitializer<NioSocketChannel> channelInitializer() {
        return new TestChannelInitializer<>();
    }
    
    // 简简单单写个main进行测试
    public static void main(String[] args) {
       // 先创建当前单通道客户端的实例, 并在构造函数中指定要server的地址
        TestSingleTcp testClient = new TestSingleTcp(new InetSocketAddress("3.11.134.88", 9888));
		// 后续会详细介绍Nettyx对监听器的扩展, 这里先看下如何使用
        ChannelFutureListener listener = new ActionChannelFutureListener()
                .whenSuccess((ls, cf) -> {
                    executor.scheduleAtFixedRate(() -> {
                        byte[] msg = new byte[300];
                        Arrays.fill(msg, (byte) 1);
                        testClient.writeAndFlush(Unpooled.wrappedBuffer(msg));
                    }, 2, 30, TimeUnit.MILLISECONDS);

                    System.err.println(cf.channel().localAddress() + ": ok");
                })
                .whenCancel((ls, cf) -> System.err.println("cancel"))
                // redo为ActionChannelFutureListener中的静态方法, 直接静态引入即可, 用来重试的方法
                .whenFailure(redo(testClient::connect, 2, SECONDS))
                .whenDone((ls, cf) -> System.err.println("done"));
		// 单通道客户端的connect方法返回channelfuture, 我们这里添加监听器即可
        testClient.connect().addListener(listener);
    }
}

  以上便实现了一个单通道的客户端, 调用connect方法即可开始使用此客户端. 此客户端亦可成为spring的bean, 单通道客户端的实现是线程安全的, 当成类似于HttpTemplate, RestTemplate一样去使用即可

2. MultiTcpChannelTemplate

  讲完单通道, 接下来讲多通道客户端, 部分业务场景需要你保存一组功能相同的channel, 针对此场景Nettyx提供了MultiTcpChannelTemplate 泛型为key的类型, 此类所有方法都需要你带上相应的Key,以便内部检索出对应的channel来使用,来看一个简单的MultiTcpChannelTemplate实现

public class TestMultiTcp extends MultiTcpChannelTemplate<String> {

    static ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();

    protected TestMultiTcp(Map<String, InetSocketAddress> inetSocketAddressMap) {
        super(inetSocketAddressMap);
    }

   // 因为设置为同一组相同功能的channel, channel初始化器并没有根据key去进行特例化设置
    @Override
    protected ChannelInitializer<NioSocketChannel> channelInitializer() {
        return new TestChannelInitializer<>();
    }

   // 对channel进行设置 
    @Override
    protected void doChannelConfig(String key, SocketChannelConfig channelConfig) {
        // 进行设置, 可以根据key进行独有的设置 
        super.doChannelConfig(key, channelConfig);
    }

	// 也是简单写个main测试下
    public static void main(String[] args) {
        Map<String, InetSocketAddress> map = new HashMap<>(2);
		// 在构造TestMultiTcp 时需要你传入, key和远端地址的映射
        map.put("a", new InetSocketAddress(9888));
        map.put("b", new InetSocketAddress(9887));

        TestMultiTcp testMultiTcp = new TestMultiTcp(map);
        ChannelFutureListener listener = new ActionChannelFutureListener()
                .whenSuccess((l, cf) -> {
                    executor.scheduleAtFixedRate(() -> {
                        byte[] msg = new byte[300];
                        Arrays.fill(msg, (byte) 1);
                        cf.channel().writeAndFlush(Unpooled.wrappedBuffer(msg));
                    }, 2, 30, TimeUnit.MILLISECONDS);

                    System.err.println(cf.channel().localAddress() + ": ok");
                })
                .whenCancel((l, cf) -> System.err.println("cancel"))
                .whenFailure(redo(cf -> testMultiTcp.connect(channelKey(cf)), 2, SECONDS))
                .whenDone((l, cf) -> System.err.println("done"));

        testMultiTcp.connectAll().values().forEach(c -> c.addListener(listener));
    }
}

  至此我们完成了单通道和多通道tcp客户端的创建


二. 服务端模板

  相较于客户端, 服务端因为自身的特性, 导致它并没有multiserver此类的封装, Nettyx暂时只提供了TcpServerTemplate一种模板

1. TcpServerTemplate

   如下展示了一个简单的TCP服务端, 它继承自Nettyx中的模板TcpServerTemplate 实现指定方法后即可使用

public class TestServer extends TcpServerTemplate {


    public TestServer(SocketAddress bindAddress) {
        super(bindAddress);
    }

    public TestServer(int bindPort) {
        super(bindPort);
    }

    public static void main(String[] args) {
        TestServer    testServer = new TestServer(9888);
        ChannelFuture bindFuture = testServer.bind();
        bindFuture.addListener(cf -> System.err.println("binding state:" + cf.isSuccess()));
        bindFuture.channel().closeFuture().addListener(cf -> {
            System.err.println("关闭了");
            testServer.shutdownGracefully();
        });

    }

    @Override
    protected ChannelInitializer<SocketChannel> childChannelInitializer() {
        return new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel channel) {
                InboundAdvice inboundAdvice = new InboundAdvice(channel)
                        .whenExceptionCaught((c, t) -> System.err.println("in error: [" + t + "]"));
                OutboundAdvice outboundAdvice = new OutboundAdvice(channel)
                        .whenExceptionCaught((c, t) -> System.err.println("out error: [" + t + "]"));

                channel.pipeline().addLast(
                        outboundAdvice
                        , new StartEndFlagFrameCodec(320, true, wrappedBuffer(new byte[]{(byte) 0x7e}))
                        , new EscapeCodec(EscapeMap.mapHex("7e", "7d5e"))
                        , new ChannelInboundHandlerAdapter() {
                            @Override
                            public void channelRead(ChannelHandlerContext ctx, Object in) throws Exception {
                                byte[] msg = new byte[300];
                                Arrays.fill(msg, (byte) 1);
                                ctx.channel().writeAndFlush(Unpooled.wrappedBuffer(msg));
                                super.channelRead(ctx, in);
                            }
                        }
                        , new UserCodec()

                        , inboundAdvice);
            }
        };
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值