28

Zinx --基于Golang的轻量级并发服务器框架

 4 years ago
source link: https://studygolang.com/articles/24597
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Zinx 是一个基于Golang的轻量级并发服务器框架

说明:目前zinx已经在很多企业进行开发使用,具体使用领域包括:后端模块的消息中转、长链接游戏服务器、Web框架中的消息处理插件等。zinx的定位是代码简洁,让更多的开发者迅速的了解框架的内脏细节并且可以快速基于zinx DIY一款适合自己企业场景的模块。

开发者

zinx(C++版本)

开发者

Github

Git: github.com/aceld/zinx

码云(Gitee)

Git: gitee.com/Aceld/zinx

在线开发教程

[B站]

NzMJryF.jpg!web

[YouTube]

6F3eeq6.jpg!web

一、写在前面

我们为什么要做Zinx,Golang目前在服务器的应用框架很多,但是应用在游戏领域或者其他长链接的领域的轻量级企业框架甚少。

设计Zinx的目的是我们可以通过Zinx框架来了解基于Golang编写一个TCP服务器的整体轮廓,让更多的Golang爱好者能深入浅出的去学习和认识这个领域。

Zinx框架的项目制作采用编码和学习教程同步进行,将开发的全部递进和迭代思维带入教程中,而不是一下子给大家一个非常完整的框架去学习,让很多人一头雾水,不知道该如何学起。

教程会一个版本一个版本迭代,每个版本的添加功能都是微小的,让一个服务框架小白,循序渐进的曲线方式了解服务器框架的领域。

当然,最后希望Zinx会有更多的人加入,给我们提出宝贵的意见,让Zinx成为真正的解决企业的服务器框架!在此感谢您的关注!

zinx荣誉

开源中国GVP年度最有价值开源项目

Iz6f6nn.jpg!web

二、初探Zinx架构

AfMfMv6.png!webyUbeYbv.gif

三、Zinx详细教程及文档

《Zinx框架教程-基于Golang的轻量级并发服务器》

四、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 v-0.10 demoApp",
  "Host":"127.0.0.1",
  "TcpPort":7777,
  "MaxConn":3,
  "WorkerPoolSize":10,
  "LogDir": "./mylog",
  "LogFile":"zinx.log"
}
复制代码

Name :服务器应用名称

Host :服务器IP

TcpPort :服务器监听端口

MaxConn :允许的客户端链接最大数量

WorkerPoolSize :工作任务池最大工作Goroutine数量

LogDir : 日志文件夹

LogFile : 日志文件名称(如果不提供,则日志信息打印到Stderr)

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(刘丹冰)

mail : [email protected] github : github.com/aceld 原创书籍gitbook : legacy.gitbook.com/@aceld


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK