base業務框架開發手冊
base 業務框架使用手冊
開發手冊
本專案是對當前使用 base 業務框架的開發人員,提供一份完整的開發手冊,方便開發人員使用。
> 希望本文件能夠給大家帶來方便。同時業務框架在不斷更新中..., 如果文件沒有及時更新,也可以 email 或者提相關的 issue
編寫微服務
採用demo專案,作為初始專案,然後修改為自己的專案。
總體推薦一個新建立的產品,專案結構目錄推薦以下呈現方式:
chen:demo chenchao$ ls
README.md common glide.lock token vendor
address gateway glide.yaml user
chen:demo chenchao$ ls gateway/
gateway main.go r_address.go r_errors.go r_me.go r_token.go schema.go
chen:demo chenchao$ ls address/
controllers main.go models schema.go
chen:demo chenchao$ ls common/
consts rpc types utils
- 對於專案目錄來說,比如:demo 是由四個微服務加上一個
common
目錄構成,其他是相關依賴包。一個專案必然有一個閘道器服務,對外暴露 API 的; - 對於 gateway 閘道器服務,它是直接轉發 web 的請求訪問,同時可能會做一些身份登入校驗,校驗 token 的有效性;以及可以做鑑權和流控;不過後者可以在上 istio 後,流控就可以業務不關心了
- 其他微服務程式碼結構,則是由
main.go, schema.go, types, controllers, models
五個檔案或者目錄構成; -
common
目錄,主要用於一些 schema 的資源定義,公共元件,錯誤碼註冊,微服務埠和名稱定義、rpc 微服務呼叫和常量定義。
> 對於上面的第 3 點需要說明的是,types 目錄可能也不需要,它主要定義 schema 的資源物件,如果 gateway 需要與微服務共用,就需要寫入到 common 目錄中
> 這裡說明一點, 理論上微服務容器化後,所有微服務埠都可以是相同的,但是我們為了相容非容器化,所以微服務埠是列舉型的,從 8081 開始進行 iota。
環境變數
名稱 | 值 | 描述 |
---|---|---|
APP_ZK | 服務地址 | 配置中心地址,預設值:127.0.0.1:2181 |
APP_NAME | 專案名稱 | 產品名稱 |
APP_LOG | log 絕對路徑 | 預設:/var/log/app |
APP_ENV | 部署環境 | 本地環境、開發、測試和生產, 預設:developer |
APP_VERSION | 產品版本 | 預設: v1.0 |
APP_TRACER_AGENT | jaeger agent 地址 | 預設值: 0.0.0.0:6831 |
graphql 常用方法
graphql 是 facebook 發明的,對外提供 API 的兩種方式:RESTful 與 graphql,各有優劣;graphql 最大的優勢所見即所得,協議即介面。
下面給出的常用方法通常是 rpc 呼叫後,需要進行 graphql 變數值與 golang 變數值的相互轉化。
// FixTypeFromGoToGraphql方法
// @params0: rpc返回的資料
// @params1: rpc返回的資料graphql schema資源物件定義
// 含義:把graphql資源物件資料轉化為golang變數值
// 比如:graphql中的列舉Enum定義,需要轉化為golang中的列舉定義
// 比如:graphql中的資源列表定義,需要轉化為golang中的列表定義,因為在graphql中的列表都是interface型別的,需要轉化為golang中的資源定義的已知型別
// ...
**func FixTypeFromGoToGraphql(v interface{}, argType graphql.Input) (result interface{})**
// FixTypeFromGraphqlToGo方法, 與上面的方法作用相反
// @param0:
func FixTypeFromGraphqlToGo(data interface{}, t graphql.Output) interface{}
這裡提供了幾個有關這兩個方法的單元測試:
cd $GOPATH/src/github.com/microsvs/base
go test -cover=true -run TestFixTypeFromGraphqlToGoSimple
# 上面單元測試返回結果:
10,20,30
go test -cover=true -run TestFixTypeFromGraphqlToGoComplex
# 上面單元測返回結果:
map[created_at:2019-11-24 03:29:08 +0800 CST name:kim age:16 vehicle:10 workers:[map[company:alibaba position:30] map[company:tencent position:40]] updated_at:2019-11-24 03:29:08 +0800 CST]
通過上面兩個單元測試,我們可以明白 FixTypeFromGraphqlToGo 是用來把 graphql 資料轉化為 golang 識別的資料型別;
注意,在 rpc 呼叫過程中,我們使用的還是 graphql 資料模型的通訊,所以需要在請求資料和響應資料前後進行相關型別的資料轉換,比如:列舉型別、時間型別和其他不相同形式的資料轉換
cd $GOPATH/src/github.com/microsvs/base
go test -cover=true -run TestFixTypeFromGoToGraphqlSimple
# 上面單元測試返回結果
CEO
go test -cover=true -run TestFixTypeFromGoToGraphqlComplex
# 上面單元測試返回結果:
map[created_at:2018-12-27T14:12:49.523371245+08:00 updated_at:2018-12-27T14:12:49.523371228+08:00 name:"kim" age:16 vehicle:Bike workers:[map[company:"alibaba" position:CEO] map[company:"tencent" position:CTO]]]
通過上面兩個單元測試,我們可以明白 FixTypeFromGoToGraphql 方法是用來把 go 型別資料轉化為 graphql 資料;
注意 graphql 型別資料與 golang 型別資料的相互轉化後的資料對比,看看哪裡資料有變化
// 方法用於隱藏不想展示給呼叫方的欄位資料,比如,有些密碼、token 等隱私資料。 > func HideGLFields(obj *graphql.Object, v ...string) *graphql.Object
// 方法用於 gateway 直接呼叫後端,複用請求,指定目標服務 > func RedirectRequest(p graphql.ResolveParams, target rpc.FGService) (interface{}, error)
// 修改 gateway 接收的請求,然後轉發給指定目標服務 > func RedirectRequestEx( p graphql.ResolveParams, exArgs map[string] interface{}, excommon map[string] graphql.Input, targetService rpc.FGService, targetObj interface{}) (interface{}, error)
該方法使用場景在於,當我們需要組織後端服務收集到的資料,然後再轉發到其他微服務時,就需要構建一個請求
> base.RedirectRequestEx(
"QUERY/MUATION",
p,
map[string]interface{}{"user_id": user.ID},
map[string]graphql.Input{"user_id": graphql.String},
base.FGSUser,
"beat_map_type",
nil)
// GLObjectFields 獲取 graphql.Object 的所有欄位, 作為 graphql Query/Mutation 的返回值 > func GLObjectFields(obj *graphql.Object) string
配置中心
配置中心,主要是對專案需要用到的初始化或者環境配置,都可以放到配置中心;包括但不限於:專案名、版本號、服務部署環境、cache 配置、db 配置、mq 配置、dns 配置和其他引數配置
每個微服務都單獨配置各項所用到的配置,該業務框架認為各個微服務配置都是不一樣的。
對於儲存配置,包括 cache 和 db,都是主從配置 master 和 slave,一主多從的配置方式;可以不填寫 slave 配置, 則 slave 自動共用 master 配置
slave 可以指定多個,配置由,
逗號分隔 slave。
目前環境氛圍四個環境:developer 本地開發環境,dev-開發環境、test-QA 環境、prod-生產環境
快取配置
// 快取,比如 cache,可以指定密碼和沒有密碼兩種方式, 當不指定 redis 密碼時,則@
非必空
> /demo/v1.0/dev/cache/user=master=redis://auth-pass@127.0.0.1:6379
> /demo/v1.0/dev/cache/user=master=redis://auth-pass@192.168.1.100:6379,redis://auth-pass@192.168.1.101:6379,192.168.1.102:6379
db
// db 儲存,比如 mysql > /demo/v1.0/dev/db/user=master=username:password@tcp(192.168.1.101:3306)/demo?charset=utf8&parseTime=True&loc=Local
// mongodb > /demo/v1.0/dev/mongo/user=master=username:password@192.168.1.101:3000/demo
訊息佇列
> /demo/v1.0/dev/mq/user=maste=amqp://username:password@192.168.1.101:3001/queue?heartbeat=15
dns
> /demo/v1.0/dev/dns=gateway.api.xhj.com=192.168.1.101:8081
注意 dns 的 key 為 xxx.api.xhj.com, xhj
是表示 base 業務框架作者的江湖稱呼:小黃雞;value 可以為 service 的高可用配置, 比如: VIP
引數配置
專案除了通用配置外,可能還會遇到其他引數配置,比如設定 feature 開關等, oss 路徑,sms 配置等
配置儲存
- 當微服務第一次從配置中心獲取,需要一次冷載入,可以設定在微服務啟動時,也就是程式的 init 方法中,初始化將要使用的儲存或者其他配置;
- 該業務框架使用了本地快取,儲存配置,當微服務再次獲取配置時,則直接在本地快取中獲取的。
- 同時第一次從配置中心獲取配置後,就開始了監聽配置中心的 key,並更新到本地快取中。如果是儲存相關包括 db、cache、mq、config 等會監聽到配置變化後,會自動重連各個儲存 server。則對業務是無感知的
日誌模組
base 業務框架的日誌模組,是採用的本地落日誌。這裡講下我做過的日誌優化相關工作。
現象: 因為我在使用該base業務框架進行業務上線後,我們使用的k8s叢集,微服務經常由於記憶體佔用過大(100Mi),導致被殺,然後又漲又殺。我發現微服務本身只佔用了20M的記憶體。
原因:k8s監控pod,不僅僅是微服務程式本身的記憶體佔用;它是對container記憶體資源佔用的總和,我監控container,top發現cache/buffer不斷升高,後來查了文件,發現k8s是統計的cgroup記憶體資源佔用,所以導致的container被殺後,k8s又重新排程。
解決思路:在本地落日誌時,作業系統認為落日誌的檔案會立即讀取,所以作業系統會在日誌落到磁碟之前,會儲存到page cache中,這樣page/cache數值越來越大。
解決方法:在落日誌時,我們業務框架使用bytes.buffer進行4kb的快取,每次寫入4kb,也就是一頁的資料量,這種寫入的方式是直接IO方式,這樣就不會在page cache進行日誌快取了。os.DIRECT_IO,又因為各個平臺底層架構不同,對於macos用的flag與linux用的flag不同等原因,最後就是用的一個簡單封裝庫directio。
效果:優化後,效果非常理想,現線上上業務一直在跑,但是記憶體佔用基本上穩定不變。
後話:cache/buffer是可以重複利用的記憶體,只是對於k8s叢集平臺,它是監控的cgroup資源,所以遇到了這個問題。
日誌使用方法
日誌的使用,不需要初始化,呼叫即初始化。
提供的方法有: log.ErrorRaw
, log.Debug
, log.Info
等方法可以使用。
日誌寫入到的檔案路徑:環境變數 ${APP_LOG},預設/var/log/app 目錄下,再根據微服務名稱進行日誌分資料夾。日誌目錄層次可以自己調整,目前業務預設支援的日誌路徑:
/var/log/app/${service name}/YYYYMM/DD/${service name}_YYYYMMDD.log
> 備註:當日志落地到本地後,你可以通過啟動一個 agent 日誌收集服務,比如 fluentd,ELK。
db 模組
db 模組,我們使用了一個 DAL,資料訪問層,遮蔽了底層 db 儲存的細節,底層支援 PostgreSQL, MySQL, SQLite, MSSQL, QL and MongoDB;
大家可以檢視這個 DAL 資料訪問層的 github 庫,upper/db, 自認為比其他的 orm 好用很多。
對於資料庫的初始化,我們可以微服務啟動時,在 init 方法中進行儲存 client 的初始化連線 > db.InitDB(rpc.FGSUser)
然後在業務需要使用的時候,比如:更新、新增和刪除操作
// 從 slave 獲取一個 db 連線 > db.SlaveDB(rpc.FGSUser) // 從 master 獲取一個 db 連線 > db.MasterDB(rpc.FGSUser)
快取模組
對於 redis 快取,不需要業務端做相關的連線釋放動作。並提供了對快取的 interface。
type Connection interface {
Set(key string, value interface{}) error
Get(key string) (interface{}, error)
Del(key string) error
Expire(key string, sec int) error
Exist(key string) (bool, error)
TTL(key string) (time.Duration, error)
Close() error
ComplexCmd(cmd string, values ...interface{}) (interface{}, error)
}
// 這個interface前6個方法基本上滿足了90%的需求,最後一個方法ComplexCmd則是對複雜需求的快取操作命令。
初始化和獲取快取連線的方法,同 db 操作
訊息佇列模組
提供了兩個初始化和監聽消費佇列訊息的 API。
// 初始化訊息佇列連線 > func InitMQ(service rpc.FGService)
// 使用預設的方法監聽消費佇列產生的訊息 > func ConsumeDefault(service rpc.FGService, queue string) <-chan amqp.Delivery
// 同時提供了一個多配置監聽消費佇列產生的訊息 > func Consume(service rpc.FGService, queue, consumer string, autoAck, exclusive, noLocal, noWait bool, args amqp.Table) <-chan amqp.Delivery
> 訊息佇列 client 具有重連機制
錯誤碼
base 業務框架本身已經提供了一些錯誤碼,我們可以通過 gateway 的 errors 查詢介面,去檢視這些錯誤碼及其含義。
比如:系統錯誤,客戶端請求錯誤。可以在$GOPATH/src/github.com/microsvs/base/pkg/errors/const.go
檔案中檢視。
同時如果業務微服務需要其他錯誤碼,則需要進行錯誤碼的註冊, 通過呼叫pkg/errors/register.go
檔案中的 Register 方法進行錯誤碼註冊。
// 業務微服務錯誤碼註冊 > func Register(errMap map[FGErrorCode] string) error
服務註冊
base 業務框架本身也提供了一些服務,包括:閘道器、簽名、流控、使用者、token、地址和圖片共 7 個服務。如果還需要其他微服務,則通過 RegisterService 方法註冊。
檔案路徑:$GOPATH/src/github.com/microsvs/base/pkg/rpc/service.go
> func RegisterService(service FGService, serviceName string) error
rpc 呼叫
當微服務之間需要進行 rpc 呼叫時,框架提供了一個 API 進行跨服務呼叫。
// dns 引數值可以通過 base.Service2Url(service) 獲取 > func CallService(ctx context.Context, dns string, data string) (map[string] interface{}, error)
常用方法
在$GOPATH/src/github.com/microsvs/demo/common/utils
目錄下提供了一些常用方法。比如:
- http 呼叫, HttpPostBody 和 HttpPostJson API;
- 提供了阿里雲 oss 儲存封裝好的 API;
- 對於所有 graphql 請求的引數必填校驗 API,CheckAndAssignParams,該方法的實用性非常高
- ArrayToString 方法,slice 型別的資料,按照指定格式進行字串化;
- 生成 4 位的簡訊驗證碼,GenerateVerifyCode
- GenerateUUID 方法,生成唯一的 uuid
- 還提供了一個獲取時間變數 timer.Now,由一個 goroutine 進行託管, 這樣系統不用頻繁去呼叫時間
// 必填引數校驗,並把資料返回
if err = utils.CheckAndAssignParams(p.Args, map[string]interface{}{
"sale_order_id": &saleOrderId,
"vehicle_no": &vehicleNo,
}); err != nil {
return false, err
}
身份攜帶
最後一個需要說明的小點:
> 有身份的使用者在跨微服務呼叫時,都會把自身的身份帶到 context 中,這樣直接從 context 中獲取 token、mobile 和其他相關資訊是非常方便的。
使用方式如下所示:
if _, ok = p.Context.Value(rpc.KeyUser).(*itypes.User); !ok {
log.ErrorRaw("[GetAdOnlineLaunchStatis] get user from context failed.")
return nil, errors.FGEInvalidToken
}
通過上面的這種方式,我們就可以校驗使用者是否有登入態,context 目前支援的 key 有
KeyRPCID
KeyService
KeyRawRequest
KeyMobile
KeyUser
KeyConsoleInfo
KeyProtocalType
- 加微信實戰群請加微信(註明:實戰群):gocnio
相關文章
- MaxPHP(原Yao框架)完全開發手冊PHP框架
- 針對microsvs/base業務框架的DEMO演示ROS框架
- EDP .Net開發框架--業務模型框架模型
- 阿里java開發手冊阿里Java
- matrixone/ematrix開發手冊
- mpaas-springboot-base操作手冊Spring Boot
- [開發文件]bootstrap中文手冊boot
- DATA CARTRIDGE開發手冊
- FFMpeg SDK 開發手冊 1
- FFMpeg SDK 開發手冊(2)
- FFMpeg SDK 開發手冊(3)
- 物件相關開發手冊物件
- 開心網外掛開發手冊
- Web 開發手冊——PHP 開發環境搭建WebPHP開發環境
- 阿里Java開發手冊思考(三)阿里Java
- 阿里Java開發手冊思考(一)阿里Java
- 阿里Java開發手冊思考(二)阿里Java
- Java開發手冊精華總結Java
- 阿里Java開發手冊思考(五)阿里Java
- 阿里Java開發手冊思考(四)阿里Java
- Web前端開發規範手冊Web前端
- Xiuno 開發手冊正式釋出。
- ZooKeeper開發手冊中文翻譯
- XML資料庫開發手冊XML資料庫
- 安卓開發開發規範手冊V1.0安卓
- 安卓開發開發規範手冊 V1.0安卓
- wxpython - 快速開發封裝手冊Python封裝
- 阿里巴巴Java開發手冊阿里Java
- Java Web程式開發參考手冊JavaWeb
- 物件相關開發手冊總結物件
- DATA CARTRIDGE開發手冊總結
- ColyseusJS 輕量級多人遊戲伺服器開發框架 - 中文手冊(上)JS遊戲伺服器框架
- ColyseusJS 輕量級多人遊戲伺服器開發框架 - 中文手冊(中)JS遊戲伺服器框架
- ColyseusJS 輕量級多人遊戲伺服器開發框架 - 中文手冊(下)JS遊戲伺服器框架
- [譯]SearchFragment --Android TV 開發手冊十二FragmentAndroid
- XML資料庫開發手冊總結XML資料庫
- 安全檔案和大物件開發手冊物件
- Koa框架教程,Koa框架開發指南,Koa框架中文使用手冊,Koa框架中文文件框架