简体   繁体   English

Java,如何管理线程读取套接字(websocket)?

[英]Java, how manage threads to read socket (websocket)?

I have a WebSocket server.我有一个 WebSocket 服务器。

my server create a new thread for handle a new connection.the thread is live until websocket break.我的服务器创建一个新线程来处理新连接。该线程一直有效,直到 websocket 中断。

my problem: for 1_000_000 connections i need 1_000_000 threads.我的问题:对于 1_000_000 个连接,我需要 1_000_000 个线程。 how i can handle many websockets by a thread?我如何通过一个线程处理多个 websocket? without wait?无需等待?

ServerSocket server;
private ExecutorService executor = new ThreadPoolExecutor(1_000_000 , 1_000_000 , 7, TimeUnit.SECONDS, queue, threadFactory);

try
{
    server = new ServerSocket(port); 
}
catch (IOException e) {}

while (true)
 {
    Socket client = null;

   try
   {
        client = server.accept();

        Runnable r = new Runnable()
        {
           run()
           {
              // this is simple, original is complete WebSocket imp
               client.getInputStream().read();
           }
        };

        executor.execute(r);
    }

 catch (IOException e) {}
}

Your concept is wrong.你的概念是错误的。 You should not start a new thread every few milliseconds because that will slow down your system a lot.您不应该每隔几毫秒启动一个新线程,因为这会大大降低您的系统速度。 Also you cannot have 1 Million connections open at the same time.此外,您不能同时打开 100 万个连接。 No normal operating system would allow that.任何正常的操作系统都不允许这样做。

Rather than that, normal web servers run a maximum number of threads (eg 100 on an average server) which process the incoming requests sequentially.与此不同的是,普通的 Web 服务器运行最大数量的线程(例如,平均服务器上 100 个)依次处理传入的请求。

Think about this you have a map of sockets and every time a message received to server you will get message and related socket !想想看,您有一个套接字映射,每次收到服务器的消息时,您都会收到消息和相关的套接字!

this operation done with OS(linux , windows , unix , mac-OS , ...) kernel !这个操作是用 OS(linux , windows , unix , mac-OS , ...) 内核完成的!

so you can handle a million connection just in one thread !所以你可以在一个线程中处理一百万个连接!

we call this None-Blocking sockets which means they never block your thread to read or write or any other operation such as accept and ... !我们称之为无阻塞套接字,这意味着它们永远不会阻止您的线程读取或写入或任何其他操作,例如接受和...!

java has a package to handle this ! java有一个包来处理这个! java.nio.* java.nio.*

how it's work ?它是如何工作的?

  • A Thread to handle IO operations一个线程来处理 IO 操作
  • A Selector to select which sockets has operation and what type of operation一个选择器来选择哪些套接字有操作和什么类型的操作
  • ByteBuffer to handle read and write instead of using socket.stream in blocking-socket ByteBuffer 来处理读写而不是在阻塞套接字中使用 socket.stream

also you can use multiple thread and selectors (each selector has its own thread)您也可以使用多个线程和选择器(每个选择器都有自己的线程)

look at this example :看看这个例子:

NoneBlockingServer.java : NoneBlockingServer.java :

import java.net.InetSocketAddress;
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.util.Iterator;

