操作系统 CentOS Linux release 7.3.1611 (Core) 
Redis Redis server v=6.2.14
Python Python 3.6.8

任务需按 **“环境准备→任务执行→流量监控”** 的逻辑分步操作,以下是详细步骤(含配置修复、代码实现、工具使用),确保每一步可落地:

前置准备:确保 Redis 可正常运行且支持远程连接

首先解决之前 Redis 启动失败(status=217/USER)的问题,并配置 Redis 允许 172.21.204.200 访问(否则 200 无法连接 201 的 Redis)。

任务 1:在 172.21.204.201 的 Redis 上新建 Key

需创建普通小 Key和大 Value 的 Key(为任务 3 “大带宽读取” 做准备,小 Key 无法占用大量带宽)。

1.1. 连接Redis(若设密码,加 -a 密码,或连接后AUTH)

redis-cli -h 172.21.204.201 # 若有密码:redis-cli -h 172.21.204.201 -a your_redis_password

1.2. 新建普通小Key(用于测试基础读写)

set user:1 '{"name":"test","age":20}'
set product:1001 "iPhone 15"
mset city:beijing "capital" city:shanghai "finance" # 批量设Key

1.3. 新建大Value的Key(10MB/个,用于任务3占用带宽)

# 生成10MB的随机字符串(通过循环拼接实现)
方法1:
redis-cli -h 172.21.204.201 -a your_redis_password set bigkey:1 $(printf "x%.0s" {1..10485760})
redis-cli -h 172.21.204.201 -a your_redis_password set bigkey:2 $(printf "y%.0s" {1..10485760})
-bash: /usr/bin/redis-cli: Argument list too long # 报错

方法2:
# 生成 10MB 的文件(内容为重复的空字节,如需 "x" 可后续替换,不影响带宽测试)
dd if=/dev/zero of=/tmp/bigvalue.txt bs=10485760 count=1
# 解释:
# if=/dev/zero:从空字节设备读取(生成纯空内容)
# of=/tmp/bigvalue.txt:输出到临时文件
# bs=10485760:每次读写 10MB(10*1024*1024)
# count=1:只读写 1 次,总大小 = 10MB*1 = 10MB

# 将 /tmp/bigvalue.txt 的内容设置为 bigkey:1 的值
redis-cli -h 172.21.204.201 -x set bigkey:1 < /tmp/bigvalue.txt

# 检查 Key 的长度(单位:字节,应为 10485760)
redis-cli -h 172.21.204.201 strlen bigkey:1
# 输出应为:(integer) 10485760

如需生成纯 "x" 字符的大文件
若必须用 "x" 填充(非空字节),可先用 head 生成:
# 生成 10MB 的 "x" 字符文件(通过循环拼接)
yes x | head -c 10485760 > /tmp/bigvalue_x.txt
# 再设置 Key
redis-cli -h 172.21.204.201 -x set bigkey:1 < /tmp/bigvalue_x.txt

redis-cli -h 172.21.204.201 -a your_redis_password strlen bigkey:1 # 确认大Key长度(10485760 = 10MB)

1.4.查看所有key

redis-cli -h 172.21.204.201 keys "*"
1) "bigkey:2"
2) "product:1001"
3) "city:beijing"
4) "city:shanghai"
5) "bigkey:1"
6) "user:1"

任务 2:在 172.21.204.200 上用 Python3 操作 Redis

需先安装 Redis Python 客户端,再编写代码实现 “连接→读写 Key”。

2.1. 204.200 机器环境准备

# 安装Python3和redis库(CentOS示例)
yum install -y python3 python3-pip
pip3 install redis  # Redis Python客户端(redis-py)

2.2. Python 操作 Redis 代码(基础读写)

创建redis_operation.py,实现连接、读小 Key、读大 Key:
 
import redis

# 1. 配置Redis连接信息(需与201的Redis配置一致)
REDIS_CONFIG = {
    "host": "172.21.204.201",
    "port": 6379,
    "db": 0,  # 操作的数据库(默认16个,0-15)
    "password": "your_redis_password",  # 若未设密码,删除该参数
    "socket_timeout": 5,  # 连接超时时间
    "decode_responses": True  # 使返回值为字符串(而非bytes)
}

