网络异常测试

在这几年的微服务开发过程中遇到过两次因为网络问题导致的系统故障,并且没有做好降级策略,导致系统的不可用时间增加,所以今天专门整理一篇关于网络故障的问题分析处理以及开发中需要注意的地方。

基础部分

TCP 连接,先抛大图:

a

主要分为三部分:

  1. 建立连接
  2. 传输数据
  3. 关闭连接

原理不做过多介绍,主要说说常见的异常和模拟方式。

常见的异常类型

b
上面的异常是一些常见的功能性异常,其它性能方面的异常不在本文讨论范围。

实施手段

需要的工具

  • python 脚本
  • iptables,对网络流量进行规则过滤
  • tcpkill,用来断开网络构造异常
  • curl,发起 http 访问请求

Python脚本

主要作用是启动一个TCP监听,然后将接收到的数据在转发回去。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#! /usr/bin/python
# -*- coding:utf-8 -*-
import socket
import sys
def start_tcp_server(ip, port):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = (ip, port)
print('Starting listen on ip %s, port %s' % server_address)
sock.bind(server_address)
try:
sock.listen(1)
except socket.error as exc:
print('Fail to listen on port %s' % exc)
return
while True:
print("Waiting for connection")
client, addr = sock.accept()
data = client.recv(1000)
client.send(data)
client.close()
print(data)
if __name__ == '__main__':
start_tcp_server('0.0.0.0', 12345)

iptables 基本使用

1
2
3
4
5
6
// 查看当前生效规则
iptables -L -n
// 清空所有规则
iptables --flush
iptables -F

tcpkill 基本使用

https://yq.aliyun.com/articles/59308

1
2
3
4
// 安装
sudo apt-get install dsniff
// 使用
...

curl 超时设置

使用 curl 有两个超时时间,一个是连接超时时间,另一个是数据传输的最大允许时间。
连接超时时间用 --connect-timeout 参数来指定,数据传输的最大允许时间用 -m 参数来指定。

1
curl --connect-timeout 10 -m 20 "http://192.168.1.110:12345"

实施过程

  1. A机器启动Python脚本,监听12345端口。
  2. B级器通过curl命令进行访问。
  3. 在访问过程中通过配置iptables来实现网络的各种异常情况。
  4. 通过 tcpkill 来实现连接中断的异常情况。

正常访问

1
2
3
4
5
6
xyz@xyz-pc:~$ curl "http://192.168.1.110:12345"
GET / HTTP/1.1
Host: 192.168.1.110:12345
User-Agent: curl/7.58.0
Accept: */*

查看和清除规则

1
2
3
4
5
6
7
8
9
10
11
12
13
xyz@xyz-pc:~$ sudo iptables -L -n
[sudo] xyz 的密码:
Chain INPUT (policy ACCEPT)
target prot opt source destination

Chain FORWARD (policy ACCEPT)
target prot opt source destination

Chain OUTPUT (policy ACCEPT)
target prot opt source destination
DROP tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:12345 flags:0x17/0x02

xyz@xyz-pc:~$ sudo iptables -F

连接超时

1
2
3
xyz@xyz-pc:~$ sudo iptables -A OUTPUT -p tcp --syn --dport 12345 -j DROP
xyz@xyz-pc:~$ curl --connect-timeout 10 "http://192.168.1.110:12345"
curl: (28) Connection timed out after 10001 milliseconds

读取数据超时

1
2
3
xyz@xyz-pc:~$ sudo iptables -A OUTPUT -p tcp -m state --state ESTABLISHED --dport 12345 -j DROP
xyz@xyz-pc:~$ curl --connect-timeout 10 -m 20 "http://192.168.1.110:12345"
curl: (28) Operation timed out after 20001 milliseconds with 0 bytes received

拒绝连接

1
2
3
xyz@xyz-pc:~$ sudo iptables -A OUTPUT -p tcp --dport 12345 -j REJECT
xyz@xyz-pc:~$ curl --connect-timeout 10 -m 20 "http://192.168.1.110:12345"
curl: (7) Failed to connect to 192.168.1.110 port 12345: 拒绝连接

连接被重置

这里需要将Python脚本的 client.close() 注释掉。

1
2
3
4
5
6
7
xyz@xyz-pc:~$ sudo tcpkill -iwlp8s0 port 12345
xyz@xyz-pc:~$ curl --connect-timeout 10 "http://192.168.1.110:12345"
GET / HTTP/1.1
Host: 192.168.1.110:12345
User-Agent: curl/7.58.0
Accept: */*
curl: (56) Recv failure: 连接被对方重设

总结

在越来越多的企业微服务化进程中,肯定会遇到网络请求的各种问题,当我们在做一个基础组件或者进行网络通信请求时需要考虑到这些异常情况,最好还是将各种常见的情况模拟实施一下,来保证服务的稳定性。首先要说的是请求的超时设置,不论是在进行 HTTP 访问还是封装后的 RPC 请求,超时设置是最基本的。
基于不同语言的不同组件实现质量来说。曾经遇到过一个问题是,一个服务处于假死状态,Java 的客户端中默认超时和多线程可以使主线程服务不会受到过多影响,golang 中的客户端默认设置了一个很长的超时时间,服务在一定程度上受到了影响,而Python的客户端超时时间也是很长,还有就是Python只有一个主线程再跑,所以此时服务会被 hang 住了。
所以这里还有一个问题就是服务降级,当前服务如果出现问题,重试几次后仍然失败,那么是否降级来保证当前服务的可用性,其实考虑的是异常服务对于当下的重要性,是否在整个核心服务链路当中,否则的话进行降级处理。
还有一个关键点是慎用重试,偶然的网络波动导致的异常在重试下会很有效,但是当遇到服务性能导致的超时问题时,就遇到大量的客户端重试导致请求翻倍,很可能会直接把服务打挂,所以不要轻易使用重试,可以通过一些额外的补偿机制来提高服务稳定性。

未防止爬虫盗爬我的文章,在末尾打个广告。这篇文章首发在我的个人博客 知一的指纹 http://noogel.xyz ,有需要技术交流的朋友可以加我个人微信:zhi2012666

参考

https://blog.csdn.net/llianlianpay/article/details/79768890
https://www.cnblogs.com/gl1573/p/10129382.html
https://blog.csdn.net/wangyiyungw/article/details/85004905