標題起的是有點大,不過還好本片文章主要也是使用 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如下:
上面總體介紹了下當咱們的接口耗時較長的時候的通常處理方案,固然具體問題還得具體分析,因此當出現接口反應慢的狀況的時候,咱們應該具體分析接口反應慢的具體緣由,方可對症下藥!