詳解redis服務

redis簡介

Redis單純程模型,支持主從模式,提高可用性,是一個開源項目,經常用來當一個數據結構服務器。其是內存級別的緩存服務器並可實現持久化功能. 據稱一百萬的變量存儲(字串)佔用100M內存空間,單臺redis服務器可達到5萬併發的能力。

redis與memcache的對比

redis的優勢

  • 支持豐富的操作
  • 主從複製和集羣
  • 就地更新操作
  • 支持持久化(磁盤),避免雪崩效應

memcache優勢

  • 多線程,善用多核CPU,更少的阻塞操作
  • 更少的內存開銷
  • 更少的內存分配壓力
  • 可能有更少的內存碎片

redis的組件

  •  redis-server
  •  redis-cli
  •  redis-benchmark
  •  redis-check-dump & redis-check-aof

redis的工作端口

  • 6379/TCP

redis-cli命令的參數

  • -h HOST : 連接的主機地址或主機名
  • -p PORT :連接的端口
  • -s socket : 指定套接字
  • -a password : 指定連接密碼
  • -r <repeat> : 指定命令運行多次

redis-cli中相關的命令

  •  connection相關的命令
    • auth PASS : 認證
    • ping : 測試服務器是否在線
    • echo "string" : 顯示string
    • quit : 退出
    • select # : 挑選指定的名稱空間(即數據庫)
    • help @connection : 獲取與連接相關的命令幫助
  •  與服務器端支持的命令
    • help @server : 獲取與服務器端相關的命令幫助
    • bgsave : 實現異步將數據集同步到磁盤上
    • client getname : 獲取當前客戶端的連接名
    • client kill IP:PORT : 指定IP:PORT可關閉相關的連接信息
    • client list : 查看客戶端的連接信息

      172.16.36.70:6379> client list id=5 addr=172.16.36.70:57606 fd=8 name= age=904 idle=879 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=get
    • client setname CONNECTION-NAME: 設定當前連接的名稱
    • info : 查看當前服務器的狀態信息
    • info memory : 只顯示memory段的相關信息
    • config resetstart : 重置info中所統計的數據
    • config set PARAMETER value : 運行時修改設定指定參數的值,只保存在內存中
    • config rewrite : 將修改在內存中的參數值同步到配置文件中
    • config get dir : 查看redis的文件保存目錄
    • dbsize : 顯示數據庫中所有鍵的數量
    • lastsave : 用來獲取最新一次save執行的時間戳
    • save : 保存數據到磁盤
    • monitor : 實時監控所接收到的請求
    • shutdown : 將所有數據從內存同步到磁盤,並安全關閉
    • shutdown [nosave][save] : 關閉程序並選擇是否將數據同步到磁盤上
    • salveof HOST PORT :配置主從,當前節點將變成從節點
    • slowlog : 查看慢查詢日誌,需要開啓慢查詢日誌功能
    • sync : 複製功能的內建命令
  •  與訂閱相關的命令
    • help @pubsub : 獲取與訂閱相關的命令
    • psubscribe : 基於模式進行訂閱
    • publish : 向頻道發送消息
    • subscribe CHANNEL : 訂閱一個頻道

redis的認證功能

vim /etc/redis.conf
requirepass zhenping.com

重啓redis服務
systemctl restart redis

連接redis並認證的方法
redis-cli -h 172.16.36.70
172.16.36.70:6379> select 0
(error) NOAUTH Authentication required.
172.16.36.70:6379> auth zhenping
OK

redis清空數據庫

  • flushdb : 清空當前庫
  • flushall : 清空所有庫

      [root@Centos7 ~]# redis-cli -h 172.16.36.70
      172.16.36.70:6379> flushall
      OK
      172.16.36.70:6379> flushdb
      OK

redis的事務功能

通過multi,exec,watch等命令來實現事務功能,將多個命令打包,多個命令按順序執行,並一次執行完成,並將執行結果一次性全部返回給客戶端。redis事務不支持回滾操作,在事務中應避免發生錯誤(如命令寫錯等),事務了也將會執行失敗。

  • multi : 啓動一個事務
  • exec : 執行事務,一次性將事務中的所有操作執行完成後,返回給客戶端
  • watch : 樂觀鎖機制,在EXEC命令執行之前 ,用於監視指定數據鍵,如果監視的某任意鍵數據被修改,服務器拒絕執行事務
####建立一個事務
[root@Centos7 ~]# redis-cli -h 172.16.36.70
172.16.36.70:6379> multi
OK
172.16.36.70:6379> set ip 172.16.36.70
QUEUED
172.16.36.70:6379> get ip
QUEUED
172.16.36.70:6379> set prot 8080
QUEUED
172.16.36.70:6379> exec
1) OK
2) "172.16.36.70"
3) OK

####使用watch機制監控鍵
[root@Centos7 ~]# redis-cli -h 172.16.36.70
172.16.36.70:6379> watch ip
OK
172.16.36.70:6379> multi
OK
172.16.36.70:6379> set ip 172.16.36.71
QUEUED
172.16.36.70:6379> get ip
QUEUED
172.16.36.70:6379> exec
(nil)

說明: 當watch ip指令發出後,其它的請求操作改變了IP鍵的值,此時新發起的事務中,如果也要改變其值, 此事務申請將會失敗

redis的發佈和訂閱功能(publish/subscribe)

發佈和訂閱功能被廣泛用於構建即時通信應用,比如:網絡聊天室和實時廣播、實時提醒等。 訂閱發佈功能就能幫你很輕鬆地實現通知、監控程序

  • subsciribe CHANNEL_NAME : 訂閱一個頻道
  • publish CHANNEL_NAME : 向頻道發送一個消息
  • unsubscribe CHANNEL_NAME : 退訂頻道
  • psubscribe CHANNEL_NAME_PATTERN : 基於正則表達式模式定義多個頻道
1、訂閱一個頻道:
72.16.36.70:6379> subscribe news
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "news"
3) (integer) 1

2、向頻道發送消息
172.16.36.70:6379> publish news weizhenping
(integer) 1

3、頻道會接收下來消息
[root@Centos7 ~]# redis-cli -h 172.16.36.70
172.16.36.70:6379> subscribe news
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "news"
3) (integer) 1
1) "message"
2) "news"
3) "weizhenping"   #此內容爲頻道發送過來的消息

基於模式的頻道訂閱示例
1、基於模式訂閱頻道
[root@Centos7 ~]# redis-cli -h 172.16.36.70
172.16.36.70:6379> PSUBSCRIBE news.i[to]    #訂閱了news.io,news,io兩個頻道
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "news.i[to]"
3) (integer) 1

2、向頻道發送消息
172.16.36.70:6379> PUBLISH news.io hello
(integer) 1

3、頻道會接收下來消息
[root@Centos7 ~]# redis-cli -h 172.16.36.70
172.16.36.70:6379> PSUBSCRIBE news.i[to]
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "news.i[to]"
3) (integer) 1
1) "pmessage"
2) "news.i[to]"
3) "news.io"
4) "hello"

