日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢(xún)
選擇下列產(chǎn)品馬上在線(xiàn)溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷(xiāo)解決方案
使用JavaNIO編寫(xiě)高性能的服務(wù)器

從JDK 1.4開(kāi)始,Java的標(biāo)準(zhǔn)庫(kù)中就包含了NIO,即所謂的“New IO”。其中最重要的功能就是提供了“非阻塞”的IO,當(dāng)然包括了Socket。NonBlocking的IO就是對(duì)select(Unix平臺(tái)下)以及 WaitForMultipleObjects(Windows平臺(tái))的封裝,提供了高性能、易伸縮的服務(wù)架構(gòu)。

說(shuō)來(lái)慚愧,直到JDK1.4才有這種功能,但遲到者不一定沒(méi)有螃蟹吃,NIO就提供了優(yōu)秀的面向?qū)ο蟮慕鉀Q方案,可以很方便地編寫(xiě)高性能的服務(wù)器。

話(huà)說(shuō)回來(lái),傳統(tǒng)的Server/Client實(shí)現(xiàn)是基于Thread per request,即服務(wù)器為每個(gè)客戶(hù)端請(qǐng)求建立一個(gè)線(xiàn)程處理,單獨(dú)負(fù)責(zé)處理一個(gè)客戶(hù)的請(qǐng)求。比如像Tomcat(新版本也會(huì)提供NIO方案)、Resin等Web服務(wù)器就是這樣實(shí)現(xiàn)的。當(dāng)然為了減少瞬間峰值問(wèn)題,服務(wù)器一般都使用線(xiàn)程池,規(guī)定了同時(shí)并發(fā)的最大數(shù)量,避免了線(xiàn)程的無(wú)限增長(zhǎng)。

但這樣有一個(gè)問(wèn)題:如果線(xiàn)程池的大小為100,當(dāng)有100個(gè)用戶(hù)同時(shí)通過(guò)HTTP現(xiàn)在一個(gè)大文件時(shí),服務(wù)器的線(xiàn)程池會(huì)用完,因?yàn)樗械木€(xiàn)程都在傳輸大文件了,即使第101個(gè)請(qǐng)求者僅僅請(qǐng)求一個(gè)只有10字節(jié)的頁(yè)面,服務(wù)器也無(wú)法響應(yīng)了,只有等到線(xiàn)程池中有空閑的線(xiàn)程出現(xiàn)。

另外,線(xiàn)程的開(kāi)銷(xiāo)也是很大的,特別是達(dá)到了一個(gè)臨界值后,性能會(huì)顯著下降,這也限制了傳統(tǒng)的Socket方案無(wú)法應(yīng)對(duì)并發(fā)量大的場(chǎng)合,而“非阻塞”的IO就能輕松解決這個(gè)問(wèn)題。

下面只是一個(gè)簡(jiǎn)單的例子:服務(wù)器提供了下載大型文件的功能,客戶(hù)端連接上服務(wù)器的12345端口后,就可以讀取服務(wù)器發(fā)送的文件內(nèi)容信息了。注意這里的服務(wù)器只有一個(gè)主線(xiàn)程,沒(méi)有其他任何派生線(xiàn)程,讓我們看看NIO是如何用一個(gè)線(xiàn)程處理N個(gè)請(qǐng)求的。

NIO服務(wù)器最核心的一點(diǎn)就是反應(yīng)器模式:當(dāng)有感興趣的事件發(fā)生的,就通知對(duì)應(yīng)的事件處理器去處理這個(gè)事件,如果沒(méi)有,則不處理。所以使用一個(gè)線(xiàn)程做輪詢(xún)就可以了。當(dāng)然這里這是個(gè)例子,如果要獲得更高性能,可以使用少量的線(xiàn)程,一個(gè)負(fù)責(zé)接收請(qǐng)求,其他的負(fù)責(zé)處理請(qǐng)求,特別是對(duì)于多CPU時(shí)效率會(huì)更高。

關(guān)于使用NIO過(guò)程中出現(xiàn)的問(wèn)題,最為普遍的就是為什么沒(méi)有請(qǐng)求時(shí)CPU的占用率為100%?出現(xiàn)這種問(wèn)題的主要原因是注冊(cè)了不感興趣的事件,比如如果沒(méi)有數(shù)據(jù)要發(fā)到客戶(hù)端,而又注冊(cè)了寫(xiě)事件(OP_WRITE),則在 Selector.select()上就會(huì)始終有事件出現(xiàn),CPU就一直處理了,而此時(shí)select()應(yīng)該是阻塞的。

另外一個(gè)值得注意的問(wèn)題是:由于只使用了一個(gè)線(xiàn)程(多個(gè)線(xiàn)程也如此)處理用戶(hù)請(qǐng)求,所以要避免線(xiàn)程被阻塞,解決方法是事件的處理者必須要即刻返回,不能陷入循環(huán)中,否則會(huì)影響其他用戶(hù)的請(qǐng)求速度。

