Go秒杀服务端优化

架构调整

旧架构
新架构

预备知识

cookie,session

  1. cookie和session的区别

  2. cookie和session的联系

  3. chrome抓包查看 // TODO

  4. 普通登录场景下cookie和session的配合使用流程

对称加密和非对称加密

参考附录1,2

  • 对称加密
  • 非对称加密,如: AES, RSA
  • AES的使用 // 参考aes.NewCipher(key)函数注释
    16,24,32位字符串的话,分别对应AES-128,AES-192,AES-256 加密方法

base64 // 附录3

Q: base64最开始的功能
A:

  1. 其实BASE64编码的初衷是为了满足电子邮件中不能直接使用非ASCII码字符的规定。
  2. 但是也有其他重要意义:所有的二进制文件,都可以因此转化为可打印的文本编码,使用文本软件进行编辑,并且对于数据流来说是一种简单的加密。

Q: 为什么要使用base64编码,有哪些情景需求?
A: 有2个场景不能传输二进制

  1. 电子邮件的历史原因
  2. 纯文本协议,不能传送二进制,可以借助base64编码后传输。因为传输一个纯文本协议,二进制中可能会出现被当做控制字符处理的部分。

服务端优化点

第10章 实现cookie验证(单节点)

  • 改进点
    突破session的限制(用cookie替代session实现验证)
  • 思路
    去掉session,在cookie中存入处理过的用户关键信息,处理方法:
    step1. 公钥加密: 把uid使用私钥加密。// 私钥16位,加密结果128位。
    step2. base64编码: // 因为http的cookie中不能存放二进制数据,只能存放ascll字符。
  • 相关代码
1. user_controller.go 
登录接口的后端实现。本来登录成功后往只cookie写入了uid字段,为了防止uid被篡改,往cookie中写入了一个验证uid是否被篡改的sign字段。
2. auth.go
验证用户是否登录的中间件。验证uid是否存在。代码不变。

第11章 服务端性能优化之分布式验证实现

  • 分布式架构
    从原理部署在一个机器的一个进程中,改为部署到多个机器的不同进程中。
    拆分后的构成: 权限验证(水平扩容)+数量控制(水平扩容)+web服务器+消息队列+mysql

  • 权限验证: 使用拦截器实现
  1. 拦截器(传递函数) // 装饰器模式的go实现 // 附录4
    功能: 验证cookie中的用户信息是否可信(没有被篡改)。
    思路: 通过验证cookie中的uid和sign解密结果是否匹配来验证是否被篡改。
    代码: filter.go(mian包) + validate.go单独部署. 验证代码从代码中提取处理单独部署
    扩展: OAuth协议
  • Q: 我们这里为什么需要一致性hash算法
    A: 一致性hash算法的主要应用场景是 分布式存储,分布式缓存, 负载均衡。我们这里属于分布式缓存这类应用,注意我们并没有用redis,而是用的内存缓存,每个机器存放不同的用户信息缓存。
    数据存储需求: 每台机器(进程)存放uid及其请求时间。比如uid=1,2,3缓存在机器1,uid=4,5,6缓存在机器2,那么uid=1请求机器2时就会去机器1的缓存上去拿数据(内网)

  • 一致性Hash算法 //附录5
    Q: 一致性Hash算法主要作用
    A: 一致性哈希是为了解决节点可扩展的问题。
  1. 普通的hash算法,在节点增删时会产生缓存雪崩(同一时间大量缓存同时失效)。
  2. 一致性hash算法把这种缓存失效的情况,缩小为增删节点与相邻旧节点之间的区域。
  • 一致性Hash算法代码实现 / /TODO
    关键点:
  1. IP=Local IP,则读取本机缓存。否则IP != Local IP, 则本机充当代理去另一台机器获取数据。(内网传输)
  2. 关键结构体
//创建结构体保存一致性hash信息
type Consistent struct {
    //hash环,key为哈希值,值存放节点的信息 // TODO k: 请求key或节点的hash值  v: 真实节点或虚拟节点信息(如果有2个真实节点,每个节点20个虚拟节点,那么一共有20个v需要存储,环上其他位置并不需要存放数据)
    circle map[uint32]string
    //已经排序的节点hash切片 // TODO 排好序的虚拟节点hash值,40个元素,对应2个v。
    sortedHashes units
    //虚拟节点个数,用来增加hash的平衡性
    VirtualNode int
    //map 读写锁
    sync.RWMutex
}
  • validate.go中应用一致性hash算法
//设置集群地址,最好内网IP
var hostArray = []string{"192.168.0.1", "192.168.0.2"}
var localHost = "192.168.0.1"

// 创建Consistent结构体
hashConsistent = common.NewConsistent()
// 添加服务器节点
for _, v := range hostArray {
    hashConsistent.Add(v)
}

// 作为缓存的结构体
type AccessControl struct {
    //用来存放用户想要存放的信息
    sourcesArray map[int]interface{}
    sync.RWMutex
}
  • 使用了一致性hash算法后的使用流程
  1. 阿里云SLB来负责负载均衡到不同的云服务器ECS上
    是随机选择一个ECS来处理请求的。

第12章 服务端性能优化解决超卖&引入消息队列

  • wrk
    wrk参数和输出结果

  • 修改fronted/web/controllers/product_controller.GetOrder方法
    原有方法对数据访问压力很大,每调用一次创建一个订单就要访问几次数据库。
    新方法:

  • rabbitmq的消费模式配置
  1. 消息者流量控制: 每次只发送一个消息给消费者。配置项: channel.Qos
  2. 确认模式: channel.Consume(autoAck:false)
  • rabbimq相关代码实现
    product_controller.go: 在下单接口GetOrder()中生产消息(消息结构体有2个字段: userId+productId)
    consumer.go :消息消费,流量控制(1次取1个消息),手动确认后删除消息 (消费成功,调用msg.Ack(false)使得rabbitmq删除该消息 // TODO 消费失败应该怎么处理)

  • 当前代码存在的问题
    没有事务(商品数量减1+生成订单)

参考

  1. 对称加密、非对称加密、RSA(总结)
  2. 对称加密和非对称加密结合使用的例子:https
  3. 为什么要使用Base64及其编码原理和实现
  4. 装饰器本质上允许您包装现有功能并在开始或结尾处添加您自己的自定义功能
    扩展: 装饰器绝对不是处理 REST API 保护的正确方法,我建议您使用 JWT 或 OAuth2 来实现这一目标!OAuth2.0协议
    扩展: 明白了,原来Go web框架中的中间件都是这样实现的
  5. 面试必备:什么是一致性Hash算法?
    扩展: Redis与Mysql双写一致性方案解析

猜你喜欢

转载自www.cnblogs.com/yudidi/p/12518529.html