我是靠谱客的博主 矮小草丛,最近开发中收集的这篇文章主要介绍软件系统架构分析之一:传统socket通讯阻塞现象分析,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

如果想要做一个高性能的软件系统,如何处理好系统各方面的瓶颈问题非常重要。

我觉得在一个基于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的代码:

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后设置了5秒的等待时间。
运行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===============>>>>>>>>>>>>>>10012

3. 我们多运行几次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通讯阻塞现象分析所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(44)

评论列表共有 0 条评论

立即
投稿
返回
顶部