具體到本例子中,由于文件比較大,如果一次性發(fā)送整個(gè)文件(這里的一次性不是指send整個(gè)文件內(nèi)容,而是通過(guò)while循環(huán)不間斷的發(fā)送分組包),則主線(xiàn)程就會(huì)阻塞,其他用戶(hù)就不能響應(yīng)了。這里的解決方法是當(dāng)有WRITE事件時(shí),僅僅是發(fā)送一個(gè)塊(比如4K字節(jié))。發(fā)完后,繼續(xù)等待WRITE事件出現(xiàn),依次處理,直到整個(gè)文件發(fā)送完畢,這樣就不會(huì)阻塞其他用戶(hù)了。

服務(wù)器的例子:

 
 
 
  1. package nio.file;   
  2. import java.io.FileInputStream;   
  3. import java.io.IOException;   
  4. import java.net.InetSocketAddress;   
  5. import java.nio.ByteBuffer;   
  6. import java.nio.CharBuffer;    
  7. import java.nio.channels.FileChannel;   
  8. import java.nio.channels.SelectionKey;   
  9. import java.nio.channels.Selector;   
  10. import java.nio.channels.ServerSocketChannel;   
  11. import java.nio.channels.SocketChannel;   
  12. import java.nio.charset.Charset;   
  13. import java.nio.charset.CharsetDecoder;   
  14. import java.util.Iterator;    
  15. /**   
  16. * 測(cè)試文件下載的NIOServer   
  17. *   
  18. * @author tenyears.cn   
  19. */  
  20. public class NIOServer {    
  21. static int BLOCK = 4096;    
  22. // 處理與客戶(hù)端的交互   
  23. public class HandleClient {   
  24. protected FileChannel channel;    
  25. protected ByteBuffer buffer;   
  26. public HandleClient() throws IOException {   
  27. this.channel = new FileInputStream(filename).getChannel();    
  28. this.buffer = ByteBuffer.allocate(BLOCK);    
  29. }    
  30. public ByteBuffer readBlock() {   
  31. try {   
  32. buffer.clear();    
  33. int count = channel.read(buffer);    
  34. buffer.flip();    
  35. if (count <= 0)    
  36. return null;    
  37. catch (IOException e) {   
  38. e.printStackTrace();    
  39. }   
  40. return buffer;    
  41. }   
  42. public void close() {   
  43. try {   
  44. channel.close();   
  45. catch (IOException e) {    
  46. e.printStackTrace();   
  47. }   
  48. }   
  49. }    
  50. protected Selector selector;    
  51. protected String filename = "d:\\bigfile.dat"; // a big file    
  52. protected ByteBuffer clientBuffer = ByteBuffer.allocate(BLOCK);   
  53. protected CharsetDecoder decoder;   
  54. public NIOServer(int port) throws IOException {   
  55. selector = this.getSelector(port);   
  56. Charset charset = Charset.forName("GB2312");    
  57. decoder = charset.newDecoder();    
  58. }   
  59. // 獲取Selector   
  60. protected Selector getSelector(int port) throws IOException {   
  61. ServerSocketChannel server = ServerSocketChannel.open();   
  62. Selector sel = Selector.open();   
  63. server.socket().bind(new InetSocketAddress(port));   
  64. server.configureBlocking(false);   
  65. server.register(sel, SelectionKey.OP_ACCEPT);   
  66. return sel;    
  67. }    
  68. // 監(jiān)聽(tīng)端口   
  69. public void listen() {   
  70. try {   
  71. for (;;) {   
  72. selector.select();   
  73. Iterator iter = selector.selectedKeys()   
  74. .iterator();   
  75. while (iter.hasNext()) {   
  76. SelectionKey key = iter.next();   
  77. iter.remove();   
  78. handleKey(key);   
  79. }   
  80. }   
  81. catch (IOException e) {   
  82. e.printStackTrace();    
  83. }    
  84. }    
  85. // 處理事件    
  86. protected void handleKey(SelectionKey key) throws IOException {   
  87. if (key.isAcceptable()) { // 接收請(qǐng)求   
  88. ServerSocketChannel server = (ServerSocketChannel) key.channel();   
  89. SocketChannel channel = server.accept();   
  90. channel.configureBlocking(false);    
  91. channel.register(selector, SelectionKey.OP_READ);    
  92. else if (key.isReadable()) { // 讀信息    
  93. SocketChannel channel = (SocketChannel) key.channel();    
  94. int count = channel.read(clientBuffer);   
  95. if (count > 0) {   
  96. clientBuffer.flip();   
  97. CharBuffer charBuffer = decoder.decode(clientBuffer);   
  98. System.out.println("Client >>" + charBuffer.toString());    
  99. SelectionKey wKey = channel.register(selector,    
  100. SelectionKey.OP_WRITE);    
  101. wKey.attach(new HandleClient());    
  102. else  
  103. channel.close();    
  104. clientBuffer.clear();   
  105. else if (key.isWritable()) { // 寫(xiě)事件   
  106. SocketChannel channel = (SocketChannel) key.channel();   
  107. HandleClient handle = (HandleClient) key.attachment();    
  108. ByteBuffer block = handle.readBlock();    
  109. if (block != null)    
  110. channel.write(block);   
  111. else {   
  112. handle.close();   
  113. channel.close();    
  114. }   
  115. }    
  116. }   
  117. public static void main(String[] args) {   
  118. int port = 12345;   
  119. try {   
  120. NIOServer server = new NIOServer(port);   
  121. System.out.println("Listernint on " + port);   
  122. while (true) {   
  123. server.listen();   
  124. }   
  125. catch (IOException e) {    
  126. e.printStackTrace();    
  127. }    
  128. }   
  129. }  

該代碼中,通過(guò)一個(gè)HandleClient來(lái)獲取文件的一塊數(shù)據(jù),每一個(gè)客戶(hù)都會(huì)分配一個(gè)HandleClient的實(shí)例。

下面是客戶(hù)端請(qǐng)求的代碼,也比較簡(jiǎn)單,模擬100個(gè)用戶(hù)同時(shí)下載文件。

 
 
 
  1. package nio.file;   
  2. import java.io.IOException;   
  3. import java.net.InetSocketAddress;    
  4. import java.nio.ByteBuffer;    
  5. import java.nio.CharBuffer;    
  6. import java.nio.channels.SelectionKey;   
  7. import java.nio.channels.Selector;    
  8. import java.nio.channels.SocketChannel;    
  9. import java.nio.charset.Charset;    
  10. import java.nio.charset.CharsetEncoder;    
  11. import java.util.Iterator;   
  12. import java.util.concurrent.ExecutorService;    
  13. import java.util.concurrent.Executors;    
  14. /**    
  15. * 文件下載客戶(hù)端    
  16. * @author tenyears.cn   
  17. */   
  18. public class NIOClient {    
  19. static int SIZE = 100;    
  20. static InetSocketAddress ip = new InetSocketAddress("localhost",12345);    
  21. static CharsetEncoder encoder = Charset.forName("GB2312").newEncoder();   
  22. static class Download implements Runnable {    
  23. protected int index;    
  24. public Download(int index) {    
  25. this.index = index;    
  26. }    
  27. public void run() {    
  28. try {    
  29. long start = System.currentTimeMillis();    
  30. SocketChannel client = SocketChannel.open();   
  31. client.configureBlocking(false);   
  32. Selector selector = Selector.open();    
  33. client.register(selector, SelectionKey.OP_CONNECT);    
  34. client.connect(ip);   
  35. ByteBuffer buffer = ByteBuffer.allocate(8 * 1024);   
  36. int total = 0;   
  37. FOR: for (;;) {    
  38. selector.select();    
  39. Iterator iter = selector.selectedKeys()    
  40. .iterator();    
  41. while (iter.hasNext()) {    
  42. SelectionKey key = iter.next();   
  43. iter.remove();   
  44. if (key.isConnectable()) {   
  45. SocketChannel channel = (SocketChannel) key   
  46. .channel();    
  47. if (channel.isConnectionPending())   
  48. channel.finishConnect();   
  49. channel.write(encoder.encode(CharBuffer   
  50. .wrap("Hello from " + index)));   
  51. channel.register(selector, SelectionKey.OP_READ);    
  52. else if (key.isReadable()) {    
  53. SocketChannel channel = (SocketChannel) key    
  54. .channel();   
  55. int count = channel.read(buffer);   
  56. if (count > 0) {    
  57. total += count;    
  58. buffer.clear();   
  59. else {    
  60. client.close();    
  61. break FOR;    
  62. }    
  63. }   
  64. }    
  65. }    
  66. double last = (System.currentTimeMillis() - start) * 1.0 / 1000;   
  67. System.out.println("Thread " + index + " downloaded " + total    
  68. + "bytes in " + last + "s.");    
  69. catch (IOException e) {    
  70. e.printStackTrace();    
  71. }    
  72. }   
  73. }   
  74. public static void main(String[] args) throws IOException {   
  75. ExecutorService exec = Executors.newFixedThreadPool(SIZE);    
  76. for (int index = 0; index < SIZE; index++) {   
  77. exec.execute(new Download(index));    
  78. }    
  79. exec.shutdown();   
  80. }    
  81. }  

操作系統(tǒng)的API epoll, select, NonBlocking的IO就是對(duì)select(Unix平臺(tái)下)以及 WaitForMultipleObjects(Windows平臺(tái))的封裝,是OS級(jí)別下的支持。

責(zé)任編輯:金賀
來(lái)源: JavaEye博客 Java NIO


當(dāng)前標(biāo)題:使用JavaNIO編寫(xiě)高性能的服務(wù)器
網(wǎng)站網(wǎng)址:http://www.5511xx.com/article/dheepoe.html