Practical development case 2 of using swote project in laravel (back end actively divides scenarios to push messages to the interface)

Time:2020-10-18

Recommended reading:Practical development case 1 of using swote project in laravel (establishing swote and front-end communication)

 

requirement analysis

Let’s assume that there is a requirement. I click button 1 in the back end, and “button 1 triggered by the back end” pops up on the home page. Click button 2 at the back end, and “button 2 triggered by back end” pops up in the list page. Push to different pages according to different scenarios.

Code ideas

  • Swoole fd
    When the client browser opens or refreshes the interface, a process handle FD will be generated in the swote service. Every time the browser page has JS code linking websocket, it will be generated. Each time the browser page refreshes, it will close the previously opened FD and regenerate a new one. When closing the interface, a new one will be generated. The FD generation rule of droole is incremented from 1.
  • Redis hash storage FD
    We create a key for swoole:fds Redis hash type data, FD is the hash field. The value of each field is stored in the URL parameter information of the front-end websocket request (flexible according to the business complexity, I will carry the sessionid in the URL in the project). Every time a link opens the tool service, we store its information, and every time we close the page, we clear its fields. Store in redis as follows

 

    • Trigger sub scenario push
      When the trigger operation is performed on the interface, the spool HTTP service is requested through curl in the background. The spool HTTP service is distributed to the corresponding logical processing according to the parameters you pass to me. If curl requests127.0.0.1:9502page=back&func=pushHomeLogic&token=123456We can distribute it to the corresponding logic processing in the background according to the func parameter passed in. If it is distributed to the pushhomelogic method. Realize your own logic in it. In order to prevent too many “ifelse” and “foreach” operations, we use closure, call_ user_ Func and other methods are implemented as follows

 

1 public function onRequest($request,$response)
 2 {
 3     if ($this->checkAccess("", $request)) {
 4         $param = $request->get;
 5 // distribution processing request logic
 6         if (isset($param['func'])) {
 7             if (method_exists($this,$param['func'])) {
 8                 call_user_func([$this,$param['func']],$request);
 9             }
10         }
11     }
12} // push logic processing to the home page
13 public function pushHomeLogic($request)
14 {
15     $callback = function (array $aContent,int $fd,SwooleDemo $oSwoole)use($request) {
16         if ($aContent && $aContent['page'] == "home") {
17 $ares ['message '] = button 1 is pressed at the back end;
18             $aRes['code'] = "200";
19             $oSwoole::$server->push($fd,xss_json($aRes));
20         }
21     };
22     $this->eachFdLogic($callback);
23 }

 

Complete code

Code logic of SWOT script

