部署LNMP動靜分離並搭建memcache快取伺服器

  • 2020 年 2 月 17 日
  • 筆記

一、MemCache簡介 MemCache 是一個自由、源碼開放、高性能、分散式的分散式記憶體對象快取系統,用於動態Web應用以減輕資料庫的負載。它通過在記憶體中快取數據和對象來減少讀取資料庫的次數,從而提高了網站訪問的速度。 MemCaChe 是一個存儲鍵值對的 HashMap,在記憶體中對任意的數據(比如字元串、對象等)所使用的 key-value 存儲,數據可以來自資料庫調用、API調用,或者頁面渲染的結果。MemCache 設計理念就是小而強大,它簡單的設計促進了快速部署、易於開發並解決面對大規模的數據快取的許多難題,而所開放的 API 使得 MemCache用於 Java、C/C++/C#、Perl、Python、PHP、Ruby 等大部分流行的程式語言。 另外,說一下為什麼會有 Memcache 和 memcached 兩種名稱?其實 Memcache 是這個項目的名稱(也時它客戶端的名稱),而 memcached 是它伺服器端的主程式文件名。

memcached是一個鍵/值系統,系統相對於MySQL簡單很多,雖然MySQL也有快取,但是資料庫的SQL解析會耗費性能,查詢慢於memcached,另外MySQL的快取設計得更加複雜,因為要考慮事務,日誌,存儲引擎等模組,它的性能也沒有memcached好。

memcached只做一件事情,簡單高效,在cache上比MySQL強,這應該容易理解。

memcached作為高速運行的分散式快取伺服器,具有以下的特點:

  • 協議簡單;
  • 基於libevent的事件處理;
  • 內置記憶體存儲方式;
  • memcached不互相通訊的分散式;

1、協議 memcached的伺服器客戶端通訊並不使用複雜的XML等格式,而使用簡單的基於文本行的協議。 因此,通過telnet也能在memcached上保存數據、取得數據。

2、事件處理 libevent是個程式庫,它將Linux的epoll、BSD類作業系統的kqueue等事件處理功能封裝成統一的介面。即使對伺服器的連接數增加,也能發揮O(1)的性能。memcached使用這個libevent庫,因此能在Linux、BSD、Solaris等作業系統上發揮其高性能。

3、存儲方式 為了提高性能,memcached中保存的數據都存儲在memcached內置的記憶體存儲空間中。由於數據僅存在於記憶體中,因此重啟memcached、重啟作業系統會導致全部數據消失。另外,內容容量達到指定值之後,就基於LRU(Least Recently Used)演算法自動刪除不使用的快取。memcached本身是為快取而設計的伺服器,因此並沒有過多考慮數據的永久性問題。

4、通訊分散式 memcached儘管是「分散式」快取伺服器,但伺服器端並沒有分散式功能。各個memcached不會互相通訊以共享資訊。那麼,怎樣進行分散式呢?這完全取決於客戶端的實現。

5、memcached的應用場景 1)資料庫的前端快取應用:讓它來分擔數據的並發壓力,當數據更新時,可以使程式通知快取進行更新 2)session會話共享的共享存儲

6、memcached應用中的工作流程 它是一種記憶體快取,可通過API的方式讀取記憶體中快取的這些數據,當用戶需要讀取數據時,會首先訪問memcached快取,如果快取中有數據就直接返回給前端的應用程式,如果沒有,再轉發給後台端的伺服器,這時伺服器除了返回數據給用戶,就會將數據更新給memcached快取。

如果實際生產環境中,快取伺服器需要重啟(或者斷電),那麼快取中的數據將會丟失,那麼這時替換的伺服器並發壓力會擴大,可能會導致引入的伺服器也跟著停機,無法提供服務,那麼這時我們的處理流程是這樣的: 首先從負載均衡中將WEB應用停掉- – – >讓負載均衡不再轉發數據給WEB – – >接著啟動快取伺服器- – – – > 通過程式把資料庫的內容初始化到快取伺服器中- – – – >然後將網頁應用啟用- – – – >重啟資料庫伺服器 7、memcached的一致性Hash演算法 一致性 Hash 演算法通過一個叫做一致性 Hash 環的數據結構實現 Key 到快取伺服器的 Hash 映射。簡單地說,一致性哈希將整個哈希值空間組織成一個虛擬的圓環(這個環被稱為一致性Hash 環),如假設某空間哈希函數 H 的值空間是 0~2^ 32 -1(即哈希值是一個 32 位無符號整型),整個哈希空間如下:

