本文介绍多连接的netty客户端设计
目标
Netty(二)一文中实现了单连接客户端,也就是说客户端只有一个连接,这就不利于高并发RPC的设计,本文尝试设计一个多连接的客户端,支持断线重连
UML类图
实现
多连接客户端
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
本次实现仍有可优化的地方,欢迎留言给出建议