java - 制作服务器队列java的最简单和最好的方法

标签 java sockets

我目前有一台服务器,它为每个连接的用户创建一个新线程,但大约 6 个人在服务器上运行超过 15 分钟后,它往往会失败并给我 java 堆内存不足错误,我有 1 个线程每 30 秒检查一次 mysql 数据库,看看当前登录的用户是否有任何新消息。实现服务器队列的最简单方法是什么?

这是我的服务器的主要方法:

public class Server {

    public static int MaxUsers = 1000;
    //public static PrintStream[] sessions = new PrintStream[MaxUsers];
    public static ObjectOutputStream[] sessions = new ObjectOutputStream[MaxUsers];
    public static ObjectInputStream[] ois = new ObjectInputStream[MaxUsers];
    private static int port = 6283;
   public static Connection conn;
       static Toolkit toolkit;
    static Timer timer;

    public static void main(String[] args) {
        try {
            conn = (Connection) Mysql.getConnection();
        } catch (Exception ex) {
            Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
        }
        System.out.println("****************************************************");
        System.out.println("*                                                  *");
        System.out.println("*                    Cloud Server                  *");
        System.out.println("*                       ©2010                      *");
        System.out.println("*                                                  *");
        System.out.println("*                   Luke Houlahan                  *");
        System.out.println("*                                                  *");
        System.out.println("* Server Online                                    *");
        System.out.println("* Listening On Port " + port + "                           *");
        System.out.println("*                                                  *");
        System.out.println("****************************************************");
        System.out.println("");
        mailChecker();
        try {
            int i;
            ServerSocket s = new ServerSocket(port);
            for (i = 0; i < MaxUsers; ++i) {
                sessions[i] = null;
            }
            while (true) {
                try {
                    Socket incoming = s.accept();                    
                    boolean found = false;
                    int numusers = 0;
                    int usernum = -1;
                    synchronized (sessions) {
                        for (i = 0; i < MaxUsers; ++i) {
                            if (sessions[i] == null) {
                                if (!found) {
                                    sessions[i] = new ObjectOutputStream(incoming.getOutputStream());
                                    ois[i]= new ObjectInputStream(incoming.getInputStream());
                                    new SocketHandler(incoming, i).start();
                                    found = true;
                                    usernum = i;
                                }
                            } else {
                                numusers++;
                            }
                        }
                        if (!found) {
                            ObjectOutputStream temp = new ObjectOutputStream(incoming.getOutputStream());
                            Person tempperson = new Person();
                            tempperson.setFlagField(100);
                            temp.writeObject(tempperson);
                            temp.flush();
                            temp = null;
                            tempperson = null;
                            incoming.close();
                        } else {
                        }
                    }
                } catch (IOException ex) {
                    System.out.println(1);
                    Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        } catch (IOException ex) {
            System.out.println(2);
            Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
        public static void mailChecker() {
        toolkit = Toolkit.getDefaultToolkit();
        timer = new Timer();
        timer.schedule(new mailCheck(), 0, 10 * 1000);
    }
}

最佳答案

看来你有内存泄漏。 6个线程并不算多。我怀疑这是因为ObjectInputStream和ObjectOutputStream缓存了所有传输的对象。这使得它们非常不适合长时间传输。您认为您正在发送一个对象,然后对其进行GC,但它实际上是由对象流保存在内存中的。

要刷新流缓存,请使用

  objectOutputStream.reset()

使用 writeObject() 写入对象后

编辑: 要获得线程池,SocketHandler 可以传递给 Executor,而不是启动它自己的线程。您创建一个执行器,例如:

Executor executor = Executors.newFiexThreadPool(MaxUsers);

执行器作为字段创建,或者与服务器套接字处于同一级别。然后 接受连接时,您将 SocketHandler 添加到执行器:

executor.execute(new SocketHandler(...));

但是,如果您的客户端生命周期很长,那么这不会有什么改善,因为与每个线程上完成的工作量相比,线程启动时间很小。池对于执行许多小任务而不是几个大任务最有效。

关于使服务器更加健壮 - 一些快速提示

  • 确保启动时有足够的内存,或者至少将最大内存设置为可以满足 1000 个用户的需求。
  • 使用负载测试框架(例如 Apache JMeter)来验证其是否能够扩展到最大用户数。
  • 为数据库使用连接池,并且不要手动编写 JDBC 调用代码 - 使用已建立的框架,例如Spring JDBC。
  • 每个线程默认以 2MB 堆栈开始。因此,如果您有 1000 个用户,那么仅用于堆栈就会使用约 2GB 的虚拟进程空间。在许多 32 位系统上,这是您可以拥有的用户空间量,因此将没有空间容纳数据。如果您需要更多用户,则可以扩展到更多进程,使用负载均衡器将请求传递到每个进程,或者查看不需要每个连接一个线程的服务器解决方案。
  • 注重细节,尤其是异常处理。
  • 日志记录,用于诊断故障。
  • JMX 或其他可管理功能来监控服务器运行状况,当值超出范围时(例如,内存/CPU 使用率长时间过高,或请求时间过慢),会向您发出通知。

参见Architecture of a Highly Scalable Server

关于java - 制作服务器队列java的最简单和最好的方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3010245/

相关文章:

java - nextLine() 方法如何工作

java - Android Socket不发送数据

java - 如何使用 JAVA 或 C# 中的 Web 服务或 REST API 在 Microsoft Dynamics CRM 2016(版本 8)中创建案例?

当 url 中存在查询字符串时,java webstart 重新下载 jnlp

sockets - 在没有 SSL 的情况下保护现有套接字连接的最佳实践

linux - 修改socket结构将数据包发送到另一个IP

java - 使用 rxandroidble 进行写入/通知处理

sockets - 通过 Wifi 进行原始套接字通信 - 接收器无法接收数据包

python - Socket Python 选择和多处理

c++ - Boost 连接后从 TCP 服务器访问 TCP 客户端 IP 地址 + 端口