#973 Golang KV 缓存实现

2024-01-12

后期优化:

  1. items 按照链表的方式组织起来,按过期时间排序,加快清理速度。
    或者另外设计一个数据结构,存储缓存过期时间。
  2. 放弃每次 Get 的时候判断是否过期
package main

import (
    "fmt"
    "sync"
    "time"
)

type CacheItem struct {
    key        string
    value      interface{}
    expiration time.Time
}

type Cache struct {
    items           map[string]CacheItem // 缓存项
    mu              sync.Mutex           // 锁保护并发读写
    cleanupInterval time.Duration        // 清理缓存的时间间隔
}

// 创建一个新的缓存实例
func NewCache(cleanupInterval time.Duration) *Cache {
    c := &Cache{
        items:           make(map[string]CacheItem),
        cleanupInterval: cleanupInterval,
    }
    // 启动后台清理任务
    go c.startCleanup()
    return c
}

// 启动后台定期清理过期缓存的任务
func (c *Cache) startCleanup() {
    ticker := time.NewTicker(c.cleanupInterval)
    for {
        <-ticker.C
        c.cleanUpExpiredItems()
    }
}

// 清理过期缓存项
func (c *Cache) cleanUpExpiredItems() {
    c.mu.Lock()
    defer c.mu.Unlock()

    // 清理有过期时间的缓存项
    for key, item := range c.items {
        if time.Now().After(item.expiration) {
            delete(c.items, key)
        }
    }

    // 打印清理日志(可以根据需要调整)
    fmt.Println("Expired cache items cleaned up")
}

// 添加缓存项,支持过期时间
func (c *Cache) Set(key string, value interface{}, expiration time.Time) {
    c.mu.Lock()
    defer c.mu.Unlock()

    c.items[key] = CacheItem{key: key, value: value, expiration: expiration}
}

// 获取缓存项
func (c *Cache) Get(key string) (interface{}, bool) {
    c.mu.Lock()
    defer c.mu.Unlock()

    // 再检查有过期时间的缓存项
    if item, found := c.items[key]; found {
        // if item.expiration.IsZero() || time.Now().Before(item.expiration) {
        //  return item.value, true
        // }
        // // 如果缓存项已过期,则删除并返回未找到
        // delete(c.items, key)
        return item.value, true
    }
    return nil, false
}

// 删除缓存项
func (c *Cache) Delete(key string) {
    c.mu.Lock()
    defer c.mu.Unlock()

    delete(c.items, key)
}

func main() {
    // 创建一个缓存实例,每 1 秒清理一次过期缓存
    cache := NewCache(1 * time.Second)

    // 设置缓存项(带过期时间)
    cache.Set("key1", "value1", time.Now().Add(5*time.Second))

    // 设置没有过期时间的缓存项
    cache.Set("key2", "value2", time.Time{}) // 空时间表示永不过期

    // 获取缓存项
    if value, found := cache.Get("key1"); found {
        fmt.Println("Found key1:", value)
    } else {
        fmt.Println("key1 not found")
    }

    if value, found := cache.Get("key2"); found {
        fmt.Println("Found key2:", value)
    } else {
        fmt.Println("key2 not found")
    }

    // 等待 6 秒后,key1 会过期
    time.Sleep(6 * time.Second)

    // 再次获取缓存项
    if value, found := cache.Get("key1"); found {
        fmt.Println("Found key1:", value)
    } else {
        fmt.Println("key1 not found (after expiration)")
    }

    if value, found := cache.Get("key2"); found {
        fmt.Println("Found key2:", value)
    } else {
        fmt.Println("key2 not found")
    }

    // 让清理任务继续运行
    select {}
}

#970 邮件地址黑名单

2023-12-25
  1. 退订
  2. FBL 举报
  3. 运营商反馈
  4. 收件人反馈(投诉)
  5. 地址不存在
  6. 邮箱容量已满
  7. 平台或客户手动指定的域名或地址
  8. 第三方提供的黑名单地址

有些是永久禁止,有些是临时禁止(比如一个月、三个月、半年)。

#969 Windows 操作习惯养成

2023-12-23

Windows 11

触摸板

  • 单指:点击,双击,双击拖动
  • 双指:滚动,点击(鼠标右键),捏合(缩放场景)
  • 三指:
  • 上、下:多任务视图/显示桌面
  • 左、右:切换应用
  • 点击:搜索
  • 四指:
  • 上、下:多任务视图/显示桌面(调整为:最大化窗口/最小化窗口)
  • 左、右:切换桌面
  • 点击:通知中心

快捷键

按键 作用
Win + ~ 下拉命令行窗口

TAB

按键 作用
Win + Tab 多任务视图
Alt + Tab 切换应用
Alt + Ctrl + Tab 应用切换(应用选择)

任务栏应用

按键 作用
Win + Num 启动任务栏应用/窗口切换
Win + Ctrl + Num 启动任务栏应用/窗口切换(有点不一样)
Win + Shift + Num 启动任务栏应用(新窗口)
Win + Shift + Alt + Num 启动任务栏应用(管理员权限)
Win + Alt + Num 任务栏应用菜单

