• <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>

               C++ 技術(shù)中心

               :: 首頁 :: 聯(lián)系 ::  :: 管理
              160 Posts :: 0 Stories :: 87 Comments :: 0 Trackbacks

            公告

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

            留言簿(27)

            搜索

            •  

            最新隨筆

            最新評論

            評論排行榜

            origin 游戲服務(wù)器引擎簡介
            ==================
            origin 是一個由 Go 語言(golang)編寫的分布式開源游戲服務(wù)器引擎。origin適用于各類游戲服務(wù)器的開發(fā),包括 H5(HTML5)游戲服務(wù)器。
            origin 解決的問題:
            * origin總體設(shè)計如go語言設(shè)計一樣,總是盡可能的提供簡潔和易用的模式,快速開發(fā)。
            * 能夠根據(jù)業(yè)務(wù)需求快速并靈活的制定服務(wù)器架構(gòu)。
            * 利用多核優(yōu)勢,將不同的service配置到不同的node,并能高效的協(xié)同工作。
            * 將整個引擎抽象三大對象,node,service,module。通過統(tǒng)一的組合模型管理游戲中各功能模塊的關(guān)系。
            * 有豐富并健壯的工具庫。
            Hello world!
            ---------------
            下面我們來一步步的建立origin服務(wù)器,先下載[origin引擎](https://github.com/duanhf2012/origin "origin引擎"),或者使用如下命令:
            ```go
            go get -v -u  github.com/duanhf2012/origin
            ```
            于是下載到GOPATH環(huán)境目錄中,在src中加入main.go,內(nèi)容如下:
            ```go
            package main
            import (
            "github.com/duanhf2012/origin/node"
            )
            func main() {
            node.Start()
            }
            ```
            一個origin進(jìn)程需要創(chuàng)建一個node對象,Start開始運(yùn)行。您也可以直接下載origin引擎示例:
            ```
            go get -v -u github.com/duanhf2012/originserver
            ```
            本文所有的說明都是基于該示例為主。
            origin引擎三大對象關(guān)系
            ---------------
            * Node:   可以認(rèn)為每一個Node代表著一個origin進(jìn)程
            * Service:一個獨(dú)立的服務(wù)可以認(rèn)為是一個大的功能模塊,他是Node的子集,創(chuàng)建完成并安裝Node對象中。服務(wù)可以支持對外部RPC等功能。
            * Module: 這是origin最小對象單元,強(qiáng)烈建議所有的業(yè)務(wù)模塊都劃分成各個小的Module組合,origin引擎將監(jiān)控所有服務(wù)與Module運(yùn)行狀態(tài),例如可以監(jiān)控它們的慢處理和死循環(huán)函數(shù)。Module可以建立樹狀關(guān)系。Service本身也是Module的類型。
            origin集群核心配置文件在config的cluster目錄下,在cluster下有子網(wǎng)目錄,如github.com/duanhf2012/originserver的config/cluster目錄下有subnet目錄,表示子網(wǎng)名為subnet,可以新加多個子網(wǎng)的目錄配置。子網(wǎng)與子網(wǎng)間是隔離的,后續(xù)將支持子網(wǎng)間通信規(guī)則,origin集群配置以子網(wǎng)的模式配置,在每個子網(wǎng)下配置多個Node服務(wù)器,子網(wǎng)在應(yīng)對復(fù)雜的系統(tǒng)時可以應(yīng)用到各個子系統(tǒng),方便每個子系統(tǒng)的隔離。在示例的subnet目錄中有cluster.json與service.json配置:
            cluster.json如下:
            ---------------
            ```
            {
                "NodeList":[
                    {
                      "NodeId": 1,
                      "ListenAddr":"127.0.0.1:8001",
                      "NodeName": "Node_Test1",
              "remark":"http://以_打頭的,表示只在本機(jī)進(jìn)程,不對整個子網(wǎng)開發(fā)",
                      "ServiceList": ["TestService1","TestService2","TestServiceCall","GateService","_TcpService","HttpService","WSService"]
                    },
            {
                      "NodeId": 2,
                      "ListenAddr":"127.0.0.1:8002",
                      "NodeName": "Node_Test1",
              "remark":"http://以_打頭的,表示只在本機(jī)進(jìn)程,不對整個子網(wǎng)開發(fā)",
                      "ServiceList": ["TestService1","TestService2","TestServiceCall","GateService","TcpService","HttpService","WSService"]
                    }
                ]
            ```
            ---------------
            以上配置了兩個結(jié)點(diǎn)服務(wù)器程序:
            * NodeId: 表示origin程序的結(jié)點(diǎn)Id標(biāo)識,不允許重復(fù)。
            * ListenAddr:Rpc通信服務(wù)的監(jiān)聽地址
            * NodeName:結(jié)點(diǎn)名稱
            * remark:備注,可選項
            * ServiceList:該Node將安裝的服務(wù)列表
            ---------------
            在啟動程序命令program start nodeid=1中nodeid就是根據(jù)該配置裝載服務(wù)。
            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中配置的對應(yīng)結(jié)點(diǎn)中服務(wù)的配置,如果啟動程序中根據(jù)nodeid查找該域的對應(yīng)的服務(wù),如果找不到時,從Service公共部分查找。
            **HttpService配置**
            * ListenAddr:Http監(jiān)聽地址
            * ReadTimeout:讀網(wǎng)絡(luò)超時毫秒
            * WriteTimeout:寫網(wǎng)絡(luò)超時毫秒
            * ProcessTimeout: 處理超時毫秒
            * CAFile: 證書文件,如果您的服務(wù)器通過web服務(wù)器代理配置https可以忽略該配置
            **TcpService配置**
            * ListenAddr: 監(jiān)聽地址
            * MaxConnNum: 允許最大連接數(shù)
            * PendingWriteNum:發(fā)送網(wǎng)絡(luò)隊列最大數(shù)量
            * LittleEndian:是否小端
            * MinMsgLen:包最小長度
            * MaxMsgLen:包最大長度
            **WSService配置**
            * ListenAddr: 監(jiān)聽地址
            * MaxConnNum: 允許最大連接數(shù)
            * PendingWriteNum:發(fā)送網(wǎng)絡(luò)隊列最大數(shù)量
            * MaxMsgLen:包最大長度
            ---------------
            第一章:origin基礎(chǔ):
            ---------------
            查看github.com/duanhf2012/originserver中的simple_service中新建兩個服務(wù),分別是TestService1.go與CTestService2.go。
            simple_service/TestService1.go如下:
            ```
            package simple_service
            import (
            "github.com/duanhf2012/origin/node"
            "github.com/duanhf2012/origin/service"
            )
            //模塊加載時自動安裝TestService1服務(wù)
            func init(){
            node.Setup(&TestService1{})
            }
            //新建自定義服務(wù)TestService1
            type TestService1 struct {
            //所有的自定義服務(wù)必需加入service.Service基服務(wù)
            //那么該自定義服務(wù)將有各種功能特性
            //例如: Rpc,事件驅(qū)動,定時器等
            service.Service
            }
            //服務(wù)初始化函數(shù),在安裝服務(wù)時,服務(wù)將自動調(diào)用OnInit函數(shù)
            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運(yùn)行代碼
            ```go
            package main
            import (
            "github.com/duanhf2012/origin/node"
            //導(dǎo)入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://以_打頭的,表示只在本機(jī)進(jìn)程,不對整個子網(wǎng)開發(fā)",
                      "ServiceList": ["TestService1","TestService2"]
                    }
                ]
            }
            ```
            編譯后運(yùn)行結(jié)果如下:
            ```
            #originserver start nodeid=1
            TestService1 OnInit.
            TestService2 OnInit.
            ```
            第二章:Service中常用功能:
            ---------------
            定時器:
            ---------------
            在開發(fā)中最常用的功能有定時任務(wù),origin提供兩種定時方式:
            一種AfterFunc函數(shù),可以間隔一定時間觸發(fā)回調(diào),參照simple_service/TestService2.go,實現(xiàn)如下:
            ```
            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.",如果下次還需要觸發(fā),需要重新設(shè)置定時器
            另一種方式是類似Linux系統(tǒng)的crontab命令,使用如下:
            ```
            func (slf *TestService2) OnInit() error {
            fmt.Printf("TestService2 OnInit.\n")
            //crontab模式定時觸發(fā)
            //NewCronExpr的參數(shù)分別代表:Seconds Minutes Hours DayOfMonth Month DayOfWeek
            //以下為每換分鐘時觸發(fā)
            cron,_:=timer.NewCronExpr("0 * * * * *")
            slf.CronFunc(cron,slf.OnCron)
            return nil
            }
            func (slf *TestService2) OnCron(){
            fmt.Printf(":A minute passed!\n")
            }
            ```
            以上運(yùn)行結(jié)果每換分鐘時打印:A minute passed!
            打開多協(xié)程模式:
            ---------------
            在origin引擎設(shè)計中,所有的服務(wù)是單協(xié)程模式,這樣在編寫邏輯代碼時,不用考慮線程安全問題。極大的減少開發(fā)難度,但某些開發(fā)場景下不用考慮這個問題,而且需要并發(fā)執(zhí)行的情況,比如,某服務(wù)只處理數(shù)據(jù)庫操作控制,而數(shù)據(jù)庫處理中發(fā)生阻塞等待的問題,因為一個協(xié)程,該服務(wù)接受的數(shù)據(jù)庫操作只能是一個
            一個的排隊處理,效率過低。于是可以打開此模式指定處理協(xié)程數(shù),代碼如下:
            ```
            func (slf *TestService1) OnInit() error {
            fmt.Printf("TestService1 OnInit.\n")
            //打開多線程處理模式,10個協(xié)程并發(fā)處理
            slf.SetGoRouterNum(10)
            return nil
            }
            ```
            為了
            性能監(jiān)控功能:
            ---------------
            我們在開發(fā)一個大型的系統(tǒng)時,經(jīng)常由于一些代碼質(zhì)量的原因,產(chǎn)生處理過慢或者死循環(huán)的產(chǎn)生,該功能可以被監(jiān)測到。使用方法如下:
            ```
            func (slf *TestService1) OnInit() error {
            fmt.Printf("TestService1 OnInit.\n")
            //打開性能分析工具
            slf.OpenProfiler()
            //監(jiān)控超過1秒的慢處理
            slf.GetProfiler().SetOverTime(time.Second*1)
            //監(jiān)控超過10秒的超慢處理,您可以用它來定位是否存在死循環(huán)
            //比如以下設(shè)置10秒,我的應(yīng)用中是不會發(fā)生超過10秒的一次函數(shù)調(diào)用
            //所以設(shè)置為10秒。
            slf.GetProfiler().SetMaxOverTime(time.Second*10)
            slf.AfterFunc(time.Second*2,slf.Loop)
            //打開多線程處理模式,10個協(xié)程并發(fā)處理
            //slf.SetGoRouterNum(10)
            return nil
            }
            func (slf *TestService1) Loop(){
            for {
            time.Sleep(time.Second*1)
            }
            }
            func main(){
            //打開性能分析報告功能,并設(shè)置10秒?yún)R報一次
            node.OpenProfilerReport(time.Second*10)
            node.Start()
            }
            ```
            上面通過GetProfiler().SetOverTime與slf.GetProfiler().SetMaxOverTimer設(shè)置監(jiān)控時間
            并在main.go中,打開了性能報告器,以每10秒?yún)R報一次,因為上面的例子中,定時器是有死循環(huán),所以可以得到以下報告:
            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服務(wù)中的Loop函數(shù)
            第三章:Module使用:
            ---------------
            Module創(chuàng)建與銷毀:
            ---------------
            可以認(rèn)為Service就是一種Module,它有Module所有的功能。在示例代碼中可以參考o(jì)riginserver/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添加到服務(wù)中
            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中創(chuàng)建了一條線型的模塊關(guān)系TestService3->module1->module2,調(diào)用AddModule后會返回Module的Id,自動生成的Id從10e17開始,內(nèi)部的id,您可以自己設(shè)置Id。當(dāng)調(diào)用ReleaseModule釋放時module1時,同樣會將module2釋放。會自動調(diào)用OnRelease函數(shù),日志順序如下:
            ```
            Module1 OnInit.
            Module2 OnInit.
            module1 id is 100000000000000001, module2 id is 100000000000000002
            Module2 Release.
            Module1 Release.
            ```
            在Module中同樣可以使用定時器功能,請參照第二章節(jié)的定時器部分。
            第四章:事件使用
            ---------------
            事件是origin中一個重要的組成部分,可以在同一個node中的service與service或者與module之間進(jìn)行事件通知。系統(tǒng)內(nèi)置的幾個服務(wù),如:TcpService/HttpService等都是通過事件功能實現(xiàn)。他也是一個典型的觀察者設(shè)計模型。在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以內(nèi)給系統(tǒng)預(yù)留
            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秒后觸發(fā)廣播事件
            slf.AfterFunc(time.Second*10,slf.TriggerEvent)
            return nil
            }
            func (slf *TestService4) TriggerEvent(){
            //廣播事件,傳入event.Event對象,類型為EVENT1,Data可以自定義任何數(shù)據(jù)
            //這樣,所有監(jiān)聽者都可以收到該事件
            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{
            //在當(dāng)前node中查找TestService4
            pService := node.GetService("TestService4")
            //在TestModule中,往TestService4中注冊EVENT1類型事件監(jiān)聽
            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)
            }
            //服務(wù)初始化函數(shù),在安裝服務(wù)時,服務(wù)將自動調(diào)用OnInit函數(shù)
            func (slf *TestService5) OnInit() error {
            //通過服務(wù)名獲取服務(wù)對象
            pService := node.GetService("TestService4")
            ////在TestModule中,往TestService4中注冊EVENT1類型事件監(jiān)聽
            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)
            }
            ```
            程序運(yùn)行10秒后,調(diào)用slf.TriggerEvent函數(shù)廣播事件,于是在TestService5中會收到
            ```
            OnServiceEvent type :1001 data:event data.
            OnModuleEvent type :1001 data:event data.
            ```
            在上面的TestModule中監(jiān)聽的事情,當(dāng)這個Module被Release時監(jiān)聽會自動卸載。
            第五章:RPC使用
            ---------------
            RPC是service與service間通信的重要方式,它允許跨進(jìn)程node互相訪問,當(dāng)然也可以指定nodeid進(jìn)行調(diào)用。如下示例:
            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
            //同步調(diào)用其他服務(wù)的rpc,input為傳入的rpc,output為輸出參數(shù)
            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){
            })*/
            //異步調(diào)用,在數(shù)據(jù)返回時,會回調(diào)傳入函數(shù)
            //注意函數(shù)的第一個參數(shù)一定是RPC_Sum函數(shù)的第二個參數(shù),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
            //在某些應(yīng)用場景下不需要數(shù)據(jù)返回可以使用Go,它是不阻塞的,只需要填入輸入?yún)?shù)
            err := slf.Go("TestService6.RPC_Sum",&input)
            if err != nil {
            fmt.Printf("Go error :%+v\n",err)
            }
            //以下是廣播方式,如果在同一個子網(wǎng)中有多個同名的服務(wù)名,CastGo將會廣播給所有的node
            //slf.CastGo("TestService6.RPC_Sum",&input)
            }
            ```
            您可以把TestService6配置到其他的Node中,比如NodeId為2中。只要在一個子網(wǎng),origin引擎可以無差別調(diào)用。開發(fā)者只需要關(guān)注Service關(guān)系。同樣它也是您服務(wù)器架構(gòu)設(shè)計的核心需要思考的部分。
            第六章:HttpService使用
            ---------------
            HttpService是origin引擎中系統(tǒng)實現(xiàn)的http服務(wù),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{})
            }
            //新建自定義服務(wù)TestService1
            type TestHttpService struct {
            service.Service
            }
            func (slf *TestHttpService) OnInit() error {
            //獲取系統(tǒng)httpservice服務(wù)
            httpervice := node.GetService("HttpService").(*sysservice.HttpService)
            //新建并設(shè)置路由對象
            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測試返回結(jié)果為:
            //head uid:1000, nickname:boyce
            httpRouter.GET("/get/query", slf.HttpGet)
            //POST方法 請求url:http://127.0.0.1:9402/post/query
            //返回結(jié)果為:{"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對應(yīng)的值
            uid := session.GetHeader("uid")
            //從url參數(shù)中獲取key為nickname對應(yīng)的值
            nickname,_ := session.Query("nickname")
            //向body部分寫入數(shù)據(jù)
            session.Write([]byte(fmt.Sprintf("head uid:%s, nickname:%s",uid,nickname)))
            //寫入http狀態(tài)
            session.WriteStatusCode(http.StatusOK)
            //完成返回
            session.Done()
            }
            type HttpRespone struct {
            Msg string `json:"msg"`
            }
            func (slf *TestHttpService) HttpPost(session *sysservice.HttpSession){
            //也可以采用直接返回數(shù)據(jù)對象方式,如下:
            session.WriteJsonDone(http.StatusOK,&HttpRespone{Msg: "hello world"})
            }
            ```
            注意,要在main.go中加入import _ "orginserver/simple_service",并且在config/cluster/subnet/cluster.json中的ServiceList加入服務(wù)。
            第七章:TcpService服務(wù)使用
            ---------------
            TcpService是origin引擎中系統(tǒng)實現(xiàn)的Tcp服務(wù),可以支持自定義消息格式處理器。只要重新實現(xiàn)network.Processor接口。目前內(nèi)置已經(jīng)實現(xiàn)最常用的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{})
            }
            //新建自定義服務(wù)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)
            //新建內(nèi)置的protobuf處理器,您也可以自定義路由器,比如json,后續(xù)會補(bǔ)充
            slf.processor = processor.NewPBProcessor()
            //注冊監(jiān)聽客戶連接斷開事件
            slf.processor.RegisterDisConnected(slf.OnDisconnected)
            //注冊監(jiān)聽客戶連接事件
            slf.processor.RegisterConnected(slf.OnConnected)
            //注冊監(jiān)聽消息類型MsgType_MsgReq,并注冊回調(diào)
            slf.processor.Register(uint16(msgpb.MsgType_MsgReq),&msgpb.Req{},slf.OnRequest)
            //將protobuf消息處理器設(shè)置到TcpService服務(wù)中
            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){
            //解析客戶端發(fā)過來的數(shù)據(jù)
            pReq := msg.(*msgpb.Req)
            //發(fā)送數(shù)據(jù)給客戶端
            err := slf.tcpService.SendMsg(clientid,&msgpb.Req{
            Msg: proto.String(pReq.GetMsg()),
            })
            if err != nil {
            fmt.Printf("send msg is fail %+v!",err)
            }
            }
            ```
            第八章:其他系統(tǒng)模塊介紹
            ---------------
            * sysservice/wsservice.go:支持了WebSocket協(xié)議,使用方法與TcpService類似
            * sysmodule/DBModule.go:對mysql數(shù)據(jù)庫操作
            * sysmodule/RedisModule.go:對Redis數(shù)據(jù)進(jìn)行操作
            * sysmodule/HttpClientPoolModule.go:Http客戶端請求封裝
            * log/log.go:日志的封裝,可以使用它構(gòu)建對象記錄業(yè)務(wù)文件日志
            * util:在該目錄下,有常用的uuid,hash,md5,協(xié)程封裝等工具庫
            * https://github.com/duanhf2012/originservice: 其他擴(kuò)展支持的服務(wù)可以在該工程上看到,目前支持firebase推送的封裝。
            **歡迎加入origin服務(wù)器開發(fā)QQ交流群:168306674,有任何疑問我都會及時解答**
            提交bug及特性: https://github.com/duanhf2012/origin/issues
            posted on 2020-05-07 16:06 C++技術(shù)中心 閱讀(1153) 評論(0)  編輯 收藏 引用 所屬分類: 游戲開發(fā)其他編程
            人妻精品久久无码区| 品成人欧美大片久久国产欧美| 狠狠色狠狠色综合久久| 久久发布国产伦子伦精品| 999久久久国产精品| 亚洲欧美日韩中文久久| 久久精品国产只有精品2020| 人妻无码久久精品| 欧美久久精品一级c片片| 亚洲午夜无码AV毛片久久| 成人国内精品久久久久一区| 亚洲伊人久久成综合人影院 | 伊人久久综合精品无码AV专区| 99久久99这里只有免费的精品| 少妇久久久久久被弄到高潮 | 香蕉久久夜色精品国产2020| 久久精品国产精品亚洲精品| 久久久久久久久久久| 国产ww久久久久久久久久| 久久久久久久久久久久中文字幕| 久久影视综合亚洲| 国产精品一区二区久久精品无码 | 国产成人精品久久| 青草影院天堂男人久久| 久久九九精品99国产精品| 国产成人无码精品久久久性色 | 狠狠色丁香久久婷婷综合图片| 国产精品综合久久第一页 | 亚洲国产成人久久综合碰| 国产免费久久精品99久久| 久久亚洲综合色一区二区三区| 久久天天躁狠狠躁夜夜avapp| 国色天香久久久久久久小说| 国产69精品久久久久APP下载| 久久久久久亚洲精品影院| 亚洲欧美久久久久9999| 久久久这里有精品| AV无码久久久久不卡蜜桃| 无码专区久久综合久中文字幕| 亚洲va国产va天堂va久久| 久久精品亚洲日本波多野结衣|