使用 ab 进行测试时,套接字服务器挂起,等待 BufferedReader.readline()
我正在制作一个基于 Java 的网络服务器。但是当我用 ApacheBench 测试它时,它有时会停止响应。
在 Macbook Air 上:
ab -n 20000 -c 40 -d http://localhost:1080/
在完成 16400 或更多请求后保证超时。
在 Ubuntu 桌面上
ab -n 20000 -c 1000 -d http://localhost:1080/
大多数情况下都可以成功完成,但有时运行几次后就会停止响应。
我已经确定(使用 Eclipse),当服务器停止响应时,它正在等待 BufferedReader.readline() ,我用它来读取 HTTP 请求标头。但我不知道为什么要等待。
测试代码在这里:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestServer {
public static void main(String[] args){
ServerSocket socket = null;
try{
socket = new ServerSocket(1080);
ExecutorService pool = Executors.newFixedThreadPool(10);
while(true){
Socket s = socket.accept();
pool.execute(new RequestHandler(s) );
}
}
catch(Exception e){
e.printStackTrace();
}
finally{
if(null!=socket){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
class RequestHandler implements Runnable{
final Socket s;
public RequestHandler(Socket s) {
this.s = s;
}
public void run() {
try {
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = br.readLine();
PrintWriter pw = new PrintWriter(s.getOutputStream());
pw.print("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n");
pw.print(line);
pw.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
finally{
if(s!=null){
try {
s.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
顺便说一句,在编写测试代码时,我发现了一些奇怪的东西
如果
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = br.readLine();
替换为
String line = "don't read the socket";
ab 将失败并显示这样的消息:“apr_socket_recv:连接被拒绝(111)连接由对等方重置(104) )"
但是用 Firefox 4 打开 localhost:1080 会看到“不读取套接字”的混乱情况。
I am making a Java-based web server. But when I am testing it with ApacheBench, it sometimes stop responding.
On a Macbook Air:
ab -n 20000 -c 40 -d http://localhost:1080/
is guaranteed to timeout after 16400 or more requests were done.
On Ubuntu desktop
ab -n 20000 -c 1000 -d http://localhost:1080/
could done successfully most of the time, but sometimes stop responding after several runs.
I've identified (using Eclipse) that when the server stop responding, it is waiting for BufferedReader.readline() which I use it to read HTTP request header. But I have no idea why is it waiting.
Test code is here:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestServer {
public static void main(String[] args){
ServerSocket socket = null;
try{
socket = new ServerSocket(1080);
ExecutorService pool = Executors.newFixedThreadPool(10);
while(true){
Socket s = socket.accept();
pool.execute(new RequestHandler(s) );
}
}
catch(Exception e){
e.printStackTrace();
}
finally{
if(null!=socket){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
class RequestHandler implements Runnable{
final Socket s;
public RequestHandler(Socket s) {
this.s = s;
}
public void run() {
try {
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = br.readLine();
PrintWriter pw = new PrintWriter(s.getOutputStream());
pw.print("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n");
pw.print(line);
pw.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
finally{
if(s!=null){
try {
s.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
BTW, when writing the test code, I found something else strange
If
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = br.readLine();
is replaced with
String line = "don't read the socket";
ab will fail with such message: "apr_socket_recv: Connection refused (111)Connection reset by peer (104)"
But open localhost:1080 with Firefox 4 will see the "don't read the socket" mess show up.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我想知道这是否是 ApacheBench 测试的故意部分:查看当打开连接但没有发送数据时服务器的行为。据推测 ApacheBench 是开源的,因此您可以看看它在 16400 次尝试后是否会调用一些特殊行为(我打赌它会打开套接字,然后不发送请求)。
无论如何,您可能需要确保在套接字上设置显式超时,以防您的 Java 版本默认为 0(=无限)。不要假设每个客户端都会表现得完美并且总是向您发送您所期望的精确数据。
因此,作为一般规则,您需要确保您的 Web 服务器不会在“发生异常情况”时崩溃 - 网络就是这样,有时数据包/连接会随机丢失,您需要处理它。操作系统很可能会限制连接可以打开的时间长度,因此您的服务器可能会突然看到操作系统“从其脚下拉起地毯”。我想 ApacheBench 测试可能会模拟一些像这样的小精灵(这甚至可能是您在 Ubuntu 中看到的,尽管 readLine() 挂起可能是模拟不在开放连接上发送请求,正如我提到的)。
I wonder if this is a deliberate part of the ApacheBench test: to see how your server behaves when a connection is opened to it but then no data sent. Presumably ApacheBench is open source so you can have a look at see if it invokes some special behaviour (my bet is on it opening the socket and then not sending a request) after 16400 tries.
In any case, you probably want to make sure you set an explicit timeout on the socket in case your version of Java is defaulting to 0 (=infinite). Don't assume that every client will behave perfectly and always send you precisely the data you're expecting.
So as a general rule, you need to make sure that your web server doesn't fall over if "something unusual happens"-- networks are like that, and sometimes packets/connections will get randomly dropped and you need to deal with it. Operating systems may well impose limits on e.g. how long a connection can be open for and so your server could suddenly see the "rug pulled from beneath it's feet" by the OS. I imagine the ApacheBench test may simulate a few gremlins like this (which could even be what you're seeing in Ubuntu, though the readLine() hanging is probably a simulation of not sending a request on an open connection as I mention).