当前位置:首页 > 后端开发 > 正文内容

PHP转Go系列 | ThinkPHP与Gin结构之打造根据WebSocket技能的音讯推送中心

邻居的猫1个月前 (12-09)后端开发1078

咱们好,我是码农先森。

在早些年前客户端想要实时获取到最新音讯,都是运用定时长轮询的办法,不断的从服务器上获取数据,这种粗犷的骚操作实属不雅观。不过现如今我也还见有人还在一些场景下运用,比如在 PC 端扫描二维码,然后运用长轮询的办法从服务端获取最新的扫码信息,来判别用户是否现已扫码完结,比如这种场景还有不少。其实咱们都知道长轮询的办法欠好,那为什么还有人运用呢?

我想最直接的原因便是「开发起来简略明了」,人道决议了人类都是趋易流亡的高档物种,那个简略上手就用那个。可是我想表达的是除了长轮询的办法外,WebSocket 技能其实也不难,只不过关于从来没有触摸过长衔接的人来说,刚开端上手时会有一些思想上的妨碍。这次我同享的内容是根据 WebSocket 技能的音讯推送中心,看起来很巨大上,其实也便是经过一些小的比如来演示,从服务端推送数据到客户端的这个进程,接下来的比如简略明了简略上手,咱们赶忙开端吧。

话不多说,开整!咱们先来看一下全体的项目目录结构,内容首要分为 PHP 和 Go 两部分。

[manongsen@root php_to_go]$ tree -L 2
.
├── go_websocket
│   ├── app
│   │   ├── controller
│   │   |	|── message.go
│   │   │   └── websocket.go
│   │   └── route.go
│   ├── go.mod
│   ├── go.sum
│   └── main.go
└── php_websocket
│   ├── app
│   │   ├── controller
│   │   |	|── Push.php
│   │   │   └── Worker.php
│   ├── composer.json
│   ├── composer.lock
│   ├── config
│   │   |── worker_server.php
│   │   └── worker.php
│   ├── route
│   │   └── app.php
│   ├── think
│   ├── vendor
│   └── .env

ThinkPHP

运用 composer 创立根据 ThinkPHP 结构的 php_websocket 项目。

## 当时目录
[manongsen@root ~]$ pwd
/home/manongsen/workspace/php_to_go/php_websocket

## 装置 ThinkPHP 结构
[manongsen@root php_websocket]$ composer create-project topthink/think php_websocket
[manongsen@root php_websocket]$ cp .example.env .env

## 装置 Composer 依靠包
[manongsen@root php_websocket]$ composer require topthink/think-worker
[manongsen@root php_websocket]$ composer require predis/predis

运用 php think make:controller Worker 指令创立 Worker.php 控制器。这个控制器中首要完成了 onWorkerStart 这个办法,首要增加了一个 Timer 异步定时器,然后从 Redis 行列中读取音讯,最终将音讯推送到客户端,这个定时器会每距离一秒钟调度一次。

// ./php_to_go/php_websocket/app/controller/Worker.php
<?php
declare (strict_types = 1);

namespace app\controller;

use think\Request;
use think\worker\Server;
use Workerman\Lib\Timer;
use think\facade\Cache;
use think\facade\Env;

class Worker extends Server
{
    protected $socket = 'websocket://0.0.0.0:2345';
    protected static $connections = [];

    public function onWorkerStart($worker) {
        // 增加一个异步定时器使命
        Timer::add(1, function () use ($worker) {
            // 从音讯中心行列中读取音讯
            $redis = Cache::store('redis')->handler();
            $content = $redis->rpop(Env::get("MESSAGE_CENTER_KEY"));

            // 发送音讯到客户端
            foreach ($worker->connections as $connection) {
                if (!empty($content)) {
                    $connection->send("PHP言语音讯中心: " . $content);
                }
            }
        });
    }

    public function onWorkerReload($worker) {
    }

    public function onConnect($connection) {
    }

	public function onMessage($connection, $data){
	}

    public function onClose($connection) {
    }

    public function onError($connection, $code, $msg) {
    }
}