redis的持久化功能

  •  1、RDB(redisDB)

    •  工作原理
      RDB爲snapshot(快照)存儲機制,其也是redis默認的存儲機制,按照事先定製的策略,週期性地將數據從內存中讀取出來保存至磁盤,當到達save指令指定的時間,redis主進程將fork一個子進程,負責內存中的內容快照並保存到磁盤中。 Linux系統有寫時複製機制,父進程與子進程會共享相同的物理頁面,當父進程處理寫請求時,操作系統爲寫的數據創建一個副本,因此子進程保存的數據一定是與時間點一致的數據。當子進程將快照寫入臨時文件後,會使用臨時文件替換舊的文件,然後子進程完成退出 。保存的數據文件默認爲dump.rdb. 如果在SAVE週期之前停電了,會造成部分數據丟失。
    •  數據保存機制
      • save : 在主線程中保存快照,保存時會阻塞所有客戶端請求,每次將完整數據寫至dump.rdb文件中,會帶來大量的IO壓力
      • bgsave : 異步保存機制,在後端自動保存,其不會阻塞窩客戶端請求
    •  RDB相關的配置參數(/etc/redis/redis.conf)
      • stop-writes-on-bgsave-error yes : 在基於快照備份時,一旦發生錯誤,是否停止寫操作
      • rdbcompression yes : rdb文件是否壓縮來節約空間
      • rdbchecksum yes : 是否對rdb的鏡像文件做校驗碼檢測,當redis啓動時會根據事先保存好的校驗碼進行對比,保證數據的完整性
      • dbfilename dump.rdb : 保存的文件名
      • dir /var/lib/redis/ : 文件保存目錄
      • save "" : 關閉rdb功能
  •  2、AOF(append only file)

    •  工作原理

      redis以順序IO的方式附加在文件的尾部,將每一次的寫命令操作都通過write函數追加到文件後面,其是比RDB更好的持久化方案,但文件會變得越來越大。當redis重啓時,可通過執行文件中的命令在內存中重建數據庫

    •  數據保存機制
      • bgrewriteaof : AOF文件重寫,它不會讀取正在使用的AOF文件,而是通過將內存中的數據以命令的方式保存至臨時文件中,完成之後替換原來的AOF文件,這樣可以減少AOF的大小
    •  AOF重寫過程
      • 1、redis主進程通過fork機制創建子線程
      • 2、子進程根據redis內存中現有的數據通過重建命令創建數據庫於臨時文件中
      • 3、父進程繼續接收客戶請求,並會把這些請求中的寫操作繼續追加到原來的AOF文件中,額外地,也將新的寫請求放置於一個緩衝隊列中
      • 4、子進程重寫完成,會通知父進程,父進程把緩衝中的隊列命令寫到臨時文件中
      • 5、父進程用臨時文件替換老的AOF文件
    •  AOF相關的配置參數(/etc/redis/redis.conf)
      • appendonly no : 是否啓用AOF功能,yes表示啓用
      • appendfilename "appendonly.aof" : aof存儲文件
      • appendfsync always : 每次收到寫命令,立即寫入磁盤
      • appendfsync everysec : 每秒鐘寫一次,推薦操作
      • appendfsync no : append功能自己觸發寫操作,將所有操作都提交給OS,由操作系統決定什麼時候寫
      • no-appendfsync-no-rewrite no: 在重寫的過程中是否調用fsync
      • auto-aof-rewrite-percentage 100 : 在aof文件已經是上次重寫時的2倍大小,將自動啓動重寫操作
      • auto-aof-rewrite-min-size 64mb : 當文件達到64MB才執行重寫操作

        RDB與AOF同時啓用時:
        1、bgsave和bgrewriteaof不會同時執行
        2、在redis服務器啓動用於恢復數據時,會優先使用AOF

redis的主從複製

  •  1、redis的主從複製特點

    • 一個master可以有多個slave
    • 支持鏈式複製
    • master以非阻塞方式同步數據至slave,意味着可同時與多個slave同步
  •  2、複製的工作原理

    • 1、主庫會自己基於ping check機制來檢查從庫是否在線
    • 2、如果從庫在線就同步數據文件至從服務器端
    • 3、從服務器也可以發送請求同步的請求,主庫將啓動一個線程,把內存中的數據同步給從庫,從庫將數據保存至文件中
    • 4、從庫再把文件裝載到內存中,從而完成複製功能
  •  3、主從相關配置(/etc/redis.conf)

    • slave-serve-stale-data yes : 當主服務器連接不上了,從服務器是否可以使用過期數據響應
    • repl-diskless-sync no : 是否基於diskless機制同步
    • slave-priority 100 : 指定從服務器優先級
    • min-slave-to-write 3 : 如果從節點小於三個,主服務器將拒絕寫操作
    • min-slave-max-lag 10 : 從服務器不能晚於主服務器10秒鐘,是否將停止複製

      如果master使用了requirepass開啓了認證功能,從服務器要使用masterauth <password>來連接,使用指定的密碼進行認證

      主從設置示例:
        172.16.36.70 : 主redis服務
        172.16.36.71 : 從redis服務
      
      172.16.36.70配置
      vim /etc/redis.conf
      bind 172.16.36.70
      
      172.16.36.71配置:
      vim /etc/redis.conf
      bind 172.16.36.71
      
      [root@Centos7 ~]# redis-cli -h 172.16.36.71
      172.16.36.71:6379> SLAVEOF 172.16.36.70 6379
      OK
      172.16.36.71:6379> get ip   #查看是否能獲取到主庫數據
      "172.16.36.70"
      172.16.36.71:6379> dbsize
      (integer) 3

