【Zinx第一章-引言】Golang輕量級併發伺服器框架

Aceld發表於2019-04-29

【Zinx教程目錄】 Zinx原始碼

https://github.com/aceld/zinx

完整教程電子版(線上高清)-下載

Zinx框架視訊教程(框架篇)(完整版下載)連結在下面正文

Zinx框架視訊教程(應用篇)(完整版下載)連結在下面正文

Zinx開發API文件

Zinx第一章-引言

Zinx第二章-初識Zinx框架

Zinx第三章-基礎路由模組

Zinx第四章-全域性配置

Zinx第五章-訊息封裝

Zinx第六章-多路由模式

Zinx第七章-讀寫分離模型

Zinx第八章-訊息佇列及多工

Zinx第九章-連結管理

Zinx第十章-連線屬性設定


【Zinx應用案例-MMO多人線上遊戲】

(1)案例介紹

(2)AOI興趣點演算法

(3)資料傳輸協議protocol buffer

(4)Proto3協議定義

(5)構建專案及使用者上線

(6)世界聊天

(7)上線位置資訊同步

(8)移動位置與AOI廣播

(9)玩家下線

1、寫在前面

​ 我們為什麼要做Zinx,Golang目前在伺服器的應用框架很多,但是應用在遊戲領域或者其他長連結的領域的輕量級企業框架甚少。

​ 設計Zinx的目的是我們可以通過Zinx框架來了解基於Golang編寫一個TCP伺服器的整體輪廓,讓更多的Golang愛好者能深入淺出的去學習和認識這個領域。

​ Zinx框架的專案製作採用編碼和學習教程同步進行,將開發的全部遞進和迭代思維帶入教程中,而不是一下子給大家一個非常完整的框架去學習,讓很多人一頭霧水,不知道該如何學起。

​ 教程會一個版本一個版本迭代,每個版本的新增功能都是微小的,讓一個服務框架小白,循序漸進的曲線方式瞭解伺服器框架的領域。

​ 當然,最後希望Zinx會有更多的人加入,給我們提出寶貴的意見,讓Zinx成為真正的解決企業的伺服器框架!在此感謝您的關注!

二、初探Zinx架構

1-Zinx框架.png

zinx-start.gif

三、Zinx開發API文件

快速開始

server

基於Zinx框架開發的伺服器應用,主函式步驟比較精簡,最多主需要3步即可。

  1. 建立server控制程式碼
  2. 配置自定義路由及業務
  3. 啟動服務
func main() {
    //1 建立一個server控制程式碼
    s := znet.NewServer()

    //2 配置路由
    s.AddRouter(0, &PingRouter{})

    //3 開啟服務
    s.Serve()
}

其中自定義路由及業務配置方式如下:

import (
    "fmt"
    "zinx/ziface"
    "zinx/znet"
)

//ping test 自定義路由
type PingRouter struct {
    znet.BaseRouter
}

//Ping Handle
func (this *PingRouter) Handle(request ziface.IRequest) {
    //先讀取客戶端的資料
    fmt.Println("recv from client : msgId=", request.GetMsgID(), ", data=", string(request.GetData()))

    //再回寫ping...ping...ping
    err := request.GetConnection().SendBuffMsg(0, []byte("ping...ping...ping"))
    if err != nil {
        fmt.Println(err)
    }
}

client

Zinx的訊息處理採用,[MsgLength]|[MsgID]|[Data]的封包格式

package main

import (
    "fmt"
    "io"
    "net"
    "time"
    "zinx/znet"
)

/*
    模擬客戶端
 */
