一。 thread-per-connection

The thread-per-connection approach uses an exclusive worker thread for each connection. Within the handling loop, a worker thread

waits for new incoming data, processes the request, returns the response data, and calls the blocking socket's read method

public class Server {
private ExecutorService executors = Executors.newFixedThreadPool(10);
private boolean isRunning = true; public static void main(String... args) throws ... {
new Server().launch(Integer.parseInt(args[0]));
} public void launch(int port) throws ... {
ServerSocket sso = new ServerSocket(port);
while (isRunning) {
Socket s = sso.accept();
executors.execute(new Worker(s));
}
} private class Worker implements Runnable {
private LineNumberReader in = null; Worker(Socket s) throws ... {
in = new LineNumberReader(new InputStreamReader(...));
out = ...
} public void run() {
while (isRunning) {
try {
// blocking read of a request (line)
String request = in.readLine(); // processing the request
String response = ... // return the response
out.write(resonse);
out.flush();
} catch (Exception e ) {
...
}
}
in.close();
...
}
}
}

Because each connection has an associated thread waiting on the server side, very good response times can be achieved. However,

higher loads require a higher number of running, concurrent threads, which limits scalability. In particular, long-living connections

like persistent HTTP connections lead to a lot of concurrent worker threads, which tend to waste their time waiting concurrent

threads can waste a great deal of stack space. Note, for example, that the default Java thread stack size for Solaris is 512 KB.

二。thread-on-event

If a readiness event occurs, an event handler will be notified to perform the appropriate processing within dedicated worker threads.

To participate in the event architecture, the connection's Channel has to be registered on a Selector. This will be done by calling

the register method. Although this method is part of the SocketChannel, the channel will be registered on the Selector, not the

other way around.

SocketChannel channel = serverChannel.accept();
channel.configureBlocking(false); // register the connection
SelectionKey sk = channel.register(selector, SelectionKey.OP_READ);

To detect new events, the Selector provides the capability to ask the registered channels for their readiness events. By calling the select

method, the Selector collects the readiness events of the registered channels. This method call blocks until at least one event has been

occurred. In this case, the method returns the number of connections that have become ready for I/O operations since the last select call.

The selected connections can be retrieved by calling the Selector's selectedKey method. This method returns a set of SelectionKey objects,

which holds the IO event status and the reference of the connection's Channel.

Selector is held by the Dispatcher. This is a single-threaded active class that surrounds the Selector. The Dispatcher is responsible to

retrieve the events and to dispatch the handling of the consumed events to the EventHandler.

Within the dispatch loop, the Dispatcher calls the Selector's select method to wait for new events. If at least one event has been occurred,

the method call returns and the associated channel for each event can be acquired by calling the selectedKeys method.

while (isRunning) {
// blocking call, to wait for new readiness events
int eventCount = selector.select(); // get the events
  Iterator it = selector.selectedKeys().iterator();
  while (it.hasNext()) {
SelectionKey key = it.next();
it.remove(); // readable event?
if (key.isValid() && key.isReadable()) {
eventHandler.onReadableEvent(key.channel());
} // writable event?
if (key.isValid() && key.isWritable()) {
key.interestOps(SelectionKey.OP_READ); // reset to read only
eventHandler.onWriteableEvent(key.channel());
}
...
}
...
}

Because worker threads are not forced to waste time by waiting for new requests to open a connection, the scalability and

throughput of this approach is conceptually only limited by system resources like CPU or memory. That said, the response

times wouldn't be as good as for the thread-per-connection approach, because of the required thread switches and

synchronization. The challenge of the event-driven approach is therefore to minimize synchronizations and optimize thread

management, so that this overhead will be negligible.

三。构成

1.Acceptor

The Acceptor is a single threaded active class. Because it is only responsible for handling the very short-running
client connection request, it is often sufficient to implement the Acceptor using the blocking I/O model.

class Acceptor implements Runnable {
...
void init() {
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(true);
serverChannel.socket().bind(new InetSocketAddress(serverPort));
} public void run() {
while (isRunning) {
try {
SocketChannel channel = serverChannel.accept(); Connection con = new Connection(channel, appHandler);
dispatcherPool.nextDispatcher().register(con);
} catch (...) {
...
}
}
}
}

2.Dispatcher

Because the scalability of a single Dispatcher is limited, often a small pool of Dispatchers will be used. One reason for this limitation

is the operating-system-specific implementation of the Selector.

Most popular operating systems map a SocketChannel to a file handle in a one-to-one relationship. Depending on the concrete system,

the maximum number of file handles per Selector is limited in adifferent way.

The Selector manages the registered channels internally by using key sets. This means that by registering a channel, an associated

SelectionKey will be created and be added to the Selector's registered key set. At the same time, the concurrent dispatcher thread

could call the Selector's select method, which also accesses the key set.

Because the key sets are not thread-safe, an unsynchronized registration in the context of the Acceptor thread can lead to deadlocks

and race conditions. This can be solved by implementing the selector guard object idiom, which allows suspending the dispatcher