运用 php think make:controller Push 指令创立 Push.php 控制器。这个控制器的首要作用是接纳外部的音讯内容,然后推送到 Redis 音讯行列中,这儿供给的是 API 接口,这个接口能够在外部的后台体系调用。

// ./php_to_go/php_websocket/app/controller/Push.php
<?php

namespace app\controller;

use app\BaseController;
use think\facade\Cache;
use think\facade\Env;

class Push extends BaseController
{
    public function msg()
    {
        // 接纳 GET 参数
        $params = $this->request->param();
        if (empty($params["content"])) {
            return json(["code" => -1, "msg" => "内容不能为空"]);
        }
        $content = $params["content"];

        // 推送音讯到音讯中心行列
        $redis = Cache::store('redis')->handler();
        $redis->lpush(Env::get("MESSAGE_CENTER_KEY"), $content);

        return json(["code" => 0, "msg" => "success"]);
    }
}

先运转 php think worker 发动 HTTP 服务,再运转 php think worker:server 发动 WebSocket 服务,最终来测验一波。

Gin

经过 go mod 初始化 go_websocket 项目。

## 当时目录
[manongsen@root ~]$ pwd
/home/manongsen/workspace/php_to_go/go_websocket

## 初始化项目
[manongsen@root go_websocket]$ go mod init go_websocket

## 装置第三方依靠库
[manongsen@root go_websocket]$ go get github.com/gin-gonic/gin
[manongsen@root go_websocket]$ go get github.com/gorilla/websocket

在 go_websocket 项目中创立 websocket 控制器。这个控制器会将客户端衔接存储到指定的 Map 数据结构中,其次还供给了 WaitMessage 等候音讯的办法,假如从 MsgQueue 通道中读取到了音讯,则把音讯推送给一切的客户端。

// ./php_to_go/go_websocket/app/controller/websocket.php
package controller

import (
	"fmt"
	"net/http"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
)

// 界说一个音讯传输通道
var MsgQueue = make(chan string, 10)

// 界说一个存储客户端衔接的 Map
var Clients = make(map[*websocket.Conn]bool)

// 将 HTTP 协议晋级至 WebSocket 协议
var upgrader = websocket.Upgrader{
	CheckOrigin: func(r *http.Request) bool {
		return true // 答应一切来历
	},
}

// 将客户端衔接存储到 Map
func HandleConnection(c *gin.Context) {
	conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
	if err != nil {
		fmt.Printf("客户端衔接协议晋级失利: %v\n", err)
		return
	}
	Clients[conn] = true
}

// 等候音讯中
func WaitMessage() {
	go func() {
		for {
			select {
			case msg, ok := <-MsgQueue:
				if ok {
					for client := range Clients {
						err := client.WriteMessage(websocket.TextMessage, []byte("Go言语音讯中心: "+string(msg)))
						if err != nil {
							fmt.Printf("音讯推送失利: %v\n", err)
						}
					}
				}
			default:
				// 防止忙等
				time.Sleep(500 * time.Millisecond)
			}
		}
	}()
}

在 go_websocket 项目中创立 message 控制器。这个控制器的首要作用是接纳外部的音讯内容,然后推送到 MsgQueue 通道中,这儿供给的是 API 接口,这个接口能够在外部的后台体系调用。这儿和 PHP 中有一点不同的是,在 Go 中无需引进像 Redis 相同的第三方组件,而是运用本身的 Channel 特性即可完成音讯的传递。

// ./php_to_go/go_websocket/app/controller/message.php
package controller

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

func PushMsg(c *gin.Context) {
	// 接纳 GET 参数
	content := c.Query("content")
	if len(content) == 0 {
		c.JSON(http.StatusOK, gin.H{
			"msg":  "内容不能为空",
			"code": -1,
		})
		return
	}

	// 往通道推送音讯
	MsgQueue <- content

	c.JSON(http.StatusOK, gin.H{
		"msg":  "ok",
		"code": 0,
	})
}

运转 go run main.go 发动服务,然后进行音讯推送测验。

