ServerSocketChannel实现多Selector高并发server
参考hbase RpcServer,编写了一个简洁版多Selector server,对nio怎么用,Selector如何选择事件会有更深入的认识。
client端发送消息:内容长度 + 内容,200线程同时发送
server端接收消息:解析内容长度和内容,返回2MB测试数据给客户端
Server端:一个accept selector,多个read selector,一个write selector
package com.ai.nio; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.LinkedBlockingQueue; /**
* Created by wangkai8 on 17/1/5.
*/
public class Server { public static final Log LOG = LogFactory.getLog(Server.class); private BlockingQueue<Call> queue = new LinkedBlockingQueue<Call>(); private Queue<Call> responseCalls = new ConcurrentLinkedQueue<Call>(); volatile boolean running = true; private Responder responder = null; private static int NIO_BUFFER_LIMIT = 64 * 1024; private int handler = 10; class Listener extends Thread { Selector selector;
Reader[] readers;
int robin;
int readNum; Listener(int port) throws IOException {
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.socket().bind(new InetSocketAddress(port), 150);
selector = Selector.open();
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
readNum = 10;
readers = new Reader[readNum];
for(int i = 0; i < readNum; i++) {
readers[i] = new Reader(i);
readers[i].start();
}
} public void run() {
while(running) {
try {
selector.select();
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while(it.hasNext()) {
SelectionKey key = it.next();
it.remove();
if(key.isValid()) {
if(key.isAcceptable()) {
doAccept(key);
}
}
}
} catch (IOException e) {
LOG.error("", e);
}
}
} public void doAccept(SelectionKey selectionKey) throws IOException {
ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectionKey.channel();
SocketChannel socketChannel;
while((socketChannel = serverSocketChannel.accept()) != null) {
try {
socketChannel.configureBlocking(false);
socketChannel.socket().setTcpNoDelay(true);
socketChannel.socket().setKeepAlive(true);
} catch (IOException e) {
socketChannel.close();
throw e;
}
Reader reader = getReader();
try {
reader.startAdd();
SelectionKey readKey = reader.registerChannel(socketChannel);
Connection c = new Connection(socketChannel);
readKey.attach(c);
} finally {
reader.finishAdd();
}
}
} public Reader getReader() {
if(robin == Integer.MAX_VALUE) {
robin = 0;
}
return readers[(robin ++) % readNum];
}
} class Reader extends Thread { Selector readSelector;
boolean adding; Reader(int i) throws IOException {
setName("Reader-" + i);
this.readSelector = Selector.open();
LOG.info("Starting Reader-" + i + "...");
} @Override
public void run() {
while(running) {
try {
readSelector.select();
while(adding) {
synchronized(this) {
this.wait(1000);
}
} Iterator<SelectionKey> it = readSelector.selectedKeys().iterator();
while(it.hasNext()) {
SelectionKey key = it.next();
it.remove();
if(key.isValid()) {
if(key.isReadable()) {
doRead(key);
}
}
}
} catch (IOException e) {
LOG.error("", e);
} catch (InterruptedException e) {
LOG.error("", e);
}
}
} public void doRead(SelectionKey selectionKey) {
Connection c = (Connection) selectionKey.attachment();
if(c == null) {
return;
} int n;
try {
n = c.readAndProcess();
} catch (IOException e) {
LOG.error("", e);
n = -1;
} catch (Exception e) {
LOG.error("", e);
n = -1;
}
if(n == -1) {
c.close();
}
} public SelectionKey registerChannel(SocketChannel channel) throws IOException {
return channel.register(readSelector, SelectionKey.OP_READ);
} public void startAdd() {
adding = true;
readSelector.wakeup();
} public synchronized void finishAdd() {
adding = false;
this.notify();
}
} class Connection {
private SocketChannel channel;
private ByteBuffer dataBufferLength;
private ByteBuffer dataBuffer;
private boolean skipHeader; public Connection(SocketChannel channel) {
this.channel = channel;
this.dataBufferLength = ByteBuffer.allocate(4);
} public int readAndProcess() throws IOException {
int count;
if(!skipHeader) {
count = channelRead(channel, dataBufferLength);
if (count < 0 || dataBufferLength.remaining() > 0) {
return count;
}
} skipHeader = true; if(dataBuffer == null) {
dataBufferLength.flip();
int dataLength = dataBufferLength.getInt();
dataBuffer = ByteBuffer.allocate(dataLength);
} count = channelRead(channel, dataBuffer); if(count >= 0 && dataBuffer.remaining() == 0) {
process();
} return count;
} /**
* process the dataBuffer
*/
public void process() {
dataBuffer.flip();
byte[] data = dataBuffer.array();
Call call = new Call(this, data, responder);
try {
queue.put(call);
} catch (InterruptedException e) {
LOG.error("", e);
} } public void close() {
if(channel != null) {
try {
channel.close();
} catch (IOException e) {
}
}
}
} class Responder extends Thread { Selector writeSelector; public Responder() throws IOException {
writeSelector = Selector.open();
} public void run() {
while(running) {
try {
registWriters();
int n = writeSelector.select(1000);
if(n == 0) {
continue;
}
Iterator<SelectionKey> it = writeSelector.selectedKeys().iterator();
while(it.hasNext()) {
SelectionKey key = it.next();
it.remove();
if(key.isValid() && key.isWritable()) {
doAsyncWrite(key);
}
}
} catch (IOException e) {
LOG.error("", e);
}
}
} public void registWriters() throws IOException {
Iterator<Call> it = responseCalls.iterator();
while(it.hasNext()) {
Call call = it.next();
it.remove();
SelectionKey key = call.conn.channel.keyFor(writeSelector);
try {
if (key == null) {
try {
call.conn.channel.register(writeSelector, SelectionKey.OP_WRITE, call);
} catch (ClosedChannelException e) {
//the client went away
if (LOG.isTraceEnabled())
LOG.trace("the client went away", e);
}
} else {
key.interestOps(SelectionKey.OP_WRITE);
}
} catch (CancelledKeyException e) {
if (LOG.isTraceEnabled())
LOG.trace("the client went away", e);
}
}
} public void registerForWrite(Call call) throws IOException {
responseCalls.add(call);
writeSelector.wakeup();
} private void doAsyncWrite(SelectionKey key) throws IOException {
Call call = (Call) key.attachment();
if(call.conn.channel != key.channel()) {
throw new IOException("bad channel");
}
int numBytes = channelWrite(call.conn.channel, call.response);
if(numBytes < 0 || call.response.remaining() == 0) {
try {
key.interestOps(0);
} catch (CancelledKeyException e) {
LOG.warn("Exception while changing ops : " + e);
}
}
} private void doResponse(Call call) throws IOException {
//if data not fully send, then register the channel for async writer
if(!processResponse(call)) {
registerForWrite(call);
}
} private boolean processResponse(Call call) throws IOException {
boolean error = true;
try {
int numBytes = channelWrite(call.conn.channel, call.response);
if (numBytes < 0) {
throw new IOException("error socket write");
}
error = false;
} finally {
if(error) {
call.conn.close();
}
}
if(!call.response.hasRemaining()) {
call.done = true;
return true;
}
return false;
}
} class Handler extends Thread { public Handler(int i) {
setName("handler-" + i);
LOG.info("Starting Handler-" + i + "...");
} public void run() {
while(running) {
try {
Call call = queue.take();
process(call);
} catch (InterruptedException e) {
LOG.error("", e);
} catch (IOException e) {
LOG.error("", e);
}
}
} public void process(Call call) throws IOException {
byte[] request = call.request;
String message = new String(request);
LOG.info("received mseesage: " + message); //each channel write 2MB data for test
int dataLength = 2 * 1024 * 1024;
ByteBuffer buffer = ByteBuffer.allocate(4 + dataLength); buffer.putInt(dataLength);
writeDataForTest(buffer);
buffer.flip(); call.response = buffer;
responder.doResponse(call);
}
} public void writeDataForTest(ByteBuffer buffer) {
int n = buffer.limit() - 4;
for(int i = 0; i < n; i++) {
buffer.put((byte)0);
}
} class Call {
Connection conn;
byte[] request;
Responder responder;
ByteBuffer response;
boolean done;
public Call(Connection conn, byte[] request, Responder responder) {
this.conn = conn;
this.request = request;
this.responder = responder;
}
} public int channelRead(ReadableByteChannel channel, ByteBuffer buffer) throws IOException {
return buffer.remaining() <= NIO_BUFFER_LIMIT ? channel.read(buffer) : channleIO(channel, null, buffer);
} public int channelWrite(WritableByteChannel channel, ByteBuffer buffer) throws IOException {
return buffer.remaining() <= NIO_BUFFER_LIMIT ? channel.write(buffer) : channleIO(null, channel, buffer);
} public int channleIO(ReadableByteChannel readCh, WritableByteChannel writeCh, ByteBuffer buffer) throws IOException {
int initRemaining = buffer.remaining();
int originalLimit = buffer.limit(); int ret = 0;
try {
while (buffer.remaining() > 0) {
int ioSize = Math.min(buffer.remaining(), NIO_BUFFER_LIMIT);
buffer.limit(buffer.position() + ioSize);
ret = readCh == null ? writeCh.write(buffer) : readCh.read(buffer);
if (ret < ioSize) {
break;
}
}
} finally {
buffer.limit(originalLimit);
} int byteRead = initRemaining - buffer.remaining();
return byteRead > 0 ? byteRead : ret;
} public void startHandler() {
for(int i = 0; i < handler; i++) {
new Handler(i).start();
}
} public void start() throws IOException {
new Listener(10000).start();
responder = new Responder();
responder.start();
startHandler();
LOG.info("server startup! ");
} public static void main(String[] args) throws IOException {
Server server = new Server();
server.start();
}
}
Client端:
package com.ai.nio; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import javax.net.SocketFactory;
import java.io.*;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel; /**
* Created by wangkai8 on 17/1/6.
*/
public class Client { public static final Log LOG = LogFactory.getLog(Client.class); Socket socket;
OutputStream out;
InputStream in; public Client() throws IOException {
socket = SocketFactory.getDefault().createSocket();
socket.setTcpNoDelay(true);
socket.setKeepAlive(true);
InetSocketAddress server = new InetSocketAddress("localhost", 10000);
socket.connect(server, 10000);
out = socket.getOutputStream();
in = socket.getInputStream();
} public void send(String message) throws IOException {
byte[] data = message.getBytes();
DataOutputStream dos = new DataOutputStream(out);
dos.writeInt(data.length);
dos.write(data);
out.flush();
} public static void main(String[] args) throws IOException {
int n = 200;
for(int i = 0; i < n; i++) {
new Thread() {
Client client = new Client(); public void run() {
try {
client.send(getName() + "_xiaomiemie"); DataInputStream inputStream = new DataInputStream(client.in);
int dataLength = inputStream.readInt();
byte[] data = new byte[dataLength];
inputStream.readFully(data);
client.socket.close();
LOG.info("receive from server: dataLength=" + data.length);
} catch (IOException e) {
LOG.error("", e);
} catch (Exception e) {
LOG.error("", e);
}
}
}.start();
}
} }
转载请标注原文地址:http://www.cnblogs.com/yueweimian/p/6262211.html
ServerSocketChannel实现多Selector高并发server的更多相关文章
- 3高并发server:多路IO之epoll
1 epoll epoll是Linux下多路复用IO接口select/poll的增强版本号,它能显著提高程序在大量并.发连接中仅仅有少量活跃的情况下的系统CPU利用率,由于它会复用文件描写叙述符 ...
- 1高并发server:多路IO之select
1 select A:select能监听的文件描写叙述符个数受限于FD_SETSIZE,一般为1024.单纯改变进程打开 的文件描写叙述符个数并不能改变select监听文件个数 B:解决1024 ...
- 关于SQL SERVER高并发解决方案
现在大家都比较关心的问题就是在多用户高并发的情况下,如何开发系统,这对我们程序员来说,确实是值得研究,最近找工作面试时也经常被问到,其实我早有去关心和了解这类问题,但一直没有总结一下,导致面试时无法很 ...
- SQL Server 高并发Insert数据解析,实践
在现实的生产环境中,有可能遇到高并发insert的应用.在此应用时由于堆表(Heap)和聚集表的结构不同导致在高并发的情形下insert效率不尽相同.接下来我会简单的以测试用例来简要说明.并举例说明如 ...
- ql Server 高频,高并发访问中的键查找死锁解析
死锁对于DBA或是数据库开发人员而言并不陌生,它的引发多种多样,一般而言,数据库应用的开发者在设计时都会有一定的考量进而尽量避免死锁的产生.但有时因为一些特殊应用场景如高频查询,高并发查询下由于数据库 ...
- Sql Server 高频,高并发访问中的键查找死锁解析
死锁对于DBA或是数据库开发人员而言并不陌生,它的引发多种多样,一般而言,数据库应用的开发者在设计时都会有一定的考量进而尽量避免死锁的产生.但有时因为一些特殊应用场景如高频查询,高并发查询下由于数据库 ...
- 基于tomcat为了应对高并发模型实现webserver
在博客上,一个简单的AIOweb来样加工.查看AIO异步处理,依靠操作系统完成IO操作Proactor处理模型确实很强大,它可以实现高并发.高响应server一个很好的选择,但在tomcat中间con ...
- JAVA NIO non-blocking模式实现高并发服务器(转)
原文链接:JAVA NIO non-blocking模式实现高并发服务器 Java自1.4以后,加入了新IO特性,NIO. 号称new IO. NIO带来了non-blocking特性. 这篇文章主要 ...
- Java进阶知识点:服务端高并发的基石 - NIO与Reactor AIO与Proactor
一.背景 要提升服务器的并发处理能力,通常有两大方向的思路. 1.系统架构层面.比如负载均衡.多级缓存.单元化部署等等. 2.单节点优化层面.比如修复代码级别的性能Bug.JVM参数调优.IO优化等等 ...
随机推荐
- 关于ionic的一些坑(1)
既然来了,总要留下点什么证明自己来过不是,今天就扒一扒自己在ionic上面遇到的坑,因为在项目中2还没出来,所以现在所遇到的都是1中的,关于2的,待老夫以后详细摸索之后在与君细细道来. 1.ionic ...
- win32线程池代码(WinApi/C++)
win32线程池代码(WinApi/C++) 健壮, 高效,易用,易于扩, 可用于任何C++编译器 //说明, 这段代码我用了很久, 我删除了自动调整规模的代码(因为他还不成熟)/********** ...
- [小技巧][ASP.Net MVC Hack] 使用 HTTP 报文中的 Header 字段进行身份验证
在一些 Web 系统中,身份验证是依靠硬件证书进行的:在电脑上插入 USB 证书,浏览器插件读取证书的相关信息,然后在发送 HTTP 登录请求时顺便在 Header 字段附加上身份信息.服务器端处理这 ...
- SAN和NAS的区别
SAN : STORAGE AREA NETWORK 存储区域网络 NAS : NETWORK ATTACHED STORAGE 网络附加存储 NAS不一定是盘阵,一台普通的主机就可以做出NAS, ...
- JS简单实现图片切换
<!DOCTYPE html><html> <head> <meta charset="utf-8" /> <title> ...
- 配置 VS 2015 开发跨平台手机应用
为了使用 VS 2015 开发跨平台手机应用,VS 2015 装了很多次,遇到了很多坑,才终于弄明白怎样配置才能正常使用C#开发手机应用,现把步骤分享给大家,以免大家少走弯路. 运行环境: Windo ...
- \r \r\n \t 的区别
http://www.360doc.com/content/12/0530/15/16538_214756101.shtml \n 软回车: 在Windows 中表示换行且回到下一行的最开 ...
- JS/CSS/IMG加载顺序关系之DOMContentLoaded事件
DOMContentLoaded介绍 DOMContentLoaded事件的触发条件是: 将会在“所有的DOM全部加载完毕并且JS加载执行后触发”. 但如果“js是通过动态加载进来的话,是不会影响到D ...
- 为CKEDITOR内容中图片加上 图片服务器路径
做网站的时候,前台和后台是分开的, 用了CKEDITOR上传图片,但是发现内容带图片的时候,前台Web浏览的时候是一个红X,一看路径不对,上传的到数据库中的是相对的虚拟路径,例如:<img al ...
- 关于ubuntu 系统
1. ubuntu怎么卸载自带的软件中心?sudo apt-get remove software-center 2. 重新安装ubuntu软件中心!sudo apt-get install --re ...