thread temporarily.

class Dispatcher implements Runnable {
private Object guard = new Object();
… void register(Connection con) {
// retrieve the guard lock and wake up the dispatcher thread
// to register the connection's channel
synchronized (guard) {
selector.wakeup();
con.getChannel().register(selector, SelectionKey.OP_READ, con);
} // notify the application EventHandler about the new connection
} void announceWriteNeed(Connection con) {
SelectionKey key = con.getChannel().keyFor(selector);
synchronized (guard) {
selector.wakeup();
key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
}
} public void run() {
while (isRunning) {
synchronized (guard) {
// suspend the dispatcher thead if guard is locked
}
int eventCount = selector.select(); Iterator it = selector.selectedKeys().iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
it.remove(); // read event?
if (key.isValid() && key.isReadable()) {
Connection con = (Connection) key.attachment();
disptacherEventHandler.onReadableEvent(con);
} // write event?
}
}
}
}

4.Dispatcher-Level EventHandler

5.Application-Level EventHandler

 

Architecture of a Highly Scalable NIO-Based Server的更多相关文章

  1. Java NIO: Non-blocking Server 非阻塞网络服务器

    本文翻译自 Jakob Jenkov 的 Java NIO: Non-blocking Server ,原文地址:http://tutorials.jenkov.com/java-nio/non-bl ...

  2. Java NIO: Non-blocking Server

    Even if you understand how the Java NIO non-blocking features work (Selector, Channel, Buffer etc.), ...

  3. NIO的一些相关链接

    Architecture of a Highly Scalable NIO-Based Server Scalable IO in Java Tricks and Tips with NIO part ...

  4. 高吞吐高并发Java NIO服务的架构(NIO架构及应用之一)

    高吞吐高并发Java NIO服务的架构(NIO架构及应用之一) http://maoyidao.iteye.com/blog/1149015   Java NIO成功的应用在了各种分布式.即时通信和中 ...

  5. 如何设计scalable 的系统 (转载)

    Design a Scalable System Design a system that scales to millions of users (AWS based) Step 1: Outlin ...

  6. Cross-Domain Security For Data Vault

    Cross-domain security for data vault is described. At least one database is accessible from a plural ...

  7. 可扩展的Web系统和分布式系统(Scalable Web Architecture and Distributed Systems)

    Open source software has become a fundamental building block for some of the biggest websites. And a ...

  8. Scalable Web Architecture and Distributed Systems

    转自:http://aosabook.org/en/distsys.html Scalable Web Architecture and Distributed Systems Kate Matsud ...

  9. 一段关于java NIO server端接受客户端socket连接;演示了关于channel,selector等组件的整合使用

    public class ReactorDemo { public static void main(String[] args) throws IOException { ServerSocketC ...

随机推荐

  1. Javac早期(编译期)

    从Sun Javac的代码来看,编译过程大致可以分为3个过程: 解析与填充符号表过程. 插入式注解处理器的注解处理过程. 分析与字节码生成过程. Javac编译动作的入口是com.sun.tools. ...

  2. Xamarin Android布局文件没有智能提示

    Xamarin Android布局文件没有智能提示 在Visual Studio 2015中,Android项目的Main.axml文件没有智能提示,不便于布局文件的编写.解决办法:(1)从Xamar ...

  3. Ajax优缺点

    1.页面无刷新,用户体验好: 2.异步通信,迅速的响应能力: 3.按需取数据,减少冗余需求,减少服务器负荷. 4.破坏浏览器后退按钮的正常行为. 5.Ajax的核心是JavaScript对象XmlHt ...

  4. http://blog.csdn.net/shangboerds/article/details/44175667

    http://blog.csdn.net/shangboerds/article/details/44175667

  5. win产品密钥大搜集

    Win7产品密钥大收集 一.先说的还是4枚神Key如下: KH2J9-PC326-T44D4-39H6V-TVPBY TFP9Y-VCY3P-VVH3T-8XXCC-MF4YK 236TW-X778T ...

  6. A C[HDU1570]

    A C Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submiss ...

  7. BZOJ1100 : [POI2007]对称轴osi

    将多边形转化为如下的环: 1到2的边,角2,2到3的边,角3,...,n-1到n的边,角n,n到1的边,角1 然后枚举对称轴,如果i是对称轴,那么[i-n,i+n]是一个回文串 用Manacher算法 ...

  8. CCSpriteBatchNode的优化性能

    当将大量精灵加载到CCLayer时,如果直接利用[self addChild:sprite]去加载,每加载一个精灵,都必须open,draw,close, 而利用 CCSpriteBatchNode去 ...

  9. Win2K3一句话开3389

    网上流传了很多开3389的exe小工具,还有读写注册表的bat批处理,有的是操作注册表,有的是操作各种组件这里提供一个不会被拦截,而且可以使用的开3389的cmd一句话: wmic RDTOGGLE  ...

  10. 对于默认 Windows NT 安装的 SID 值

    https://support.microsoft.com/en-us/kb/163846/zh-cn