將各個伺服器使用 H 進行一個哈希計算,具體可以使用伺服器的 IP 地址或者主機名作為關鍵字,這樣每台機器能確定其在上面的哈希環上的位置了,並且是按照順時針排列,這裡我們假設三台節點 memcache經計算後位置如下:

接下來使用相同演算法計算出數據的哈希值 h,並由此確定數據在此哈希環上的位置。假如我們有數據 A、B、C、D、4 個對象,經過哈希計算後位置如下:

根據一致性哈希演算法,數據 A 就被綁定到了 server01 上,D 被綁定到了 server02 上,B、C在 server03 上,是按照順時針找最近服務節點方法。

這樣得到的哈希環調度方法,有很高的容錯性和可擴展性: 假設 server03 宕機:

可以看到此時 C、B 會受到影響,將 B、C 節點被重定位到 Server01。一般的,在一致性哈希演算法中,如果一台伺服器不可用,則受影響的數據僅僅是此伺服器到其環空間中前一台伺服器(即順著逆時針方向行走遇到的第一台伺服器)之間數據,其它不會受到影響。 考慮另外一種情況,如果我們在系統中增加一台伺服器 Memcached Server 04:

此時 A、D、C 不受影響,只有 B 需要重定位到新的 Server04。一般的,在一致性哈希演算法中,如果增加一台伺服器,則受影響的數據僅僅是新伺服器到其環空間中前一台伺服器(即順著逆時針方向行走遇到的第一台伺服器)之間數據,其它不會受到影響。

綜上所述,一致性哈希演算法對於節點的增減都只需重定位環空間中的一小部分數據,具有較好的容錯性和可擴展性。 一致性哈希的缺點:在服務節點太少時,容易因為節點分部不均勻而造成數據傾斜問題。我們可以採用增加虛擬節點的方式解決。 更重要的是,集群中快取伺服器節點越多,增加/減少節點帶來的影響越小,很好理解。換句話說,隨著集群規模的增大,繼續命中原有快取數據的概率會越來越大,雖然仍然有小部分數據快取在伺服器中不能被讀到,但是這個比例足夠小,即使訪問資料庫,也不會對資料庫造成致命的負載壓力。 二、部署LNMP動靜分離&&memcache快取伺服器 環境如下:

所需源碼包可在此處下載並上傳至各伺服器https://pan.baidu.com/s/1-2pS702mz41e94nBXSgUnA 提取碼:rldk

1、部署Nginx服務

[root@nginx /]# yum -y install openssl-devel pcre-devel     # 安裝所需依賴包  [root@nginx /]# mkdir nginx           # 個人習慣而已  [root@nginx /]# cd nginx/  [root@nginx nginx]# rz          # 使用的xshell連接的伺服器,使用rz上傳所需的源碼包  [root@nginx nginx]# tar zxf nginx-1.14.0.tar.gz       # 解壓到當前目錄  [root@nginx nginx]# cd nginx-1.14.0/  [root@nginx nginx-1.14.0]# useradd -M -s /sbin/nologin www          # 創建Nginx運行用戶  [root@nginx nginx-1.14.0]# ./configure --prefix=/usr/local/nginx --user=www --group=www && make && make install         # 編譯並安裝  [root@nginx nginx-1.14.0]# ln -s /usr/local/nginx/sbin/nginx /usr/local/sbin/         # 創建命令軟連接  [root@nginx nginx-1.14.0]# nginx          # 啟動服務  [root@nginx nginx-1.14.0]# netstat -anput | grep 80        # 查看埠,確定服務已經啟動  tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      6827/nginx: master  [root@nginx nginx-1.14.0]# cd /  [root@nginx /]# vim /usr/local/nginx/conf/nginx.conf  ...............................               // 省略部分內容  在server{} 欄位中添加如下內容  location ~ .php$ {              root           /var/www/html;            # 指定PHP的網頁存放路徑              fastcgi_pass   192.168.171.133:9000;           # 指定PHP服務監聽埠及地址              fastcgi_index  index.php;              fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;              include        fastcgi.conf;          }    [root@nginx /]# nginx -s reload              # 重啟服務使配置生效

