青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

   C++ 技術中心

   :: 首頁 :: 聯系 ::  :: 管理
  160 Posts :: 0 Stories :: 87 Comments :: 0 Trackbacks

公告

鄭重聲明:本BLOG所發表的原創文章,作者保留一切權利。必須經過作者本人同意后方可轉載,并注名作者(天空)和出處(CppBlog.com)。作者Email:coder@luckcoder.com

留言簿(27)

搜索

  •  

最新隨筆

最新評論

評論排行榜

origin 游戲服務器引擎簡介
==================
origin 是一個由 Go 語言(golang)編寫的分布式開源游戲服務器引擎。origin適用于各類游戲服務器的開發,包括 H5(HTML5)游戲服務器。
origin 解決的問題:
* origin總體設計如go語言設計一樣,總是盡可能的提供簡潔和易用的模式,快速開發。
* 能夠根據業務需求快速并靈活的制定服務器架構。
* 利用多核優勢,將不同的service配置到不同的node,并能高效的協同工作。
* 將整個引擎抽象三大對象,node,service,module。通過統一的組合模型管理游戲中各功能模塊的關系。
* 有豐富并健壯的工具庫。
Hello world!
---------------
下面我們來一步步的建立origin服務器,先下載[origin引擎](https://github.com/duanhf2012/origin "origin引擎"),或者使用如下命令:
```go
go get -v -u  github.com/duanhf2012/origin
```
于是下載到GOPATH環境目錄中,在src中加入main.go,內容如下:
```go
package main
import (
"github.com/duanhf2012/origin/node"
)
func main() {
node.Start()
}
```
一個origin進程需要創建一個node對象,Start開始運行。您也可以直接下載origin引擎示例:
```
go get -v -u github.com/duanhf2012/originserver
```
本文所有的說明都是基于該示例為主。
origin引擎三大對象關系
---------------
* Node:   可以認為每一個Node代表著一個origin進程
* Service:一個獨立的服務可以認為是一個大的功能模塊,他是Node的子集,創建完成并安裝Node對象中。服務可以支持對外部RPC等功能。
* Module: 這是origin最小對象單元,強烈建議所有的業務模塊都劃分成各個小的Module組合,origin引擎將監控所有服務與Module運行狀態,例如可以監控它們的慢處理和死循環函數。Module可以建立樹狀關系。Service本身也是Module的類型。
origin集群核心配置文件在config的cluster目錄下,在cluster下有子網目錄,如github.com/duanhf2012/originserver的config/cluster目錄下有subnet目錄,表示子網名為subnet,可以新加多個子網的目錄配置。子網與子網間是隔離的,后續將支持子網間通信規則,origin集群配置以子網的模式配置,在每個子網下配置多個Node服務器,子網在應對復雜的系統時可以應用到各個子系統,方便每個子系統的隔離。在示例的subnet目錄中有cluster.json與service.json配置:
cluster.json如下:
---------------
```
{
    "NodeList":[
        {
          "NodeId": 1,
          "ListenAddr":"127.0.0.1:8001",
          "NodeName": "Node_Test1",
  "remark":"http://以_打頭的,表示只在本機進程,不對整個子網開發",
          "ServiceList": ["TestService1","TestService2","TestServiceCall","GateService","_TcpService","HttpService","WSService"]
        },
{
          "NodeId": 2,
          "ListenAddr":"127.0.0.1:8002",
          "NodeName": "Node_Test1",
  "remark":"http://以_打頭的,表示只在本機進程,不對整個子網開發",
          "ServiceList": ["TestService1","TestService2","TestServiceCall","GateService","TcpService","HttpService","WSService"]
        }
    ]
```
---------------
以上配置了兩個結點服務器程序:
* NodeId: 表示origin程序的結點Id標識,不允許重復。
* ListenAddr:Rpc通信服務的監聽地址
* NodeName:結點名稱
* remark:備注,可選項
* ServiceList:該Node將安裝的服務列表
---------------
在啟動程序命令program start nodeid=1中nodeid就是根據該配置裝載服務。
service.json如下:
---------------
```
{
  "Service":{
  "HttpService":{
"ListenAddr":"0.0.0.0:9402",
"ReadTimeout":10000,
"WriteTimeout":10000,
"ProcessTimeout":10000,
"CAFile":[
{
"Certfile":"",
"Keyfile":""
}
]
  },
  "TcpService":{
"ListenAddr":"0.0.0.0:9030",
"MaxConnNum":3000,
"PendingWriteNum":10000,
"LittleEndian":false,
"MinMsgLen":4,
"MaxMsgLen":65535
  },
  "WSService":{
"ListenAddr":"0.0.0.0:9031",
"MaxConnNum":3000,
"PendingWriteNum":10000,
"MaxMsgLen":65535
  }  
  },
  "NodeService":[
   {
      "NodeId":1,
  "TcpService":{
"ListenAddr":"0.0.0.0:9830",
"MaxConnNum":3000,
"PendingWriteNum":10000,
"LittleEndian":false,
"MinMsgLen":4,
"MaxMsgLen":65535
  },
  "WSService":{
"ListenAddr":"0.0.0.0:9031",
"MaxConnNum":3000,
"PendingWriteNum":10000,
"MaxMsgLen":65535
  }  
   },
   {
      "NodeId":2,
  "TcpService":{
"ListenAddr":"0.0.0.0:9030",
"MaxConnNum":3000,
"PendingWriteNum":10000,
"LittleEndian":false,
"MinMsgLen":4,
"MaxMsgLen":65535
  },
  "WSService":{
"ListenAddr":"0.0.0.0:9031",
"MaxConnNum":3000,
"PendingWriteNum":10000,
"MaxMsgLen":65535
  }  
   }
  ]
 
}
```
---------------
以上配置分為兩個部分:Service與NodeService,NodeService中配置的對應結點中服務的配置,如果啟動程序中根據nodeid查找該域的對應的服務,如果找不到時,從Service公共部分查找。
**HttpService配置**
* ListenAddr:Http監聽地址
* ReadTimeout:讀網絡超時毫秒
* WriteTimeout:寫網絡超時毫秒
* ProcessTimeout: 處理超時毫秒
* CAFile: 證書文件,如果您的服務器通過web服務器代理配置https可以忽略該配置
**TcpService配置**
* ListenAddr: 監聽地址
* MaxConnNum: 允許最大連接數
* PendingWriteNum:發送網絡隊列最大數量
* LittleEndian:是否小端
* MinMsgLen:包最小長度
* MaxMsgLen:包最大長度
**WSService配置**
* ListenAddr: 監聽地址
* MaxConnNum: 允許最大連接數
* PendingWriteNum:發送網絡隊列最大數量
* MaxMsgLen:包最大長度
---------------
第一章:origin基礎:
---------------
查看github.com/duanhf2012/originserver中的simple_service中新建兩個服務,分別是TestService1.go與CTestService2.go。
simple_service/TestService1.go如下:
```
package simple_service
import (
"github.com/duanhf2012/origin/node"
"github.com/duanhf2012/origin/service"
)
//模塊加載時自動安裝TestService1服務
func init(){
node.Setup(&TestService1{})
}
//新建自定義服務TestService1
type TestService1 struct {
//所有的自定義服務必需加入service.Service基服務
//那么該自定義服務將有各種功能特性
//例如: Rpc,事件驅動,定時器等
service.Service
}
//服務初始化函數,在安裝服務時,服務將自動調用OnInit函數
func (slf *TestService1) OnInit() error {
return nil
}
```
simple_service/TestService2.go如下:
```
import (
"github.com/duanhf2012/origin/node"
"github.com/duanhf2012/origin/service"
)
func init(){
node.Setup(&TestService2{})
}
type TestService2 struct {
service.Service
}
func (slf *TestService2) OnInit() error {
return nil
}
```
* main.go運行代碼
```go
package main
import (
"github.com/duanhf2012/origin/node"
//導入simple_service模塊
_"orginserver/simple_service"
)
func main(){
node.Start()
}
```
* config/cluster/subnet/cluster.json如下:
```
{
    "NodeList":[
        {
          "NodeId": 1,
          "ListenAddr":"127.0.0.1:8001",
          "NodeName": "Node_Test1",
  "remark":"http://以_打頭的,表示只在本機進程,不對整個子網開發",
          "ServiceList": ["TestService1","TestService2"]
        }
    ]
}
```
編譯后運行結果如下:
```
#originserver start nodeid=1
TestService1 OnInit.
TestService2 OnInit.
```
第二章:Service中常用功能:
---------------
定時器:
---------------
在開發中最常用的功能有定時任務,origin提供兩種定時方式:
一種AfterFunc函數,可以間隔一定時間觸發回調,參照simple_service/TestService2.go,實現如下:
```
func (slf *TestService2) OnInit() error {
fmt.Printf("TestService2 OnInit.\n")
slf.AfterFunc(time.Second*1,slf.OnSecondTick)
return nil
}
func (slf *TestService2) OnSecondTick(){
fmt.Printf("tick.\n")
slf.AfterFunc(time.Second*1,slf.OnSecondTick)
}
```
此時日志可以看到每隔1秒鐘會print一次"tick.",如果下次還需要觸發,需要重新設置定時器
另一種方式是類似Linux系統的crontab命令,使用如下:
```
func (slf *TestService2) OnInit() error {
fmt.Printf("TestService2 OnInit.\n")
//crontab模式定時觸發
//NewCronExpr的參數分別代表:Seconds Minutes Hours DayOfMonth Month DayOfWeek
//以下為每換分鐘時觸發
cron,_:=timer.NewCronExpr("0 * * * * *")
slf.CronFunc(cron,slf.OnCron)
return nil
}
func (slf *TestService2) OnCron(){
fmt.Printf(":A minute passed!\n")
}
```
以上運行結果每換分鐘時打印:A minute passed!
打開多協程模式:
---------------
在origin引擎設計中,所有的服務是單協程模式,這樣在編寫邏輯代碼時,不用考慮線程安全問題。極大的減少開發難度,但某些開發場景下不用考慮這個問題,而且需要并發執行的情況,比如,某服務只處理數據庫操作控制,而數據庫處理中發生阻塞等待的問題,因為一個協程,該服務接受的數據庫操作只能是一個
一個的排隊處理,效率過低。于是可以打開此模式指定處理協程數,代碼如下:
```
func (slf *TestService1) OnInit() error {
fmt.Printf("TestService1 OnInit.\n")
//打開多線程處理模式,10個協程并發處理
slf.SetGoRouterNum(10)
return nil
}
```
為了
性能監控功能:
---------------
我們在開發一個大型的系統時,經常由于一些代碼質量的原因,產生處理過慢或者死循環的產生,該功能可以被監測到。使用方法如下:
```
func (slf *TestService1) OnInit() error {
fmt.Printf("TestService1 OnInit.\n")
//打開性能分析工具
slf.OpenProfiler()
//監控超過1秒的慢處理
slf.GetProfiler().SetOverTime(time.Second*1)
//監控超過10秒的超慢處理,您可以用它來定位是否存在死循環
//比如以下設置10秒,我的應用中是不會發生超過10秒的一次函數調用
//所以設置為10秒。
slf.GetProfiler().SetMaxOverTime(time.Second*10)
slf.AfterFunc(time.Second*2,slf.Loop)
//打開多線程處理模式,10個協程并發處理
//slf.SetGoRouterNum(10)
return nil
}
func (slf *TestService1) Loop(){
for {
time.Sleep(time.Second*1)
}
}
func main(){
//打開性能分析報告功能,并設置10秒匯報一次
node.OpenProfilerReport(time.Second*10)
node.Start()
}
```
上面通過GetProfiler().SetOverTime與slf.GetProfiler().SetMaxOverTimer設置監控時間
并在main.go中,打開了性能報告器,以每10秒匯報一次,因為上面的例子中,定時器是有死循環,所以可以得到以下報告:
2020/04/22 17:53:30 profiler.go:179: [release] Profiler report tag TestService1:
process count 0,take time 0 Milliseconds,average 0 Milliseconds/per.
too slow process:Timer_orginserver/simple_service.(*TestService1).Loop-fm is take 38003 Milliseconds
直接幫助找到TestService1服務中的Loop函數
第三章:Module使用:
---------------
Module創建與銷毀:
---------------
可以認為Service就是一種Module,它有Module所有的功能。在示例代碼中可以參考originserver/simple_module/TestService3.go。
```
package simple_module
import (
"fmt"
"github.com/duanhf2012/origin/node"
"github.com/duanhf2012/origin/service"
)
func init(){
node.Setup(&TestService3{})
}
type TestService3 struct {
service.Service
}
type Module1 struct {
service.Module
}
type Module2 struct {
service.Module
}
func (slf *Module1) OnInit()error{
fmt.Printf("Module1 OnInit.\n")
return nil
}
func (slf *Module1) OnRelease(){
fmt.Printf("Module1 Release.\n")
}
func (slf *Module2) OnInit()error{
fmt.Printf("Module2 OnInit.\n")
return nil
}
func (slf *Module2) OnRelease(){
fmt.Printf("Module2 Release.\n")
}
func (slf *TestService3) OnInit() error {
//新建兩個Module對象
module1 := &Module1{}
module2 := &Module2{}
//將module1添加到服務中
module1Id,_ := slf.AddModule(module1)
//在module1中添加module2模塊
module1.AddModule(module2)
fmt.Printf("module1 id is %d, module2 id is %d",module1Id,module2.GetModuleId())
//釋放模塊module1
slf.ReleaseModule(module1Id)
fmt.Printf("xxxxxxxxxxx")
return nil
}
```
在OnInit中創建了一條線型的模塊關系TestService3->module1->module2,調用AddModule后會返回Module的Id,自動生成的Id從10e17開始,內部的id,您可以自己設置Id。當調用ReleaseModule釋放時module1時,同樣會將module2釋放。會自動調用OnRelease函數,日志順序如下:
```
Module1 OnInit.
Module2 OnInit.
module1 id is 100000000000000001, module2 id is 100000000000000002
Module2 Release.
Module1 Release.
```
在Module中同樣可以使用定時器功能,請參照第二章節的定時器部分。
第四章:事件使用
---------------
事件是origin中一個重要的組成部分,可以在同一個node中的service與service或者與module之間進行事件通知。系統內置的幾個服務,如:TcpService/HttpService等都是通過事件功能實現。他也是一個典型的觀察者設計模型。在event中有兩個類型的interface,一個是event.IEventProcessor它提供注冊與卸載功能,另一個是event.IEventHandler提供消息廣播等功能。
在目錄simple_event/TestService4.go中
```
package simple_event
import (
"github.com/duanhf2012/origin/event"
"github.com/duanhf2012/origin/node"
"github.com/duanhf2012/origin/service"
"time"
)
const (
//自定義事件類型,必需從event.Sys_Event_User_Define開始
//event.Sys_Event_User_Define以內給系統預留
EVENT1 event.EventType =event.Sys_Event_User_Define+1
)
func init(){
node.Setup(&TestService4{})
}
type TestService4 struct {
service.Service
}
func (slf *TestService4) OnInit() error {
//10秒后觸發廣播事件
slf.AfterFunc(time.Second*10,slf.TriggerEvent)
return nil
}
func (slf *TestService4) TriggerEvent(){
//廣播事件,傳入event.Event對象,類型為EVENT1,Data可以自定義任何數據
//這樣,所有監聽者都可以收到該事件
slf.GetEventHandler().NotifyEvent(&event.Event{
Type: EVENT1,
Data: "event data.",
})
}
```
在目錄simple_event/TestService5.go中
```
package simple_event
import (
"fmt"
"github.com/duanhf2012/origin/event"
"github.com/duanhf2012/origin/node"
"github.com/duanhf2012/origin/service"
)
func init(){
node.Setup(&TestService5{})
}
type TestService5 struct {
service.Service
}
type TestModule struct {
service.Module
}
func (slf *TestModule) OnInit() error{
//在當前node中查找TestService4
pService := node.GetService("TestService4")
//在TestModule中,往TestService4中注冊EVENT1類型事件監聽
pService.(*TestService4).GetEventProcessor().RegEventReciverFunc(EVENT1,slf.GetEventHandler(),slf.OnModuleEvent)
return nil
}
func (slf *TestModule) OnModuleEvent(ev *event.Event){
fmt.Printf("OnModuleEvent type :%d data:%+v\n",ev.Type,ev.Data)
}
//服務初始化函數,在安裝服務時,服務將自動調用OnInit函數
func (slf *TestService5) OnInit() error {
//通過服務名獲取服務對象
pService := node.GetService("TestService4")
////在TestModule中,往TestService4中注冊EVENT1類型事件監聽
pService.(*TestService4).GetEventProcessor().RegEventReciverFunc(EVENT1,slf.GetEventHandler(),slf.OnServiceEvent)
slf.AddModule(&TestModule{})
return nil
}
func (slf *TestService5) OnServiceEvent(ev *event.Event){
fmt.Printf("OnServiceEvent type :%d data:%+v\n",ev.Type,ev.Data)
}
```
程序運行10秒后,調用slf.TriggerEvent函數廣播事件,于是在TestService5中會收到
```
OnServiceEvent type :1001 data:event data.
OnModuleEvent type :1001 data:event data.
```
在上面的TestModule中監聽的事情,當這個Module被Release時監聽會自動卸載。
第五章:RPC使用
---------------
RPC是service與service間通信的重要方式,它允許跨進程node互相訪問,當然也可以指定nodeid進行調用。如下示例:
simple_rpc/TestService6.go文件如下:
```
package simple_rpc
import (
"github.com/duanhf2012/origin/node"
"github.com/duanhf2012/origin/service"
)
func init(){
node.Setup(&TestService6{})
}
type TestService6 struct {
service.Service
}
func (slf *TestService6) OnInit() error {
return nil
}
type InputData struct {
A int
B int
}
func (slf *TestService6) RPC_Sum(input *InputData,output *int) error{
*output = input.A+input.B
return nil
}
```
simple_rpc/TestService7.go文件如下:
```
package simple_rpc
import (
"fmt"
"github.com/duanhf2012/origin/node"
"github.com/duanhf2012/origin/service"
"time"
)
func init(){
node.Setup(&TestService7{})
}
type TestService7 struct {
service.Service
}
func (slf *TestService7) OnInit() error {
slf.AfterFunc(time.Second*2,slf.CallTest)
slf.AfterFunc(time.Second*2,slf.AsyncCallTest)
slf.AfterFunc(time.Second*2,slf.GoTest)
return nil
}
func (slf *TestService7) CallTest(){
var input InputData
input.A = 300
input.B = 600
var output int
//同步調用其他服務的rpc,input為傳入的rpc,output為輸出參數
err := slf.Call("TestService6.RPC_Sum",&input,&output)
if err != nil {
fmt.Printf("Call error :%+v\n",err)
}else{
fmt.Printf("Call output %d\n",output)
}
}
func (slf *TestService7) AsyncCallTest(){
var input InputData
input.A = 300
input.B = 600
/*slf.AsyncCallNode(1,"TestService6.RPC_Sum",&input,func(output *int,err error){
})*/
//異步調用,在數據返回時,會回調傳入函數
//注意函數的第一個參數一定是RPC_Sum函數的第二個參數,err error為RPC_Sum返回值
slf.AsyncCall("TestService6.RPC_Sum",&input,func(output *int,err error){
if err != nil {
fmt.Printf("AsyncCall error :%+v\n",err)
}else{
fmt.Printf("AsyncCall output %d\n",*output)
}
})
}
func (slf *TestService7) GoTest(){
var input InputData
input.A = 300
input.B = 600
//在某些應用場景下不需要數據返回可以使用Go,它是不阻塞的,只需要填入輸入參數
err := slf.Go("TestService6.RPC_Sum",&input)
if err != nil {
fmt.Printf("Go error :%+v\n",err)
}
//以下是廣播方式,如果在同一個子網中有多個同名的服務名,CastGo將會廣播給所有的node
//slf.CastGo("TestService6.RPC_Sum",&input)
}
```
您可以把TestService6配置到其他的Node中,比如NodeId為2中。只要在一個子網,origin引擎可以無差別調用。開發者只需要關注Service關系。同樣它也是您服務器架構設計的核心需要思考的部分。
第六章:HttpService使用
---------------
HttpService是origin引擎中系統實現的http服務,http接口中常用的GET,POST以及url路由處理。
simple_http/TestHttpService.go文件如下:
```
package simple_http
import (
"fmt"
"github.com/duanhf2012/origin/node"
"github.com/duanhf2012/origin/service"
"github.com/duanhf2012/origin/sysservice"
"net/http"
)
func init(){
node.Setup(&sysservice.HttpService{})
node.Setup(&TestHttpService{})
}
//新建自定義服務TestService1
type TestHttpService struct {
service.Service
}
func (slf *TestHttpService) OnInit() error {
//獲取系統httpservice服務
httpervice := node.GetService("HttpService").(*sysservice.HttpService)
//新建并設置路由對象
httpRouter := sysservice.NewHttpHttpRouter()
httpervice.SetHttpRouter(httpRouter,slf.GetEventHandler())
//GET方法,請求url:http://127.0.0.1:9402/get/query?nickname=boyce
//并header中新增key為uid,value為1000的頭,則用postman測試返回結果為:
//head uid:1000, nickname:boyce
httpRouter.GET("/get/query", slf.HttpGet)
//POST方法 請求url:http://127.0.0.1:9402/post/query
//返回結果為:{"msg":"hello world"}
httpRouter.POST("/post/query", slf.HttpPost)
//GET方式獲取目錄下的資源,http://127.0.0.1:port/img/head/a.jpg
httpRouter.SetServeFile(sysservice.METHOD_GET,"/img/head/","d:/img")
return nil
}
func (slf *TestHttpService) HttpGet(session *sysservice.HttpSession){
//從頭中獲取key為uid對應的值
uid := session.GetHeader("uid")
//從url參數中獲取key為nickname對應的值
nickname,_ := session.Query("nickname")
//向body部分寫入數據
session.Write([]byte(fmt.Sprintf("head uid:%s, nickname:%s",uid,nickname)))
//寫入http狀態
session.WriteStatusCode(http.StatusOK)
//完成返回
session.Done()
}
type HttpRespone struct {
Msg string `json:"msg"`
}
func (slf *TestHttpService) HttpPost(session *sysservice.HttpSession){
//也可以采用直接返回數據對象方式,如下:
session.WriteJsonDone(http.StatusOK,&HttpRespone{Msg: "hello world"})
}
```
注意,要在main.go中加入import _ "orginserver/simple_service",并且在config/cluster/subnet/cluster.json中的ServiceList加入服務。
第七章:TcpService服務使用
---------------
TcpService是origin引擎中系統實現的Tcp服務,可以支持自定義消息格式處理器。只要重新實現network.Processor接口。目前內置已經實現最常用的protobuf處理器。
simple_tcp/TestTcpService.go文件如下:
```
package simple_tcp
import (
"fmt"
"github.com/duanhf2012/origin/network/processor"
"github.com/duanhf2012/origin/node"
"github.com/duanhf2012/origin/service"
"github.com/duanhf2012/origin/sysservice"
"github.com/golang/protobuf/proto"
"orginserver/simple_tcp/msgpb"
)
func init(){
node.Setup(&sysservice.TcpService{})
node.Setup(&TestTcpService{})
}
//新建自定義服務TestService1
type TestTcpService struct {
service.Service
processor *processor.PBProcessor
tcpService *sysservice.TcpService
}
func (slf *TestTcpService) OnInit() error {
//獲取安裝好了的TcpService對象
slf.tcpService =  node.GetService("TcpService").(*sysservice.TcpService)
//新建內置的protobuf處理器,您也可以自定義路由器,比如json,后續會補充
slf.processor = processor.NewPBProcessor()
//注冊監聽客戶連接斷開事件
slf.processor.RegisterDisConnected(slf.OnDisconnected)
//注冊監聽客戶連接事件
slf.processor.RegisterConnected(slf.OnConnected)
//注冊監聽消息類型MsgType_MsgReq,并注冊回調
slf.processor.Register(uint16(msgpb.MsgType_MsgReq),&msgpb.Req{},slf.OnRequest)
//將protobuf消息處理器設置到TcpService服務中
slf.tcpService.SetProcessor(slf.processor,slf.GetEventHandler())
return nil
}
func (slf *TestTcpService) OnConnected(clientid uint64){
fmt.Printf("client id %d connected\n",clientid)
}
func (slf *TestTcpService) OnDisconnected(clientid uint64){
fmt.Printf("client id %d disconnected\n",clientid)
}
func (slf *TestTcpService) OnRequest (clientid uint64,msg proto.Message){
//解析客戶端發過來的數據
pReq := msg.(*msgpb.Req)
//發送數據給客戶端
err := slf.tcpService.SendMsg(clientid,&msgpb.Req{
Msg: proto.String(pReq.GetMsg()),
})
if err != nil {
fmt.Printf("send msg is fail %+v!",err)
}
}
```
第八章:其他系統模塊介紹
---------------
* sysservice/wsservice.go:支持了WebSocket協議,使用方法與TcpService類似
* sysmodule/DBModule.go:對mysql數據庫操作
* sysmodule/RedisModule.go:對Redis數據進行操作
* sysmodule/HttpClientPoolModule.go:Http客戶端請求封裝
* log/log.go:日志的封裝,可以使用它構建對象記錄業務文件日志
* util:在該目錄下,有常用的uuid,hash,md5,協程封裝等工具庫
* https://github.com/duanhf2012/originservice: 其他擴展支持的服務可以在該工程上看到,目前支持firebase推送的封裝。
**歡迎加入origin服務器開發QQ交流群:168306674,有任何疑問我都會及時解答**
提交bug及特性: https://github.com/duanhf2012/origin/issues
posted on 2020-05-07 16:06 C++技術中心 閱讀(1177) 評論(0)  編輯 收藏 引用 所屬分類: 游戲開發其他編程
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            一区二区三区国产在线| 欧美日韩视频免费播放| 久久裸体视频| 久久婷婷国产麻豆91天堂| 久久精品伊人| 久久久视频精品| 老司机免费视频一区二区| 欧美激情在线播放| 日韩亚洲欧美一区二区三区| 亚洲性线免费观看视频成熟| 国产亚洲午夜高清国产拍精品| 一区二区三区 在线观看视| 亚洲一区二区三区激情| 久久精品国产精品| 女同性一区二区三区人了人一| 欧美日韩国产小视频| 国产精品亚发布| 亚洲国产精品一区二区www| 夜夜精品视频一区二区| 欧美在线视频网站| 亚洲黑丝在线| 午夜在线精品| 欧美刺激午夜性久久久久久久| 国产精品久久久亚洲一区| 在线免费观看欧美| 亚洲欧美国产另类| 亚洲第一精品影视| 欧美一级精品大片| 欧美性视频网站| 亚洲片国产一区一级在线观看| 欧美一级专区| 亚洲国产美女久久久久| 久久九九99| 国产日产欧产精品推荐色| 一本久道久久综合狠狠爱| 久久久成人精品| 中文日韩在线| 欧美日韩视频在线一区二区观看视频| 在线观看视频一区二区| 欧美中文字幕在线播放| 99精品视频一区二区三区| 免费不卡欧美自拍视频| 国产在线视频不卡二| 亚洲欧美日韩在线| 日韩亚洲一区二区| 欧美精品在线免费观看| 亚洲国产视频直播| 男男成人高潮片免费网站| 久久精品91| 国外成人在线视频| 久久中文在线| 久久成人在线| 黄色欧美成人| 蜜臀91精品一区二区三区| 久久岛国电影| 黄色精品在线看| 欧美电影电视剧在线观看| 久久五月激情| 亚洲国产日韩在线一区模特| 欧美aⅴ99久久黑人专区| 久久久久久久综合| 亚洲黄色影片| 亚洲国产美女久久久久| 欧美日韩国产黄| 亚洲欧美日韩国产综合精品二区| 中日韩美女免费视频网站在线观看| 欧美吻胸吃奶大尺度电影| 亚洲一区二区在线免费观看| 亚洲免费观看| 亚洲在线观看视频| 一区二区三区四区精品| 国产精品久久久对白| 亚洲欧美自拍偷拍| 亚洲欧美视频在线| 狠狠色丁香久久婷婷综合_中| 美女图片一区二区| 欧美国产日韩一区二区| 亚洲一区二区在线播放| 亚洲欧美日韩第一区| 激情欧美一区| 亚洲日本无吗高清不卡| 欧美午夜宅男影院在线观看| 久久精品五月婷婷| 欧美承认网站| 午夜精品影院| 蜜桃精品久久久久久久免费影院| 日韩视频在线免费| 亚洲已满18点击进入久久| 激情亚洲成人| 日韩一二三在线视频播| 国户精品久久久久久久久久久不卡| 欧美电影电视剧在线观看| 欧美午夜电影网| 免费视频一区二区三区在线观看| 欧美日本韩国一区| 久久综合电影一区| 欧美日韩中文字幕精品| 麻豆精品网站| 国产精品入口夜色视频大尺度| 欧美xart系列高清| 国产精品一区二区黑丝| 亚洲国产你懂的| 狠色狠色综合久久| 中文在线不卡视频| 亚洲精品影院在线观看| 亚洲欧美日本视频在线观看| 99爱精品视频| 麻豆精品一区二区综合av| 午夜影视日本亚洲欧洲精品| 欧美高清视频一区| 女生裸体视频一区二区三区| 国产乱码精品一区二区三区五月婷 | 亚洲成色精品| 国产喷白浆一区二区三区| 日韩视频一区| 亚洲精品视频一区| 噜噜噜躁狠狠躁狠狠精品视频| 欧美一区免费| 国产精品www色诱视频| 欧美激情在线有限公司| 狠狠色丁香久久婷婷综合丁香| 亚洲天堂激情| 亚洲一区三区视频在线观看| 欧美精品一区二区三区蜜桃| 欧美成人嫩草网站| 亚洲成色最大综合在线| 久久久精品性| 欧美第一黄色网| 影音先锋中文字幕一区| 欧美一二三视频| 久久精品国产亚洲一区二区三区| 国产精品国产三级国产普通话三级| 亚洲高清一区二| 亚洲三级国产| 欧美风情在线观看| 亚洲国产日韩精品| 亚洲精品日韩精品| 欧美精品99| 日韩午夜在线视频| 亚洲图片在线| 国产精品美女www爽爽爽| 亚洲在线视频观看| 久久精品首页| 亚洲高清在线播放| 欧美成人精品一区二区三区| 亚洲国产精品一区在线观看不卡| 亚洲精品午夜精品| 欧美三级在线视频| 亚洲欧美国产毛片在线| 午夜国产欧美理论在线播放| 国产精品一区在线播放| 欧美一区国产二区| 欧美第十八页| 亚洲影院色无极综合| 国产日韩欧美中文| 久久久久久尹人网香蕉| 亚洲激情不卡| 午夜精品美女自拍福到在线| 国产欧美一区在线| 久久在线观看视频| 亚洲毛片一区二区| 久久国产精品亚洲77777| 亚洲成色www久久网站| 欧美日韩亚洲免费| 久久精品二区三区| 日韩午夜av电影| 久久久久这里只有精品| 亚洲精选91| 国产一级揄自揄精品视频| 欧美成人按摩| 亚洲在线网站| 亚洲国产欧美国产综合一区 | 韩日精品视频一区| 欧美精品一区二区久久婷婷| 亚洲色图制服丝袜| 欧美二区视频| 午夜性色一区二区三区免费视频| 在线欧美日韩精品| 国产精品视区| 欧美另类久久久品| 久久久人成影片一区二区三区| 一区二区久久| 亚洲国产精品一区二区尤物区| 欧美一区二区三区日韩视频| 亚洲美女毛片| 伊人久久久大香线蕉综合直播| 欧美三级乱人伦电影| 久久综合色天天久久综合图片| 亚洲一区在线看| 亚洲日本欧美| 欧美激情视频一区二区三区免费 | 免费观看久久久4p| 欧美一区二区网站| 亚洲视频一二区| 在线观看91精品国产麻豆| 久久精品免费| 在线亚洲欧美视频| 最新精品在线| 免费人成精品欧美精品| 久久久不卡网国产精品一区|