文章851
标签121
分类10

Centos7 PHP 获取时间异常 取到时间比真实时间快十几分钟

在 CentOS 7 上,如果 PHP 获取的时间比真实时间快十几分钟,通常是由于系统时间设置不正确或时区配置问题。以下是解决此问题的步骤。

1. 检查系统时间

首先,检查系统的当前时间和时区设置。

检查当前时间

在终端中运行以下命令:

date

这将显示当前的系统时间和日期。

检查时区

您还可以检查当前的时区设置:

timedatectl

输出中应该有类似于以下的内容:

      Local time: Fri 2024-10-21 15:20:15 CST
  Universal time: Fri 2024-10-21 07:20:15 UTC
        RTC time: Fri 2024-10-21 07:20:15
       Time zone: Asia/Shanghai (CST, +0800)
     NTP enabled: yes
NTP synchronized: yes

确保 Time zone 显示的是您所在的正确时区。

2. 设置正确的时区

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

sudo timedatectl set-timezone Asia/Shanghai

Asia/Shanghai 替换为您需要的时区。

3. 检查 PHP 配置

确保 PHP 的时区配置也是正确的。您可以在 php.ini 文件中设置时区。

编辑 php.ini

找到您的 php.ini 文件路径:

php --ini

然后使用文本编辑器编辑 php.ini 文件:

sudo nano /etc/php.ini

在文件中找到 date.timezone 设置并更新为您的时区,例如:

date.timezone = "Asia/Shanghai"

如果没有找到 date.timezone 这一行,可以添加:

date.timezone = "Asia/Shanghai"

4. 重启 Web 服务器

修改 php.ini 后,您需要重启 Web 服务器以使更改生效。对于 Apache:

sudo systemctl restart httpd

对于 Nginx:

sudo systemctl restart nginx

5. 检查 PHP 获取的时间

在 PHP 中检查获取的时间是否正常:

<?php
echo date('Y-m-d H:i:s');
?>

将此代码放置在您的 PHP 文件中并访问,确保获取的时间是正确的。

6. 使用 NTP 同步时间(可选)

为了确保系统时间的准确性,您可以启用 NTP(网络时间协议)以自动同步时间。

安装 NTP

如果未安装 NTP,可以使用以下命令安装:

sudo yum install ntp -y

启用并启动 NTP 服务

sudo systemctl start ntpd
sudo systemctl enable ntpd

PHP 抓取 交易对 日线数据生成csv脚本

抓取 ht_usdt 交易对日线数据脚本

<?php
$context = stream_context_create(array(
        "http" => [
            "timeout" => 3
        ],
    ));

$data = file_get_contents('https://apiv2.bitz.com/Market/kline?symbol=ht_usdt&resolution=1day&size=300', false, $context);

echo "字节", strlen($data),PHP_EOL;
if (!$data) {
    echo '失败',PHP_EOL;
    exit;
}

$dataArr = json_decode($data, true)['data']['bars'] ?? [];

$newDataArr = [];
foreach ($dataArr as $ke => $v) {
    
    if (empty($v['time'])) continue;

    $newDataArr[$v['time']] = [
        '开盘' => $v['open'],
        '收盘' => $v['close'],
        '最高' => $v['high'],
        '最低' => $v['low'],
        '时间' => date('Ymd', ceil($v['time']/1000)),
    ];
}

create_excel($newDataArr, './ht_usdt');

 // 创建表格文件 xls,csv
function create_excel($data = array(), $file_name = '', $file_type = 'csv')
{
    $file_name .= date('Ymd');
    $split = ",";
    if ($file_type == 'xls') {
        $split = "\t";
    }
    $output = '';
    $first = reset($data);
    foreach ($first as $key => $value) {
        $output .= $key . $split;
    }
    $output .= "\r\n";
    foreach ($data as $value) {
        foreach ($value as $v) {
            $output .= $v . $split;
        }
        $output .= "\r\n";
    }
    $fileName = $file_name . "." . $file_type;
    $fp = fopen($fileName, "w+");
    fwrite($fp, $output);
    fclose($fp);
}

Nginx在CDN加速之后,获取用户真实IP做并发访问限制的方法

当使用 CDN(内容分发网络)加速时,Nginx 可能接收到来自 CDN 的请求,这些请求通常会替代用户的真实 IP 地址。为了对用户进行并发访问限制,您需要确保 Nginx 能够获取用户的真实 IP。以下是实现这一目标的方法。

1. 配置 Nginx 以获取真实 IP

1.1 使用 set_real_ip_from

在 Nginx 配置文件中,您需要使用 set_real_ip_from 指令来指定 CDN 的 IP 地址范围。然后使用 real_ip_header 指令来设置 Nginx 从哪个头部获取真实 IP。

示例配置