2、部署PHP服務

#首先需要為PHP安裝依賴包  [root@php php]# yum -y install libxml2-devel openssl-devel bzip2-devel  [root@php php]# tar zxf libmcrypt-2.5.7  [root@php php]# cd libmcrypt-2.5.7/  [root@php libmcrypt-2.5.7]# ./configure --prefix=/usr/local/libmcrypt && make && make install  [root@php libmcrypt-2.5.7]# cd ..  [root@php php]# tar zxf php-5.6.27.tar.gz  [root@php php]# cd php-5.6.27/  [root@php php-5.6.27]# ./configure --prefix=/usr/local/php5.6 --with-mysql=mysqlnd --with-pdo-mysql=mysqlnd --with-mysqli=mysqlnd --with-openssl --enable-fpm --enable-sockets --enable-sysvshm --enable-mbstring --with-freetype-dir --with-jpeg-dir --with-png-dir --with-zlib --with-libxml-dir=/usr --enable-xml --with-mhash --with-mcrypt=/usr/local/libmcrypt --with-config-file-path=/etc --with-config-file-scan-dir=/etc/php.d --with-bz2 --enable-maintainer-zts && make && make install    #以下為調整PHP的配置文件及控制服務的啟停  [root@php php-5.6.27]# cp php.ini-production /etc/php.ini    #複製源碼中中提供的PHP配置文件  [root@php php-5.6.27]# cp sapi/fpm/init.d.php-fpm /etc/init.d/php-fpm  #複製其服務控制腳本文件  [root@php php-5.6.27]# chmod +x /etc/init.d/php-fpm    #賦予執行許可權  [root@php php-5.6.27]# chkconfig --add php-fpm    #添加為系統服務,以便支援systemctl管理  [root@php php-5.6.27]# chkconfig php-fpm on   #開啟  #複製php-fpm提供的默認配置文件並編輯它  [root@php php-5.6.27]# cp /usr/local/php5.6/etc/php-fpm.conf.default /usr/local/php5.6/etc/php-fpm.conf  [root@php php-5.6.27]# vim /usr/local/php5.6/etc/php-fpm.conf  listen = 192.168.171.133:9000    #監聽地址是本機的IP9000埠  pm.max_children = 50         #最大啟動的進程數  pm.start_servers = 5          #初始啟動進程數  pm.min_spare_servers = 5     #最小空閑進程  pm.max_spare_servers = 35     #最大空閑進程  #修改完成後,保存退出即可  [root@php /]# service php-fpm restart              # 重啟PHP使配置生效  Gracefully shutting down php-fpm . done  Starting php-fpm  done  [root@php /]# netstat -anput | grep 9000                  # 查看是否運行  tcp        0      0 192.168.171.133:9000    0.0.0.0:*               LISTEN      3054/php-fpm: maste 
# 準備網頁測試文件  [root@php /]# mkdir -p /var/www/html  [root@php /]# cd /var/www/html/  [root@php html]# cat index.php  <?php  phpinfo();  ?>  [root@php html]# cat index1.php  <?php  $link=mysqli_connect('192.168.171.135','zyz','pwd@123');  if($link) echo "恭喜你,資料庫連接成功!!!"; else echo "connect shibai";  mysqli_close($link);  ?>

到這,即可訪問Nginx伺服器的80埠來查看php伺服器上定義的兩個網頁文件(在訪問連接資料庫的腳本文件時,需要先部署資料庫,並創建用來連接的用戶): 3、部署MySQL服務