#967 OpenSSL 检查 HTTPS 和 SMTP (STARTTLS) 连接

2023-12-10

HTTPS

echo | openssl s_client -connect sendcloud.net:443
echo | openssl s_client -showcerts -connect sendcloud.net:443
echo | openssl s_client -showcerts -debug -connect sendcloud.net:443 >/dev/null
echo | openssl s_client -showcerts -debug -connect sendcloud.net:443 2>/dev/null | openssl x509 -inform pem -noout -text

# 导出证书
openssl s_client -connect sendcloud.net:443 </dev/null | openssl x509 -outform pem > sendcloud.net.cer
# 导出所有证书
openssl s_client -showcerts -connect sendcloud.net:443 </dev/null | sed -n '/-----BEGIN/,/-----END/p' > sendcloud.net.cer
# 查看证书信息
openssl s_client -showcerts -connect sendcloud.net:443 </dev/null | sed -n '/-----BEGIN/,/-----END/p' | openssl x509 -noout -text
# 查看证书链上所有证书信息
OLDIFS=$IFS; IFS=':' certs=$(openssl s_client -showcerts -connect sendcloud.net:443 2>/dev/null </dev/null | sed -n '/-----BEGIN/,/-----END/{/-----BEGIN/ s/^/:/; p}'); for cert in ${certs#:}; do echo $cert | openssl x509 -noout -text; done; IFS=$OLDIFS
# # 查看证书链上所有证书 OCSP URI
OLDIFS=$IFS; IFS=':' certs=$(openssl s_client -showcerts -connect sendcloud.net:443 2>/dev/null </dev/null | sed -n '/-----BEGIN/,/-----END/{/-----BEGIN/ s/^/:/; p}'); for cert in ${certs#:}; do echo $cert | openssl x509 -noout -ocsp_uri; done; IFS=$OLDIFS
# -ocsp_uri
# -serial
# -fingerprint
# -subject
# 检查证书在 30 天之后有没有过期
openssl s_client -connect sendcloud.net:443 2>/dev/null </dev/null | openssl x509 -outform pem | openssl x509 -noout -checkend 2592000
# -checkend intmax  Check whether the cert expires in the next arg seconds
# -checkhost val    Check certificate matches host
# -checkemail val   Check certificate matches email
# -checkip val      Check certificate matches ipaddr
echo | openssl s_client -verify_hostname baidu.com -connect sendcloud.net:443 1>/dev/null

SMTP

一样的,只是加 -starttls smtp

echo | openssl s_client -connect smtp.sendcloud.net:25 -starttls smtp
echo | openssl s_client -connect smtp.sendcloud.net:25 -starttls smtp 2>/dev/null | openssl x509 -inform pem -noout -text

其他

校验证书

# echo | openssl s_client -connect smtp.sendcloud.net:25 -starttls smtp 2>/dev/null | openssl verify
# echo | openssl s_client -showcerts -connect smtp.sendcloud.net:25 -starttls smtp 2>/dev/null | openssl verify
echo | openssl s_client -showcerts -connect smtp.sendcloud.net:25 -starttls smtp 2>/dev/null | sed -n '/-----BEGIN/,/-----END/p' > smtp.sendcloud.net.cer
openssl verify -CAfile smtp.sendcloud.net.cer smtp.sendcloud.net.cer

IMAP

echo | openssl s_client -connect imap.126.com:143 -starttls imap

测试 TLS 1.3 支持

echo | openssl s_client -connect smtp.sendcloud.net:25 -starttls smtp -tls1_3

OCSP 检测

用上面的 sendcloud.net.cer 举例,需要将文件中第一个证书和下面的其他证书拆开,分成 sendcloud.cer 和 chain.cer

openssl x509 -noout -ocsp_uri -in sendcloud.cer
# http://ocsp.sectigo.com
openssl ocsp -issuer chain.cer -cert sendcloud.cer -text -url http://ocsp.sectigo.com
# Error querying OCSP responder
# 140052849842064:error:27076072:OCSP routines:PARSE_HTTP_LINE1:server response error:ocsp_ht.c:314:Code=400,Reason=Bad Request

遇到上面报错是因为 OSCP 客户端使用 HTTP 1.0,但是服务器端现在都是 1.1,需要 Host 头。

$ openssl ocsp -issuer chain.cer -cert sendcloud.cer -text -url http://ocsp.sectigo.com -header "HOST" "ocsp.sectigo.com"
OCSP Request Data:
    Version: 1 (0x0)
    Requestor List:
        Certificate ID:
          Hash Algorithm: sha1
          Issuer Name Hash: 21F3459A10CAA6C84BDA1E3962B127D5338A7C48
          Issuer Key Hash: 17D9D6252767F931C24943D93036448C6CA94FEB
          Serial Number: DB5F1FFAFFB770CA38E8120A6121852E
    Request Extensions:
        OCSP Nonce:
            0410F47624DDB17E9703BFCDCC96E983018B
OCSP Response Data:
    OCSP Response Status: successful (0x0)
    Response Type: Basic OCSP Response
    Version: 1 (0x0)
    Responder Id: 17D9D6252767F931C24943D93036448C6CA94FEB
    Produced At: Dec  9 07:52:17 2023 GMT
    Responses:
    Certificate ID:
      Hash Algorithm: sha1
      Issuer Name Hash: 21F3459A10CAA6C84BDA1E3962B127D5338A7C48
      Issuer Key Hash: 17D9D6252767F931C24943D93036448C6CA94FEB
      Serial Number: DB5F1FFAFFB770CA38E8120A6121852E
    Cert Status: good
    This Update: Dec  9 07:52:17 2023 GMT
    Next Update: Dec 16 07:52:16 2023 GMT

    Signature Algorithm: sha256WithRSAEncryption
         5f:65:bf:3e:d1:8c:16:63:76:bc:83:82:b8:a3:67:54:1d:26:
         78:e1:b9:7f:64:c7:61:bc:40:0d:4b:b0:7f:49:29:bc:38:48:
         43:87:a5:dd:a1:e6:b4:74:ce:58:44:24:30:c3:0d:f5:ab:da:
         8c:f9:25:0e:3e:e2:fe:5a:64:5f:32:d9:f5:15:6f:0c:0c:89:
         97:30:f6:6c:07:56:6e:54:81:4f:d3:22:1f:16:94:a0:2b:99:
         49:2f:2c:0f:c8:b7:b4:90:2f:60:01:54:9c:f9:34:c0:c6:e1:
         09:3f:93:d4:dd:a7:0b:34:bb:cb:4b:06:c3:5a:8c:fc:dc:85:
         4f:9d:a7:08:c3:22:98:06:b8:b9:d4:47:51:9c:36:43:f3:53:
         db:f5:d1:2f:4c:a6:97:c7:5a:f5:15:04:c4:94:a4:9e:95:4c:
         03:fd:5a:60:b8:4c:75:e8:02:74:e4:80:1c:8f:17:85:8a:a2:
         9e:b9:5d:74:4a:2e:7d:9f:5e:d8:40:6b:60:63:74:3f:dc:11:
         d4:f6:b4:86:6e:6b:83:8a:ff:57:cf:b4:41:1f:a3:66:b2:e2:
         00:6a:3a:33:dc:c3:3d:13:1d:37:97:d9:9c:d9:b5:9b:24:74:
         24:82:7a:f9:ca:51:b3:39:24:e3:90:f4:ff:4b:8e:be:f8:0f:
         ec:7a:16:55
WARNING: no nonce in response
Response Verify Failure
139766041024400:error:27069076:OCSP routines:OCSP_basic_verify:signer certificate not found:ocsp_vfy.c:92:
sendcloud.cer: good
        This Update: Dec  9 07:52:17 2023 GMT
        Next Update: Dec 16 07:52:16 2023 GMT

#966 异常流量监控

2023-12-09

突然来了大量客户请求,我们无法判断这些请求是正常的,还是客户被攻击所致。为了避免产生严重客损,我们需要识别到这些突然出现的流量高峰,并进行通知以及执行一些处理措施。

客户配置

比如:

request_limit = {
  rate: {
    // 客户配置
    3600: 1000, // 指定 API 1 小时之内的请求量上限
    86400: 8000, // 指定 API 1 天之内的请求量上限
  },
  rate_computed: {
    // 根据之前的请求情况,结合客户配置计算出来的值(不小于客户配置)
    3600: 1000,
    86400: 8000,
  },
  strategy: {
    times_to_notify: 1, // 连续触发几次之后通知
    times_to_suspend: 3, // 连续触发几次之后暂停
    times_to_intercept: 12, // 连续触发几次之后拦截请求
  },
};
  1. 默认给所有客户开启最近 1 小时和最近 24 小时的限制。
  2. 速率控制以小时为单位([1, 24]

定时检查

  1. 定时执行
 5 10 15 20 25 30
35 40 45 50 55  0
  1. 查询过去 1 天有请求的客户清单,根据系统参数(可能还有业务上其他条件)跳过忽略检查客户

  2. 逐个客户查询最近 5 分钟内的请求量,并将 5 分钟数据统计到小时内
    然后逐个时间区间配置,判断总请求量是否超出预设值

end_min = (minute // 5) * 5
start_min = end_minute - 5
  1. 如果流量异常,按照指定策略触发相关事件

客户处理

PS:这个机制的目的是处理异常流量,如果正常的请求量上来了应该调整客户配置。

  1. 警告页面给出客户过去的请求情况,标出告警时间段的数据
  2. 让客户选择处理方案:
  3. 忽略并继续
  4. 清空暂停队列并继续
    可以查看下载受影响的请求信息
  5. 暂不处理
  6. 往后 24 小时之内,下一次触发告警的数量,提供一个默认值(当前请求量的 1.5 倍)
    另一种思路:如果客户选择继续请求,不用填下一次告警的数量,后面计算的时候直接跳过过去一段时间(比如 6 小时)的请求量。

#965 Python UCS 相关问题

2023-12-08

老项目中有时可以看到报错:unichr() arg not in range(0x10000) (narrow Python build)
经过一番调研之后,发现是和 Python 内部 Unicode 编码有关。