redis高可用的實現

  •  sentinel機制

    •  工作原理

      找一臺專用的監控主機,即能提供監控又可以提供配置功能,如果發現master離線了,監控主機會從從節點選擇新的主節點。爲了不誤判,setinel至少奇數個節點,同時監控。如果主節點不在線,多個setinel會協調一個新的主節點,以免發生誤判,所有setinel每秒一次向所有服務器發送ping請求,判斷節點是否在線。setinel是一個分佈式系統,使用流言協議和投票協議來決定故障遷移。可以監控多組redis實例。

    •  sentinel的功用
      • 用於管理多個redis服務,實現HA
      • 監控
      • 通知
      • 自動故障轉移
    •  工作過程
      • 啓用sentinel時, 首先服務器做自身初始化,運行redis-server中專用於sentinel功能中的代碼
      • 初始化sentinel狀態,根據給定的配置文件,初始化監控的master服務列表(可以根據master的配置,獲取從服務器節點)
      • 創建連向Master的連接
    •  sentinel下線機制
      • 主觀下線 : 一個sentinel實例判斷出某節點下線
      • 客觀下線 : 多個sentinel節點協商後判斷出某節點下線
  •  setinel程序

    • redis-sentinel /path/to/sentinel.conf
    • redis-server /path/to/sentinel --sentinel
  •  setinel的工作端口

    • 26379/TCP
  •  專用配置文件

    • /etc/redis-sentinel.conf
  •  redis-sentinel.conf配置參數
    • prot 26379
    • dir /tmp
    • sentinel monitor mymaster 127.0.0.1 6379 2

      mymaster: sentinel要監控的實例名稱,此名稱可以隨意取
      127.0.0.1 : 主節點的IP地址
      6379 : 主節點的端口
      2 : sentinel節點的票數,此值要大於sentinel節點數量的半數
    • sentinel down-after-milliseconds mymaster 30000 : 判斷主節點不在線的默認超時時長,默認30秒

    • sentinel parallel-syncs mymaster 1 : 故障轉移時最多能有多少個從服務器向主服務器發起同步請求
    • sentinel failover-timeout mymaster 180000 : 提升主節點的超時時長,表示提升新的主節點在3分鐘內未完成,操作將失敗
  •  sentinel專用命令

    • sentinel masters : 列出所有主服務器
    • sentinel slaves <master name> : 獲取所有當前redis實例中的從節點信息
    • sentinel get-master-addr-by name <master name> :直接獲取當前redis實例主節點的IP地址及端口

      172.16.36.74:26379> sentinel get-master-addr-by-name mymaster
      1) "172.16.36.72"
      2) "6379"
    • sentinel reset : 重置服務器所有狀態

    • sentinel failover <master name> : 手動實現故障轉移
  •  sentinel配置實例

實驗環境說明:
    172.16.36.70 : redis主節點
    172.16.36.71 : redis從節點
    172.16.36.72 : redis從節點
    172.16.36.74 : sentinel節點1
    172.16.36.75 : sentinel節點2
    172.16.36.76 : sentinel節點3

####配置redis主節點
操作主機: 172.16.36.70
#vim /etc/redis.conf
bind 172.16.36.70
daemonize yes    
啓動服務
#redis-server /etc/redis.conf
[root@Centos7 ~]# ss -tnl
State      Recv-Q Send-Q                                       Local Address:Port                                         Peer Address:Port
LISTEN     0      128                                           172.16.36.70:6379                                                    *:*

####配置redis從節點
操作主機: 172.16.36.71
#vim /etc/redis.conf
bind 172.16.36.71
daemonize yes

啓動服務
#redis-server /etc/redis.conf
[root@Centos7 ~]# ss -tnl
State      Recv-Q Send-Q                                       Local Address:Port                                         Peer Address:Port
LISTEN     0      128                                           172.16.36.70:6379                                                    *:*

配置主節點信息
[root@Centos7 ~]# redis-cli -h 172.16.36.71 -p 6379
172.16.36.71:6379> SLAVEOF 172.16.36.70 6379
OK

操作主機: 172.16.36.72
#vim /etc/redis.conf
bind 172.16.36.72
daemonize yes

啓動服務
#redis-server /etc/redis.conf
[root@Centos7 ~]# ss -tnl
State      Recv-Q Send-Q                                       Local Address:Port                                         Peer Address:Port
LISTEN     0      128                                           172.16.36.70:6379                                                    *:*

配置主節點信息
[root@Centos7 ~]# redis-cli -h 172.16.36.71 -p 6379
172.16.36.71:6379> SLAVEOF 172.16.36.70 6379
OK

####配置sentinel節點
操作主機: 172.16.36.74
# vim /etc/redis-sentinel.conf
port 26379
dir "/tmp"
daemonize yes
sentinel monitor mymaster 172.16.36.70 6379 2
sentinel parallel-syncs mymaster 3
sentinel down-after-milliseconds mymaster 30000
sentinel failover-timeout mymaster 180000

啓動服務
redis-sentinel /etc/redis-sentinel.conf

查看服務啓動狀態
users:(("master",2112,14))
[root@Centos7 ~]# ss -tln
State      Recv-Q Send-Q                                       Local Address:Port                                         Peer Address:Port
LISTEN     0      128                                                      *:26379                                                   *:*

操作主機: 172.16.36.75
# vim /etc/redis-sentinel.conf
port 26379
dir "/tmp"
daemonize yes
sentinel monitor mymaster 172.16.36.70 6379 2
sentinel parallel-syncs mymaster 3
sentinel down-after-milliseconds mymaster 30000
sentinel failover-timeout mymaster 180000

啓動服務
redis-sentinel /etc/redis-sentinel.conf

查看服務啓動狀態
users:(("master",2112,14))
[root@Centos7 ~]# ss -tln
State      Recv-Q Send-Q                                       Local Address:Port                                         Peer Address:Port
LISTEN     0      128                                                      *:26379                                                   *:*

操作主機: 172.16.36.76
# vim /etc/redis-sentinel.conf
port 26379
dir "/tmp"
daemonize yes
sentinel monitor mymaster 172.16.36.70 6379 2
sentinel parallel-syncs mymaster 3
sentinel down-after-milliseconds mymaster 30000
sentinel failover-timeout mymaster 180000

啓動服務
redis-sentinel /etc/redis-sentinel.conf

查看服務啓動狀態
users:(("master",2112,14))
[root@Centos7 ~]# ss -tln
State      Recv-Q Send-Q                                       Local Address:Port                                         Peer Address:Port
LISTEN     0      128                                                      *:26379                                                   *:*

查看sentinel相關的日誌信息
tailf /var/log/redis/redis-sentinel.log
[32524] 25 Mar 10:05:02.290 # Sentinel runid is a44025e518b65c512340c48535df119496f7a0d8
[32524] 25 Mar 10:05:02.291 # +monitor master mymaster 172.16.36.70 6379 quorum 2
[32524] 25 Mar 10:05:32.322 # +sdown slave 172.16.36.71:6379 172.16.36.71 6379 @ mymaster 172.16.36.70 6379
[32524] 25 Mar 10:05:46.311 * +sentinel sentinel 172.16.36.76:26379 172.16.36.76 26379 @ mymaster 172.16.36.70 6379
[32524] 25 Mar 10:05:57.071 * +sentinel sentinel 172.16.36.75:26379 172.16.36.75 26379 @ mymaster 172.16.36.70 6379
[32524] 25 Mar 10:09:03.727 # +sdown master mymaster 172.16.36.70 6379

將當前的Master節點shudown,查看sentinel的相關日誌信息
[32524] 25 Mar 10:09:03.747 # +new-epoch 4
[32524] 25 Mar 10:09:03.749 # +vote-for-leader 90c011868befc3047a8527886efce8f8c6f9ea34 4
[32524] 25 Mar 10:09:03.836 # +odown master mymaster 172.16.36.70 6379 #quorum 3/2
[32524] 25 Mar 10:09:03.836 # Next failover delay: I will not start a failover before Fri Mar 25 10:15:04 2016
[32524] 25 Mar 10:09:04.854 # +config-update-from sentinel 172.16.36.75:26379 172.16.36.75 26379 @ mymaster 172.16.36.70 6379
[32524] 25 Mar 10:09:04.854 # +switch-master mymaster 172.16.36.70 6379 172.16.36.72 6379
[32524] 25 Mar 10:09:04.855 * +slave slave 172.16.36.71:6379 172.16.36.71 6379 @ mymaster 172.16.36.72 6379
[32524] 25 Mar 10:09:04.856 * +slave slave 172.16.36.70:6379 172.16.36.70 6379 @ mymaster 172.16.36.72 6379

