Bagaimana cara menggunakan banyak inti di API JAVA Non Blocking I/O (NIO)?

JAVA NIO menyediakan API untuk menulis server TCP menggunakan arsitektur NIO, sebagai berikut.

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);
                }
            }
        }
    }
}

Ini menggunakan satu thread yang akan memproses peristiwa seperti membaca, menulis, dan menerima.

Dibandingkan dengan arsitektur Pemblokiran thread per koneksi, ini lebih disukai karena sifatnya yang non-pemblokiran yang menyebabkan kesalahan cache minimum, overhead thread, dan migrasi CPU yang rendah.

Namun, arsitektur ini hanya menggunakan satu thread. Dalam lingkungan multi-proses (misalnya cpu 4 inti), arsitektur NIO membuang-buang inti lainnya. Apakah ada pendekatan desain yang dapat saya gunakan untuk memanfaatkan semua inti dengan arsitektur NIO?

NIO2 (yang didasarkan pada pola proactor) adalah salah satu opsi tersebut. Namun arsitektur dasarnya sangat berbeda dengan NIO asli.


person Pasindu Tennage    schedule 25.06.2019    source sumber


Jawaban (1)


ide dasarnya adalah untuk membagi tugas:

    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();
        }
    }
}

Sebenarnya ada banyak model tentang NIO. Misalnya, kita juga dapat menggunakan beberapa thread untuk menangani tugas penerimaan (juga disebut model beberapa reaktor atau model multiple eventloop).

BTW, Netty adalah kerangka aplikasi jaringan berbasis peristiwa yang dikemas java NIO

person Wang Kenneth    schedule 25.06.2019
comment
Menurut NIO2 (yang didasarkan pada pola proactor) yang Anda sebutkan, saya ingin berbagi beberapa diskusi tentang NIO vs NIO2(AIO) :github.com/netty/netty/issues/2515 Tidak ada peningkatan performa dengan menggunakan NIO2 khususnya di LInux, karena menggunakan fasilitas OS yang sama - epoll. - person Wang Kenneth; 26.06.2019
comment
Saya telah menggunakan pendekatan di atas dan pendekatan ini tidak meningkatkan kinerja secara signifikan, khususnya ketika persyaratan pemrosesan permintaan rendah. Namun, seperti yang telah Anda sebutkan, model beberapa reaktor atau model beberapa eventloop tampaknya lebih menjanjikan. Bisakah Anda menjelaskan metode ini? - person Pasindu Tennage; 26.06.2019
comment
@PasinduTennage Pertama, dibandingkan dengan BIO, NIO tidak akan membuat kemajuan besar jika koneksi/permintaan tidak mencukupi (berdasarkan pengalaman saya mungkin koneksi ‹ 2000). Keuntungan NIO adalah Anda dapat menggunakan beberapa thread untuk menangani banyak koneksi, bayangkan 2000 koneksi akan dikenakan biaya 2000 thread dalam model BIO. Thread adalah sejenis sumber daya yang mahal. Sebenarnya, untuk pendekatan saya vs. versi Anda, diperlukan lebih banyak koneksi untuk menunjukkan perbedaan kinerja (mungkin hingga 10k ~ 50k koneksi/ per detik). - person Wang Kenneth; 26.06.2019
comment
@PasinduTennage Sekali lagi, perlu diingat bahwa thread itu mahal. Jumlah thread harus fleksibel sesuai dengan koneksi sebenarnya dan inti CPU, dan terlalu banyak thread dapat berdampak buruk pada kinerja karena memerlukan banyak waktu untuk mengganti konteks thread. dan mengenai model beberapa reaktor, ini untuk keluaran yang lebih besar. Anda dapat melihat pendekatan saya sebagai 1 master/bos dengan banyak budak/pekerja, jadi beberapa reaktor adalah 1~3 master/bos dengan banyak budak/pekerja. beberapa reaktor dapat melakukan perbaikan hanya jika terjadi hambatan kinerja dalam menerima sambungan. - person Wang Kenneth; 26.06.2019
comment
Ilustrasi mainReactors, subReactors dengan banyak pekerja: user-gold-cdn .xitu.io/2018/11/5/166e31ccfc6fd6b1?imageslim - person Wang Kenneth; 26.06.2019