如上模型中,存在的问题
基于以上的问题,大师Doug Lea出了一篇关于分析与构建可伸缩的高性能IO服务的经典文章《Scalable IO in Java》;在文章中Doug Lea通过各个角度,循序渐进的梳理了服务开发中的相关问题,以及在解决问题的过程中服务模型的演变与进化,文章中基于Reactor反应器模式的几种服务模型架构,也被Netty、Mina等大多数高性能IO服务框架所采用,因此阅读这篇文章有助于你更深入了解Netty、Mina等服务框架的编程思想与设计模式。同时Doug Lea 也是java.util.concurrent包的作者,大师当之无愧
用如下NIO简单群聊系统来验证单Reactor单线程模式
Client 端代码
/**
* 群聊客户端
* @author liaojiamin
* @Date:Created in 16:03 2022/8/22
*/
public class GroupChatClient {
private SocketChannel socketChannel;
private Selector selector;
private Integer port = 6667;
private String HOST = "127.0.0.1";
private String userName;
/**
* 初始化
* */
public GroupChatClient() throws IOException {
socketChannel = SocketChannel.open(new InetSocketAddress(HOST, port));
selector = Selector.open();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
userName = socketChannel.getLocalAddress().toString().substring(1);
System.out.println(userName + " is ok");
}
public void sendInfo(String info){
info = userName + " say "+ info;
try {
socketChannel.write(ByteBuffer.wrap(info.getBytes()));
} catch (IOException e) {
e.printStackTrace();
}
}
public void readInfo(){
try{
int readChannels = selector.select();
if(readChannels > 0){
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()){
SelectionKey selectionKey = iterator.next();
if(selectionKey.isReadable()){
SocketChannel readSocketChannel = (SocketChannel) selectionKey.channel();
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
readSocketChannel.read(byteBuffer);
String msg = new String(byteBuffer.array());
System.out.println("读取消息: "+ msg.trim());
}
iterator.remove();
}
}else {
// System.out.println("没有可用通道.....");
}
}catch (Exception e){
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException {
GroupChatClient groupChatClient = new GroupChatClient();
new Thread(){
@Override
public void run(){
while (true){
groupChatClient.readInfo();
try {
Thread.currentThread().sleep(3000);
}catch (Exception e){
e.printStackTrace();
}
}
}
}.start();
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextLine()){
String s = scanner.next();
groupChatClient.sendInfo(s);
}
}
}
/**
* 群聊服务器
* @author liaojiamin
* @Date:Created in 15:49 2022/8/22
*/
public class GroupChatService {
private Selector selector;
private ServerSocketChannel serverSocketChannel;
private Integer port = 6667;
/**
* 数据初始化
*/
public GroupChatService() {
try {
selector = Selector.open();
serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.socket().bind(new InetSocketAddress("127.0.0.1", port));
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
} catch (IOException e) {
e.printStackTrace();
}
}
public void listen() {
System.out.println("listen thread name "+ Thread.currentThread().getName());
try {
while (true) {
int count = selector.select(2000);
if (count > 0) {
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
if (selectionKey.isAcceptable()) {
SocketChannel socketChannel = serverSocketChannel.accept();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
System.out.println(socketChannel.getRemoteAddress() + " 上线 ");
}
if (selectionKey.isReadable()) {
readData(selectionKey);
}
iterator.remove();
}
}else {
// System.out.println("等待....");
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("关闭各种管道");
}
}
/**
* 读取数据
*/
public void readData(SelectionKey key) {
SocketChannel socketChannel = null;
try {
socketChannel = (SocketChannel) key.channel();
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
int count = socketChannel.read(byteBuffer);
if (count > 0) {
String msg = new String(byteBuffer.array());
System.out.println("from 客户端: " + msg);
sendInfoToOtherClients(msg, socketChannel);
}
} catch (IOException e) {
try {
System.out.println(socketChannel.getRemoteAddress()+ "离线了....");
key.cancel();
socketChannel.close();
}catch (Exception e1){
e1.printStackTrace();
}
e.printStackTrace();
}
}
/**
* 消息转发到其他客户端
*/
public void sendInfoToOtherClients(String msg, SocketChannel socketChannel) {
try {
System.out.println("消息转发中.....");
System.out.println("sendInfoToOtherClients thread name "+ Thread.currentThread().getName());
for (SelectionKey selectionKey : selector.keys()) {
Channel targetChannel = selectionKey.channel();
if (targetChannel instanceof SocketChannel && targetChannel != socketChannel) {
SocketChannel targetSocketChannel = (SocketChannel) targetChannel;
targetSocketChannel.write(ByteBuffer.wrap(msg.getBytes()));
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
GroupChatService groupChatService = new GroupChatService();
groupChatService.listen();
}
}
案例说明:
优缺点分析
单Reactor多线程图示说明
方案优缺点
Scalable IO in Java 原文链接
方案优缺点说明:
响应快,不必为单个同步事件所阻塞,虽然Reactor本身是同步的
可以最大程度避免复杂多线程同步问题,并且避免了多线程/进程的切换开销
扩展性好,可以方便的通过增加Reactor实例个数来充分利用CPU资源
复用新好,Reactor模型本身与具体事件处理逻辑无关,具有很高的复用性。
下一篇:Netty启动流程源码剖析