Netty4(三)多连接客户端设计与实现

本文介绍多连接的netty客户端设计

目标

Netty(二)一文中实现了单连接客户端,也就是说客户端只有一个连接,这就不利于高并发RPC的设计,本文尝试设计一个多连接的客户端,支持断线重连

UML类图

mutilClient.png

实现

多连接客户端

package com.mym.netty.client;


import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 多连接客户端
 */
public class MutilClient {

    /**服务类*/
    private Bootstrap bootstrap = new Bootstrap();

    /**会话集合*/
    private List<Channel>  channels = new ArrayList<Channel>();

    /**引用计数*/
    private final AtomicInteger index = new AtomicInteger();

    /**初始化*/
    public void init(int count){
        //worker
        EventLoopGroup worker = new NioEventLoopGroup();

        //设置工作线程
        this.bootstrap.group(worker);

        //初始化channel
        bootstrap.channel(NioSocketChannel.class);

        //设置handler管道
        bootstrap.handler(new ChannelInitializer<Channel>() {
            @Override
            protected void initChannel(Channel channel) throws Exception {
                channel.pipeline().addLast(new StringDecoder());
                channel.pipeline().addLast(new StringEncoder());
                channel.pipeline().addLast(new ClientHandler());
            }
        });

        //根据连接数建立连接
        for(int i = 0;i < count;i++){
            ChannelFuture channelFuture = bootstrap.connect("0.0.0.0",9099);
            channels.add(channelFuture.channel());
        }

    }

    /**获取channel(会话)*/
    public Channel nextChannel(){
        return getFirstActiveChannel(0);
    }

    private Channel getFirstActiveChannel(int count) {
        Channel channel = channels.get(Math.abs(index.getAndIncrement() % channels.size()));
        if(!channel.isActive()){
            //重连
            reconect(channel);
            if(count > channels.size()){
                throw new RuntimeException("no Idle channel!");
            }

            return getFirstActiveChannel(count + 1);
        }
        return channel;
    }

    /**重连*/
    private void reconect(Channel channel) {
        //此处可改为原子操作
        synchronized(channel){
            if(channels.indexOf(channel) == -1){
                return ;
            }

            Channel newChannel = bootstrap.connect("0.0.0.0", 9099).channel();
            channels.set(channels.indexOf(channel), newChannel);

            System.out.println(channels.indexOf(channel) + "位置的channel成功进行重连!");
        }
    }

}

本类采用对象组的方式保存连接。因为一个thread + 队列 == 一个单线程线程池 是线程安全的,任务是线性串行执行的

客户端handler

package com.mym.netty.client;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

public class ClientHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("client receive msg:"+msg.toString());
    }
}

测试类

package com.mym.netty.client;

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class StartClient {

    public static void main(String[] args) {
        mutilClient();
    }

    public static void mutilClient(){
        MutilClient client = new MutilClient();
        client.init(5);

        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
        while(true){
            try {
                System.out.println("请输入:");
                String msg = bufferedReader.readLine();
                client.nextChannel().writeAndFlush(msg);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

}

测试

测试步骤:连接服务端后,客户端先向服务端发送消息,客户端进行断网,然后开网,然后再想服务端发送消息

客户端输出如下:

请输入:
nihao
client receive msg:this is ServerHandler reply msg happend at !1531894695900this is ServerHandler2 reply msg happend at !1531894695902

此处断网,一大堆错。然后重新开网,再次发送消息
请输入:
hello
-1位置的channel成功进行重连!
client receive msg:this is ServerHandler reply msg happend at !1531894961093
client receive msg:this is ServerHandler2 reply msg happend at !1531894961094

本次实现仍有可优化的地方,欢迎留言给出建议

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Netty客户端实现并发发送数据有多种方式,下面是其中两种常见的方式: 1. 使用线程池: - 创建一个线程池,例如`ExecutorService`。 - 在需要发送数据的地方,将发送逻辑封装成一个`Runnable`或`Callable`任务。 - 将任务提交给线程池执行,可以通过`execute()`方法提交`Runnable`任务,或者通过`submit()`方法提交`Callable`任务。 - 线程池会自动管理线程的创建和销毁,并发执行任务。 示例代码: ```java ExecutorService executorService = Executors.newFixedThreadPool(10); // 创建一个大小为10的线程池 // 循环发送数据 for (int i = 0; i < 10; i++) { final int index = i; executorService.execute(() -> { // 发送数据的逻辑 // ... System.out.println("Task " + index + " executed"); }); } executorService.shutdown(); // 关闭线程池 ``` 2. 使用Netty的EventLoopGroup: - 创建一个`NioEventLoopGroup`,它管理了一组NIO线程,用于处理I/O操作。 - 在需要发送数据的地方,通过调用`ChannelHandlerContext`的`writeAndFlush()`方法发送数据。 - 由于EventLoopGroup内部已经实现了并发处理,因此可以同时处理多个请求。 示例代码: ```java EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap bootstrap = new Bootstrap() .group(group) .channel(NioDatagramChannel.class) .handler(new ChannelInitializer<Channel>() { @Override protected void initChannel(Channel ch) throws Exception { // 添加处理器 } }); // 连接到服务器 Channel channel = bootstrap.connect(host, port).sync().channel(); // 循环发送数据 for (int i = 0; i < 10; i++) { channel.writeAndFlush(Unpooled.copiedBuffer("data", CharsetUtil.UTF_8)); } // 等待关闭连接 channel.closeFuture().sync(); } finally { group.shutdownGracefully(); } ``` 这两种方式都可以实现Netty客户端的并发发送数据,具体选择哪种方式取决于你的需求和场景。使用线程池可以更灵活地控制并发度,而使用EventLoopGroup则更符合Netty设计思想。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值