# 這裡部署一個簡單的資料庫即可  [root@mysql /]# mkdir -p mysql  [root@mysql /]# cd mysql/  [root@mysql mysql]# rz             # 上傳所需  [root@mysql mysql]# sh mysql.sh          # 直接sh執行腳本安裝即可,安裝完畢之後默認密碼是123  Starting MySQL.. SUCCESS!  mysql: [Warning] Using a password on the command line interface can be insecure.  [root@mysql mysql]# netstat -anput | grep 3306            # 確保服務已經啟動  tcp6       0      0 :::3306                 :::*                    LISTEN      3290/mysqld  [root@mysql mysql]# mysql -u root -p        # 登錄資料庫  Enter password:  mysql> create database bbs;  Query OK, 1 row affected (0.00 sec)    mysql> grant all on bbs.* to zyz@"192.168.171.%" identified by 'pwd@123';  Query OK, 0 rows affected, 1 warning (0.00 sec)

測試驗證:

4、部署Memcached服務

[root@memcached /]# mkdir memcached  [root@memcached /]# cd memcached/  [root@memcached memcached]# rz           # 上傳所需源碼包  [root@memcached memcached]# tar zxf libevent-2.0.22-stable.tar.gz           # 解包  [root@memcached memcached]# cd libevent-2.0.22-stable/  [root@memcached libevent-2.0.22-stable]# ./configure && make && make install              # 編譯並安裝  [root@memcached libevent-2.0.22-stable]# cd ..  [root@memcached memcached]# tar zxf memcached-1.4.33.tar.gz  [root@memcached memcached]# cd memcached-1.4.33/  [root@memcached memcached-1.4.33]# ./configure --prefix=/usr/local/memcached --with-libevent=/usr/local  / && make && make install  [root@memcached memcached-1.4.33]# ln -s /usr/local/memcached/bin/memcached /usr/local/bin/            # 命令製作軟連接  [root@memcached memcached-1.4.33]# memcached -d -m 1024 -l 192.168.171.132 -p 11211 -c 10240 -P /usr/local/memcached/memcached.pid -u root  #啟動memcached服務,上述啟動參數說明如下:  # -d 選項是啟動一個守護進程。  # -m 分配給 Memcache 使用的記憶體數量,單位是 MB,默認 64MB。  # -l 監聽的 IP 地址。(默認:INADDR_ANY,所有地址)  # -p 設置 Memcache 的 TCP 監聽的埠,最好是 1024 以上的埠。  # -u 運行 Memcache 的用戶,如果當前為 root 的話,需要使用此參數指定用戶。  # -c 選項是最大運行的並發連接數,默認是 1024。  # -P 設置保存 Memcache 的 pid 文件路徑。  # -M 記憶體耗盡時返回錯誤,而不是刪除項  # -f 塊大小增長因子,默認是 1.25  # -n 最小分配空間,key+value+flags 默認是 48  # -h 顯示幫助  [root@memcached memcached-1.4.33]# netstat -anput | grep 11211              # 確定TCP及udp都在監聽  tcp        0      0 192.168.171.132:11211   0.0.0.0:*               LISTEN      11666/memcached  udp        0      0 192.168.171.132:11211   0.0.0.0:*                           11666/memcached    

5、部署Memcache客戶端(返回PHP伺服器操作)

[root@php /]# mkdir memcache  [root@php /]# cd memcache/  [root@php memcache]# rz      # 上傳如下源碼包  [root@php memcache]# ls  memcache-3.0.8.tgz  [root@php memcache]# tar zxf memcache-3.0.8.tgz  [root@php memcache]# cd memcache-3.0.8/  [root@php memcache-3.0.8]# /usr/local/php5.6/bin/phpize       #  #執行該命令,以便生成configure文件  # 若在執行上述命令時報錯,則需要執行「yun -y install autoconf "安裝提示的autoconf包。  Configuring for:          # 執行成功會顯示次此幾行  PHP Api Version:         20131106  Zend Module Api No:      20131226  Zend Extension Api No:   220131226  [root@php memcache-3.0.8]# ./configure --enable-memcache --with-php-config=/usr/local/php5.6/bin/php-config && make && make install             # 編譯並安裝  # 執行完上述命令後,會顯示memcache.so存放的路徑  [root@php memcache-3.0.8]# vim /etc/php.ini       # 編輯此文件  # 在最後一行添加如下內容,注意不要直接複製本人的路徑  extension = /usr/local/php5.6/lib/php/extensions/no-debug-non-zts-20131226/memcache.so  [root@php memcache-3.0.8]# service php-fpm restart          # 重啟php使配置生效  Gracefully shutting down php-fpm . done  Starting php-fpm  done

