Location>code7788 >text

PHP to Go Series | ThinkPHP and Gin framework to build WebSocket technology based on the message push center

Popularity:203 ℃/2024-08-15 09:10:03

Hi everyone, I'm Codemaster Pioneer.

Some years ago the client wanted to get the latest news in real time, it was to use long polling to get data from the server, this kind of rough operation is really not elegant. Nowadays, however, I still see people still use it in some scenarios, such as scanning the QR code on the PC, and then using long polling to get the latest scanning information from the server to determine whether the user has finished scanning the code, such as this scenario, there are still a lot of. In fact, we all know that long polling is not good, so why do people still use it?

I think the most direct reason is that it's "easy to develop" and human nature dictates that humans are an advanced species that tends to take refuge in what's easy to use, so that's what we'll use. However, what I want to express is that besides the long polling method, WebSocket technology is actually not that difficult, but for those who have never been exposed to long connections, there will be some obstacles in their minds when they start to use it. This time I share the content is based on WebSocket technology, message push center, it seems very high, in fact, through some small examples to demonstrate that the process of pushing data from the server to the client, the next example is simple and easy to get started, let's hurry up and start it.

Without further ado, let's get started! Let's take a look at the overall project directory structure, which is divided into two main sections, PHP and Go.

[manongsen@root php_to_go]$ tree -L 2
.
├── go_websocket
│   ├── app
│   │   ├── controller
│   │   |	|── 
│   │   │   └── 
│   │   └── 
│   ├── 
│   ├── 
│   └── 
└── php_websocket
│   ├── app
│   │   ├── controller
│   │   |	|── 
│   │   │   └── 
│   ├── 
│   ├── 
│   ├── config
│   │   |── worker_server.php
│   │   └── 
│   ├── route
│   │   └── 
│   ├── think
│   ├── vendor
│   └── .env

ThinkPHP

Use composer to create a php_websocket project based on the ThinkPHP framework.

## current catalog
[manongsen@root ~]$ pwd
/home/manongsen/workspace/php_to_go/php_websocket

## mounting ThinkPHP organizing plan
[manongsen@root php_websocket]$ composer create-project topthink/think php_websocket
[manongsen@root php_websocket]$ cp . .env

## mounting Composer dependency package
[manongsen@root php_websocket]$ composer require topthink/think-worker
[manongsen@root php_websocket]$ composer require predis/predis

utilizationphp think make:controller Worker command to create Controller. The main implementation in this controller is theonWorkerStart This method, first adds a Timer asynchronous timer, then reads the message from the Redis queue, and finally pushes the message to the client, which is scheduled at one-second intervals.

// ./php_to_go/php_websocket/app/controller/
<?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) {
        // Add an asynchronous timer task
        Timer::add(1, function () use ($worker) {
            // Reading messages from the message center queue
            $redis = Cache::store('redis')->handler();
            $content = $redis->rpop(Env::get("MESSAGE_CENTER_KEY"));

            // Send a message to the client
            foreach ($worker->connections as $connection) {
                if (!empty($content)) {
                    $connection->send("PHPLanguage News Center: " . $content);
                }
            }
        });
    }

    public function onWorkerReload($worker) {
    }

    public function onConnect($connection) {
    }

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

    public function onClose($connection) {
    }

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

utilizationphp think make:controller Push command to create Controller. The main role of this controller is to receive external message content and then push it to the Redis message queue, where an API interface is provided that can be called from an external backend system.

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

namespace app\controller;

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