1 redis = Redis::connection('websocket');
 27         $server = self::getWebSocketServer();
 28         $server->on('open',[$this,'onOpen']);
 29         $server->on('message', [$this, 'onMessage']);
 30         $server->on('close', [$this, 'onClose']);
 31         $server->on('request', [$this, 'onRequest']);
 32 $this - > line ("spoole service started successfully...);
 33         $server->start();
 34     }
 35 
 36 // get service
 37     public static function getWebSocketServer()
 38     {
 39         if (!(self::$server instanceof \swoole_websocket_server)) {
 40             self::setWebSocketServer();
 41         }
 42         return self::$server;
 43     }
 44 // service setting
 45     protected static function setWebSocketServer():void
 46     {
 47         self::$server  = new \swoole_websocket_server("0.0.0.0", 9502);
 48         self::$server->set([
 49             'worker_num' => 1,
 50             'heartbeat_ check_ Interval '= > 60, // detect once in 60 seconds
 51             'heartbeat_ idle_ Time '= > 121, // 121 seconds inactive
 52         ]);
 53     }
 54 
 55 // open the spool websocket service callback code
 56     public function onOpen($server, $request)
 57     {
 58         if ($this->checkAccess($server, $request)) {
 59             self::$server->push($request->fd,xss_ JSON (["code" = > 200, "message" = > "swoole service opened successfully"]);
 60         }
 61     }
 62 // send message callback code to swote websocket
 63     public function onMessage($server, $frame)
 64     {
 65 
 66     }
 67 // HTTP request spool websocket callback code
 68     public function onRequest($request,$response)
 69     {
 70         if ($this->checkAccess("", $request)) {
 71             $param = $request->get;
 72 // distribution processing request logic
 73             if (isset($param['func'])) {
 74                 if (method_exists($this,$param['func'])) {
 75                     call_user_func([$this,$param['func']],$request);
 76                 }
 77             }
 78         }
 79     }
 80 
 81 // websocket close callback code
 82     public function onClose($serv,$fd)
 83     {
 84         $this->redis->hdel('swoole:fds', $fd);
 85 $this - > line ("client {$FD} closed");
 86     }
 87 
 88 // verify the validity of the client connection. Invalid connections are not allowed
 89     public function checkAccess($server, $request):bool
 90     {
 91         $bRes = true;
 92         if (!isset($request->get) || !isset($request->get['token'])) {
 93             self::$server->close($request->fd);
 94 $this - > line ("incomplete interface verification fields");
 95             $bRes = false;
 96         } else if ($request->get['token'] != 123456) {
 97 $this - > line ("interface validation error");
 98             $bRes = false;
 99         }
100         $this->storeUrlParamToRedis($request);
101         return $bRes;
102     }
103 
104 // store the URL of websocket in each interface
105     public function storeUrlParamToRedis($request):void
106     {
107 // store the information of the request URL
108         $sContent = json_encode(
109             [
110                 'page' => $request->get['page'],
111                 'fd' => $request->fd,
112             ], true);
113         $this->redis->hset("swoole:fds", $request->fd, $sContent);
114     }
115 
116     /**
117      * @param $request
118 * @ see circular logic processing
119      */
120     public function eachFdLogic(Closure $callback = null)
121     {
122         foreach (self::$server->connections as $fd) {
123             if (self::$server->isEstablished($fd)) {
124                 $aContent = json_decode($this->redis->hget("swoole:fds",$fd),true);
125                 $callback($aContent,$fd,$this);
126             } else {
127                 $this->redis->hdel("swoole:fds",$fd);
128             }
129         }
130     }
131 // push logic processing to the home page
132     public function pushHomeLogic($request)
133     {
134         $callback = function (array $aContent,int $fd,SwooleDemo $oSwoole)use($request) {
135             if ($aContent && $aContent['page'] == "home") {
136 $ares ['message '] = button 1 is pressed at the back end;
137                 $aRes['code'] = "200";
138                 $oSwoole::$server->push($fd,xss_json($aRes));
139             }
140         };
141         $this->eachFdLogic($callback);
142     }
143 // push logical processing to the list page
144     public function pushListLogic($request)
145     {
146         $callback = function (array $aContent,int $fd,SwooleDemo $oSwoole)use($request) {
147             if ($aContent && $aContent['page'] == "list") {
148 $ares ['message '] = button 2 is pressed at the back end;
149                 $aRes['code'] = "200";
150                 $oSwoole::$server->push($fd,xss_json($aRes));
151             }
152         };
153         $this->eachFdLogic($callback);
154     }
155 
156 // start websocket service
157     public function start()
158     {
159         self::$server->start();
160     }
161 }
162 controller code
163 
164 method() == 'POST') {
186            $this->curl_get($this->getUrl());
187            return json_ Encode (['code '= > 200, "message" = > "success");
188         } else {
189             return view("back");
190         }
191 
192     }
193 // obtain the service address of the swoole websocet to be requested
194     public function getUrl():string
195     {
196 // method for domain name port to request the swote service
197         $sBase = request()->server('HTTP_HOST');
198         $iPort = 9502;
199         $sFunc = request()->post('func');
200         $sPage = "back";
201         return $sBase.":".$iPort."?func=".$sFunc."&token=123456&page=".$sPage;
202     }
203 // curl push
204     public function curl_get(string $url):string
205     {
206         $ch_curl = curl_init();
207         curl_setopt ($ch_curl, CURLOPT_TIMEOUT_MS, 3000);
208         curl_setopt($ch_curl, CURLOPT_SSL_VERIFYPEER, 0);
209         curl_setopt ($ch_curl, CURLOPT_HEADER,false);
210         curl_setopt($ch_curl, CURLOPT_HTTPGET, 1);
211         curl_setopt($ch_curl, CURLOPT_RETURNTRANSFER,true);
212         curl_setopt ($ch_curl, CURLOPT_URL,$url);
213         $str  = curl_exec($ch_curl);
214         curl_close($ch_curl);
215         return $str;
216     }
217 }

 

Page JS code

  • Back end control page
1 
 2 
 3 
 4 
 5 back end interface
 6 
 7 
 8 
 9 button 1