編寫測試文件:

[root@php memcache-3.0.8]# cd /var/www/html/  [root@php html]# vim test1.php  <?php  $memcache = new Memcache;  $memcache->connect('192.168.171.132', 11211) or die ("Could not connect");  $version = $memcache->getVersion();  echo "Server's version: ".$version."<br/>";  $tmp_object = new stdClass;  $tmp_object->str_attr = 'test';  $tmp_object->int_attr = 123;  $memcache->set('key', $tmp_object, false, 600) or die ("Failed to save data at the server");  echo "Store data in the cache (data will expire in 600 seconds)<br/>";  $get_result = $memcache->get('key');  echo "Data from the cache:<br/>";  var_dump($get_result);  ?>  #編輯完成後,保存退出即可,此測試腳本是顯示memcached的版本  #並且向裡面插入了一個快取時間為600秒的鍵值對「test=123」,其ID為「key」

客戶端訪問編輯的test1.php文件,會看到以下內容:

在memcached伺服器上安裝Telnet命令,並登陸快取庫,查看是否可以得到其鍵值對

[root@memcached /]# yum -y install telnet        # 安裝Telnet命令  [root@memcached /]# telnet 192.168.171.132 11211  Trying 192.168.171.132...  Connected to 192.168.171.132.  Escape character is '^]'.  get key              # 查詢ID為「key」的鍵值對,可以看到我們測試腳本寫入的「test=123」  VALUE key 1 66  O:8:"stdClass":2:{s:8:"str_attr";s:4:"test";s:8:"int_attr";i:123;}  END  #在進行上面的get驗證時,需要將test1.php文件中插入的鍵值對的保存時間值改大一些  #或者重新訪問一下,以免快取失效,查詢不到

至此,LNMP動靜分離&&memcache快取伺服器已經基本部署完成,接下來,配置PHP與memcached伺服器溝通保存session會話 6、使用 memcache 實現 session 共享(在PHP伺服器進行以下操作)

[root@php /]# vim /etc/php.ini  # 在末尾添加如下內容  session.save_handler = memcache  session.save_path = "tcp://192.168.171.132:11211?persistent=1&weight=1&timeout=1&retry_interval=15"  # 內容解釋如下:  # session.save_handler:設置 session 的儲存方式為 memcache 。  #默認以文件方式存取 session數據。  #session.save_path: 設置 session 儲存的位置  #使用多個 memcached server 時用逗號」,」隔開,  #可以帶額外的參數」persistent」、」weight」、」timeout」、」retry_interval」等等,  #類似這樣的:"tcp://host:port?persistent=1&weight=2,tcp://host2:port2"  [root@php /]# service php-fpm restart          # 重啟服務使配置生效  Gracefully shutting down php-fpm . done  Starting php-fpm  done  [root@php /]# vim /var/www/html/test2.php     # 編寫配置文件  <?php  session_start();  if (!isset($_SESSION['session_time']))  {  $_SESSION['session_time'] = time();  }  echo "session_time:".$_SESSION['session_time']."<br />";  echo "now_time:".time()."<br />";  echo "session_id:".session_id()."<br />";  ?>

客戶端訪問編寫的test2.php測試文件,如下:

同樣,使用Telnet命令在memcached伺服器上進行查詢其session_id的值,如下:

