จะใช้หลายคอร์ใน JAVA Non Blocking I/O (NIO) API ได้อย่างไร

JAVA NIO จัดเตรียม API เพื่อเขียนเซิร์ฟเวอร์ TCP โดยใช้สถาปัตยกรรม NIO ดังนี้

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.text.ParseException;
import java.util.*;

public class NIOServer implements Runnable{
    private InetAddress addr;
    private int port;
    private Selector selector;

    public NIOServer(InetAddress addr, int port) throws IOException {
        this.addr = addr;
        this.port = port;
    }

    public void run(){
        try {
            startServer();
        }catch(IOException ex){
            System.out.println(ex.getMessage());
        }
    }

    private void startServer() throws IOException {

        this.selector = Selector.open();
        ServerSocketChannel serverChannel = serverSocketChannel.open();
        serverChannel.configureBlocking(false);
        InetSocketAddress listenAddr = new InetSocketAddress(this.addr, this.port);
        serverChannel.socket().bind(listenAddr);
        serverChannel.register(this.selector, SelectionKey.OP_ACCEPT);


        while (true) {

            this.selector.select();


            Iterator keys = this.selector.selectedKeys().iterator();
            while (keys.hasNext()) {
                SelectionKey key = (SelectionKey) keys.next();


                keys.remove();

                if (! key.isValid()) {
                    continue;
                }

                if (key.isAcceptable()) {
                    this.accept(key);
                }
                else if (key.isReadable()) {
                    this.read(key);
                }
                else if (key.isWritable()) {
                    this.write(key);
                }
            }
        }
    }
}

ซึ่งใช้เธรดเดียวที่จะประมวลผลเหตุการณ์ต่างๆ เช่น อ่าน เขียน และยอมรับ

เมื่อเปรียบเทียบกับการบล็อกเธรดต่อสถาปัตยกรรมการเชื่อมต่อ สิ่งนี้เป็นที่ต้องการมากกว่า เนื่องจากมีลักษณะไม่บล็อกซึ่งทำให้แคชพลาดขั้นต่ำ ค่าโสหุ้ยของเธรด และการย้าย CPU ต่ำ

อย่างไรก็ตาม สถาปัตยกรรมนี้ใช้เพียงเธรดเดียวเท่านั้น ในสภาพแวดล้อมที่มีหลายกระบวนการ (เช่น CPU 4 คอร์) สถาปัตยกรรม NIO จะทำให้คอร์อื่นๆ สิ้นเปลืองไป มีวิธีการออกแบบที่ฉันสามารถใช้เพื่อใช้ประโยชน์จากคอร์ทั้งหมดด้วยสถาปัตยกรรม NIO หรือไม่

NIO2 (ซึ่งขึ้นอยู่กับรูปแบบ proactor) เป็นหนึ่งในตัวเลือกดังกล่าว แต่สถาปัตยกรรมพื้นฐานนั้นแตกต่างไปจาก NIO ดั้งเดิมมาก


person Pasindu Tennage    schedule 25.06.2019    source แหล่งที่มา


คำตอบ (1)


แนวคิดพื้นฐานคือแบ่งงาน:

    ExecuterService workers = Executors.newFixedThreadPool(50);

    ....
    while (true) {

                this.selector.select();

                Iterator keys = this.selector.selectedKeys().iterator();
                while (keys.hasNext()) {
                    SelectionKey key = (SelectionKey) keys.next();

                    keys.remove();

                    if (! key.isValid()) {
                        continue;
                    }

                    if (key.isAcceptable()) {
                        this.accept(key);
                    }
                    else if (key.isReadable()) {
                        workers.execute(new ReadTaskHandler(key));
                    }
                    else if (key.isWritable()) {
                        workers.execute(new WriteTaskHandler(key));
                    }
                }
            }
class ReadTaskHandler implements Runnable {
    SelectionKey key;

    public ReadTaskHandler(SelectionKey key) {
        this.key = key;
    }