10 button 2
11 
12 
13 
14 $(function () {
15     $(".push").on('click',function(){
16         var func = $(this).attr('data-func').trim();
17         ajaxGet(func)
18     })
19     function ajaxGet(func) {
20         url = "{{route('back')}}";
21         token = "{{csrf_token()}}";
22         $.ajax({
23             url: url,
24             type: 'post',
25             dataType: "json",
26             data:{func:func,_token:token},
27             error: function (data) {
28 alert ("the server is busy, please contact the administrator! "";
29                 return;
30             },
31             success: function (result) {
32 
33             },
34         })
35     }
36 
37 })
38 
39

 

home page

1 
 2 
 3 
 4 
 5 swote Homepage
 6 
 7 
 8 
 This is the front page
10 
11 
12 var WS; // websocket instance
13 var lockconnect = false; // avoid duplicate connection
14 var wsUrl = 'ws://{{$_SERVER["HTTP_HOST"]}}:9502?page=home&token=123456';
15 
16 function initEventHandle() {
17     ws.onclose = function () {
18         reconnect(wsUrl);
19     };
20     ws.onerror = function () {
21         reconnect(wsUrl);
22     };
23     ws.onopen = function () {
24 // heartbeat detection reset
25         heartCheck.reset().start();
26     };
27     ws.onmessage = function (event) {
28 // if the message is obtained, the heartbeat detection is reset
29 // getting any message indicates that the current connection is normal
30         var data = JSON.parse(event.data);
31         if (data.code == 200) {
32             console.log(data.message)
33         }
34         heartCheck.reset().start();
35     }
36 }
37 createWebSocket(wsUrl);
38 /**
39 * create link
40  * @param url
41  */
42 function createWebSocket(url) {
43     try {
44         ws = new WebSocket(url);
45         initEventHandle();
46     } catch (e) {
47         reconnect(url);
48     }
49 }
50 function reconnect(url) {
51     if(lockReconnect) return;
52     lockReconnect = true;
53 // if there is no connection, it will be reconnected all the time. Set the delay to avoid too many requests
54     setTimeout(function () {
55         createWebSocket(url);
56         lockReconnect = false;
57     }, 2000);
58 }
59 // heartbeat detection
60 var heartCheck = {
61 timeout: 60000, // 60 seconds
62     timeoutObj: null,
63     serverTimeoutObj: null,
64     reset: function(){
65         clearTimeout(this.timeoutObj);
66         clearTimeout(this.serverTimeoutObj);
67         return this;
68     },
69     start: function(){
70         var self = this;
71         this.timeoutObj = setTimeout(function(){
72 // a heartbeat is sent here. After receiving the heartbeat message, a heartbeat message is returned,
73 // onmessage getting the heartbeat back indicates that the connection is normal
74             ws.send("heartbeat");
seventy-five self.serverTimeoutObj  =SetTimeout (function() {// if it has not been reset after a certain period of time, it indicates that the back end has been actively disconnected
seventy-six ws.close (); // if onclose will execute reconnect, we will execute ws.close () is OK. If you execute reconnect directly, onclose will be triggered, resulting in reconnection twice
77             }, self.timeout);
78         }, this.timeout);
79     },
80     header:function(url) {
81         window.location.href=url
82     }
83 
84 }
85 
86

 

List page

1 
 2 
 3 
 4 
 5 swote list page
 6 
 7 
 8 
 9 swote list page
10 
11 
12 var WS; // websocket instance
13 var lockconnect = false; // avoid duplicate connection
14 var wsUrl = 'ws://{{$_SERVER["HTTP_HOST"]}}:9502?page=list&token=123456';
15 
16 function initEventHandle() {
17     ws.onclose = function () {
18         reconnect(wsUrl);
19     };
20     ws.onerror = function () {
21         reconnect(wsUrl);
22     };
23     ws.onopen = function () {
24 // heartbeat detection reset
25         heartCheck.reset().start();
26     };
27     ws.onmessage = function (event) {
28 // if the message is obtained, the heartbeat detection is reset
29 // getting any message indicates that the current connection is normal
30         var data = JSON.parse(event.data);
31         if (data.code == 200) {
32             console.log(data.message)
33         }
34         heartCheck.reset().start();
35     }
36 }
37 createWebSocket(wsUrl);
38 /**
39 * create link
40  * @param url
41  */
42 function createWebSocket(url) {
43     try {
44         ws = new WebSocket(url);
45         initEventHandle();
46     } catch (e) {
47         reconnect(url);
48     }
49 }
50 function reconnect(url) {
51     if(lockReconnect) return;
52     lockReconnect = true;
53 // if there is no connection, it will be reconnected all the time. Set the delay to avoid too many requests
54     setTimeout(function () {
55         createWebSocket(url);
56         lockReconnect = false;
57     }, 2000);
58 }
59 // heartbeat detection
60 var heartCheck = {
61 timeout: 60000, // 60 seconds
62     timeoutObj: null,
63     serverTimeoutObj: null,
64     reset: function(){
65         clearTimeout(this.timeoutObj);
66         clearTimeout(this.serverTimeoutObj);
67         return this;
68     },
69     start: function(){
70         var self = this;
71         this.timeoutObj = setTimeout(function(){
72 // a heartbeat is sent here. After receiving the heartbeat message, a heartbeat message is returned,
73 // onmessage getting the heartbeat back indicates that the connection is normal
74             ws.send("heartbeat");
seventy-five self.serverTimeoutObj  =SetTimeout (function() {// if it has not been reset after a certain period of time, it indicates that the back end has been actively disconnected
seventy-six ws.close (); // if onclose will execute reconnect, we will execute ws.close () is OK. If you execute reconnect directly, onclose will be triggered, resulting in reconnection twice
77             }, self.timeout);
78         }, this.timeout);
79     },
80     header:function(url) {
81         window.location.href=url
82     }
83 
84 }
85 
86

 

Interface effect

Background control click button 1

 

 

 

Click button 2 on the back-end interface

 

Source of original text