func main() {

    fmt.Println("Client Test ... start")
    //3秒之後發起測試請求,給服務端開啟服務的機會
    time.Sleep(3 * time.Second)

    conn,err := net.Dial("tcp", "127.0.0.1:7777")
    if err != nil {
        fmt.Println("client start err, exit!")
        return
    }

    for n := 3; n >= 0; n-- {
        //發封包message訊息
        dp := znet.NewDataPack()
        msg, _ := dp.Pack(znet.NewMsgPackage(0,[]byte("Zinx Client Test Message")))
        _, err := conn.Write(msg)
        if err !=nil {
            fmt.Println("write error err ", err)
            return
        }

        //先讀出流中的head部分
        headData := make([]byte, dp.GetHeadLen())
        _, err = io.ReadFull(conn, headData) //ReadFull 會把msg填充滿為止
        if err != nil {
            fmt.Println("read head error")
            break
        }
        //將headData位元組流 拆包到msg中
        msgHead, err := dp.Unpack(headData)
        if err != nil {
            fmt.Println("server unpack err:", err)
            return
        }

        if msgHead.GetDataLen() > 0 {
            //msg 是有data資料的,需要再次讀取data資料
            msg := msgHead.(*znet.Message)
            msg.Data = make([]byte, msg.GetDataLen())

            //根據dataLen從io中讀取位元組流
            _, err := io.ReadFull(conn, msg.Data)
            if err != nil {
                fmt.Println("server unpack data err:", err)
                return
            }

            fmt.Println("==> Recv Msg: ID=", msg.Id, ", len=", msg.DataLen, ", data=", string(msg.Data))
        }

        time.Sleep(1*time.Second)
    }
}

Zinx配置檔案

{
  "Name":"Zinx Game", 
  "Host":"0.0.0.0",
  "TcpPort":8999,
  "MaxConn":3000,
  "WorkerPoolSize":10
}

Name:伺服器應用名稱 Host:伺服器IP TcpPort:伺服器監聽埠 MaxConn:允許的客戶端連結最大數量 WorkerPoolSize:工作任務池最大工作Goroutine數量

I.伺服器模組Server

  func NewServer () ziface.IServer 

建立一個Zinx伺服器控制程式碼,該控制程式碼作為當前伺服器應用程式的主樞紐,包括如下功能:

1) 開啟服務

  func (s *Server) Start()

2) 停止服務

  func (s *Server) Stop()

3) 執行服務

  func (s *Server) Serve()

4) 註冊路由

func (s *Server) AddRouter (msgId uint32, router ziface.IRouter) 

5) 註冊連結建立Hook函式

func (s *Server) SetOnConnStart(hookFunc func (ziface.IConnection))

6) 註冊連結銷燬Hook函式

func (s *Server) SetOnConnStop(hookFunc func (ziface.IConnection))

II. 路由模組

//實現router時,先嵌入這個基類,然後根據需要對這個基類的方法進行重寫
type BaseRouter struct {}

//這裡之所以BaseRouter的方法都為空,
// 是因為有的Router不希望有PreHandle或PostHandle
// 所以Router全部繼承BaseRouter的好處是,不需要實現PreHandle和PostHandle也可以例項化
func (br *BaseRouter)PreHandle(req ziface.IRequest){}
func (br *BaseRouter)Handle(req ziface.IRequest){}
func (br *BaseRouter)PostHandle(req ziface.IRequest){}

III. 連結模組

1) 獲取原始的socket TCPConn

  func (c *Connection) GetTCPConnection() *net.TCPConn 

2) 獲取連結ID

  func (c *Connection) GetConnID() uint32 

3) 獲取遠端客戶端地址資訊

  func (c *Connection) RemoteAddr() net.Addr 

4) 傳送訊息

  func (c *Connection) SendMsg(msgId uint32, data []byte) error 
  func (c *Connection) SendBuffMsg(msgId uint32, data []byte) error

5) 連結屬性

//設定連結屬性
func (c *Connection) SetProperty(key string, value interface{})

//獲取連結屬性
func (c *Connection) GetProperty(key string) (interface{}, error)

//移除連結屬性
func (c *Connection) RemoveProperty(key string) 

關於作者:

作者:Aceld(劉丹冰) 簡書號:IT無崖子

mail: danbing.at@gmail.com">danbing.at@gmail.com github: https://github.com/aceld 原創書籍gitbook: http://legacy.gitbook.com/@aceld

原創宣告:未經作者允許請勿轉載,或者轉載請註明出處!

相關文章