首先提供代码:

   public static  void main(String [] args) throws IOException {
        //创建一个服务端的ServerSocket,监听8801端口
        ServerSocket serverSocket = new ServerSocket(8801);

        while (true){
            //利用循环不停的监听用户的消息
            Socket socket= serverSocket.accept();
            service(socket);
        }
    }


    public  static void service(Socket socket){
        try {
            PrintWriter printWriter = new PrintWriter(socket.getOutputStream(),true);
            printWriter.println("HTTP/1.1 200 OK");
            printWriter.println("Content-Type:text/html;charset=utf-8");
            String body = "Hello,Nio Word";
            //这里注意,一定要准确的输出Content-Length,如果没有准确的进行输出Content-length.
            //虽然可以正确的输出Hello,Nio Word,但是用Curl的指令会出现  curl: (56) Recv failure: Connection was aborted
//            printWriter.println("Content-Length:"+body.getBytes().length);
            printWriter.println("Content-Length:"+333333);
            printWriter.println();
            printWriter.write(body);
            printWriter.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

 

这个代码段的部分有一个很有意思的地方,在

printWriter.println("Content-Length:"+body.getBytes().length);

如果没有输出正确的Content-Length,通过浏览器访问例如:“http://localhost:8801”,可以在浏览器正确的显示出body的内容

但是通过Curl指令就会在输出Hellp,Nio Word 这个地方额外输出一串内容:

*这里附带上一个Curl指令的详解,转载一波:https://www.ruanyifeng.com/blog/2019/09/curl-reference.html

 

 

 curl: (56) Recv failure: Connection was aborted

网络上馊了一个,大部分是将这个错误信息作为问题提出,而没有一个准确的回答,经过一段信息检索后,感觉回答上比较靠谱的应该是这个。

https://zhidao.baidu.com/question/1801282023653491227.html

这个问题先留给后面查吧。

 

出现了这个错误后,使用压测工具(SuperBenchMarker)使用指令

sb -u http://localhosst:8801 -c 40 -N 30 

就会发现压测状态变成了303 ,而不是200.

----------------------------------------------------------------------------------------------------------------------------------------------分界线------------------------------------------------------------------------------------------------------------------------------------------------------------

附上代码后,先用IDE简单的启动一下,通过Chrome浏览器(任意你常用的)简单的调用一下:http://localhost:8801/

可以看到页面显示了Hello,Nio Word。

接着使用SuperBenchMarker做一个简单的压测

sb -u http://localhosst:8801 -c 40 -N 30 

 

 

 

 * PS:这里出现了Status303确实不清楚怎么产生的。

看到平均是11.8MS,最大请求是160MS。

在并发量更高的情况下,这个显然是不合理的,那么自然而然的就想到需要使用多线程。那么多线程需要在什么地方使用呢?

回头去看代码

这里可以看到代码块

   while (true){
            //利用循环不停的监听用户的消息
            Socket socket= serverSocket.accept();
            service(socket);
        }

在执行代码的时候,是使用的单线程的方式,通过While循环不断的接受消息,接收到消息后同步的执行service的方法,直到service方法执行完成后系统才会继续接收新的消息。

那么看第一行,serverSocket.accept(),这个是用于接收消息的,没有接受到消息的时候,这里是属于Block状态,直到有消息进来,才会到下面的service,那么我们多线程改进的地方就在service这一块。

那么我们稍微的改进一下代码

  new Thread(()->{
              service(socket);
            }).start();

简单的将service放在一个新建的线程里面,现在再进行一次压测,看看性能如何。

 

 

 优化后的代码看到平均响应时间在7.4MS,最大是151Ms

平均时间整体降低了4MS,证明我们的改动是有效的。西卡西!!!!

线程这么重量级的单位,直接NEW了就不用了,是一个什么情况呢?打个比方:

你有一家餐馆,来了一个客人你就招一个服务员来进行服务,客户走了后你就把服务员开除了。

累不累?招人累吗?开人累吗?谈合同,薪资累吗?

这就是目前做的,new Thread(()->{});每次创建一个线程,用完就丢。

 

多线程优化是可以的,那么为了避免多线程被反复创建,节省开销,可以想到:

我先招10个人,有客人来了就让10个人去服务,服务完了就回休息室等着,直到有新的客人来。

那么,就可以使用Java的线程池进行一个改进:

    ExecutorService service = Executors.newFixedThreadPool(
                Runtime.getRuntime().availableProcessors()+2
        );
        ServerSocket serverSocket = new ServerSocket(8801);
        while (true){
            Socket socket = serverSocket.accept();
            service.execute(()->{ service(socket);});
        }

 

Runtime.getRuntime().availableProcessors()  是获取当前的CPU核心数,在CPU核心数的基础上+2个线程。

 

 第三次用线程池压测,可以发现一个很神奇的事情。

平均响应时间在3MS,这可比单线程的11MS快了整整8MS!!!比直接New一个线程的响应速度要快了4MS!

 

看到这里,应该就清楚后面写代码更应该用啥了吧。。。。

Socket 以及相关的一些简单的拓展内容就写到这里。