class Push extends BaseController
{
    public function msg()
    {
        // reception (of transmitted signal) GET parameters
        $params = $this->request->param();
        if (empty($params["content"])) {
            return json(["code" => -1, "msg" => "Content cannot be empty"]);
        }
        $content = $params["content"];

        // Push messages to message center queue
        $redis = Cache::store('redis')->handler();
        $redis->lpush(Env::get("MESSAGE_CENTER_KEY"), $content);

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

first runphp think worker Start the HTTP service and runphp think worker:server Start the WebSocket service for a final test wave.

Gin

Initialize the go_websocket project with the go mod.

## Current directory
[manongsen@root ~]$ pwd
/home/manongsen/workspace/php_to_go/go_websocket

## Initialize the project
[manongsen@root go_websocket]$ go mod init go_websocket

## Install third-party dependencies
[manongsen@root go_websocket]$ go get /gin-gonic/gin
[manongsen@root go_websocket]$ go get /gorilla/websocket

Create a websocket controller in the go_websocket project. This controller stores the client connections in the specified Map data structure and also provides theWaitMessage The method of waiting for a message, if it is received from theMsgQueue The message is read in the channel, then the message is pushed to all clients.

// . /php_to_go/go_websocket/app/controller/
package controller

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

"/gin-gonic/gin"
"/gorilla/websocket"
)

// Define a messaging channel
var MsgQueue = make(chan string, 10)

// Define a Map to store client connections
var Clients = make(map[*]bool)

// Upgrade the HTTP protocol to the WebSocket protocol.
var upgrader = {
CheckOrigin: func(r *) bool {
return true // Allow all sources
}, }
}

// Store client connections to Map
func HandleConnection(c *) {
conn, err := (, , nil)
if err ! = nil {
("Client connection protocol upgrade failed: %v\n", err)
return
}
Clients[conn] = true
}

// Waiting for a message
func WaitMessage() {
go func() {
for {
select {
case msg, ok := <-MsgQueue.
if ok {
for client := range Clients {
err := (, []byte("Go Language Message Center: " + string(msg)))
if err ! = nil {
("Message push failed: %v\n", err)
}
}
}
default.
// Avoid busy etc.
(500 * )
}
}
}()
}

Create a message controller in the go_websocket project. The main purpose of this controller is to receive external messages and push them to the MsgQueue channel, which provides an API interface that can be called from an external backend system. The difference between this and PHP is that in Go you don't need to introduce a third-party component like Redis, but instead you can use your own channel features to deliver the message.

// ./php_to_go/go_websocket/app/controller/
package controller

import (
	"net/http"

	"/gin-gonic/gin"
)

func PushMsg(c *) {
	// reception (of transmitted signal) GET parameters
	content := ("content")
	if len(content) == 0 {
		(, {
			"msg":  "Content cannot be empty",
			"code": -1,
		})
		return
	}

	// Push message to channel
	MsgQueue <- content

	(, {
		"msg":  "ok",
		"code": 0,
	})
}

(of a computer) rungo run Start the service and then perform a message push test.

Through these two simple examples, I believe that you have been on WebSocket technology has been some understanding of it. From the examples can also be seen, in fact, in PHP and Go in the realization of the difference, PHP needs to start two services, one is the HTTP service, a WebSocket service, and both services are directly separate processes, can not communicate with each other, you need to additional use of third-party middleware Redis to achieve the transfer of data. On the contrary, in Go, one service covers both HTTP service and WebSocket service, sharing the data resources of one process, and passing messages through the use of Channel channel.

In addition, in PHP, you need to use the Timer asynchronous timer to read the data in the Redis message queue, and you can't use a for loop or Redis's blocking queue because it will block the entire process execution. In Go, on the other hand, you can open a thread and wait for the message in the channel in the thread, and it will block until the message arrives, and it will not block the entire process, which shows that Go has a significant advantage over PHP in this case. Finally, some people who have never used WebSocket technology may still be confused after reading this article, so I suggest that these friends can personally practice the cases in the article, and after practicing, I believe you will have a different technical experience. If you want to get the complete case code of friends, you can reply "2463" in the public number, I hope you can help.

Thanks for reading, personal opinion is for reference only, welcome to express different views in the comment section.

Welcome to follow, share, like, favorite, in the watch, I'm the author of WeChat public number "code farmer forefather".