go-zero和WebSocket 使用

前言:

七牛云 Go 模块代理。 goproxy.cn/ 解决包下载问题

WebSocket

Go语言标准套件里面没有提供对WebSocket的支持,但是在由官方维护的go.net子套件中有对这个的支持,你可以透过如下的命令取得该套件:

go get -u golang.org/x/net/websocket
type  WebSocket  struct {
}

func (ws *WebSocket) ServerWs(w http.ResponseWriter, r *http.Request) {
    websocket.Server{Handler: ws.Handler}.ServeHTTP(w, r)
}

func (sw *WebSocket) Handler(ws *websocket.Conn) {
    webSocketClient := sw.AddClinetJson(ws, "", 0)
    webSocketClient.Start()
}

go-zero

go-zero 是一个集成了各种工程实践的 web 和 rpc 框架。通过弹性设计保障了大并发服务端的稳定性,经受了充分的实战检验。
go-zero官网

go get -u github.com/zeromicro/go-zero@latest

go-zero 使用gorilla/websocket示例
github.com/zeromicro/zero-examples...

Actor

Actor模型介绍
gonet 游戏服务器架构
每个Actor都是一个独立的计算实体,Actor之间不共享数据,各个Actor只能操作自己的数据,所有的交互全部通过传递消息的方式进行,可以有效避免共享数据带来的并发竞争问题。

服务端示例代码

base.go

package base
import (
    "github.com/erdong01/kit/actor"
    "github.com/erdong01/kit/network"
)
var SERVER *MessageMrg

type MessageMrg struct {
    actor.Actor
    *network.WebSocket
}

main.go

package main

import (
 "flag"
 "fmt"
 "net/http"
 "marstm/applet/internal/config"
 "marstm/applet/internal/handler"
 "marstm/applet/internal/handler/base"
 "marstm/applet/internal/svc"
 "github.com/erdong01/kit/network"
 "github.com/zeromicro/go-zero/core/conf"
 "github.com/zeromicro/go-zero/rest"
)

var configFile = flag.String("f", "applet/etc/applet-api.yaml", "the config file")

func main() {
    flag.Parse()
    var c config.Config
    conf.MustLoad(*configFile, &c)
    server := rest.MustNewServer(c.RestConf)
    defer server.Stop()
    ctx := svc.NewServiceContext(c)
    handler.RegisterHandlers(server, ctx)
    server.AddRoute(rest.Route{
        Method: http.MethodGet,
        Path:   "/",
        Handler: func(w http.ResponseWriter, r *http.Request) {
            if r.URL.Path != "/" {
                http.Error(w, "Not found", http.StatusNotFound)
                return
            }
            if r.Method != "GET" {
                http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
                return
            }
            http.ServeFile(w, r, "home.html")
        },
    })
    handler.Init()
    server.AddRoute(rest.Route{
        Method: http.MethodGet,
        Path:   "/ws",
        Handler: func(w http.ResponseWriter, r *http.Request) {
            base.SERVER.WebSocket.ServerWs(w, r)
        },
    })
    fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
    server.Start()
}

handler.go

package handler

import (
    "context"
    "fmt"
    "marstm/api"
    "marstm/applet/internal/handler/base"
    kitApi "github.com/erdong01/kit/api"
    "github.com/erdong01/kit/network"
)

type Handler struct {
    *base.MessageMrg
}

func (a *Handler) GetHead(ctx context.Context) kitApi.JsonHead {
    rpcHead := ctx.Value("Head").(kitApi.JsonHead)
    return rpcHead
}
// 初始化
func Init() {
    base.SERVER = &base.MessageMrg{}
    base.SERVER.Actor.Init()
    var this Handler
    this.MessageMrg = base.SERVER
    base.SERVER.RegisterCall("2000", this.Connect) // 业务方法注册
    base.SERVER.Actor.Start()
    base.SERVER.WebSocket = &network.WebSocket{}
    base.SERVER.WebSocket.Init("", 9797)    base.SERVER.WebSocket.BindPacketFuncJson(base.SERVER.PacketFuncJson)
}
// 客户链接
func (h *Handler) Connect(ctx context.Context, req *api.ConnectReq) {
    head := h.GetHead(ctx)
    client :=    base.SERVER.WebSocket.GetClientById(head.SocketId)
        client.SendJson(kitApi.JsonHead{}, "2000", "链接成功", "test")
}

微信小程序

App.js

App({
    webSocketUrl: "ws://127.0.0.1:9696/ws",
    globalData: {
        socketOpen: false,
        SocketTask: {},
        SocketTimeId: 0,
    },
    openSocket() {
        var that = this
        that.SocketTask = wx.connectSocket({
            url: that.webSocketUrl
        })
        //打开时的动作
        that.SocketTask.onOpen(() => {
            console.log('WebSocket 已连接')
            that.socketOpen = true
            var session_key = wx.getStorageSync('session_key')
            that.sendSocketMessage({
                "FuncName":"2000",
                "Data":{"token":session_key}
            })
            that.SocketTimeId = setInterval(that.sendSocketMessage, 10000)
        })
        //断开时的动作
        that.SocketTask.onClose(() => {
            console.log("WebSocket 已断开")
            that.socketOpen = false
            clearInterval(that.SocketTimeId)
        })
        //报错时的动作
        that.SocketTask.onError(error => {
            console.error('socket error:', error)
        })
    },

    sendSocketMessage(msg) {
        if (this.socketOpen) {
            if (msg == undefined) {
                msg = {}
            }
            var s = JSON.stringify(msg);
            this.SocketTask.send({
                data: s
            })
        }
    },

    closeSocket() {
        var that = this
        that.SocketTask.close({
            success: () => {
                that.socketOpen = false
                clearInterval(that.SocketTimeId)
            }
        });
    },
})

测试

go服务端:

Apifox客户端:

本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 1

你这套代码是把重要的原理都包装到你的kit库里,然后给大家看脚手架吗?

10个月前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!