TOMCAT 连接数 线程以及开启SERVERSOCKET 测试

一直以来很迷惑TOMCAT的连接数,网上也是众说纷纭,所以今天自己专门来测试一下,配置参数请参考: TOMCAT配置参数说明 ,本人比较懒,所以只测试了BIO和NIO,没有APR模式(主要是看说的那么多,不想配置),下面正式开始:

1、测试环境

    TOMCAT:TOMCAT8

    JAVA:JDK1.8.0_121

    ServerSocket的启动方式:继承至HttpServlet随容器启动

2、服务端统一测试代码

    ServerSocket

package test.server;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;

public class StartSocketServlet extends HttpServlet {

	ControlScoketServer ass = null;

	@Override
	public void init() throws ServletException {
		// TODO Auto-generated method stub
		super.init();

		// MiniBasic.isDebug=true;

		ass = new ControlScoketServer();
		ass.setDaemon(true);
		ass.start();

		System.out.println("服务已经开启");

	}

	@Override
	public void destroy() {
		// TODO Auto-generated method stub
		super.destroy();
		try {
			ass.stopScoketServer();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		try {
			ass.interrupt();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}

package test.server;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import zhw.basic.CheckUtils;
import zhw.server.basic.ssh.MiniBasic;

public class ControlScoketServer extends Thread {

	public boolean isStop = false;
	protected ServerSocket serverSocket;
	// private ExecutorService cachedThreadPool ;
	private String ip;
	private int port;
	public final static ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
	
	public static void startThread(Runnable run){
		cachedThreadPool.execute(run);
	}

	public ControlScoketServer() {
		this.ip = "127.0.0.1";
		this.port = 10083;
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		try {
			startServer();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public void stopScoketServer() {
		isStop = true;
		try {
			serverSocket.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	public void startServer() throws IOException {
		isStop = false;
		serverSocket = new ServerSocket();
		try {
			serverSocket.setSoTimeout(0);
		} catch (Exception e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		if (!CheckUtils.String_IsNullOrEmpty(ip)) {
			serverSocket.bind(new InetSocketAddress(ip, port));
		} else {
			serverSocket.bind(new InetSocketAddress(port));
		}
		MiniBasic.getLogger().info("Socket服务器开启,IP:" + ip + ",端口:" + port);
		int i = 0;
		while (!isStop) {
			try {
				Socket client = serverSocket.accept();
				i++;
				ClientSocketHandler csh = new ClientSocketHandler(client, i);
				startThread(csh);
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

}
package test.server;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

import zhw.server.basic.ssh.MiniBasic;

public class ClientSocketHandler implements Runnable {

	private int threadId = 0;
	Socket client;
	private InputStream is;
	private OutputStream os;

	public ClientSocketHandler(Socket client, int threadId) {
		this.threadId = threadId;
		this.client = client;
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		try {
			is = this.client.getInputStream();
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		try {
			os = this.client.getOutputStream();
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		byte[] bt = new byte[512];
		while (true) {
			MiniBasic.showInfo("线程:  " + threadId + "  开始执行,等待客户端输入..");
			try {
				while (is.read(bt) != 0) {
					MiniBasic.showInfo("**************线程:  " + threadId + " 客户端输入为:" + new String(bt));
				}
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
				break;
			}
			try {
				Thread.sleep(5 * 1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

	}

}

HttpServer

package test.server;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class HttpServerServlet extends HttpServlet {

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		// TODO Auto-generated method stub
		
		
		System.out.println("有客户端连接,参数:"+req.getParameter("TESTPARAM"));
		
		try {
			//保持连接
			Thread.sleep(30*1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		resp.getWriter().print("SERVER RESPONSE:"+req.getParameter("TESTPARAM"));
		resp.getWriter().close();
	}

	
	
	
}

WebsocketServer

package test.server;

import java.io.IOException;
import java.util.concurrent.CopyOnWriteArrayList;

import javax.websocket.EndpointConfig;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

import zhw.server.basic.ssh.MiniBasic;
import zhw.server.basic.ssh.websocket.ZhwWebSocketServer;

@ServerEndpoint(value = "/testwebsocket")
public class WebSocketServer {

	public static CopyOnWriteArrayList<WebSocketServer> wbSockets = new CopyOnWriteArrayList<WebSocketServer>();

	private Session session;

	@OnOpen
	public void start(Session session, EndpointConfig config) {
		this.session = session;
		System.out.println("新的客户端:" + this.session.getId());
		wbSockets.add(this);
	}

	@OnClose
	public void end() {
		MiniBasic.getLogger().error("websocket关闭:" + this.session.getId());
		socketClosed();
	}

	private void socketClosed() {
		if (wbSockets != null && wbSockets.contains(this)) {
			wbSockets.remove(this);
		}
	}

	@OnMessage
	public void incoming(String message) {
		// Never trust the client
		MiniBasic.showInfo("websocket 收到信息: " + message);
	}

	@OnError
	public void onError(Throwable t) throws Throwable {

		MiniBasic.getLogger().error("websocket出错:" + this.session.getId() + "--" + t.getMessage());

	}

	public void sendData(String data) {
		try {
			MiniBasic.showInfo("------------发送socket数据。。。。。。。。。。。" + data);
			this.session.getBasicRemote().sendText(data);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}
web.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor 
	license agreements. See the NOTICE file distributed with this work for additional 
	information regarding copyright ownership. The ASF licenses this file to 
	You under the Apache License, Version 2.0 (the "License"); you may not use 
	this file except in compliance with the License. You may obtain a copy of 
	the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required 
	by applicable law or agreed to in writing, software distributed under the 
	License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 
	OF ANY KIND, either express or implied. See the License for the specific 
	language governing permissions and limitations under the License. -->
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://xmlns.jcp.org/xml/ns/javaee"
	xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
	metadata-complete="true" version="3.1">
	
	<context-param>
		<param-name>log4jConfigLocation</param-name>
		<param-value>/WEB-INF/classes/log4j.properties</param-value>
	</context-param>
	
	
	<servlet>
		<servlet-name>testserver</servlet-name>
		<servlet-class>test.server.HttpServerServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>
	
	<servlet>
		<servlet-name>socketserver</servlet-name>
		<servlet-class>test.server.StartSocketServlet</servlet-class>
		<load-on-startup>2</load-on-startup>
	</servlet>
	
	<servlet-mapping>
		<servlet-name>testserver</servlet-name>
		<url-pattern>*.test</url-pattern>
	</servlet-mapping>
	
	<session-config>
		<session-timeout>0</session-timeout>
	</session-config>
	<welcome-file-list>
		<welcome-file>index.jsp</welcome-file>
	</welcome-file-list>
</web-app>
3、客户端代码,根据测试不同需要更改

HttpClient

package test.client;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import zhw.basic.http.HttpHelper;

public class HttpClient {
	
	public final static ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
	
	public static void main(String[] args) throws Exception {
		HttpHelper helper = new HttpHelper();
		for (int i = 0; i < 260; i++) { //根据测试不同更改数量
			ExcuteHttpRequest ehr = new ExcuteHttpRequest(i + 1, helper);
			cachedThreadPool.execute(ehr);
			Thread.sleep(10);
		}
		Thread.sleep(3 * 1000);

	}
}
class ExcuteHttpRequest implements Runnable {

	private int clid;
	private HttpHelper helper;

	public ExcuteHttpRequest(int clid, HttpHelper helper) {
		this.clid = clid;
		this.helper = helper;
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		System.out.println(this.clid + "开始连接..");
		try {
			String responseText = this.helper.sendRequest("http://127.0.0.1/test/testhttp.test",
					"TESTPARAM=HTTPCLIENT-" + this.clid, "UTF-8");
			System.out.println(this.clid + "接受到的数据:" + responseText);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			System.out.println(this.clid +"连接出错:"+e.getMessage());
		}

	}

}

SocketClient

package test.client;

import java.io.IOException;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

public class SocketClient {
	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub

		List<Socket> ls = new ArrayList<Socket>();

		for (int i = 0; i < 5000; i++) { //
			Socket s = new Socket("127.0.0.1", 10083);
			// s.setKeepAlive(true);
			s.setOOBInline(false);
			s.setSoTimeout(0);
			ls.add(s);
			Thread.sleep(10);
			System.out.println("已连接:" + i);
		}

		Thread.sleep(3 * 1000);
		int i = 0;
		while (true) {
			for (Socket ws : ls) {
				i++;
				try {
					ws.getOutputStream().write(("SOCKET CLIENT:" + i).getBytes());
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				Thread.sleep(10);
			}
			i = 0;
			Thread.sleep(20 * 1000);
		}
	}
}

WebSocketClient

package test.client;

import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;

import javax.websocket.ClientEndpoint;
import javax.websocket.ContainerProvider;
import javax.websocket.Session;
import javax.websocket.WebSocketContainer;

@ClientEndpoint
public class WebSocketClient {

	private String deviceId;

	private Session session;

	public WebSocketClient() {
	}

	public WebSocketClient(String deviceId) {
		this.deviceId = deviceId;
	}

	protected boolean start() {
		WebSocketContainer container = ContainerProvider.getWebSocketContainer();
		String uri = "ws://127.0.0.1/test/testwebsocket";
		System.out.println("Connecting to " + uri);
		try {
			session = container.connectToServer(WebSocketClient.class, URI.create(uri));
			System.out.println("count: " + deviceId);
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
		return true;
	}

	public static void main(String[] args) throws InterruptedException {
		List<WebSocketClient> ar = new ArrayList<WebSocketClient>();
		for (int i = 1; i < 260; i++) {// 根据测试不同更改数量
			WebSocketClient wSocketTest = new WebSocketClient(String.valueOf(i));
			if (!wSocketTest.start()) {
				System.out.println("测试结束!");
				break;
			} else {
				ar.add(wSocketTest);
				Thread.sleep(10);
			}
		}
		Thread.sleep(3 * 1000);
		while (true) {
			for (WebSocketClient ws : ar) {
				try {
					ws.session.getBasicRemote().sendText("CLIENT:" + ws.deviceId);
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				Thread.sleep(10);
			}
			Thread.sleep(20 * 1000);
		}
	}

}

ComblineTest

package test.client;

import java.net.Socket;

import zhw.basic.http.HttpHelper;

public class ComblineTest {

	public static void main(String[] args) {

		HttpHelper helper = new HttpHelper();

		for (int i = 0; i < 100; i++) {
			ExcuteHttpRequest ehr = new ExcuteHttpRequest(i + 1, helper);
			HttpClient.cachedThreadPool.execute(ehr);
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		for (int i = 100; i < 250; i++) {// 根据测试不同更改数量
			WebSocketClient wSocketTest = new WebSocketClient(String.valueOf(i));
			if (!wSocketTest.start()) {
				System.out.println("测试结束!");
				break;
			} else {
				try {
					Thread.sleep(10);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}

		for (int i = 250; i < 400; i++) {
			ExcuteHttpRequest ehr = new ExcuteHttpRequest(i + 1, helper);
			HttpClient.cachedThreadPool.execute(ehr);
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		for (int i = 400; i < 1400; i++) { //
			try {
				Socket s = new Socket("127.0.0.1", 10083);
				// s.setKeepAlive(true);
				s.setOOBInline(false);
				s.setSoTimeout(0);
				Thread.sleep(10);
				System.out.println("已连接:" + i);
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

	}

}

BIO模式测试

    catalina.bat 启动项配置:

set JAVA_OPTS=-server -Xms128m -Xmx512m -Xss1m -XX:PermSize=128m -XX:MaxNewSize=256m -XX:MaxPermSize=256m

    server.xml配置:

 <Executor name="tomcatThreadPool" 
        namePrefix="tomcatThreadPool-" 
        maxThreads="200" 
	maxSpareThreads="300"
        minSpareThreads="100"/>
<Connector executor="tomcatThreadPool" 
	port="80" 
	protocol="org.apache.coyote.http11.Http11Protocol"
    	maxHttpHeaderSize="8192"
	acceptCount="50"
        connectionTimeout="20000"
        keepAliveTimeout="36000000" 
	maxKeepAliveRequests="-1"
        redirectPort="443" />

HttpClient

创建260个连接 连接数测试结果:

260开始连接..
251连接出错:请求失败:Connection refused: connect
252连接出错:请求失败:Connection refused: connect
253连接出错:请求失败:Connection refused: connect
254连接出错:请求失败:Connection refused: connect
255连接出错:请求失败:Connection refused: connect
256连接出错:请求失败:Connection refused: connect
257连接出错:请求失败:Connection refused: connect
258连接出错:请求失败:Connection refused: connect
259连接出错:请求失败:Connection refused: connect
260连接出错:请求失败:Connection refused: connect

可以看出第251个开始就不能连接了,符合 200个最大线程+50个 队列 数

197接受到的数据:SERVER RESPONSE:HTTPCLIENT-197
198接受到的数据:SERVER RESPONSE:HTTPCLIENT-198
199接受到的数据:SERVER RESPONSE:HTTPCLIENT-199
200接受到的数据:SERVER RESPONSE:HTTPCLIENT-200
202接受到的数据:SERVER RESPONSE:HTTPCLIENT-202
201接受到的数据:SERVER RESPONSE:HTTPCLIENT-201
....
248接受到的数据:SERVER RESPONSE:HTTPCLIENT-248
249接受到的数据:SERVER RESPONSE:HTTPCLIENT-249
250接受到的数据:SERVER RESPONSE:HTTPCLIENT-250

250个结果都正确返回

SocketClient

创建1000个连接 连接数测试结果:

已连接:996
已连接:997
已连接:998
已连接:999
服务端接受结果

ServerSocket 不受tomcat配置的连接池限制

WebSocket

创建260个连接,连接数测试结果:

count: 200
Connecting to ws://127.0.0.1/test/testwebsocket
测试结束!
javax.websocket.DeploymentException: The HTTP request to initiate the WebSocket connection failed

200就已经不能创建了,受到200个最大线程数限制

WebSocket+HttpClient+SocketServer 联合测试

  • 创建100个http连接,0-99(显示+1了)
  • 98开始连接..
    99开始连接..
    100开始连接..
    Connecting to ws://127.0.0.1/test/testwebsocket
    count: 100
    Connecting to ws://127.0.0.1/test/testwebsocket
    count: 101
    全部连接成功

  • 然后创建150个websocket连接,100-249
  • count: 199
    Connecting to ws://127.0.0.1/test/testwebsocket
    测试结束!
    javax.websocket.DeploymentException: The HTTP request to initiate the WebSocket connection failed
    第200个时连接失败,因为已经到了200MaxThread

  • 创建200个http连接,250-399(显示+1了)
  • 397开始连接..
    398开始连接..
    399开始连接..
    400开始连接..
    301连接出错:请求失败:Connection refused: connect
    300连接出错:请求失败:Connection refused: connect
    第300个失败,acceptCount 起作用

  • 创建1000个socket连接     400-1399
  • 已连接:1398
    已连接:1399
    8接受到的数据:SERVER RESPONSE:HTTPCLIENT-8

        全部连接成功

BIO模式测试完成



NIO模式测试

把server中的connector修改为如下,其他不变:

<Connector executor="tomcatThreadPool" 
	port="80" 
	protocol="org.apache.coyote.http11.Http11NioProtocol"
    	maxHttpHeaderSize="8192"
	acceptCount="50"
        connectionTimeout="20000"
        keepAliveTimeout="36000000" 
	maxKeepAliveRequests="-1"
        redirectPort="443" />

HttpClient

创建500个连接 连接数测试结果:

499开始连接..
500开始连接..
13接受到的数据:SERVER RESPONSE:HTTPCLIENT-13
17接受到的数据:SERVER RESPONSE:HTTPCLIENT-17
客户端不会报错,服务每次只处理200个,这个跟maxthread设置有关系,处理完成后再依次处理后续请求,acceptcount无效。为了确认是不是因为请求数少了而没有报错,我使用了创建5000个连接但是在1200多的时候,eclise 显示 oom,不过我估计确实NIO不会受accpet限制 1228开始连接..
1229开始连接..
1230开始连接..
Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread

SocketClient

创建3000个连接 连接数测试结果:

已连接:2997
已连接:2998
已连接:2999

同时连接且能同时正常通信


Websocket

创建500个连接,均连接成功并都能正常通信


WebSocket+HttpClient+SocketServer 联合测试

这个测试的有些多,主要修改下联合测试的代码,测试结果:

SokcetServer连接不受限制

单独HTTP和单独websocket连接不受acceptcount限制,可以无限等待队列

先HTTP连接超过maxthreads后websocket将不能连接,直接报错,也就是说一般情况下http和websocket混合连接时,超过连接数限制websocket会报错,http会等待

先websocket连接超过maxthreads后HTTP能连接,但是进入等待队列,如果HTTP没有处理完成,WEBSOCKET会进入等待状态,可能是用例中http是线程等待,而websocket是IO等待的原因(才疏学浅,弄不明白)


总结一下:

两种模式下ScoektServer虽然是从tomcat中启动,但是不受tomcat连接数限制

HTTP和WEBSOCKET 单独不混合连接时 BIO 模式下都要受Maxthreads 限制,NIO模式下连接不受限制,但是要进入等待,要受等待时间限制

HTTP和WEBSOCKET混合连接时 BIO 模式下都要受Maxthreads 限制,NIO模式下超过Maxthread, WEBSOCKET 要报错,HTTP还是可以连接但是受等待时间限制

以上如果要配置TOMCAT 并发量 需要考虑 JVM的内存大小 和 Maxthreads 的数量

其他说明:

上面Connector没有配置 maxConnections 所以测试几千的连接的时候都可以,可以确认的是Tomcat默认的maxConnections 不会是200,具体是多少,我也不知道,如果需要更高的并发量可以显视配置 maxConnections ,如其名 它会影响 HTTP和 WEBSOCKET 的连接上限,超过上限直接报错,不能连接。所以高并发时最好是 maxConnections 和 Maxthredas 一样,或maxConnections 比Maxthredas 大。







  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值