用JAVA的NIO如何实现高并发

XuanrenLu 2015-05-25 11:48:00
服务器功能:返回给客户端一个 String。
并发数目 500还算正常,到了1000,就有很多超时发生。
用NIO处理起来,感觉很耗费CPU,请大家帮忙看看程序,应该是程序的问题,已经头疼好几天了。谢谢大家。
public class DataServer {
public static ArrayList<SelectorLoop> connectionBellList = new ArrayList<>();
public boolean isReadBellRunning = false;
public long requestCount = 0;
public static ServerSocketChannel ssc;
public static ExecutorService executorService = Executors
.newFixedThreadPool(30);

public static void main(String[] args) throws IOException {
new DataServer().startServer();

}

// 启动服务器
public void startServer() throws IOException {

// 准备好闹钟.当有链接进来的时候响.
for ( int i = 0; i < 5;i++ ){
SelectorLoop selectorLoop = new SelectorLoop();
connectionBellList.add(selectorLoop);
}
// 开启一个server channel来监听
ssc = ServerSocketChannel.open();

// 开启非阻塞模式
ssc.configureBlocking(false);
ServerSocket socket = ssc.socket();
socket.bind(new InetSocketAddress("localhost", 7878));
// 给闹钟规定好要监听报告的事件,这个闹钟只监听新连接事件.

for ( int i = 0; i < connectionBellList.size();i++ ){
ssc.register(connectionBellList.get(i).getSelector(), SelectionKey.OP_ACCEPT);
new Thread(connectionBellList.get(i)).start();
}
}

public static class SendThread implements Runnable {
private SocketChannel sc;
private ByteBuffer temp;
private String msg;
private long count;

/**
* @param sc
* @param msg
*/
public SendThread(SocketChannel sc, String msg) {
this.sc = sc;
this.temp = ByteBuffer.allocate(1024);
this.msg = msg;
}

@Override
public void run() {
try {
sc.write(ByteBuffer.wrap(msg.getBytes(Charset.forName("UTF-8"))));
// 清空buffer
temp.clear();
} catch (IOException e) {
System.out.println("channel is null");
} catch (Exception e) {
System.out.println("通道异常");
}

}
}

// Selector轮询线程类

public static class SelectorLoop implements Runnable {

private Selector selector;
private ByteBuffer temp = ByteBuffer.allocate(1024);
private String msg = "this is a message...";
public static long requsts = 0;

public SelectorLoop() throws IOException {

this.selector = Selector.open();
}

public Selector getSelector() {
return this.selector;
}
public static synchronized long addCount() {
return ++requsts;
}
@Override
public void run() {
System.out.println("Server start 。。。。 ");
while (true) {
Iterator<SelectionKey> it;
try {
// 阻塞,只有当至少一个注册的事件发生的时候才会继续.
synchronized (ssc) {
this.selector.select();
}
// 获取事件
Set<SelectionKey> selectKeys = this.selector.selectedKeys();
it = selectKeys.iterator();
System.out.println("connection : " + addCount());

while (it.hasNext()) {
SelectionKey key = it.next();
it.remove();
// 处理事件. 可以用多线程来处理.
if (key.isAcceptable()) {

// 这是一个connection accept事件,
// 并且这个事件是注册在serversocketchannel上的.
ServerSocketChannel ssc = (ServerSocketChannel) key
.channel();
// 接受一个连接.
SocketChannel sc = ssc.accept();
// 在线程池中执行数据传输
executorService.execute(new SendThread(sc, msg));
this.selector.wakeup();
// new Thread(new SendThread(sc,
// msg,++requestCount)).start();
}
}
} catch (IOException e) {
System.out.println("存在客户端强制断开连接断开连接!");
}
}
}

}
}