[root@memcached /]# telnet 192.168.171.132 11211  Trying 192.168.171.132...  Connected to 192.168.171.132.  Escape character is '^]'.  get d1n4umig3aq8okqrg7ep95t321  VALUE d1n4umig3aq8okqrg7ep95t321 0 26  session_time|i:1581082210;  END  #可以看到,查詢到的session_time和我們網頁訪問到的值是一樣的,說明其被快取了

7、測試Memcached快取資料庫 在MySQL資料庫上創建用於測試的表(所有操作都在MySQL資料庫上)如下:

[root@mysql mysql]# mysql -u root -p  Enter password:  mysql> create database testdb;      # 創建資料庫  mysql> use testdb;      # 進入庫中  Database changed  mysql> create table test1(id int not null auto_increment,name varchar(20) default null,primary key (id)) engine=innodb auto_increment=1 default charset=utf8;       # 創建表  mysql> insert into test1(name) values ('aaa1'),('aaa2'),('aaa3'),('aaa4'),('aaa5');       # 向表中添加內容  mysql> select * from test1       # 查詢表中內容      -> ;  +----+------+  | id | name |  +----+------+  |  1 | aaa1 |  |  2 | aaa2 |  |  3 | aaa3 |  |  4 | aaa4 |  |  5 | aaa5 |  +----+------+  5 rows in set (0.00 sec)  mysql> desc test1;       # 可查詢表結構  +-------+-------------+------+-----+---------+----------------+  | Field | Type        | Null | Key | Default | Extra          |  +-------+-------------+------+-----+---------+----------------+  | id    | int(11)     | NO   | PRI | NULL    | auto_increment |  | name  | varchar(20) | YES  |     | NULL    |                |  +-------+-------------+------+-----+---------+----------------+  2 rows in set (0.00 sec)    mysql> grant select on testdb.* to test@'%' identified by 'pwd@123';  # 創建用於測試的用戶

在PHP伺服器上編寫以下測試文件,測試memcache 是否快取數據成功:

[root@php html]# vim test3.php  <?php  $memcachehost = '192.168.171.132';  $memcacheport = 11211;  $memcachelife = 60;  $memcache = new Memcache;  $memcache->connect($memcachehost,$memcacheport) or die ("Could not connect");  $query="select * from test1 limit 10";  $key=md5($query);  if(!$memcache->get($key))  {  $conn=mysql_connect("192.168.171.135","test","pwd@123");  mysql_select_db(testdb);  $result=mysql_query($query);  while ($row=mysql_fetch_assoc($result))  {  $arr[]=$row;  }  $f = 'mysql';  $memcache->add($key,serialize($arr),0,30);  $data = $arr ;  }  else{  $f = 'memcache';  $data_mem=$memcache->get($key);  $data = unserialize($data_mem);  }  echo $f;  echo "<br>";  echo "$key";  echo "<br>";  //print_r($data);  foreach($data as $a)  {  echo "number is <b><font color=#FF0000>$a[id]</font></b>";  echo "<br>";  echo "name is <b><font color=#FF0000>$a[name]</font></b>";  echo "<br>";  }  ?>

客戶端訪問用於測試的腳本文件,第一次訪問的頁面如下:

客戶端刷新後,會看到以下頁面:

在查詢到的快取過期前,可以在memcache上通過get 獲取到對應的快取數據,如下(在memcache伺服器上進行操作):

[root@memcached memcached]# telnet 192.168.171.132 11211  Trying 192.168.171.132...  Connected to 192.168.171.132.  Escape character is '^]'.  get d8c961e9895ba4b463841924dbcefc2b  VALUE d8c961e9895ba4b463841924dbcefc2b 0 251  a:5:{i:0;a:2:{s:2:"id";s:1:"1";s:4:"name";s:4:"aaa1";}i:1;a:2:{s:2:"id";s:1:"2";s:4:"name";s:4:"aaa2";}i:2;a:2:{s:2:"id";s:1:"3";s:4:"name";s:4:"aaa3";}i:3;a:2:{s:2:"id";s:1:"4";s:4:"name";s:4:"aaa4";}i:4;a:2:{s:2:"id";s:1:"5";s:4:"name";s:4:"aaa5";}}  END