Reactor设计模式和观察者模式非常相似,但是它比观察者模式复杂,Reactor设计模式使用一个Selector对象相当于观察模者式里面的观察者,每个
SocketServerChannal 实例和SocketChannal实例都相当于被观察者,当然它们需要在Selector对象里面注册,它们注册之后每个Channal实例都会分配一个 SelectionKey对象,SelectionKey对象可以attach(附带)一个对象,当Selector里面注册的channal有事件发生 时,Selector就会产生一个遍历,这时候可以在遍历的时候用attachment()方法把每个SelectionKey里面attach的对象o 提出来,你可以在这个时候运行在o对象所属类O里面所定义的方法,在这个方法里面可以使用socket.read()方法进行读取网络数据,将数据读出 后,可以将这些数据处理线程做成一个线程池,这样,数据读出后,立即扔到线程池中,这样加速处理速度。
注意在每次遍历提出SelectionKey,对注册的channal的读或写进行处理完毕之后,需要对channal进行重新注册,重新attach()对象。
使用nio提供的Reactor设计模式,我们可以设计一个高性能nio服务器:
1、启动一个实现Runnable接口的Reactor,在Reactor的构造方法里面使用Selector.open()方法实例化一个Selector。
2、打开一个ServerSocketChannal,代码:
ServerSocketChannel serverSocket = ServerSocketChannel.open();
InetSocketAddress address = new InetSocketAddress(InetAddress.getLocalHost(),port);
serverSocket.socket().bind(address);
serverSocket.configureBlocking(false);
3、将ServerSocketChannal在selector里进行注册,代码:
SelectionKey sk =serverSocket.register(selector,SelectionKey.OP_ACCEPT);
4、给sk附带上一个事件处理对象,代码:
sk.attach(new Acceptor());
5、在Reactor类的run方法里面对selector不停的循环扫描,如果发现注册的channal里面有OP_ACCEPT或READ事件发生,有的话就触发一个selector附带上的事件处理对象线程,处理完毕之后须将所有注册的事件清空,代码如下:
public void run() {
try {
while (!Thread.interrupted())
{
selector.select();
Set selected = selector.selectedKeys();
Iterator it = selected.iterator();
while (it.hasNext())
dispatch((SelectionKey)(it.next()));
}
selected.clear();
}catch (IOException ex) {
}
}
void dispatch(SelectionKey k) {
Runnable r = (Runnable)(k.attachment());
if (r != null){
r.run();
}
}
6、我们可以在ServerSocketChannal注册的sk里附带下面的事件处理类的对象(注意这是一个内部类):
class Acceptor implements Runnable {
public void run() {
try {
SocketChannel c = serverSocket.accept();
if (c != null)
//调用Handler来处理channel
new SocketReadHandler(selector, c);
}
catch(IOException ex) {
}
}
}
7、SocketChannel的attach功能将Hanlder和可能会发生事件的channel链接在一起,当发生事件时,可以立即触发相应 链接的Handler。我们在事件处理类里面需要做的事情是在selector里面注册socketChannal,并附带上本handler,下一步有 事件触发时会调用本类的run方法:
public class SocketReadHandler implements Runnable {
final SocketChannel socket;
final SelectionKey sk;
static final int READING = 0, SENDING = 1;
public SocketReadHandler(Selector sel, SocketChannel c)
throws IOException {
socket = c;
socket.configureBlocking(false);
sk = socket.register(sel, READING);
sk.attach(this);
//将SelectionKey注册为可读,以便读取。
sk.interestOps(SelectionKey.OP_READ);
sel.wakeup();
}
public void run() {
try{
readRequest() ;
}catch(Exception ex){
}
}
private void readRequest() throws Exception {
ByteBuffer input = ByteBuffer.allocate(1024);
try{
int bytesRead = socket.read(input);
......
//使用线程池处理request
requestHandle(new Request(socket,btt));
.....
}catch(Exception e) {
}
}
SocketServerChannal 实例和SocketChannal实例都相当于被观察者,当然它们需要在Selector对象里面注册,它们注册之后每个Channal实例都会分配一个 SelectionKey对象,SelectionKey对象可以attach(附带)一个对象,当Selector里面注册的channal有事件发生 时,Selector就会产生一个遍历,这时候可以在遍历的时候用attachment()方法把每个SelectionKey里面attach的对象o 提出来,你可以在这个时候运行在o对象所属类O里面所定义的方法,在这个方法里面可以使用socket.read()方法进行读取网络数据,将数据读出 后,可以将这些数据处理线程做成一个线程池,这样,数据读出后,立即扔到线程池中,这样加速处理速度。
注意在每次遍历提出SelectionKey,对注册的channal的读或写进行处理完毕之后,需要对channal进行重新注册,重新attach()对象。
使用nio提供的Reactor设计模式,我们可以设计一个高性能nio服务器:
1、启动一个实现Runnable接口的Reactor,在Reactor的构造方法里面使用Selector.open()方法实例化一个Selector。
2、打开一个ServerSocketChannal,代码:
ServerSocketChannel serverSocket = ServerSocketChannel.open();
InetSocketAddress address = new InetSocketAddress(InetAddress.getLocalHost(),port);
serverSocket.socket().bind(address);
serverSocket.configureBlocking(false);
3、将ServerSocketChannal在selector里进行注册,代码:
SelectionKey sk =serverSocket.register(selector,SelectionKey.OP_ACCEPT);
4、给sk附带上一个事件处理对象,代码:
sk.attach(new Acceptor());
5、在Reactor类的run方法里面对selector不停的循环扫描,如果发现注册的channal里面有OP_ACCEPT或READ事件发生,有的话就触发一个selector附带上的事件处理对象线程,处理完毕之后须将所有注册的事件清空,代码如下:
public void run() {
try {
while (!Thread.interrupted())
{
selector.select();
Set selected = selector.selectedKeys();
Iterator it = selected.iterator();
while (it.hasNext())
dispatch((SelectionKey)(it.next()));
}
selected.clear();
}catch (IOException ex) {
}
}
void dispatch(SelectionKey k) {
Runnable r = (Runnable)(k.attachment());
if (r != null){
r.run();
}
}
6、我们可以在ServerSocketChannal注册的sk里附带下面的事件处理类的对象(注意这是一个内部类):
class Acceptor implements Runnable {
public void run() {
try {
SocketChannel c = serverSocket.accept();
if (c != null)
//调用Handler来处理channel
new SocketReadHandler(selector, c);
}
catch(IOException ex) {
}
}
}
7、SocketChannel的attach功能将Hanlder和可能会发生事件的channel链接在一起,当发生事件时,可以立即触发相应 链接的Handler。我们在事件处理类里面需要做的事情是在selector里面注册socketChannal,并附带上本handler,下一步有 事件触发时会调用本类的run方法:
public class SocketReadHandler implements Runnable {
final SocketChannel socket;
final SelectionKey sk;
static final int READING = 0, SENDING = 1;
public SocketReadHandler(Selector sel, SocketChannel c)
throws IOException {
socket = c;
socket.configureBlocking(false);
sk = socket.register(sel, READING);
sk.attach(this);
//将SelectionKey注册为可读,以便读取。
sk.interestOps(SelectionKey.OP_READ);
sel.wakeup();
}
public void run() {
try{
readRequest() ;
}catch(Exception ex){
}
}
private void readRequest() throws Exception {
ByteBuffer input = ByteBuffer.allocate(1024);
try{
int bytesRead = socket.read(input);
......
//使用线程池处理request
requestHandle(new Request(socket,btt));
.....
}catch(Exception e) {
}
}