概述
如果想要做一个高性能的软件系统,如何处理好系统各方面的瓶颈问题非常重要。
我觉得在一个基于Java的系统中,最容易出现性能瓶颈的地方就在线程、IO、数据库这几个方面。Socket通讯是我们系统间最常用的通讯方式之一,而Socket通讯又是伴随着大量IO操作,而且,因为socket的连接、读、写都是阻塞的,容易成为最明显的系统性能瓶颈。这篇文章记录一下自己对于socket阻塞现象的分析。
socket主要在accept方法,read方法,write方法上阻塞,从而影响性能。
1. 先上一段Server端TcpServer的代码:
package com.john;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class TcpServer {
public static void main(String args[]) throws IOException, InterruptedException {
ServerSocket server = new ServerSocket(5050);
System.out.println("0.server start ...");
while (true) {
long startTime1 = System.currentTimeMillis();
System.out.println("1.waiting for client connect....");
System.out.println("2.Begin accept!");
Socket socket = server.accept();
System.out.println("Thread:" + Thread.currentThread().getName() + " and client socek:"+socket.getPort());
System.out.println("3.Client accept!" + socket);
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
long startTime2 = System.currentTimeMillis();
char c = (char) in.read();
long endTime2 = System.currentTimeMillis();
System.out.println("read timeUsed===>>>" + (endTime2- startTime2));
Thread.sleep(5000);
System.out.println("4.server receive msg===>>>" + c);
out.write(c);
out.flush();
in.close();
socket.close();
long endTime1 = System.currentTimeMillis();
System.out.println(socket.getPort() + "" +
"client total timeUsed===>>>" + (endTime1 - startTime1));
}
}
}
启动socket server,能看到以下日志:
0.server start ...
1.waiting for client connect....
2.Begin accept!
说明此时accept是阻塞的,等待客户端连接过来。
2. 然后,我们来看客户端TcpClient的代码:
TcpClient的代码中,在连接到TcpServer后设置了5秒的等待时间。package com.john; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; public class TcpClient { public static void main(String args[]) throws IOException, InterruptedException { //for (int i = 0; i < 5; i++) { long startTime = System.currentTimeMillis(); Socket client = new Socket("127.0.0.1", 5050); InputStream in = client.getInputStream(); OutputStream out = client.getOutputStream(); Thread.sleep(5000); out.write('a'); out.flush(); System.out.println("client send data===>>>" + 'a'); long startTime2 = System.currentTimeMillis(); char c = (char) in.read(); long endTime2 = System.currentTimeMillis(); System.out.println("return data===>>>" + c); System.out.println("read timeUsed===>>>" + (endTime2- startTime2)); out.close(); in.close(); client.close(); long endTime = System.currentTimeMillis(); long timeUsed = endTime - startTime; System.out.println("total timeUsed===============>>>>>>>>>>>>>>" + timeUsed); //} } }
运行TcpClient后,我们在服务端TcpServer的日志中能看到:
1.waiting for client connect....
2.Begin accept!
Thread:main and client socek:54848
3.Client accept!Socket[addr=/127.0.0.1,port=54848,localport=5050]
(在此处能看到光标等待了5秒时间,即服务端的in.read()方法是阻塞的)
read timeUsed===>>>5002 4.server receive msg===>>>a然后因为在TcpServer的代码中,在返回客户端之前我们也设置了5秒的等待时间,然后我们在客户端TcpClient的日志中也能看到:client send data===>>>a
(在此处能看到光标等待了5秒时间,即客户端in.read()方法是阻塞的)
return data===>>>a
read timeUsed===>>>5002
total timeUsed===============>>>>>>>>>>>>>>100123. 我们多运行几次TcpClient,能看到TcpServer的日志中:1.waiting for client connect.... 2.Begin accept! Thread:main and client socek:54883 3.Client accept!Socket[addr=/127.0.0.1,port=54883,localport=5050] read timeUsed===>>>5002 4.server receive msg===>>>a 1.waiting for client connect.... 2.Begin accept!服务端TcpServer中始终都是main单线程在accept()方法处阻塞,等待客户端连接过来。4.最后,我们给TcpClient加上for循环(将代码中的for循环注释打开)并将5秒的等待时间去掉,多运行几个client客户端(3个),会发现如下现象:TcpServer的日志能看到,其对所有的客户端处理时间都是5秒(因为设置了延时5秒):3.Client accept!Socket[addr=/127.0.0.1,port=54999,localport=5050] read timeUsed===>>>0 4.server receive msg===>>>a 54999client total timeUsed===>>>5002 1.waiting for client connect.... 2.Begin accept! Thread:main and client socek:55000 3.Client accept!Socket[addr=/127.0.0.1,port=55000,localport=5050] read timeUsed===>>>1 4.server receive msg===>>>a 55000client total timeUsed===>>>5002但是客户端的日志中,能看到3个客户端的等待时间却是15秒!read timeUsed===>>>15010 total timeUsed===============>>>>>>>>>>>>>>15014 client send data===>>>a return data===>>>a read timeUsed===>>>15019 total timeUsed===============>>>>>>>>>>>>>>15021
这又是怎么回事呢?
因为服务器是单线程的,只能依次处理3个客户端的请求,每个客户端接收到的返回延时会随着客户端的增多而翻倍!5.总结传统socket程序因为使用的是传统的IO流,在accept连接,read、write数据时都是阻塞的,线程会一直等待流结果后才能继续下一步运作,这极大地降低了线程的使用效率。如果要提高客户端响应速度,只能增加线程数,即多线程的socket编程,我们下一篇文章来讲述这部分内容。
最后
以上就是矮小草丛为你收集整理的软件系统架构分析之一:传统socket通讯阻塞现象分析的全部内容,希望文章能够帮你解决软件系统架构分析之一:传统socket通讯阻塞现象分析所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复