# 2. 连接Redis并操作
try:
    # 建立连接
    r = redis.Redis(**REDIS_CONFIG)
    print("Redis连接成功!")

    # (1)读普通小Key
    print("\n=== 读取普通Key ===")
    print(f"user:1 = {r.get('user:1')}")
    print(f"product:1001 = {r.get('product:1001')}")
    print(f"city:beijing = {r.get('city:beijing')}")

    # (2)读大Key(验证大Value读取)
    print("\n=== 读取大Key ===")
    bigkey1_len = len(r.get("bigkey:1"))
    print(f"bigkey:1 长度 = {bigkey1_len} 字节({bigkey1_len/1024/1024:.2f} MB)")

except Exception as e:
    print(f"Redis操作失败:{str(e)}")
finally:
    if "r" in locals():
        r.close()  # 关闭连接

2.3. 运行代码验证

python3 redis_operation.py
# 预期输出:连接成功 + 各Key的value,证明200可正常操作201的Redis

任务 3:在 204.200 上模拟高并发读取 + 大带宽占用

核心思路:用多线程循环读取 “大 Key”(如 10MB 的bigkey:1),通过 “高并发 + 大数据量” 触发大带宽消耗。

3.1. 高并发读取代码(redis_high_concurrency.py

使用threading库创建大量线程,每个线程循环读取大 Key(线程数和循环次数可根据机器性能调整):
 
import redis
import threading
import time
from concurrent.futures import ThreadPoolExecutor

# 1. 配置(重点:读大Key,线程数控制在100-200为宜,避免压垮Redis)
REDIS_CONFIG = {
    "host": "172.21.204.201",
    "port": 6379,
    "db": 0,
    "password": "your_redis_password",
    "socket_timeout": 10
}
BIG_KEY = "bigkey:1"  # 读取的大Key(10MB)
THREAD_NUM = 150  # 并发线程数(根据200机器CPU调整,建议≤200)
LOOP_TIMES = 200  # 每个线程循环读取次数

# 2. 单个线程的读取任务
def read_bigkey(task_id):
    """每个线程循环读取大Key,统计成功次数"""
    success_count = 0
    try:
        # 每个线程单独建立Redis连接(避免连接池瓶颈)
        r = redis.Redis(**REDIS_CONFIG)
        print(f"线程{task_id}:启动,开始循环读取{LOOP_TIMES}{BIG_KEY}")
        
        for i in range(LOOP_TIMES):
            # 读取大Key(忽略返回值,仅关注“读取动作”占用带宽)
            r.get(BIG_KEY)
            success_count += 1
            
            # 每10次打印一次进度
            if (i + 1) % 10 == 0:
                print(f"线程{task_id}:已完成{i+1}/{LOOP_TIMES}次读取")
                
    except Exception as e:
        print(f"线程{task_id}:出错 -> {str(e)}")
    finally:
        if "r" in locals():
            r.close()
    return success_count

# 3. 启动高并发任务
if __name__ == "__main__":
    print(f"=== 开始模拟高并发读取:{THREAD_NUM}线程,每个线程读{LOOP_TIMES}{BIG_KEY} ===")
    start_time = time.time()

    # 使用线程池管理并发
    with ThreadPoolExecutor(max_workers=THREAD_NUM) as executor:
        # 提交所有线程任务
        tasks = [executor.submit(read_bigkey, i) for i in range(THREAD_NUM)]
        # 统计总成功次数
        total_success = sum(task.result() for task in tasks)

    # 计算总数据量和耗时
    total_data_mb = total_success * 10  # 每个大Key≈10MB
    total_time = time.time() - start_time

    print(f"\n=== 高并发读取结束 ===")
    print(f"总读取次数:{total_success}")
    print(f"总数据量:{total_data_mb:.2f} MB(约{total_data_mb/1024:.2f} GB)")
    print(f"总耗时:{total_time:.2f} 秒")
    print(f"平均带宽:{total_data_mb / total_time:.2f} MB/s(约{total_data_mb*8 / total_time:.2f} Mbps)")

3.2. 运行高并发代码

# 后台运行(避免终端断开导致任务终止)
nohup python3 redis_high_concurrency.py > redis_concurrency.log 2>&1 &

# 查看日志,确认任务正常运行
tail -f redis_concurrency.log
此时 204.21 的 Redis 会持续向 204.200 发送数据,占用大量出站带宽。
[root@zb-yunweitest-mysql-204-200 redis]# python3 redis_high_concurrency.py 
=== 开始模拟高并发读取:10线程,每个线程读200次bigkey:1 ===
线程0:启动,开始循环读取200次bigkey:1
线程1:启动,开始循环读取200次bigkey:1
线程2:启动,开始循环读取200次bigkey:1
线程3:启动,开始循环读取200次bigkey:1
线程4:启动,开始循环读取200次bigkey:1
线程5:启动,开始循环读取200次bigkey:1
线程6:启动,开始循环读取200次bigkey:1
线程7:启动,开始循环读取200次bigkey:1
线程8:启动,开始循环读取200次bigkey:1
线程9:启动,开始循环读取200次bigkey:1
线程7:已完成10/200次读取
线程1:已完成10/200次读取
线程8:已完成10/200次读取
线程2:已完成10/200次读取
线程5:已完成10/200次读取
线程9:已完成10/200次读取
线程4:已完成10/200次读取
线程0:已完成10/200次读取
线程3:已完成10/200次读取
线程6:已完成10/200次读取
线程7:已完成20/200次读取
线程8:已完成20/200次读取
线程5:已完成20/200次读取
线程9:已完成20/200次读取
线程1:已完成20/200次读取
线程3:已完成20/200次读取
线程2:已完成20/200次读取
线程4:已完成20/200次读取
线程0:已完成20/200次读取
线程6:已完成20/200次读取

任务 4:在 204.21 的 Redis 服务器上查询 “流量最大的客户端”

Redis 本身不直接统计 “每个客户端的流量”,需结合Redis 客户端信息和Linux 网络工具定位流量最大的 IP(即 204.200)。

4.1 方法 1:用iftop实时查看客户端 IP 带宽(最直观)

iftop是 Linux 实时网络带宽监控工具,可按 “源 IP” 排序,直接看到 204.200 的带宽占用。
 
# 1. 安装iftop(CentOS示例)
yum install -y iftop

# 2. 运行iftop,监控Redis端口(6379)的连接
iftop -i eth0 -f "tcp port 6379"  # -i:指定网卡(用ip addr查,如eth0/ens33)

# 3. 交互操作(iftop界面内按以下键)
# - T:显示“发送/接收/总流量”总和
# - S:按“源IP”排序(此时204.200的IP会排在最前,因它在高并发读取)
# - q:退出
 
预期结果:172.21.204.200的 “发送带宽”(Redis 向它发数据)会远高于其他客户端,确认其为流量最大的客户端。

image

 

4.2 方法 2:用redis-cli client list关联客户端与 IP

通过 Redis 命令查看所有连接的客户端,结合iftop的流量结果,确认客户端 IP:
 
# 查看所有连接的客户端(重点看addr字段,即客户端IP:端口)
redis-cli -h 172.21.204.201 -a your_redis_password client list

# 示例输出(关键字段解释)
# addr=172.21.204.200:54321  # 客户端IP:端口(200的IP)
# fd=10  # 本地文件描述符(可关联系统网络连接)
# idle=0  # 空闲时间(0表示正在活跃读取)
# flags=N  # 客户端状态(N=普通客户端)

 4.2.1 内存指标总和统计

redis-cli client list | awk '
# 1. 解析 client list 输出,提取 IP、omem、tot-mem、argv-mem
{
    # 初始化变量
    ip = ""; omem = 0; tot_mem = 0; argv_mem = 0;
    # 遍历每个字段(格式:key=value)
    for (i=1; i<=NF; i++) {
        split($i, kv, "=");  # 分割 key 和 value(如 "addr=172.21.204.200:46102" → kv[1]="addr", kv[2]="172.21.204.200:46102"if (kv[1] == "addr") {
            split(kv[2], addr, ":");  # 提取 IP(去掉端口,如 "172.21.204.200:46102" → addr[1]="172.21.204.200")
            ip = addr[1];
        } else if (kv[1] == "omem") {
            omem = kv[2];
        } else if (kv[1] == "tot-mem") {
            tot_mem = kv[2];
        } else if (kv[1] == "argv-mem") {
            argv_mem = kv[2];
        }
    }
    # 按 IP 分组累加(存储为字节)
    sum_omem[ip] += omem;
    sum_tot_mem[ip] += tot_mem;
    sum_argv_mem[ip] += argv_mem;
    # 统计每个 IP 的连接数
    conn_count[ip]++;
}

# 2. 处理完所有行后,格式化输出结果
END {
    # 打印表头(左对齐,占20字符)
    printf "%-20s %-8s %-20s %-20s %-20s\n", 
           "客户端IP", "连接数", "omem总和(MB)", "tot-mem总和(MB)", "argv-mem总和(MB)";
    printf "%-20s %-8s %-20s %-20s %-20s\n", 
           "--------", "--------", "------------", "--------------", "--------------";
    
    # 遍历所有 IP,输出统计结果(字节转 MB:除以 1024*1024,保留2位小数)
    for (ip in sum_omem) {
        omem_mb = sum_omem[ip] / (1024*1024);
        tot_mem_mb = sum_tot_mem[ip] / (1024*1024);
        argv_mem_mb = sum_argv_mem[ip] / (1024*1024);
        printf "%-20s %-8d %.2f               %.2f               %.6f\n", 
               ip, conn_count[ip], omem_mb, tot_mem_mb, argv_mem_mb;
    }
}
'

输出如下:

客户端IP                连接数      omem总和(MB)           tot-mem总和(MB)        argv-mem总和(MB)      
--------             -------- ------------         --------------       --------------      
172.21.204.200       10       108.00               108.20               0.000000
127.0.0.1            1        0.00               0.06               0.000010

 4.2.2 出入网流量指标统计

redis-cli -h 172.21.204.201 client list | awk '
{
    # 初始化变量
    ip = "";
    omem = 0;        # 出网:Redis→客户端的输出缓冲区(字节)
    argv_mem = 0;    # 入网1:客户端命令参数内存(字节)
    qbuf = 0;        # 入网2:客户端输入缓冲区(字节)
    
    # 解析每行字段(key=value 格式)
    for (i = 1; i <= NF; i++) {
        split($i, kv, "=");  # 分割键值对
        key = kv[1];
        value = kv[2];
        
        # 提取客户端IP(去掉端口,如 172.21.204.200:46102172.21.204.200if (key == "addr") {
            split(value, addr_parts, ":");
            ip = addr_parts[1];
        }
        # 提取出网相关字段(转为数值)
        else if (key == "omem") {
            omem = value + 0;
        }
        # 提取入网相关字段(转为数值)
        else if (key == "argv-mem") {
            argv_mem = value + 0;
        }
        else if (key == "qbuf") {
            qbuf = value + 0;
        }
    }
    
    # 按IP分组累加(跳过空IP)
    if (ip != "") {
        conn_count[ip]++;                  # 统计连接数
        in_traffic[ip] += (argv_mem + qbuf);# 累计入网流量(字节)= 命令参数+输入缓冲区
        out_traffic[ip] += omem;           # 累计出网流量(字节)= 输出缓冲区
    }
}

# 统计完成后输出(直接手动转换单位:字节→MB,避免函数)
END {
    # 打印表头(左对齐,确保格式整齐)
    printf "%-20s %-8s %-25s %-25s\n", 
           "客户端IP", "连接数", "入网流量(MB)", "出网流量(MB)";
    printf "%-20s %-8s %-25s %-25s\n", 
           "--------", "--------", "------------", "------------";
    
    # 遍历所有客户端IP,计算并输出结果(1MB = 1024*1024 字节)
    for (ip in conn_count) {
        in_mb = in_traffic[ip] / (1024 * 1024);  # 入网流量转MB
        out_mb = out_traffic[ip] / (1024 * 1024);# 出网流量转MB
        printf "%-20s %-8d %.6f                  %.2f\n", 
               ip, conn_count[ip], in_mb, out_mb;
    }
}
'

输出如下:

客户端IP                连接数      入网流量(MB)                  出网流量(MB)                 
--------             -------- ------------              ------------             
172.21.204.200       10       0.000000                  72.00
172.21.204.201       1        0.000034                  0.00

4.3 方法 3:用info stats查看 Redis 总流量

若需确认 Redis 整体流量(而非单客户端),可通过 Redis 统计信息:
 
# 查看Redis流量统计(需先开启stats,默认开启)
redis-cli -h 172.21.204.201 -a your_redis_password info stats | grep "total_net_"

# 示例输出
# total_net_input_bytes:12345678  # 总入站流量(客户端向Redis发数据)
# total_net_output_bytes:10737418240  # 总出站流量(Redis向客户端发数据,高并发时会大幅增长)
[root@zb-yunweitest-mysql-204-201 data]# redis-cli -h 172.21.204.201  info stats | grep "total_net_"
total_net_input_bytes:21239003
total_net_output_bytes:100597191351
[root@zb-yunweitest-mysql-204-201 data]# 

5 注意事项

  1. 安全风险:测试完成后,需将 Redis 配置改回安全模式(bind 127.0.0.1protected-mode yes、保留密码),避免暴露公网导致未授权访问。
  2. 性能影响:高并发模拟时,若线程数过多(如>300),可能导致 204.201 的 Redis CPU / 内存占满,建议测试后及时停止任务(ps aux | grep redis_high_concurrency.py | awk '{print $2}' | xargs kill -9)。
  3. 带宽瓶颈:若模拟时带宽未达标,可增大big_key的大小(如 20MB)或增加THREAD_NUM/LOOP_TIMES

6 补充:大批量写入

6.1 大批量写入数据

import redis
import threading
import time
import random
import string
from concurrent.futures import ThreadPoolExecutor

# 1. 配置(重点:写入大Value,线程数控制在100-200为宜)
REDIS_CONFIG = {
    "host": "172.21.204.201",
    "port": 6379,
    "db": 0,
    "password": "your_redis_password",
    "socket_timeout": 10
}
KEY_PREFIX = "bigvalue:"  # 写入的大Key前缀
VALUE_SIZE_MB = 10  # 每个Value的大小(MB)
THREAD_NUM = 150  # 并发线程数(根据机器CPU调整,建议≤200)
LOOP_TIMES = 200  # 每个线程循环写入次数

# 2. 生成指定大小的随机数据
def generate_large_data(size_mb):
    """生成指定大小的随机字符串数据"""
    # 1MB ≈ 1,048,576 字节
    size_bytes = size_mb * 1024 * 1024
    # 生成随机字符串
    chars = string.ascii_letters + string.digits
    # 计算需要多少字符才能达到目标大小
    # 每个字符约1字节,所以直接使用size_bytes作为字符数
    return ''.join(random.choice(chars) for _ in range(size_bytes))

# 3. 单个线程的写入任务
def write_bigvalue(task_id):
    """每个线程循环写入大Value,统计成功次数"""
    success_count = 0
    try:
        # 预先生成大体积数据(避免每次写入时重复生成的开销)
        large_data = generate_large_data(VALUE_SIZE_MB)
        
        # 每个线程单独建立Redis连接(避免连接池瓶颈)
        r = redis.Redis(** REDIS_CONFIG)
        print(f"线程{task_id}:启动,开始循环写入{LOOP_TIMES}次大Value(每个{VALUE_SIZE_MB}MB)")
        
        for i in range(LOOP_TIMES):
            # 生成唯一的Key(包含线程ID和循环次数)
            key = f"{KEY_PREFIX}{task_id}:{i}"
            # 写入大Value
            r.set(key, large_data)
            success_count += 1
            
            # 每10次打印一次进度
            if (i + 1) % 10 == 0:
                print(f"线程{task_id}:已完成{i+1}/{LOOP_TIMES}次写入")
                
    except Exception as e:
        print(f"线程{task_id}:出错 -> {str(e)}")
    finally:
        if "r" in locals():
            r.close()
    return success_count

# 4. 启动高并发写入任务
if __name__ == "__main__":
    print(f"=== 开始模拟高并发写入:{THREAD_NUM}线程,每个线程写{LOOP_TIMES}次(每个{VALUE_SIZE_MB}MB) ===")
    start_time = time.time()

    # 使用线程池管理并发
    with ThreadPoolExecutor(max_workers=THREAD_NUM) as executor:
        # 提交所有线程任务
        tasks = [executor.submit(write_bigvalue, i) for i in range(THREAD_NUM)]
        # 统计总成功次数
        total_success = sum(task.result() for task in tasks)

    # 计算总数据量和耗时
    total_data_mb = total_success * VALUE_SIZE_MB
    total_time = time.time() - start_time

    print(f"\n=== 高并发写入结束 ===")
    print(f"总写入次数:{total_success}")
    print(f"总数据量:{total_data_mb:.2f} MB(约{total_data_mb/1024:.2f} GB)")
    print(f"总耗时:{total_time:.2f} 秒")
    print(f"平均写入带宽:{total_data_mb / total_time:.2f} MB/s(约{total_data_mb*8 / total_time:.2f} Mbps)")

6.2 运行高并发代码

[root@zb-yunweitest-mysql-204-200 redis]# python3 redis_high_concurrency_write.py 
=== 开始模拟高并发写入:10线程,每个线程写200次(每个10MB) ===
线程2:启动,开始循环写入200次大Value(每个10MB)
线程3:启动,开始循环写入200次大Value(每个10MB)
线程1:启动,开始循环写入200次大Value(每个10MB)
线程0:启动,开始循环写入200次大Value(每个10MB)
线程8:启动,开始循环写入200次大Value(每个10MB)
线程6:启动,开始循环写入200次大Value(每个10MB)
线程4:启动,开始循环写入200次大Value(每个10MB)
线程9:启动,开始循环写入200次大Value(每个10MB)
线程5:启动,开始循环写入200次大Value(每个10MB)
线程7:启动,开始循环写入200次大Value(每个10MB)
线程3:已完成10/200次写入
线程2:已完成10/200次写入
线程3:已完成20/200次写入
线程1:已完成10/200次写入
线程4:已完成10/200次写入
线程5:已完成10/200次写入
线程8:已完成10/200次写入
线程9:已完成10/200次写入
线程7:已完成10/200次写入
线程3:已完成30/200次写入
线程0:已完成10/200次写入
线程1:已完成20/200次写入
线程6:已完成10/200次写入
线程8:已完成20/200次写入
线程9:已完成20/200次写入
线程5:已完成20/200次写入
线程4:已完成20/200次写入
......
线程0:已完成200/200次写入
线程6:已完成200/200次写入
线程4:已完成200/200次写入
线程9:已完成200/200次写入
线程5:已完成200/200次写入

=== 高并发写入结束 ===
总写入次数:2000
总数据量:20000.00 MB(约19.53 GB)
总耗时:258.37 秒
平均写入带宽:77.41 MB/s(约619.27 Mbps)
[root@zb-yunweitest-mysql-204-200 redis]# 

6.2 统计入网流量

客户端IP                连接数      入网流量(MB)                  出网流量(MB)                 
--------             -------- ------------              ------------             
172.21.204.200       8        42.987526                  0.00
172.21.204.201       1        0.000034                  0.00

 

 posted on 2025-09-06 10:31  xibuhaohao  阅读(4)  评论(0)    收藏  举报