Жизнеспособность серверов сокетов

#java #sockets

#java #сокеты

Вопрос:

я разработал базовый сервер сокетов для клиентов Android, который получает запросы и обрабатывает базу данных mysql, теперь я ожидаю около 2000 запросов в час, мне интересно, жизнеспособен ли этот сервер на такую сумму или даже больше? есть ли что-нибудь еще или какой-либо лучший протокол, который я должен использовать вместо сокета, чтобы справляться с большими запросами, кроме ThreadPoolExecutor?

  public class Server {
// a unique ID for each connection
private static int uniqueId;
// an ArrayList to keep the list of the Client
private ArrayList<ClientThread> al;
// if I am in a GUI
private ServerGUI sg;
// to display time
private SimpleDateFormat sdf;
// the port number to listen for connection
private int port;
// the boolean that will be turned of to stop the server
private boolean keepGoing;
static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";  
static final String DB_URL = "jdbc:mysql://localhost:3306/openfire";
static final String USER = "admin";
static final String PASS = "admin";
static boolean jobdone = false;
protected ServerSocket serverSocket = null;
protected boolean      isStopped    = false;
protected Thread       runningThread= null;


Connection conn = null;
Statement stmt = null;

public Server(int port) {

    this(port, null);
}

public Server(int port, ServerGUI sg) {

    this.sg = sg;

    this.port = port;

    sdf = new SimpleDateFormat("HH:mm:ss");

    al = new ArrayList<ClientThread>();
}

public void start() throws InterruptedException {
    keepGoing = true;
    try 
    {

        // the socket used by the server
        ServerSocket serverSocket = new ServerSocket(port);

        // infinite loop to wait for connections
        while(keepGoing) 
        {
            // format message saying we are waiting
            display("Server waiting for Clients on port "   port   
 ".");

            Socket socket = serverSocket.accept();      // accept 
  connection
            // if I was asked to stop

            if(!keepGoing)
                break;
            ClientThread t = new ClientThread(socket);  // make a 
  thread of it
            jobdone=false;

 al.add(t);                                 // save 
 it in the ArrayList
            t.start();
        }
        // I was asked to stop
        try {
            serverSocket.close();
            for(int i = 0; i < al.size();   i) {
                ClientThread tc = al.get(i);
                try {
                tc.sInput.close();
                tc.sOutput.close();
                tc.socket.close();
                }
                catch(IOException ioE) {
                    // not much I can do
                }
            }
        }
        catch(Exception e) {
            display("Exception closing the server and clients: "   e);
        }
    }
    // something went bad
    catch (IOException e) {
        String msg = sdf.format(new Date())   " Exception on new ServerSocket: "   e   
    "n";
        display(msg);
    }
}       
/*
 * For the GUI to stop the server
 */
protected void stop() {
    keepGoing = false;
    // connect to myself as Client to exit statement 
    // Socket socket = serverSocket.accept();
    try {
        new Socket("localhost", port);
    }
    catch(Exception e) {
        // nothing I can really do
    }
}
/*
 * Display an event (not a message) to the console or the GUI
 */
private void display(String msg) {
    String time = sdf.format(new Date())   " "   msg;
    if(sg == null)
        System.out.println(time);
    else
        sg.appendEvent(time   "n");
}

    // create a server object and start it



 public static void shutdown() {
jobdone = true;


 }
/** One instance of this thread will run for each client */

   class ClientThread extends Thread {
    // the socket where to listen/talk
    String Type;
    Socket socket;
    ObjectInputStream sInput;
    ObjectOutputStream sOutput;
    // my unique id (easier for deconnection)
    int id;

    String encrypted = "'adomgaldkopnfaosdppkdsad'";


    // Constructore
    ClientThread(Socket socket) throws InterruptedException {
        // a unique id
        id =   uniqueId;
        this.socket = socket;
        /* Creating both Data Stream */
        System.out.println("Thread trying to create Object Input/Output 
 Streams");
    while (!jobdone){
        try
        {
            // create output first
            sOutput = new ObjectOutputStream(socket.getOutputStream());
            sInput  = new ObjectInputStream(socket.getInputStream());
            // read the username


            String RegisterRequest = (String) sInput.readObject();  

            String[] result = RegisterRequest.split("\,");
            String theuser = result[0];
            display("theuser=" theuser);
            String thepass = result[1];
            display("thepass=" thepass);
            String thename = result[2];   
            display("thename=" thename);
            String themail = result[3];
            display("themail=" themail);
            String thephone = result[4];
            display("thephone=" thephone);
            String newRID = result[5];
            display("RID=" newRID);



            String OldRID=newRID;
            try{
                  //STEP 2: Register JDBC driver
                  Class.forName("com.mysql.jdbc.Driver");

                  //STEP 3: Open a connection
                  System.out.println("Connecting to a selected 
 database...");
                  conn = DriverManager.getConnection(DB_URL, USER, 
 PASS);
                  System.out.println("Connected database 
 successfully...");

                  String selectSQL1 = "SELECT username FROM ofuser 
 WHERE username= '" theuser "'";
                  PreparedStatement preparedStatement = 
 conn.prepareStatement(selectSQL1);
                  ResultSet usernameRS = 
 preparedStatement.executeQuery(selectSQL1);


                  if (!usernameRS.next()){



                  //STEP 4: Execute a query
                  System.out.println("Inserting records into the  
 table...");
                  stmt = conn.createStatement();

                  String sql = "INSERT INTO ofuser VALUES 

('" theuser "','" thepass "',null,'" thename "','" themail "',
'" 4234 "','" 23432 "','" the
 phone "','" null "','" null "')";
                  stmt.executeUpdate(sql);
                     Type="T"; 
                  String REresponse ="('" Type "','" OldRID "')";
                  System.out.println(REresponse);
                  sOutput.writeObject(REresponse);
                  sOutput.flush();
                  //  sOutput.writeObject(Type);
                  System.out.println("SUCCESS");
                  }else{ 

                      Type="F";
                    String REresponse ="('" Type "','" OldRID "')";
                    Type="F";   
                            sOutput.writeObject(REresponse);
                            sOutput.flush();
                            System.out.println("FAIL");
                  }



                  return;




              }catch(SQLException se){
                  //Handle errors for JDBC
                  se.printStackTrace();
               }catch(Exception e){
                  //Handle errors for Class.forName
                  e.printStackTrace();
               }finally{
                  //finally block used to close resources
                  try{
                     if(stmt!=null)
                        conn.close();
                  }catch(SQLException se){
                  }// do nothing
                  try{
                     if(conn!=null)
                        conn.close();
                  }catch(SQLException se){
                     se.printStackTrace();
                  }//end finally try
            }
               }//end try






        catch (IOException e) {
            display("Exception creating new Input/output Streams: "   
 e);
            return;
        }
        // have to catch ClassNotFoundException
        // but I read a String, I am sure it will work
        catch (ClassNotFoundException e) {
        }
        Thread.sleep(5000);
        Server.shutdown();
    }

   }
    // what will run forever

        // remove myself from the arrayList containing the list of the
        // connected Clients
    public void main(String[] args) throws InterruptedException {
        // start server on port 1500 unless a PortNumber is specified 
        int portNumber = 1500;
        switch(args.length) {
            case 1:
                try {
                    portNumber = Integer.parseInt(args[0]);
                }
                catch(Exception e) {
                    System.out.println("Invalid port number.");
                    System.out.println("Usage is: > java 
  Server [portNumber]");
                    return;
                }
            case 0:
                break;
            default:
                System.out.println("Usage is: > java Server  
 [portNumber]");
                return;

        }
        // create a server object and start it
        Server server = new Server(portNumber);
        server.start();
    }







    // try to close everything
    private void close() {
        // try to close the connection
        try {


            if(sOutput != null) sOutput.close();
        }
        catch(Exception e) {}
        try {
            if(sInput != null) sInput.close();
        }
        catch(Exception e) {};
        try {
            if(socket != null) socket.close();
        }
        catch (Exception e) {}
    }

    /*
     * Write a String to the Client output stream
     */
    private boolean writeMsg(String msg) {
        // if Client is still connected send the message to it
        if(!socket.isConnected()) {
            close();
            return false;
        }
        // write the message to the stream
        try {
            sOutput.writeObject(msg);
        }
        // if an error occurs, do not abort just inform the user
        catch(IOException e) {
            display("Error sending message to " );
            display(e.toString());
        }
        return true;
    }
}



//public void main(String[] args) {


    //   System.out.println("Goodbye!");
//  }//end main




 }
 

Ответ №1:

2000 в час — это ~ 0,55 в секунду. Вы должны быть в состоянии обрабатывать эту скорость на мобильном телефоне, работающем в качестве вашего сервера (при условии, что запросы не являются сложными)

если вам нужно обрабатывать до 100 тыс. соединений и более миллиарда запросов в час, TCP, вероятно, подойдет. Если вы хотите гораздо большего, вы можете использовать UDP или купить второй сервер. Больше серверов, вероятно, будет намного проще 😉

Примечание: ваша база данных и / или пропускная способность вашей сети, скорее всего, будут максимальными задолго до того, как это произойдет с вашим TCP-сервером.

Комментарии:

1. да, я понимаю, большое вам спасибо, вам нужно какое-нибудь хорошее руководство, как я могу превратить это в TCP?

2. @user3764748 Я бы избегал использования потоков объектов, если вы можете, в противном случае это выглядит нормально для меня.

3. что вы предлагаете заменить мате?

4. @user3764748 Здесь слишком много вариантов для упоминания, но если вам нужен просто текст, я бы использовал BufferedReader / PrintWriter. Для данных я бы использовал буферизованный DataInputStream / DataOutputStream

5. UDP волшебным образом не делает вещи более масштабируемыми. На самом деле он имеет тенденцию быть менее масштабируемым из-за повышенной вероятности перегрузки сети при большом количестве подключений и перемещении данных. Аппаратное обеспечение также, как правило, специализируется на оптимизации TCP-соединений в большей степени, чем UDP, и может справиться с перегрузкой, которую вы получаете при сотнях тысяч одновременных подключений. Использование UDP означало бы, что вам придется в основном добавить поддержку для управления потерей пакетов и, вероятно, в любом случае создать какую-нибудь легкую вещь в стиле TCP; это вызывает проблемы с дополнительной сложностью. Чем больше оборудования, тем проще.

Ответ №2:

помните о высоком использовании оперативной памяти, кажется, вы создаете так много объектов за запрос … мое предложение заключается в том, что вы должны создать ограниченное количество объектов (например, 100), а затем поместить их в ArrayList, а затем вызвать их соответственно, я имею в виду :

 public static void startServer() {
    try {
        final ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.socket().bind(new InetSocketAddress(12345);
        final List<Operator> operators = new ArrayList<>();
        final int nOperators = /* your choice */;
        for (int i = 0; i < nOperators; i  ) {
            operators.add(i, new Operator());
        }
        final int parallelism = Runtime.getRuntime().availableProcessors();
        ForkJoinPool pool = new ForkJoinPool(parallelism);
        pool.submit(() -> new Thread(() -> {
            int i = 0;
            while (true) {
                try {
                    operators.get(i).newOperation(serverSocketChannel.accept());
                    i  ;
                    if (i == nOperators) {
                        i = 0;
                    }
                } catch (IOException ignored) {
                }
            }
        })).fork().invoke().start();
    } catch (IOException ignored) {
    }
}
 

и тогда вы увидите огромное увеличение скорости и производительности…

Редактировать:

например:

 public class Operator {

    public void newOperation(final SocketChannel socketChannel) throws IOException {
        DataInputStream dis = new DataInputStream(socketChannel.socket().getInputStream());
        DataOutputStream dos = new DataOutputStream(socketChannel.socket().getOutputStream());
        //TODO something here
        dis.close();
        dos.close();
        socketChannel.close();
    }

}
 

дополнительная информация о ServerSocketChannel и SocketChannel и ForkJoinPool

удачи 🙂


РЕДАКТИРОВАТЬ @PeterLawrey Вот как я мог бы это написать, предполагая, что у меня много недолговечных подключений с интенсивными операциями с процессором. Если у вас есть операции, которые блокируются в сети / IO, вам может потребоваться больше потоков. Поскольку вам нужен простой ввод-вывод, имеет смысл придерживаться простого ввода-вывода.

 public static void startServer() throws IOException {
    // don't ignore the server failing to bind.
    final ServerSocket ss = new ServerSocket(12345);

    final int parallelism = Runtime.getRuntime().availableProcessors();
    // add one for the acceptor
    final ExecutorService pool = ExecutorService.newFixedThreadPool(parallelism 1);

     pool.submit(() -> {
         try {
             while (!ss.isClosed())
                 pool.submit(new Handler(serverSocketChannel.accept()));
         } catch (IOException ignored) {
     });
}

class Handler implements Runnable {
   static final ThreadLocal<ExpensiveObject> expensiveObjectIWantToReuse = new ThreadLocal<>();

   final Socket s;
   final DataInputStream in;
   final DataOutputStream out;

   Handler(Socket s) {
      this.s = s;
      this.in = new DataInputStream(new BufferedInputStream(s.getInputStream()));
      this.out = new DataOutputStream(new BufferedOutputStream(s.getOutputStream()));
   }

   public void run() {
      try {
          processStream(in, out);
      } catch (Throwable t) {
          // log t
      } finally (
          close(out);  // also flush()es
          close(in);
          close(s);
      }
   }

   public static void close(Closable c) {
      try {
          if (c != null) c.close();
      } catch (IOException ignored) { }
   }

   protected void processStream(DataInput di, DataOutput out) {
      // do something
   }
 

}

Комментарии:

1. спасибо, как бы то ни было, я понятия не имею, что это за команды, но я поищу для них учебник 🙂 tnx alot mate

2. чувак, не мог бы ты дать мне несколько советов о том, что делает этот код, где я должен его использовать, и в чем его отличие от обычного serversocket, должен ли я заменить его? .. пожалуйста?

3. Можете ли вы объяснить, почему вы устанавливаете configureBlocking (true) (это значение по умолчанию), почему вы используете 64 оператора, почему это изменяет количество объектов на запрос, почему вы вообще используете структуру fork-join, почему вы отправляете потоки вместо Runnables и т. Д. Существует много кода котельной плиты, но, похоже, ничто из этого не добавляет производительности, только неясность. Зачем использовать SocketChannel для NIO и переключаться обратно на простые небуферизованные потоки данных ввода-вывода вместо собственных байт-буферов

4. Я использую что-то подобное в своем собственном серверном приложении, и я могу сказать, что это очень, очень быстро и эффективно, мое серверное приложение сейчас обрабатывает более 3000 ~ 3500 подключений в секунду, а общее использование оперативной памяти составляет чуть менее 60 МБ…

5. @AvB вы уверены, что хотите, чтобы я удалил все, что я не понимаю 😉