2017年1月

订单号的生成规则

背景

防止订单Id号泄露每日流水,暴露商业机密;需要对订单Id号进行相应的处理,但是订单号的生成又需要满足以下条件

  1. 唯一性

  2. 语义性

  3. 考虑分库分表的情况能快速路由到相应的表

  4. 长度

大厂的生成策略

#平台rule来源
1大众点评时间戳+用户标识码+随机数大众点评订单系统分库分表实践
2美团团购单表自增Id * 100 + 买家Id后2位美团团购订单系统优化记
3淘宝发号器Id + 买家Id后4位淘宝在线交易数据演变

其他策略: 生产乱序码和真实的orderId关联

发号器

MTDDL——美团点评分布式数据访问层中间件

Leaf整体架构.png

id和code的转换(Base62)

id.png

版本加密解密的实现

package lib

import (
    "fmt"
    "strconv"
)

func Id2Code(id int, version byte) string {
    var code string = ""
    if version == '1' {
        number := id
        for {
            remain := number % 10000000
            str := Base62Encode(remain)
            code = str + code
            number = number / 10000000
            if number == 0 {
                break
            }
        }
        code = string(version) + code
    }
    return code
}

func Code2Id(code string) int {
    version := code[0]
    code = code[1:]
    if version == '1' {
        var buffer string = ""
        for i := len(code); i > 0; i -= 4 {
            start := i - 4
            if start < 0 {
                start = 0
            }
            seg := code[start:i]
            segId := Base62Decode(seg)
            // 大于7位非法
            if segId >= 100000000 {
                return 0
            }
            buffer += fmt.Sprintf("%07d", Base62Decode(seg))
        }
        result, _ := strconv.Atoi(buffer)
        return result
    }
    return 0
}

Base62加密解密的实现

package lib

import (
    "math"
    "bytes"
)

const dictLength = 62

var dict []byte = []byte{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'}

func Base62Encode(id int) string {
    result := make([]byte, 0)
    number := id
    for number > 0 {
        round := number / dictLength
        remain := number % dictLength
        result = append([]byte{dict[remain]}, result...)
        number = round
    }
    return string(result)
}

func Base62Decode(code string) int {
    var result int = 0
    codeLength := len(code)
    for i, c := range []byte(code) {
        result += bytes.IndexByte(dict, c) * int(math.Pow(dictLength, float64(codeLength - 1 - i)))
    }
    return result
}

建一个socks5代理集群

背景

由于在线工具中部分工具有翻墙的需求,而又没有找到一个稳定的翻墙方案(支持集群),在此背景下产生了这个项目。

架构图

arch.png

功能点

  1. 多账户支持, socks5密码验证

  2. 可用性,集群保证

  3. 流量计算,阈值限制

  4. Custom DNS, 可进行域名白名单的控制

  5. 安全,防止暴力破解socks5密码

表设计

CREATE TABLE `pre_accounts` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `accountname` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
  `password` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
  `used` int(10) unsigned NOT NULL DEFAULT '0',
  `max` int(10) unsigned NOT NULL DEFAULT '0',
  `created_at` datetime NOT NULL,
  `updated_at` datetime NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_accountname` (`accountname`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

Socks5的基础知识

这里就不重复写了,链接到 一个简单的Golang实现的Socks5 Proxy

功能点的实现

多账户支持, socks5密码验证

实现 github.com/armon/go-socks5/credentials.go CredentialStore 接口,进行校验;注意:每次proxy都会进行验证,如果请求量比较大,自行选择缓存方式

可用性,集群保证

每台socks5 server心跳上报到etcd,通过阈值,判断socks5的存活

流量计算,阈值限制

github.com/armon/go-socks5/request.go:358 修改这个函数的调用函数

安全,防止暴力破解socks5密码

使用 ratelimit 来保证,如果错误次数过多,则直接将IP加入黑名单;原理可以看之前写过的一篇文章 使用 redis 做限流