    @Override
    public void run() {
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        SocketChannel channel = (SocketChannel) key.channel();

        int size = 0;
        try {
            while ((size = channel.read(buffer)) > 0) {
                System.out.println(new String(buffer.array()));
                buffer.flip();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

จริงๆ แล้ว NIO มีหลายแบบด้วยกัน ตัวอย่างเช่น เรายังสามารถใช้หลายเธรดเพื่อจัดการงานการยอมรับ (หรือที่เรียกว่าโมเดล เครื่องปฏิกรณ์หลายตัว หรือโมเดล หลาย eventloop)

BTW, Netty เป็นเฟรมเวิร์กแอปพลิเคชันเครือข่ายที่ขับเคลื่อนด้วยเหตุการณ์ที่ยอดเยี่ยมที่บรรจุ java NIO

person Wang Kenneth    schedule 25.06.2019
comment
ตาม NIO2 (ซึ่งขึ้นอยู่กับรูปแบบ proactor) ที่คุณกล่าวถึง ฉันต้องการแบ่งปันการสนทนาเกี่ยวกับ NIO กับ NIO2(AIO) :github.com/netty/netty/issues/2515 ไม่มีการปรับปรุงประสิทธิภาพโดยใช้ NIO2 โดยเฉพาะบน LInux เนื่องจากใช้ระบบปฏิบัติการเดียวกัน - epoll - person Wang Kenneth; 26.06.2019
comment
ฉันได้ใช้วิธีการข้างต้นและไม่ได้ปรับปรุงประสิทธิภาพอย่างมีนัยสำคัญ โดยเฉพาะอย่างยิ่งเมื่อความต้องการการประมวลผลของคำขอต่ำ อย่างไรก็ตาม ดังที่คุณได้กล่าวไปแล้ว แบบจำลองเครื่องปฏิกรณ์หลายเครื่องหรือแบบจำลองเหตุการณ์ลูปหลายตัวดูเหมือนจะมีแนวโน้มมากกว่า คุณช่วยอธิบายวิธีนี้ได้ไหม? - person Pasindu Tennage; 26.06.2019
comment
@PasinduTennage ประการแรกเมื่อเปรียบเทียบกับ BIO แล้ว NIO จะไม่ทำการปรับปรุงอย่างมากหากมีการเชื่อมต่อ / คำขอไม่เพียงพอ (ตามประสบการณ์ของฉันอาจเป็นการเชื่อมต่อ ‹ 2000) ข้อดีของ NIO คือคุณสามารถใช้สองสามเธรดเพื่อจัดการการเชื่อมต่อจำนวนมาก โดยจินตนาการว่าการเชื่อมต่อ 2,000 ครั้งจะใช้ 2,000 เธรดในโมเดล BIO เธรดเป็นทรัพยากรที่มีราคาแพง จริงๆ แล้ว สำหรับแนวทางของฉันกับเวอร์ชันของคุณ จำเป็นต้องมีการเชื่อมต่อมากกว่านี้มากเพื่อแสดงความแตกต่างของประสิทธิภาพ (อาจสูงถึง 10,000 ~ 50,000 การเชื่อมต่อ/ ต่อวินาที) - person Wang Kenneth; 26.06.2019
comment
@PasinduTennage อีกครั้ง โปรดทราบว่าเธรดมีราคาแพง จำนวนเธรดควรมีความยืดหยุ่นตามการเชื่อมต่อจริงและแกน CPU และเธรดที่มากเกินไปอาจส่งผลกระทบที่ไม่ดีต่อประสิทธิภาพ ทำให้ต้องใช้เวลานานมากในการเปลี่ยนบริบทของเธรด และสำหรับแบบจำลองเครื่องปฏิกรณ์หลายเครื่อง ก็มีไว้เพื่อปริมาณงานที่มากขึ้น คุณสามารถเห็นแนวทางของฉันในฐานะเจ้านาย/เจ้านาย 1 คนที่มีทาส/คนงานจำนวนมาก ดังนั้นเครื่องปฏิกรณ์หลายตัวจึงเป็นเจ้านาย/เจ้านาย 1~3 คนที่มีทาส/คนงานจำนวนมาก เครื่องปฏิกรณ์หลายเครื่องสามารถปรับปรุงได้ก็ต่อเมื่อประสิทธิภาพคอขวดอยู่ในการยอมรับการเชื่อมต่อเท่านั้น - person Wang Kenneth; 26.06.2019
comment
ภาพประกอบสำหรับเครื่องปฏิกรณ์หลัก เครื่องปฏิกรณ์ย่อยที่มีคนงานจำนวนมาก: user-gold-cdn .xitu.io/2018/11/5/166e31ccfc6fd6b1?imageslim - person Wang Kenneth; 26.06.2019