我编写了一个简单的TCP服务器来将一些用户数据传输到它并将其保存在一个简单的MySQL表中。如果我现在运行超过2000个客户端,它会停止工作。在跑步的同时我会得到一些IO error java.io.EOFException
,你也可能会看到我为此做出的错误。但最重要的是我得到了这个
IO error java.net.SocketException: Connection reset
Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
at java.lang.Thread.start0(Native Method)
at java.lang.Thread.start(Unknown Source)
at Server.main(Server.java:49)
足够的内存应该存在,但线程仍在运行,我不知道我在哪里做了他们不会被终止的错误。所以我运行了3900个线程。 所以这是服务器的一部分:
try {
// create new socket
ServerSocket sock = new ServerSocket(port);
textArea.setText(textArea.getText() + "Server started\n");
while (true) {
// accept the connection
Socket newsock = sock.accept();
// handle the action
Thread t = new ThreadHandler(newsock);
newsock.setSoTimeout(2000); // adding client timeout
t.start();
}
} catch (Exception e) {
猜猜真的很简单。以下是我处理套接字的方法:
class ThreadHandler extends Thread {
private Socket socket;
private MySQLConnection sqlConnection;
ThreadHandler(Socket s) {
socket = s;
sqlConnection = new MySQLConnection();
}
public void run() {
try {
DataOutputStream out = new DataOutputStream(
socket.getOutputStream());
DataInputStream in = new DataInputStream(new BufferedInputStream(
socket.getInputStream()));
Server.textArea.append((new Date()) + "\nClient connected IP: " + socket.getInetAddress().toString()+"\n");
int firstLine = in.readInt(); // get first line for switch
switch (firstLine) {
case 0:
// getting the whole objekt for the database in own lines!
String name2 = in.readUTF();
int level2 = in.readInt();
int kp2 = in.readInt();
String skill = in.readUTF();
LeadboardElement element2 = new LeadboardElement();
element2.setName(name2);
element2.setLevel(level2);
element2.setKillPoints(kp2);
element2.setSkill(skill);
sqlConnection.saveChaToLeadboard(element2);
break;
//case 1 return the top10
###.... shorten here the rest of the cases
out.close();
in.close();
//close this socket
socket.close();
Server.textArea.append("Client disconnected IP: " + socket.getInetAddress().toString()+ "\n" + (new Date())
+ "\n----------------------------------------------------\n");
// autoscrolldown
Server.textArea.setCaretPosition(Server.textArea.getDocument()
.getLength());
} catch (Exception e) {
System.out.println("IO error " + e);
try {
socket.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}finally{
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
saveChaToLeadboard
只获取名称级别kp和技能并使用preparedStatement
,因此将其保存到我的MySQL表中。
我希望你能帮助我,我只是没有看到它的错误。我想我需要在某个地方加入它但是如果我在它的末尾加入一个连接(在socket.close()之后)它仍然会这样做。
这里保存到数据库方法:
public void saveChaToLeadboard(LeadboardElement element) {
try {
// load driver
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection(this.databaseURL
+ DATABASE_NAME, this.user, this.password);
// insert values into the prep statement
preparedStatement = connection
.prepareStatement(PREP_INSERT_STATEMENT);
preparedStatement.setString(1, element.getName());
preparedStatement.setInt(2, element.getLevel());
preparedStatement.setInt(3, element.getKillPoints());
if(!element.getSkill().equalsIgnoreCase("")){
preparedStatement.setString(4, element.getSkill());
}else{
preparedStatement.setString(4, null);
}
// execute
preparedStatement.executeUpdate();
connection.close();
} catch (Exception e) {
Server.textArea.append(e.getMessage() + "\n");
Server.textArea.setCaretPosition(Server.textArea.getDocument()
.getLength());
try {
connection.close();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
非常感谢! 此致
答案 0 :(得分:3)
您的run()
方法被破坏,但我怀疑问题的一部分是您并不总是关闭网络套接字和流。特别是,如果在读取或处理您读取的数据时出现异常,我怀疑您没有关闭它们。您应该始终在finally
块(或Java 7等效块)中关闭套接字和流。
另一个潜在的问题是,由于另一端没有发送数据,某些连接可能会停止。要处理这个问题,你需要在套接字上设置一个读取超时...这样就可以关闭缓慢/卡住客户端的连接。
最后,即使尝试与每个连接的线程并行处理2000多个连接也可能是不现实的。这是很多资源 1 。我建议您使用具有固定上限的线程池,如果所有线程都在使用中,则停止接受新连接。
1 - 每个线程堆栈在HotSpot JVM上占用至少64K的内存,并且可能与1Mb一样多。然后是线程直接或间接引用的Heap资源,以及维护线程和套接字状态所需的OS资源。对于2000个线程,这可能是多Gb的内存。
答案 1 :(得分:0)
IMHO 2000线程对于单个进程来说是偏高的,肯定是2000个数据库连接。
无论您是否使用2000个传入连接达到限制,您的方法都无法扩展。
要获得可扩展性,您需要查看使用资源池 - 这意味着:
查看线程池的concurreny API和IO的NIO API。
这种安排将允许您调整服务器以实现所需的吞吐量。