public class DataClient{

public static void main(String[] args) throws IOException {
// 线程总数 Threadsum * 111
int threadSum = 5;
for ( int i = 0; i < threadSum;i++ ){
new Thread(new ClientThread(2)).start();
}

}

public static class ClientThread implements Runnable{

public static long timeoutCount = 0;
private static int idleCounter = 0;
private Selector selector;
private SocketChannel socketChannel;
private ByteBuffer temp = ByteBuffer.allocate(1024);
private int deep;

public ClientThread(int deep) {
this.deep = deep;

// 同样的,注册闹钟.
try {
this.selector = Selector.open();
// 连接远程server
socketChannel = SocketChannel.open();
// 如果快速的建立了连接,返回true.如果没有建立,则返回false,并在连接后出发Connect事件.
Boolean isConnected = socketChannel.connect(new InetSocketAddress(
"localhost", 7878));
socketChannel.configureBlocking(false);

SelectionKey key = socketChannel.register(selector,
SelectionKey.OP_READ);

if (!isConnected) {
// 如果连接还在尝试中,则注册connect事件的监听. connect成功以后会出发connect事件.
key.interestOps(SelectionKey.OP_CONNECT);
}
} catch (IOException e) {
System.out.println(Thread.currentThread().getName()+" 连接异常 : #"+ (++timeoutCount));
}

}

@Override
public void run() {
// 模拟并发
if (deep > 0) {
for ( int i = 0; i < 10;i++ ){
new Thread(new ClientThread(deep-1)).start();
}
}
while (true) {

try {
// 阻塞,等待事件发生,或者1秒超时. num为发生事件的数量.
int num = this.selector.select(5000);

if (num == 0) {

idleCounter++;

if (idleCounter > 2) {
// System.out.println("连接超时,线程 : "+Thread.currentThread().getName()+" 退出 #"+(++timeoutCount));
return;
}
continue;

}
Set<SelectionKey> keys = this.selector.selectedKeys();
Iterator<SelectionKey> it = keys.iterator();

while (it.hasNext()) {

SelectionKey key = it.next();
it.remove();

if (key.isConnectable()) {
// socket connected
SocketChannel sc = (SocketChannel) key.channel();

if (sc.isConnectionPending()) {
sc.finishConnect();
}

}
// 接收到消息
if (key.isReadable()) {

// msg received.
SocketChannel sc = (SocketChannel) key.channel();

this.temp = ByteBuffer.allocate(1024);
int count = sc.read(temp);

if (count < 0) {
sc.close();
continue;
}

// 切换buffer到读状态,内部指针归位.
temp.flip();

String msg = Charset.forName("UTF-8").decode(temp)
.toString();

System.out.println(Thread.currentThread().getName()+" Client received [" + msg
+ "] ");

return ;

}

}
} catch (IOException e) {

System.out.println("远程主机已关闭,程序退出!");
return;
}

}
}

}


}
...全文
904 7 打赏 收藏 转发到动态 举报
写回复
用AI写文章
7 条回复
切换为时间正序
请发表友善的回复…
发表回复
zhaoqiubo2012 2015-11-10
  • 打赏
  • 举报
回复
http://git.oschina.net/java616
zhaoqb2014 2015-11-10
  • 打赏
  • 举报
回复
nio的事件机制,你不应该一味的accept呀。你得处理read啊。一味的accept不就相当于建立多个连接了吗
skgary 2015-05-26
  • 打赏
  • 举报
回复
引用 4 楼 lxr0724 的回复:
[quote=引用 2 楼 skgary 的回复:] nio的目的就是让你不要一个连接占用一个线程,如果一个连接占用一个线程,那系统就会限制在线程数上面。 要仔细了解清楚事件驱动类型的开发,是想清楚,网络上来一个事件,应该怎么处理。
所以,您有什么解决方案?[/quote] 1. 仔细分析一下你的业务,按事件驱动的方式写好设计。(例如,新客户端连上来怎么处理;客户端-服务器通信协议如何设计 ;客户端发送A请求如何处理;客户端发送B请求如何处理;服务器端发生XX事件应该向客户端发什么消息;客户端断开如何处理。。。) 2. 用封装好的mina或者netty的框架来开发。
scmod 2015-05-25
  • 打赏
  • 举报
回复
是不是线程池小了点.... nio貌似本来就是减少cpu空闲的时间 充分利用= = 不懂...楼主可以试下线程池调大点看看
XuanrenLu 2015-05-25
  • 打赏
  • 举报
回复
引用 2 楼 skgary 的回复:
nio的目的就是让你不要一个连接占用一个线程,如果一个连接占用一个线程,那系统就会限制在线程数上面。 要仔细了解清楚事件驱动类型的开发,是想清楚,网络上来一个事件,应该怎么处理。
所以,您有什么解决方案?
XuanrenLu 2015-05-25
  • 打赏
  • 举报
回复
引用 1 楼 scmod 的回复:
是不是线程池小了点.... nio貌似本来就是减少cpu空闲的时间 充分利用= = 不懂...楼主可以试下线程池调大点看看
加大线程池没有用。。已经试过了
skgary 2015-05-25
  • 打赏
  • 举报
回复
nio的目的就是让你不要一个连接占用一个线程,如果一个连接占用一个线程,那系统就会限制在线程数上面。 要仔细了解清楚事件驱动类型的开发,是想清楚,网络上来一个事件,应该怎么处理。

62,614

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