swoole 的練習 demo(6)- 資料庫設計與實現

yyy123456發表於2022-09-06

一直不能下決心好好學習,仔細研究一下,決定用盡量降低難度曲線的方法,從易到難,一步一步的學習,所以整了個demo專案。

git倉庫和使用步驟

確保能看到swoole,在 php -m 命令中
php -m 
git clone https://github.com/lang123789/swoole_demo.git
然後設定了標籤,本文對應 v6.0
cd swoole_demo
git checkout v6.0
## 安裝類庫,生成 autoload.php 檔案
composer install
## 啟動 websocket 服務
php src/WebSocket/run.php

需求

10、需要把使用者的聊天訊息儲存到資料庫。
11、需要校驗使用者id是否正確,是否在表中存在

設計

建表 和插入假資料。

這裡有兩張表,1使用者表,2聊天記錄表。

CREATE TABLE users (
  id int(11) NOT NULL AUTO_INCREMENT,
  user_name varchar(255) NOT NULL DEFAULT '' comment '使用者名稱稱',
  email varchar(255) NOT NULL DEFAULT '' comment 'email',
  created_at timestamp null default current_timestamp,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB ;

CREATE TABLE messages (
  id int(11) NOT NULL AUTO_INCREMENT,
  user_id int not null default 0 comment '使用者id',
  content varchar(3000) NOT NULL DEFAULT '' comment '聊天內容',
  created_at timestamp null default current_timestamp,
  PRIMARY KEY (`id`),
  index user_id(user_id)
) ENGINE=InnoDB ;

insert into users(id,user_name)values (1,'管理員');
insert into users(id,user_name)values (2,'使用者2');
insert into users(id,user_name)values (3,'使用者3');

資料庫外掛選型

各方比較之後,選擇用 Mix Database
有一說一,是真好用。
官方地址:openmix.org/mix-php/docs/3.0/#/zh-...
安裝命令:

composer require mix/database

客戶端程式碼改變 index.php

//假定這是資料庫的查詢結果
$config = require __DIR__ .'/config/mysql.php';

$db = new \Mix\Database\Database('mysql:host='. $config['host'] .';port='. $config['port']
 .';charset='. $config['charset'] .';dbname='.$config['db_name'], $config['username'], $config['password']);

$datas = $db->table('users')->where('id = ?', $user_id)->first();

if (empty($datas)){
  die('非法使用者');
}

服務端程式碼改變

// 資料庫連線池
private function create_mysql_pool()
{

  $maxOpen = 50; // 最大開啟連線數
  $maxIdle = 20; // 最大閒置連線數
  $maxLifetime = 3600; // 連線的最長生命週期
  $waitTimeout = 0.0; // 從池獲取連線等待的時間, 0為一直等待
  $config = $this->mysql_config['mysql']; // 讀取配置檔案。
  $this->db = new \Mix\Database\Database('mysql:host='. $config['host'] .';port='. $config['port']
 .';charset='. $config['charset'] .';dbname='.$config['db_name'], $config['username'], $config['password']);

  $this->db->startPool($maxOpen, $maxIdle, $maxLifetime, $waitTimeout);
  \Swoole\Runtime::enableCoroutine(); // 必須放到最後,防止觸發協程排程導致異常

}
public function message(\swoole_websocket_server $server, \swoole_websocket_frame $frame)
{

  $data = $frame->data;
  //echo "有訊息:" . $data . "\n";
 //這裡使用者發來的資訊已經分型別了。my_id開頭,說明是系統自動從客戶端傳送的資訊,用於識別身份。

  if (preg_match('#^ping#', $data)) { // 心跳
  // echo "心跳來了 " . date("Y-m-d H:i:s") . "\n";  $server->push($frame->fd, 'pong');// 返回一個訊息,過會他會再次傳來。

  } elseif (preg_match('#^my_id#', $data)) { // 使用者上線
    $user_id = explode("|", $data)[1];
    $datas = $this->db->table('users')->where('id = ?', $user_id)->first();

    if (!$datas) {
    //如果使用者不存在,則w我直接關閉連線。
      echo "非法連線。使用者id:" . $user_id;
      $server->close($frame->fd);
      return;
    }
    $user_name = $datas->user_name;
    $user = [
      'fd' => $frame->fd,
      'user_name' => $user_name,
      'user_id' => strval($user_id),
    ];
    echo "有個人剛上線,資料:" . json_encode($user, JSON_UNESCAPED_UNICODE);

    echo "連線池當前狀態:".json_encode( $this->db->poolStats() );

    echo "\n";
    // 放入記憶體表
    $this->table->set($frame->fd, $user);

    //給本人看 歡迎您。
    $server->push($frame->fd, json_encode([
      'type' => 'my_id',
      'message' => '歡迎您,' . $user_name,
    ]));
    // 通知其他人 某某 上線了。
    foreach ($this->table as $row) {
      if ($row['fd'] != $frame->fd) {
        $server->push($row['fd'], json_encode([
          'type' => 'my_id',
          'message' => $user_name . ' 上線了',
        ]));
      }
    } 
  } elseif (preg_match('#^my_message#', $data)) { // 使用者發訊息
    $user_id = explode("|", $data)[1];
    $message = explode("|", $data)[2];

    $datas = $this->db->table('users')->where('id = ?', $user_id)->first();

    if (!$datas) {
      //如果使用者不存在,則w我直接關閉連線。
      echo "非法連線。使用者id:" . $user_id;
      $server->close($frame->fd);
      return;
    }
    $user_name = $datas->user_name;

    $data = [
      'user_id' => $user_id,
      'content' => $message,
    ];
    $this->db->insert('messages', $data); // 插入資料庫

    echo "有人發訊息,內容:" . $message;
    echo "連線池當前狀態:".json_encode( $this->db->poolStats() );
    echo "\n";

    // 通知其他人 ,進行廣播
    foreach ($this->table as $row) {
      if ($row['fd'] != $frame->fd) {
        echo "推送訊息:" . $message . "給fd:" . $row['fd'];
        $server->push($row['fd'], json_encode([
          'type' => 'my_message',
          'message' => $message,
          'from_user_name' => $user_name,

        ]));
     }
   } 
 }
}

程式碼說明

1、這次的程式碼變動其實比較少,去除了最開始的假的使用者表,使用了真實的使用者表。
2、也使用了真實的訊息表儲存使用者的訊息,上面的程式碼中能找到 “插入資料庫” 的註釋。
3、mix 這個類庫真好用,程式碼特別少特別精簡。但說明一下,我沒有在真實專案中用過 mix。只是這個 demo 中用的。
4、真實的專案中,其實客服是固定的,應該不需要廣播,資料庫使用者表中,新增是否客服的欄位,都不難設計的,程式碼也不難寫。
5、也可以有這樣的需求,程式碼對方是否已讀,也可以加欄位實現。
6、也可以有這樣的需求,使用者表加入是否線上的欄位。
7、也可以有這樣的需求,客服的前端頁面會不同,因為客服與很多人對話,他得看的方便,所以得是一個人一個視窗這樣。但這些都需要前端程式設計師的配合。

截圖


本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章