标题起的是有点大,不过还好本片文章主要也是使用 Go 来优化 HTTP 服务的,也算打个擦边球吧~程序员
特征数据暴增,致使获取一个城市下全部的特征的接口延时高,下面是监控上看到的接口响应耗时,最慢的时候接口响应时间能达到 5s 多。 golang
代码优化思路:缓存
1,使用缓存。安全
1.1为何使用内存,而不是 Redis?bash
分析业务需求,当前须要存储起来的数据是ObjectId,ObjectId 是一个长度为14左右的字符串,咱们假设平均下来ObjectId是长度为16的字符串,这样算下来就是每一个 ObjectId 占用的内存大小是2个字节,当前业务须要存储的ObjectId大概是30万条,这样算下来当前业务须要存储的 ObjectId 要占用的内存在 0.5M 彻底能够在内存中进行操做。相比于使用 Redis 来讲没有网络开销,效率更高。网络
1.2 缓存初始化:当服务启动时,本地缓存初始化为空。多线程
1.3 关于缓存版本的概念。并发
缓存版本是离线特征生产任务更新后将数据版本更新到 DB 中。函数
下面三种方案都是基于内存存储 ObjectId 数据,在内存更新的时候策略有所不一样。高并发
2.1 缓存更新
使用主动更新缓存的方式,建立定时任务,每间隔1分钟查一次 DB 的数据版本,若更新则更新缓存中的数据。
2.2 缺点
单独启动一个缓存更新线程,代码很差维护,也会有定时任务线程挂掉的状况,不易发现。还有就是须要提早把相关参数配置到代码中或者引入配置中心,维护成本较高。
3.1 缓存更新
采用被动触发的缓存更新策略,由接口调用触发。请求进来后检测当前缓存中的数据的版本与 DB 中的数据版本是否一致,若版本更新,则从新读取当前请求对应城市的全部数据到缓存中,并将更新后的数据返回给调用方。
3.2 缺点
因为是被动触发的是同步更新缓存的,容易形成接口调用时若是正好赶上版本更新,须要更新数据到内存中,会出现偶现的毛刺。
3.3 业务执行时序图
4.1,缓存更新
采用被动更新缓存的策略,由接口调用方触发。若当前缓存中有数据则直接返回缓存中的数据,而后检测当前缓存中的数据的版本与 DB 中的数据版本是否一致,若版本更新,则从新读取当前请求对应城市的全部feature数据到缓存中,反之结束缓存更新逻辑。
4.2 业务执行时序图
Go语言最大的特点就是从语言层面支持并发(Goroutine),Goroutine是Go中最基本的执行单元。事实上每个Go程序至少有一个Goroutine:主Goroutine。当程序启动时,它会自动建立。
为了更好理解Goroutine,现讲一下线程和协程的概念:
线程(Thread):有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程本身不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的所有资源。
线程拥有本身独立的栈和共享的堆,共享堆,不共享栈,线程的切换通常也由操做系统调度。
协程(coroutine):又称微线程与子例程(或者称为函数)同样,协程(coroutine)也是一种程序组件。相对子例程而言,协程更为通常和灵活,但在实践中使用没有子例程那样普遍。
和线程相似,共享堆,不共享栈,协程的切换通常由程序员在代码中显式控制。它避免了上下文切换的额外耗费,兼顾了多线程的优势,简化了高并发程序的复杂。
很显然,咱们能够用锁机制解决 Map 的并发读写问题。咱们将上面的map结构改为以下:
// M
type M struct {
Map map[string]string
lock sync.RWMutex // 加锁
}
// Set ...
func (m *M) Set(key, value string) {
m.lock.Lock()
defer m.lock.Unlock()
m.Map[key] = value
}
// Get ...
func (m *M) Get(key string) string {
return m.Map[key]
}
复制代码
在上面的代码中,咱们引入了锁机制操做,从而保证了map在多个goroutine中的安全。
这块主要是由于代码中存在太多的 if/else ,故采用策略模式来优化咱们的代码结构。这里先放上一篇网上找到的文章,以后有时间再单独出一篇相关文章吧。优化后的代码相较于以前代码量少了 50% ,更加清晰与便于维护。下面是优化的代码上线后的效果,请求耗时都在100ms如下:
上面总体介绍了下当咱们的接口耗时较长的时候的通常处理方案,固然具体问题还得具体分析,因此当出现接口反应慢的状况的时候,咱们应该具体分析接口反应慢的具体缘由,方可对症下药!