经过这两个简略的比如,我信任咱们现已对 WebSocket 技能现已有所了解吧。从比如中也能够看出来,其实在 PHP 和 Go 中完成上有所区别,PHP 中需求发动两个服务,一个是 HTTP 服务,一个是 WebSocket 服务,并且两者服务直接都是独自的进程,不能彼此通讯,需求额定凭借第三方中间件 Redis 来完成数据的传输。反观 Go 中直接一个服务涵盖了 HTTP 服务和 WebSocket 服务,同享一个进程的数据资源,经过运用 Channel 通道传递音讯。

此外,在 PHP 中需求运用 Timer 异步定时器来读取 Redis 音讯行列中的数据,不能用 for 循环或许 Redis 的堵塞行列,由于它会堵塞整个进程的履行。而在 Go 中直接敞开一个协程,在协程中等候通道中的音讯即可,会一向堵塞到音讯的到来,并且它不会堵塞整个进程的履行,由此可见在这个比如中 Go 相较于 PHP 的优势明显。最终或许有些从来没有运用过 WebSocket 技能的朋友,或许看完这篇文章之后也仍然会云里雾里,所以主张这些朋友能够自己亲身实践一下文中的事例,实践往后我信任你会别有一番技能体会。假如有想要获取完好事例代码的朋友,能够在大众号内回复「2463」即可,期望对咱们能有所协助。

感谢咱们阅览,个人观念仅供参考,欢迎在谈论区宣布不同观念。

欢迎重视、同享、点赞、保藏、在看,我是微信大众号「码农先森」作者。

扫描二维码推送至手机访问。

版权声明:本文由51Blog发布,如需转载请注明出处。

本文链接:https://www.51blog.vip/?id=194

分享给朋友:

“PHP转Go系列 | ThinkPHP与Gin结构之打造根据WebSocket技能的音讯推送中心” 的相关文章

第73篇 IdentityServer4的简略介绍

第73篇 IdentityServer4的简略介绍

1.什么是IdentityServer4? 它是一个中间件服务结构,集成OIDC与OAuth2.0, 便利建立恣意多个项目。 IdentityServer4的组成 Identity身份 Server服务器 4版别 源代码:https://github.com/IdentityServer 【1 ~...

ctfshow--web入门--文件上传

ctfshow--web入门--文件上传

ctfshow--web入门--文件上传 目录...

Flutter/Dart第11天:Dart函数办法详解

Flutter/Dart第11天:Dart函数办法详解

Dart官方文档:https://dart.dev/language/functions 重要说明:本博客依据Dart官网文档,但并不是简略的对官网进行翻译,在掩盖中心功用情况下,我会依据个人研制经历,参加自己的一些扩展问题和场景验证。 Dart言语是纯面向目标的编程言语,便是是函数也是目标,它的类...

【日记】每次修机器都有些头疼(721 字)

【日记】每次修机器都有些头疼(721 字)

正文   这一连几天都下雨,冷死了。   基本上玩了一天。没怎样干活儿。下午计划写完至少一篇文章,成果难产了。   晚上接到了搬去 5 楼的指令,这次没得商议。头疼。时刻在明日晚上。   晚上总算仍是不由得略微动了一下,成果感觉膝盖的伤要复发了……   又回到了书荒的状况。得找新书看了。   May...

go-live,什么是Go-Live?

go-live,什么是Go-Live?

“Go live”通常有几种不同的含义,具体取n2. 活动或项目启动:在某些情况下,“go live”也用于描述一个活动或项目的正式启动。例如,一个公司可能会宣布某个新服务或产品“go live”,意味着该服务或产品开始正式提供。3. 实时互动或直播:在娱乐或活动策划领域,“go live”可以指...

php递归,原理、应用与优化

php递归,原理、应用与优化

在PHP中,递归是一种常用的编程技巧,它允许函数调用自身,从而解决需要重复执行相同操作的问题。递归在处理树形结构、图结构或执行分治算法时特别有用。下面我将介绍PHP中递归的基本概念和一些使用示例。 基本概念1. 递归函数:一个函数如果在其定义中调用了自身,那么这个函数就是递归函数。2. 基准条件:在...