對當前主節點監控sentinel的活躍探測信息
root@Centos7 ~]# redis-cli -h 172.16.36.72
172.16.36.72:6379> monitor
OK
1458878723.052705 [0 172.16.36.76:53255] "PING"
1458878723.402835 [0 172.16.36.75:38413] "PING"
1458878723.586806 [0 172.16.36.74:32824] "PING"
1458878723.859748 [0 172.16.36.75:38413] "PUBLISH" "__sentinel__:hello" "172.16.36.75,26379,90c011868befc3047a8527886efce8f8c6f9ea34,4,mymaster,172.16.36.72,6379,4"
1458878724.099837 [0 172.16.36.76:53255] "PING"
1458878724.152776 [0 172.16.36.76:53255] "PUBLISH" "__sentinel__:hello" "172.16.36.76,26379,21a0a795010287138b6efc636d03edcce66bcd56,4,mymaster,172.16.36.72,6379,4"
1458878724.454214 [0 172.16.36.75:38413] "PING"
1458878724.617819 [0 172.16.36.74:32824] "PING"
1458878724.693888 [0 172.16.36.74:32824] "PUBLISH" "__sentinel__:hello" "172.16.36.74,26379,a44025e518b65c512340c48535df119496f7a0d8,4,mymaster,172.16.36.72,6379,4"

使用info sentinel命令查看主從信息
172.16.36.74:26379> info sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
master0:name=mymaster,status=ok,address=172.16.36.72:6379,slaves=2,sentinels=3

Redis Clustering

在redis3.0版本中引入clustering功能, 是去中心化的分佈式數據庫,通過分片機制進行數據分佈,clustering內的每個節點僅有數據庫一部分數據,每個節點都有全局元數據,通過查找元數據,即可查詢到數據存放在哪臺服務器

分佈式解決方案

  •  Twemproxy(Twitter)
    • 代理分片機制
    • 優點
      • 非常穩定,企業方案
    • 缺點
      • 單點故障
      • 需要依賴第三方軟件,如Keeplived
      • 無法平滑地橫向擴展
      • 沒有後臺界面
      • 代理分片機制引入更多的來回次數並提高延遲
      • 單核模式,無法充分利用多核,除非多實例
      • Twitter官方肉串不再繼續使用
  •  Codis(豌豆莢)
    • 代理分片機制
    • 2014年11月開源
    • 基於GO以及C語言開發
    • 優點
      • 非常穩定,企業級方案
      • 數據自動平衡
      • 高性能
      • 簡單的測試顯示較Twemproxy快一倍
      • 善用多核CPU
      • 簡單
        • 沒有paxos類的協調機制
        • 沒有主從複製
      • 有後臺界面
    • 缺點
      • 代理分片機制引入更多的來回次數並提高延遲
      • 需要第三方軟件支持協調機制
        • 目前支持zookeeper及Etcd
      • 不支持主從複製 ,需要另外實現
      • Codis採用proxy方案,所有必然會帶來單機性能的損失
        • 經測試,在不開Pipeline的情況下,大概會損失40%左右的性能
  •  Redis Cluster(官方)
    • 官方實現
    • 需要3.0或更高版本
    • 優點
      • 無中心的P2P Gossip分散式模式
      • 更少的來回次數並降低延遲
      • 自動於多個redis節點進行分片
      • 不需要第三方軟件支持協調機制
    • 缺點
      • 依賴於redis 3.0或更高版本
      • 需要時間驗證其穩定性
      • 沒有後臺界面
      • 需要智能客戶端
      • redis客戶端必須支持redis cluster架構
      • 較Codis有更多的維護升級版本
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章