http {
    # 指定 CDN 的 IP 地址范围
    set_real_ip_from 203.0.113.0/24;  # 示例:替换为您的 CDN IP 范围
    set_real_ip_from 198.51.100.0/24;  # 示例:替换为您的 CDN IP 范围
    real_ip_header X-Forwarded-For;

    server {
        listen 80;

        location / {
            # 处理请求
        }
    }
}

1.2 允许多级代理

如果您的 CDN 可能在 X-Forwarded-For 中添加多个 IP,您可以使用 real_ip_recursive 指令来启用递归查找。

    real_ip_recursive on;

2. 配置并发访问限制

在获取用户真实 IP 后,您可以使用 Nginx 的 limit_req 模块来限制并发访问。

2.1 配置 limit_req_zone

首先,您需要定义一个 limit_req_zone,通常放在 http 块中。

http {
    limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s; # 每秒 1 个请求

    server {
        listen 80;

        location / {
            limit_req zone=one burst=5 nodelay; # 允许突发 5 个请求
            # 处理请求
        }
    }
}

2.2 解释配置

  • limit_req_zone:定义一个名为 one 的限流区域,使用 $binary_remote_addr 作为键,限制速率为每秒 1 个请求。
  • limit_req:在指定的 location 中应用限流,允许突发的 5 个请求。

3. 完整配置示例

以下是一个完整的 Nginx 配置示例,包括获取真实 IP 和并发访问限制:

http {
    # 获取真实 IP
    set_real_ip_from 203.0.113.0/24;  # 替换为您的 CDN IP 范围
    set_real_ip_from 198.51.100.0/24;  # 替换为您的 CDN IP 范围
    real_ip_header X-Forwarded-For;
    real_ip_recursive on;

    # 并发访问限制
    limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;

    server {
        listen 80;

        location / {
            limit_req zone=one burst=5 nodelay;
            # 处理请求
            proxy_pass http://backend; # 替换为您的后端服务
        }
    }
}

4. 测试配置

在修改 Nginx 配置后,确保测试配置是否正确:

nginx -t

如果没有错误,可以重新加载 Nginx:

sudo systemctl reload nginx

5. 总结

通过正确配置 Nginx,您可以在使用 CDN 加速的情况下获取用户的真实 IP,并实现并发访问限制。这对保护您的应用程序免受恶意请求和流量攻击非常重要

RSA密文过长加密解密 越过1024的解决代码

在使用 RSA 加密时,密文的长度受限于密钥的大小。例如,使用 1024 位密钥时,加密的明文数据大小最多为 117 字节(使用 PKCS#1 v1.5 填充)。如果要加密更大的数据,你可以使用分块加密或混合加密方案。

以下是一个使用 Go 语言的示例,展示如何处理 RSA 加密解密,特别是对大于 1024 字节的数据进行加密。

1. 使用 RSA 加密的基本步骤

  • 生成 RSA 密钥对。
  • 使用 RSA 进行加密和解密。
  • 对大数据进行分块加密。

2. 示例代码

2.1 生成 RSA 密钥对

首先,您需要生成 RSA 密钥对:

package main

import (
    "crypto/rand"
    "crypto/rsa"
    "crypto/x509"
    "encoding/pem"
    "log"
    "os"
)

// 生成 RSA 密钥对
func generateKeyPair(bits int) (*rsa.PrivateKey, *rsa.PublicKey, error) {
    privateKey, err := rsa.GenerateKey(rand.Reader, bits)
    if err != nil {
        return nil, nil, err
    }
    return privateKey, &privateKey.PublicKey, nil
}

// 保存私钥到文件
func savePrivateKey(filename string, priv *rsa.PrivateKey) error {
    privBytes := x509.MarshalPKCS1PrivateKey(priv)
    block := &pem.Block{
        Type:  "RSA PRIVATE KEY",
        Bytes: privBytes,
    }
    return os.WriteFile(filename, pem.EncodeToMemory(block), 0600)
}

// 保存公钥到文件
func savePublicKey(filename string, pub *rsa.PublicKey) error {
    pubBytes := x509.MarshalPKCS1PublicKey(pub)
    block := &pem.Block{
        Type:  "RSA PUBLIC KEY",
        Bytes: pubBytes,
    }
    return os.WriteFile(filename, pem.EncodeToMemory(block), 0644)
}

2.2 加密和解密函数

接下来,您可以使用以下函数进行加密和解密:

// RSA 加密
func encrypt(publicKey *rsa.PublicKey, message []byte) ([]byte, error) {
    return rsa.EncryptPKCS1v15(rand.Reader, publicKey, message)
}

// RSA 解密
func decrypt(privateKey *rsa.PrivateKey, ciphertext []byte) ([]byte, error) {
    return rsa.DecryptPKCS1v15(rand.Reader, privateKey, ciphertext)
}

2.3 处理大数据的分块加密

对于大于 117 字节的消息,需要手动分块:

// 分块加密
func encryptLargeMessage(publicKey *rsa.PublicKey, message []byte) ([][]byte, error) {
    var chunks [][]byte
    chunkSize := 117 // 根据密钥大小调整

    for i := 0; i < len(message); i += chunkSize {
        end := i + chunkSize
        if end > len(message) {
            end = len(message)
        }
        chunk, err := encrypt(publicKey, message[i:end])
        if err != nil {
            return nil, err
        }
        chunks = append(chunks, chunk)
    }
    return chunks, nil
}

// 分块解密
func decryptLargeMessage(privateKey *rsa.PrivateKey, ciphertexts [][]byte) ([]byte, error) {
    var message []byte
    for _, ciphertext := range ciphertexts {
        chunk, err := decrypt(privateKey, ciphertext)
        if err != nil {
            return nil, err
        }
        message = append(message, chunk...)
    }
    return message, nil
}

2.4 示例主程序

将所有代码组合在一起,生成密钥并进行加密解密:

func main() {
    // 生成密钥对
    privateKey, publicKey, err := generateKeyPair(1024)
    if err != nil {
        log.Fatal(err)
    }

    // 保存密钥
    savePrivateKey("private.pem", privateKey)
    savePublicKey("public.pem", publicKey)

    // 要加密的消息
    message := []byte("这是一条长消息,超过了 RSA 的直接加密限制。" +
        "如果消息很长,这里需要分块处理。")

    // 分块加密
    ciphertexts, err := encryptLargeMessage(publicKey, message)
    if err != nil {
        log.Fatal(err)
    }

    // 分块解密
    decryptedMessage, err := decryptLargeMessage(privateKey, ciphertexts)
    if err != nil {
        log.Fatal(err)
    }

    // 输出结果
    log.Printf("解密后的消息: %s\n", decryptedMessage)
}

Mysql 5.6 sql_mode 非严格模式导致 decimal溢出 问题总结

昨天线上出现重大bug, 用户通过修改 接口请求参数 1 改为 '000000000000...1' 的方式 造成我们mysql 数据库字段在 insert 操作 decimal 字段类型溢出,

举例

-- mysql 5.6 版本运行

MySQL [test]> insert into `table_name` (id, num )values(null ,'0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001');

MySQL [test]> select * from table_name
    -> ;
+----+-----------------------+
| id | num                   |
+----+-----------------------+
|  1 | 999999999999.99999999 |
+----+-----------------------+

线上的情况 就如上面的问题一样 外部参数 没有做格式化处理 插入时带有过多的0 decimal 字段类型溢出 生成 999999999999.99999999 .....

本地docker 安装 mysql5.7 复现问题


Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MySQL connection id is 5724
Server version: 5.7.28 MySQL Community Server (GPL)

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MySQL [money]> insert into `table_name` (id,num)values(null,'000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001');
ERROR 1366 (HY000): Incorrect decimal value: '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' for column 'num' at row 1

直接插入 抛错 ERROR 1366 (HY000): Incorrect decimal value: '0000000000000000000000000000000000000000000000000' for column 'num' at row 1 , decimal值不正确 , 插入失败.

原因在于 mysql5.7 默认的 sql_mode 配置问题:

MySQL [money]> show variables like '%sql_mode%'\G;
*************************** 1. row ***************************
Variable_name: sql_mode
        Value: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

sql_mode 详情可以看 MySQLsql_mode 合理设置

mysql 5.7 默认 sql_mode 配置中 带有 STRICT_TRANS_TABLES:严格模式
在该模式下,如果一个值不能插入到一个事务表中,则中断当前的操作,对非事务表不做限制(翻译可能有问题 不好理解)

分别设置 sql_mode 为 '' 和 STRICT_TRANS_TABLES, 结果截然不同.
设置为严格模式下 抛错 Incorrect decimal value: ... 设置为空 插入成功.
1通过前面加入大量的0 插入数据库变成了 999999999.9999999.

我又问过DBA 为什么不选择用严格模式 , DBA答案是: 线上代码基本都会失败.
举例 :
程序运算得到一个浮点型字符串 0.123456789, 需要插入数据库 字段 num 中 , num 类型 decimal(20,8) 小数点后精度8位,
而你要插入的是9 位 正常情况入库 结果 0.12345678 仅保留8位, 如果使用严格模式 插入会失败抛错.

补充下: 更新或插入的字段为字符串类型 写入mysql 非严格模式会造成 decimal溢出, 数字类型不会发生溢出问题.

总结 :
外来参数一定要做格式化处理 数字类型也不能放过. 之前仅是防止数字为负数 或者 浮点溢出问题 , 忽略了 decimal的值不正确 导致的溢出问题.mysql 对字符串转换数字类型不是很给力呀。

">