The process of user access to the Web site is based on the HTTP protocol, while the HTTP protocol works by: request-response, the client makes an access request, and the server responds to the request with resource data. That is, the server is always passive, even if the server-side resource data changes, if there is no request from the client, the user will not see these changes. This pattern is not suitable for certain scenarios, such as the need for social network users to know the latest information about other users in near real time. For a normal site, the request-response pattern can meet most of the functional requirements, but there are always some features we want to be able to provide users with a real-time messaging experience.
To solve this problem, there are two options to choose from:
- Still use request-response mode, just increase the frequency of requests or use long connections to achieve as close to real-time as possible, such as using polling/long-polling, but may significantly increase server load pressure or reduce server throughput
- With the new protocol, when resource data is updated on the server side, it is actively pushed to the client, such as WebSocket, although the idea is to use a long connection, but more efficient, and is the client server side of the full duplex communication. The problem is that websocket are not supported by major browsers at the moment.
Then the best way is to combine the above two scenarios, in different browsers, as far as possible using the browser support the best scenario, that is, when the browser supports the second scenario, the second option is preferred, otherwise the first scenario is used. That's what Socket.io does, and it provides a unified interface for different scenarios on both the server side and the client.
In the station letter function of our products, we want to be able to push public or private messages to online users in real time. Given that there may be other features that require real-time message push in the future, the real-time message push is implemented as a separate service. This decoupling of functions for different characteristics also paves the ways for subsequent targeted optimizations.
The system structure after decoupling is as follows:
When the site server (A) detects that a resource data update event occurs, the data is pushed to the message push server (b), and B is determined by the type of message and the target recipient of the message to decide whether to push or not.
Since our web backend is based on the YII framework, how do we implement a and B Socket.io service communication? Socket.io has its own set of protocols, and if you implement PHP libraries to interact with Socket.io services, there is some work to do. Finally, we chose to elephant.io this PHP library and encapsulate the Elephant.io as a component of the YII framework, as follows:
<?Php$basepath= Yii::Getpathofalias(' Application.vendor.elephantio.lib.ElephantIO ');Require_once($basePath.Directory_separator. ' Client.php ');Require_once($basePath.Directory_separator. ' Payload.php ');Use Elephantio\clientAs Elephant;ClassExtelephantioExtends Capplicationcomponent{ Public$host= Null; Public$port= Null; Public$namespace= Null; Private$elephant= Null; Private$ioNameSpace= Null; Public functionInit() { If ($this-Host=== Null ||$this-Port=== Null) { Throw New Exception('%s:%s:%s, please give me parameters host and Port ',BaseName(__file__),__function__,__line__); } } Public functionSetnamespace($nameSpace) { If ($this-Elephant=== Null) {$this-Elephant= New Elephant('/HTTP ' .$this-Host. ‘:‘ .$this-Port, ' Socket.io ', 1, False, True, True);$this-Elephant-Init(); }$this-Ionamespace=$this-Elephant-Createframe(Null,$nameSpace); } Public functionSendmsg($event,$msg) { If ($this-Ionamespace=== Null) { If ($this-Namespace !== Null) {$this-Ionamespace=$this-Elephant-Createframe(Null,$this-Namespace); } Else { Throw New Exception('%s:%s:%s, please setnamespace before Sendmsg ',BaseName(__file__),__function__,__line__); } }$this-ionamespace->emit ( $event , $msg } public function Close () { $this ->elephant->close (); $this ->elephant = Span class= "KWD" >null; }} /span>
Place the code file under the app directory extensions, and add the following configuration entry for Yii:
' Components ' = Array ( ' elephantio ' Span class= "pun" >=> Array ( ' class ' Span class= "PLN" > => Application.extensions.extElephantIO ' , ' host ' => ' xxx ' ' Port ' => Xxx, ... ),
When a resource data change event occurs, such as a downgrade to send a message to the message Push server:
$elephant = Yii::app()->ElephantIO;$elephant->setNameSpace(‘/message_namespace‘);$elephant->sendMsg( ‘message_event_type‘, $messageContent); $elephant->close();
For private message push, how can I tell if a user is currently online? How do I verify a user's identity?
Can be implemented based on cookies, Socket.io provided by the browser-side JS library, in each connection, and ordinary HTTP request, will carry the site domain name of the cookie (our message push service domain name for the site server domain name of the subdomain, so can get the site domain name of all cookies), the message Push server received the connection (Connection event) request, remove all cookies from the connection and forward them to an API on the site Web backend, which validates the user identity based on the cookie and returns the user information to the message push server. The message Push server stores the current connection object based on user information, and then pushes the message to the user through the Connection object when the site server sends the user's message to the message push server.
For public messages, which are broadcast messages, the implementation is relatively straightforward and pushes messages directly to all current connections.
Perhaps some people will ask, since in some browsers Socket.io will degenerate to use polling/long-polling to transmit messages, then compared directly to the site server polling/long-polling, what is the advantage?
I think the advantage has two points:
- The Nodejs asynchronous event callback method is suitable for large concurrent long-connected scenarios. If the Web backend is implemented using PHP, it is better suited for short-connected services.
- Decoupling the business logic of the site from the message push logic, then when the browser obtains the message through polling/long-polling, it involves only the message push logic, the code that does not need to execute the business logic, and the code of the business logic can be complex, and every time polling, it needs to be executed again. Will waste a lot of resources on the server.
Real-time message push based on Socket.io