public class NoneBlockingServer {

public static void main(String[] args) throws Exception
{
    runServer("localhost" , 5050);
}


private final static void runServer(String host , int port)throws Exception {
    Selector selector = Selector.open();
    ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
    serverSocketChannel.bind(new InetSocketAddress(host, port));
    serverSocketChannel.configureBlocking(false); //config to be a none-blocking serve-socket
    SelectionKey selectionKey = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
    //register to selector for operation ACCEPT !
    //also you can use selectionKey for some other stuffs !

    while (true) {

        int numberOfReadSockets = selector.select();
        //it will wait until a socket(s) be ready for some io operation
        //or other threads call selector.wakeup()

        if(numberOfReadSockets==0){
            //maybe selector.wakeup() called
            //do some sync operations here !
            continue; // continue selecting !
        }

        Iterator<SelectionKey> keys = selector.selectedKeys().iterator();

        while (keys.hasNext())
        {
            SelectionKey key = keys.next();
            keys.remove(); //remove selected key from current selection !

            //handle selected key


            if(key.isValid() && key.isReadable())
            {

                //it means this socket is valid and has data to read


                SocketChannel socketChannel =
                        (SocketChannel) key.channel();

                ByteBuffer buffer = ByteBuffer.allocate(100); // allocate 100 bytes for buffer
                //maybe you must use an allocated buffer for each connection
                // instead of allocate for each operation

                int read = socketChannel.read(buffer);

                if(read<0)
                {
                    //need to close channel !
                    socketChannel.close(); // explicitly remove from selector
                    System.out.println("CONNECTION CLOSED");
                    continue; //socket closed and other operations will skip
                }else
                {
                    buffer.flip(); // you need to learn work with ByteBuffers
                    byte[] bytes = new byte[buffer.remaining()];
                    buffer.get(bytes);
                    //maybe convert it to String
                    String msg = new String(bytes);
                    //use msg !
                    System.out.println("MESSAGE : "+msg);

                    key.interestOps(SelectionKey.OP_READ|SelectionKey.OP_WRITE);
                    //set interestOps to WRIT and READ to write hello back message !
                    key.attach(ByteBuffer.wrap("Hello Client !".getBytes("UTF-8")));
                    //wrap a array of bytes using wrap and attach it to selectionKey
                }

            }

            if(key.isValid() && key.isWritable())
            {
                //it means this socket is valid and have space to write data !

                SocketChannel socketChannel =
                        (SocketChannel) key.channel();

                //you must represent data you want to write to this socket
                //maybe attached to selection key !
                ByteBuffer dataToWrite = (ByteBuffer) key.attachment();
                //key.attachment here to help u have some meta data about each socket
                //use it smart !

                int write = socketChannel.write(dataToWrite);

                if(write<0)
                {
                    //so means some error occurs better to close it !
                    socketChannel.close();
                    System.out.println("CONNECTION CLOSED !"); //log
                    continue;//as socket closed we will skip next operations !
                }else if(!dataToWrite.hasRemaining())
                {
                    //so all data putted to buffer !
                    key.interestOps(SelectionKey.OP_READ); // just need to read !
                }
            }

            if(key.isValid() && key.isAcceptable())
            {
                ServerSocketChannel server =
                        (ServerSocketChannel) key.channel();//just server channels has accept operation

                SocketChannel socketChannel = server.accept(); //accept it !

                socketChannel.configureBlocking(false); // config none-blocking mode

                socketChannel.register(selector , SelectionKey.OP_READ);

                //also you can register for multiple operation using | operation
                //for example register for both read and write SelectionKey.READ|SelectionKey.WRITE
                //also you can change is late using key.interestOps(int ops)


                System.out.println("NEW CONNECTION"); //log
            }

            //there is another type of key,  key.isConnectable()
            //google it !




        }
    }
}
}

and here is BlockingClient.java :这是BlockingClient.java

import java.net.InetSocketAddress;
import java.net.Socket;

public class BlockingClient {

//using blocking sockets !
public static void main(String[] args)throws Exception
{
    Socket socket = new Socket();
    socket.connect(new InetSocketAddress("localhost" , 5050));
    socket.getOutputStream()
            .write("Hello Server".getBytes("UTF-8"));
    byte[] buffer = new byte[100];
    int len  = socket.getInputStream().read(buffer);
    System.out.println(new String(buffer , 0 , len , "UTF-8"));
    socket.close();
}
}

at this example we send Hello Server message from Blocking Client to None-Blocking Server and server will response Hello Client message !在这个例子中,我们将 Hello Server 消息从 Blocking Client 发送到 None-Blocking Server,服务器将响应 Hello Client 消息!

just run !赶紧跑 !

Good luck祝你好运

A tempting thought is "don't use java".一个诱人的想法是“不要使用java”。 Java has terrible green thread support but golang and erlang are built on green threading so they do it very well. Java 对绿色线程的支持很糟糕,但是 golang 和 erlang 是建立在绿色线程之上的,所以它们做得很好。

The java way seems to be worker pools. java方式似乎是工作池。 So you create an Executor pool (see java.util.concurrent), decide how many workers you want for a given number of connections, then pass connections to workers via a queue.因此,您创建了一个 Executor 池(请参阅 java.util.concurrent),确定给定数量的连接需要多少个工作线程,然后通过队列将连接传递给工作线程。 A worker then has to iterate over its set of connections deciding to process each or yield.然后,工作人员必须迭代其连接集,决定处理每个连接还是让步。

Your number of live tcp connections is hard capped at about 2^16 (65_536: number of available ports) but the system is unlikely to be performant if you have that many connections anyway.您的实时 tcp 连接数严格限制在大约 2^16(65_536:可用端口数),但如果您有那么多连接,系统就不太可能具有高性能。 Most systems can't maintain performance for more than ~200 persistent connections.大多数系统无法维持超过 200 个持久连接的性能。 If you can far exceed that number I would assume your system isn't doing enough per connection to really justify the use of web sockets, but I'm only guessing.如果你可以远远超过这个数字,我会假设你的系统每个连接做的不够,无法真正证明使用网络套接字是合理的,但我只是猜测。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM