Architecture of a Highly Scalable NIO-Based Server
一。 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.
A 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的更多相关文章
- Java NIO: Non-blocking Server 非阻塞网络服务器
本文翻译自 Jakob Jenkov 的 Java NIO: Non-blocking Server ,原文地址:http://tutorials.jenkov.com/java-nio/non-bl ...
- Java NIO: Non-blocking Server
Even if you understand how the Java NIO non-blocking features work (Selector, Channel, Buffer etc.), ...
- NIO的一些相关链接
Architecture of a Highly Scalable NIO-Based Server Scalable IO in Java Tricks and Tips with NIO part ...
- 高吞吐高并发Java NIO服务的架构(NIO架构及应用之一)
高吞吐高并发Java NIO服务的架构(NIO架构及应用之一) http://maoyidao.iteye.com/blog/1149015 Java NIO成功的应用在了各种分布式.即时通信和中 ...
- 如何设计scalable 的系统 (转载)
Design a Scalable System Design a system that scales to millions of users (AWS based) Step 1: Outlin ...
- Cross-Domain Security For Data Vault
Cross-domain security for data vault is described. At least one database is accessible from a plural ...
- 可扩展的Web系统和分布式系统(Scalable Web Architecture and Distributed Systems)
Open source software has become a fundamental building block for some of the biggest websites. And a ...
- Scalable Web Architecture and Distributed Systems
转自:http://aosabook.org/en/distsys.html Scalable Web Architecture and Distributed Systems Kate Matsud ...
- 一段关于java NIO server端接受客户端socket连接;演示了关于channel,selector等组件的整合使用
public class ReactorDemo { public static void main(String[] args) throws IOException { ServerSocketC ...
随机推荐
- Xamarin.Android编译CPU类型选择方式
Xamarin.Android编译CPU类型选择方式 在Xamarin.Android编译的时候,默认提供了5种CPU类型供大家选择.它们分别为armeabi.armeabi-v7a.arm64-v8 ...
- Chart系列(一):Chart的基本元素
如何使用一个Chart,则首先必须要了解其组织结构,其次知道其API. Chart元素 首先,来看看Chart组成元素. Axis Label:坐标轴标签 Axis Title:坐标轴标题 C ...
- HDU3251 Being a Hero(最小割)
题目大概一个国家n个城市由m条单向边相连,摧毁每条边都有一个费用.现在你可以选择所给的f个城市中的若干个,每个城市选择后都有一定的价值,但首都1号城市必须到达不了你选择的城市,因为你可能需要摧毁一些边 ...
- 如何在 .Net Framework 4.0 项目上使用 OData?
最新的 Microsoft ASP.NET Web API 2.1 OData 5.1.0 已只能在 .Net Framework 4.5 的安装了,如果要在 VS2010的 .Net Framewo ...
- BZOJ4327 : JSOI2012 玄武密码
对所有询问串建立AC自动机. 然后将母串在AC自动机上跑,每走到一个点x,从x点出发沿着fail指针能到的所有前缀都是匹配成功的,暴力向上走,碰到走过的就break,这样每个点最多只会被标记一次. 时 ...
- BZOJ3738 : [Ontak2013]Kapitał
$C_{N+M}^N=\frac{(N+M)!}{N!M!}$ 考虑求出$ans\bmod 10^9$的值 $10^9=2^9\times5^9$ 以$2^9$为例,先预处理出$1$..$2^9$中不 ...
- Vijos 1061 迎春舞会之三人组舞(DP)
题目链接 经典DP问题,通过问题,看出结论,然后倒序,然后注意条件. #include <cstdio> #include <cstring> #include <ios ...
- BZOJ4010: [HNOI2015]菜肴制作
Description 知名美食家小 A被邀请至ATM 大酒店,为其品评菜肴. ATM 酒店为小 A 准备了 N 道菜肴,酒店按照为菜肴预估的质量从高到低给予 1到N的顺序编号,预估质量最高的菜肴编号 ...
- 发现未知字段 state ,过滤条件 [["state","not in",["draft"]]] 有误 的处理
通常该问题出现的原因在于对象定义的state字段在view中并没有出现,解决方案就是在view中添加该字段即可.
- [百科] - iLBC
iLBC是一种专为包交换网络通信设计的编解码,优于目前流行的G.729.G.723.1,对丢包进行了特有处理,即使在丢包率相当高的网络环境下,仍可获得非常清晰的语音效果. 30ms ptime的iLB ...