文章851
标签121
分类10

Redis 测试工具 `redis-benchmark`

redis-benchmark 是 Redis 自带的性能测试工具,用于评估 Redis 服务器的性能。这工具可以模拟多种不同的请求类型,并测量 Redis 处理这些请求的速度和吞吐量。以下是关于如何使用 redis-benchmark 的详细介绍。

1. 安装 Redis

在使用 redis-benchmark 之前,确保您已经安装了 Redis。可以通过以下命令安装 Redis:

在 Ubuntu 上

sudo apt update
sudo apt install redis-server

在 CentOS 上

sudo yum install epel-release
sudo yum install redis

2. 使用 redis-benchmark

2.1 基本用法

打开终端,使用以下命令运行 redis-benchmark

redis-benchmark

默认情况下,redis-benchmark 会执行 100,000 个默认的 SET 和 GET 操作,并显示结果。

2.2 常用选项

  • -h:指定 Redis 服务器的主机地址(默认是 127.0.0.1)。

    redis-benchmark -h <hostname>
  • -p:指定 Redis 服务器的端口(默认是 6379)。

    redis-benchmark -p <port>
  • -n:指定总请求数。

    redis-benchmark -n 100000
  • -c:指定并发客户端数量。

    redis-benchmark -c 50
  • -d:指定每个键的大小(字节)。

    redis-benchmark -d 32
  • -t:指定要测试的命令类型,例如 SET, GET, LPUSH, SADD 等。

    redis-benchmark -t SET,GET

2.3 示例

以下是一个具体的示例,测试 Redis 服务器的性能,使用 50 个并发客户端进行 100,000 次 GET 和 SET 操作,每个键的大小为 32 字节:

redis-benchmark -h 127.0.0.1 -p 6379 -n 100000 -c 50 -d 32 -t SET,GET

2.4 输出结果

redis-benchmark 的输出结果包含以下信息:

  • 总请求数:执行的总请求数。
  • 吞吐量:每秒能够处理的请求数。
  • 延迟:每个请求的平均延迟时间。
====== SET ======
  100000 requests completed in 1.00 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

99.99% <= 1 milliseconds
99.90% <= 1 milliseconds
99.00% <= 1 milliseconds
95.00% <= 1 milliseconds
90.00% <= 1 milliseconds
...

3. 注意事项

  • 确保 Redis 服务器正在运行并可以访问。
  • 在进行性能测试时,请注意对生产环境的影响,尽量选择合适的时间段进行。
  • 根据测试需求调整参数,以获得更准确的性能评估。

4. 结论

redis-benchmark 是一个强大的工具,可以帮助您评估 Redis 服务器的性能。通过合理配置测试参数,您可以获得有关 Redis 性能的详细信息,从而进行系统优化和调整。

使用 TCP Keepalive 还是 HeartBeat 心跳包?

在网络编程中,TCP Keepalive 和 HeartBeat 心跳包都是用于检测连接状态和保持连接活跃的机制。选择使用哪种方法取决于具体的应用场景和需求。下面将详细比较这两种机制。

1. TCP Keepalive

1.1 概述

TCP Keepalive 是一种由 TCP 协议层提供的机制,用于在连接空闲时定期发送小的数据包,以检查连接的可用性。它主要用于检测死连接。

1.2 特点

  • 自动化:由操作系统管理,无需应用层干预。
  • 低开销:发送的数据包非常小,通常只有几个字节。
  • 适用场景:适合需要在长时间空闲情况下保持连接的应用,如数据库连接、长连接等。

1.3 配置

TCP Keepalive 的参数通常可以在操作系统层进行配置,例如:

  • TCP_KEEPIDLE:在连接空闲多长时间后开始发送 Keepalive 包。
  • TCP_KEEPINTVL:发送 Keepalive 包的时间间隔。
  • TCP_KEEPCNT:在认为连接断开之前发送的最大 Keepalive 包数。

2. HeartBeat 心跳包

2.1 概述

HeartBeat 心跳包是应用层实现的机制,通常由开发者自行定义和实现。它在特定的时间间隔内发送数据包,以检测连接或服务的状态。

2.2 特点

  • 灵活性:可以根据具体需求自定义心跳包的内容和发送频率。
  • 更多信息:可以在心跳包中包含应用层的状态信息。
  • 适用场景:适合需要实时检测连接状态或动态调整连接参数的应用,如聊天应用、实时游戏等。

2.3 实现

可以通过定时任务发送心跳包,例如:

import socket
import time

def send_heartbeat(sock):
    while True:
        sock.send(b'heartbeat')
        time.sleep(30)  # 每30秒发送一次心跳包

3. 选择依据

3.1 使用 TCP Keepalive 的理由

  • 简单性:无需在应用层进行额外的实现,操作系统自动处理。
  • 低开销:对带宽的消耗非常小,适合大规模连接的场景。

3.2 使用 HeartBeat 心跳包的理由

  • 灵活性:可以根据应用的具体需求定制心跳逻辑和内容。
  • 状态监测:可以监测应用层的状态,进行更复杂的逻辑判断。

4. 综合建议

  • 长连接场景:如果您的应用主要依赖于 TCP 连接的稳定性,且不需要应用层的状态信息,TCP Keepalive 是更简单有效的选择。
  • 实时应用场景:如果您的应用需要实时监测状态,或需要发送特定的状态信息,使用 HeartBeat 心跳包会更合适。

5. 结论

在选择 TCP Keepalive 还是 HeartBeat 心跳包时,应根据具体的应用需求、网络环境和实现的复杂性进行权衡。两者各有优缺点,选择适合您应用场景的方案将有助于提高系统的稳定性和可靠性。

Awk 指定分割符 去重

awk 中,您可以使用指定的分隔符来处理文本数据并去重。以下是如何使用 awk 实现这一功能的步骤和示例。

1. 基本用法

1.1 指定分隔符

可以通过 -F 选项指定字段分隔符。例如,如果您的数据以逗号分隔,可以使用 -F,

1.2 去重

使用数组来存储唯一的值,并在输出时检查是否已经存在。

2. 示例

假设您有一个名为 data.txt 的文件,内容如下:

apple,fruit
banana,fruit
carrot,vegetable
apple,fruit
banana,fruit
date,fruit

2.1 去重特定字段

如果您想根据第一列的内容去重,可以使用以下命令:

awk -F, '!seen[$1]++' data.txt

2.2 解释

  • -F,:指定分隔符为逗号。
  • !seen[$1]++:使用 seen 数组来跟踪已经看到的第一列的值。如果这个值没有被见过,则输出。

2.3 输出结果

运行上述命令后,输出将是:

apple,fruit
banana,fruit
carrot,vegetable
date,fruit

3. 处理其他分隔符

如果您的数据使用其他分隔符(如制表符或空格),只需相应地修改 -F 选项。例如,对于制表符分隔的数据:

awk -F'\t' '!seen[$1]++' data.txt

4. 处理多个字段

如果您希望根据多个字段去重,可以将多个字段组合成一个字符串。例如:

awk -F, '!seen[$1,$2]++' data.txt

这将根据第一列和第二列的组合来去重。

PHP 对交易对汇率 转换

业务需求 nuls_bx 交易对牟定盘口做市
但是要求牟定 bx_usdt 主流交易对, 真实晕 写了个递归 经过测试可用 转换路径如下
nuls_btc btc_usdt bx_eth eth_usdt 经过 四个交易对 转换成目标交易对 汇率

<?php

// 都需要对标  bx_usdt

$symbol = 'nuls_coin';
$benchmarkSymbolList = ['nuls_btc' , 'btc_usdt', 'coin_usdt'];

$benchmarkData = [

    'nuls_btc' => [
        '0.00015635',
        '0.00015832',
    ],
    // 'coin_btc' => [
    //     '0.00000278',  // 买
    //     '0.00002329', // 卖 
    // ],

    'btc_usdt' => [
        '3995.00',
        '3998.01',
    ],
    'coin_usdt' => [
        '0.0924',
        '0.1926',
    ]
   
];


// $symbol = 'eos_bx';
// $benchmarkSymbolList = ['eos_usdt', 'bx_usdt'];

// $benchmarkData = [

//     'eos_usdt' => [
//         3.6627,
//         3.6549
//     ],
//     'bx_usdt' =>[
//         0.1022,
//         0.1011
//     ]
// ];

/**
 * @des 交易对检测函数
 * @param $symbol
 * @param $checkSymbolList
 * @return bool
 * ------------------------------------------------------------
 */
function checkSymbolList($symbol, $checkSymbolList)
{
    list($coinFrom, $coinTo) = explode('_', $symbol);

    $coinList = [];
    foreach ($checkSymbolList as $checkSymbol) {

        list($cFrom, $cTo) = explode('_', $checkSymbol);

        $coinList[$cFrom] = isset($coinList[$cFrom]) ? $coinList[$cFrom] + 1 : 1;
        $coinList[$cTo] = isset($coinList[$cTo]) ? $coinList[$cTo] + 1 : 1;
    }

    // 目标币对 仅可以出现一个
    if (empty($coinList[$coinFrom]) || $coinList[$coinFrom] != 1 || empty($coinList[$coinTo]) || $coinList[$coinTo] != 1) {
        return false;
    }

    unset($coinList[$coinFrom], $coinList[$coinTo]);

    // 除目标币种 外 其他币种最少出现两次
    if (max($coinList) != 2 || min($coinList) != 2) {
        return false;
    }

    return true;
}

/**
 * @des 调用递归
 * @param $symbol
 * @param $benchmarkSymbolList
 * @param $benchmarkData
 * @return array
 * ------------------------------------------------------------
 */
function integrateRateToBenchMarkPrice($symbol, $benchmarkSymbolList, $benchmarkData)
{
    // 选择对标方向
    list($coinFrom, $coinTo) = explode('_', $symbol);

    // 效验交易对是否正确
    if (!checkSymbolList($symbol, $benchmarkSymbolList)) return [];

    return rateSymbolConversion($coinFrom, $coinTo, $benchmarkSymbolList, $benchmarkData);
}

/**
 * @des 多交易对汇率转换 递归
 * @param $coinFrom
 * @param $coinTo
 * @param $benchmarkSymbolList
 * @param $benchmarkData
 * @param array $params
 * @return array
 * ------------------------------------------------------------
 */
function rateSymbolConversion($coinFrom, $coinTo, $benchmarkSymbolList, $benchmarkData, $params = [])
{
    // 容错 异常跳出递归
    if (!$benchmarkSymbolList) return $params;

    // 转换后数据 nuls_usdt
    $askConversion = $params[0] ?? 0;
    $bidConversion = $params[1] ?? 0;
    $conversionCoin = $params[2] ?? '';

    // 交易对排序
    foreach ($benchmarkSymbolList as $k => $benchmarkSymbol) {

        list($from, $to) = explode('_', $benchmarkSymbol);

        // 提取交易对
        list($bidBenchmark, $askBenchmark) = $benchmarkData[$benchmarkSymbol];
 
        if ($conversionCoin == $from) {
            unset($benchmarkSymbolList[$k]);
            $askNewBenchmark = bcmul($askConversion, $askBenchmark, 10);
            $bidNewBenchmark = bcmul($bidConversion, $bidBenchmark, 10);

            echo $benchmarkSymbol ,' => from ', $conversionCoin,PHP_EOL;

            return rateSymbolConversion($coinFrom, $coinTo, $benchmarkSymbolList, $benchmarkData, [$bidNewBenchmark, $askNewBenchmark, $to]);
        } else if ($conversionCoin == $to) {

            unset($benchmarkSymbolList[$k]);

            echo $benchmarkSymbol ,' => to ', $conversionCoin, PHP_EOL;

            echo 'askBenchmark =>',$askConversion,' ', $askBenchmark, ' bidBenchmark => ', $bidConversion,' ', $bidBenchmark, PHP_EOL;

            $askNewBenchmark = bcdiv($askConversion, $bidBenchmark, 10);
            $bidNewBenchmark = bcdiv($bidConversion, $askBenchmark, 10);
 
            return rateSymbolConversion($coinFrom, $coinTo, $benchmarkSymbolList, $benchmarkData, [$bidNewBenchmark, $askNewBenchmark, $from]);
        }

        // 判断当前交易对 为代币 所在代币交易对
        if ($coinFrom == $from) {

            // 第一次 参数为空 直接复制 调用递归
            if (!$params) {
                // 注销使用过的数组元素
                unset($benchmarkSymbolList[$k]);

                echo $benchmarkSymbol ,' => null ', $coinFrom, PHP_EOL;

                return rateSymbolConversion($coinFrom, $coinTo, $benchmarkSymbolList, $benchmarkData, [$bidBenchmark, $askBenchmark, $to]);
            }

        }
    }

    return $params;
}

$data = integrateRateToBenchMarkPrice($symbol, $benchmarkSymbolList, $benchmarkData);
var_export($data);
localhost:Desktop admin$ php test.php 

# 执行顺序
nuls_btc => null nuls
btc_usdt => from btc
bz_usdt => to usdt
askBenchmark =>0.6324884000 0.1926 bidBenchmark => 0.6250888635 0.0924
array (
  0 => '3.2455288862',
  1 => '6.8451125541',
  2 => 'coin',
)

解决 `unix:///tmp/supervisor.sock no such file` 的问题

在使用 Supervisor 作为进程管理工具时,您可能会遇到 unix:///tmp/supervisor.sock no such file 错误。这通常表示 Supervisor 未能创建其 UNIX 套接字文件,或者该文件已被删除或未正确配置。以下是一些常见原因及解决方法。

1. 确保 Supervisor 正在运行

首先,确保 Supervisor 服务正在运行。可以使用以下命令检查其状态:

sudo supervisorctl status

如果 Supervisor 未运行,您可以使用以下命令启动它:

sudo service supervisor start

或(根据您的系统):

sudo systemctl start supervisor

2. 检查配置文件

Supervisor 的配置文件通常位于 /etc/supervisor/supervisord.conf。请确保以下配置项正确:

[unix_http_server]
file=/tmp/supervisor.sock   ; (the path to the socket file)

[supervisord]
logfile=/var/log/supervisord.log ; (logfile path)

2.1 权限问题

确保 /tmp 目录的权限设置正确。可以使用以下命令检查:

ls -ld /tmp

输出应类似于:

drwxrwxrwt 10 root root 4096 Oct 21 10:00 /tmp

如果权限不正确,可以使用以下命令设置:

sudo chmod 1777 /tmp

3. 检查日志文件

查看 Supervisor 的日志文件以获取更多信息。日志文件的路径通常在配置文件中指定,默认情况下为 /var/log/supervisord.log。可以使用以下命令查看日志:

cat /var/log/supervisord.log

检查日志中是否有错误信息,例如配置文件错误或权限问题。

4. 再次启动 Supervisor

如果您对配置文件进行了更改,确保重新启动 Supervisor 以应用更改:

sudo supervisorctl reread
sudo supervisorctl update
sudo service supervisor restart

或(根据您的系统):

sudo systemctl restart supervisor

5. 检查进程配置

如果 Supervisor 正常运行,但仍然无法找到套接字文件,检查您的进程配置文件(通常位于 /etc/supervisor/conf.d/)是否正确。确保您配置的进程在运行并且没有错误。

6. 检查 SELinux 设置(如果适用)

如果您的系统启用了 SELinux,可能会导致权限问题。可以暂时将 SELinux 设置为宽松模式来检查是否是这个问题:

sudo setenforce 0

如果这解决了问题,您可能需要调整 SELinux 策略以允许 Supervisor 正常运行。

7. 总结

通过以上步骤,您应该能够解决 unix:///tmp/supervisor.sock no such file 的问题。确保 Supervisor 正在运行,配置正确,并检查相关的日志和权限设置。

">