CentOS7部署lsyncd+rsync實現服務器文件實時同步

Lsyncd是什麼?

Github:axkibe/lsyncd
官方文檔:Lsyncd – Live Syncing (Mirror) Daemoncss

  • Lysncd即Live Syncing Daemon,它是開源的數據實時同步工具(後臺進程),基於inotify和rsync。
  • Lsyncd是一個輔助文件同步工具。它經過監聽系統的文件變化事件,調用rsync進行同步。注意「輔助」二字,輔助的意思是:同步文件自己並非由lsyncd來實現,它只負責監測哪些文件改變了,進而調用rsync來完成同步,真正同步文件的是rsync,若是你不知道什麼是rsync,請查看使用sersync +rsync進行實時文件同步中的rsync部分。

Lsyncd的三種同步模式html

  • default.rsync
  • default.rsyncssh
  • default.direct

爲了方便,咱們直接把它們稱爲rsync、rsyncssh、direct。linux

1. rsync同步模式nginx

首先,你須要知道rsync的基本用法,如下的rsync命令就能夠把文件從本地的「/data/wwwroot」目錄推送到遠程的「remote@192.168.1.6::wwwroot/」目錄中,運行在「rsync同步模式」的lsyncd正是經過組裝相似這樣的命令來同步文件的:git

rsync -avz --partial --delete /data/wwwroot remote@192.168.1.6::wwwroot/ --password-file=/etc/rsyncd.password

有人可能會疑惑,既然rsync自己就能夠同步,那要lsyncd幹嗎?有兩個緣由:github

  • 實時同步。
  • 減小rsync掃描文件帶來的延時和性能損耗。
  • rsync沒法知道「何時同步」,由於rsync只有執行同步命令的時候,纔會去掃描文件判斷哪些文件被修改了,只能創建一個定時任務,每隔必定的時間(好比5分鐘,10分鐘等等)去執行一次同步,這樣雖然能同步,但卻「不實時」。
  • lsyncd就能夠監聽文件的修改,換句話說,某個文件修改了,lsyncd會獲得通知(原理是使用linux系統的inotify/fsevents功能),獲得通知以後,再去調用rsync把修改的文件進行同步(即組裝一句rsync同步語句並執行),達到「實時同步」的效果。
  • lsyncd調用rsync同步命令的時候,會用rsync的--include-from=FILE之類的選項來指定要同步哪些文件,這樣能夠達到「減小rsync掃描文件帶來的延時和性能損耗」。
  • 爲何這麼說呢?由於若是讓rsync本身去查詢哪些文件改變了,須要消耗較多的時間和服務器資源,試想一下,在100萬個文件中,有一個文件添加了一個英文的句號.,若是沒有lsyncd告訴rsync改變的是這個文件,那rsync就要去掃描100萬個文件來找出這個僅僅多了一個.的文件,雖然rsync的查找效率很高,但這個查找是很是沒有必要的,這就是爲何lsyncd能「減小rsync掃描文件帶來的延時和性能損耗」。

2. rsyncssh同步模式
明白了rsync同步模式,就不難明白rsyncssh模式,由於rsync自己就是有ssh模式的,lsyncd一樣是負責監聽哪些文件變化了,而後組裝rsync同步命令進行同步。
rsync的ssh模式,主要的好處是用於文件移動的時候。web

  1. 假設我如今有兩臺機A和B,A中的test目錄文件變化會自動同步到B中的test目錄,如今A、B的test目錄文件以下:
    ├── dir1
    │   ├── aa.txt
    │   ├── bb.txt
    │   └── cc.txt
    └── dir2
    └── dd.txt
  2. 假設如今把A機的bb.txt和cc.txt從dir1移動到dir2,即變成以下所示的狀態:
    ├── dir1
    │   └── aa.txt
    └── dir2
    ├── bb.txt
    ├── cc.txt
    └── dd.txt
  • 假設使用普通的rsync模式,那麼rsync會先刪除B機中的dir1中的bb.txt和cc.txt,並把A機中的bb.txt和cc.txt文件上傳到B機中的dir2目錄,達到同步的效果。
  • 但若是你rsync運行在ssh模式,那麼它會直接在B機中把dir1中的bb.txt和cc.txt用mv命令直接移動到dir2目錄,A機沒有向B機傳任何數據,效率顯而易見(特別是移動的數據特別多的時候更明顯)。
  • 這樣看,rsyncssh模式應該是最好的,但它卻有一個缺點,就是同步進程只能是單進程(maxProcesses=1),而rsync模式是能夠多進程同步的(速度快)。

3. direct同步模式
這個模式用於本地的兩個目錄之間同步,不用於遠程服務器同步。lsyncd一樣是監聽文件變化事件,而後把變化的文件從源目錄同步到目標目錄,同步的命令,就是linux系統自己的命令,好比cp、rm、mv,增長了文件用cp拷過去,刪除了文件,那邊也用rm刪除,移動了文件,那邊也用mv移動。json

簡述如何同步

假設有A和B兩臺機,A同步到B,則:vim

  • A:安裝lsyncd+rsync,並運行lsyncd服務;
  • B:只須要安裝rsync,並運行rsyncd服務;

A中的lsyncd監聽到文件變化後,調用A中的rsync向B推送文件,B由於運行有一個rsyncd服務,因此能夠接收這個推送,從而完成文件的同步。bash

另外還能夠有C、D、E、F、……,它們都與B相同,只要運行rsyncd服務便可,A上可配置一次性推送向多臺機器。

安裝lsyncd

CentOS用yum、其餘的用各自的包管理軟件,好比Ubuntu用apt-get,Mac用brew install:

yum -y install rsync lsyncd

Lsyncd安裝好以後,默認的配置文件在/etc/lsyncd.conf,另外還有些配置的例子在/usr/share/doc/lsyncd-2.2.2/examples下:

/usr/share/doc/lsyncd-2.2.2/examples/
├── lalarm.lua
├── lbash.lua
├── lecho.lua
├── lftp.lua
├── lgforce.lua
├── limagemagic.lua
├── lpostcmd.lua
├── lrsync.lua
├── lrsyncssh.lua
└── lsayirc.lua

0 directories, 10 files

/etc/lsyncd.conf也能夠寫成/etc/lsyncd.lua,它自己就是用lua(一種腳本語言)寫配置的。

Lsyncd配置文件詳解

配置文件是使用lua語言寫的,因此註釋要用lua語言的註釋符號,即兩個橫槓--。

配置文件主要有三部分:

  • settings:lsyncd自己的一些設置,好比日誌文件路徑,同步進程數,是否後臺運行等等。
  • sync:同步相關的設置,好比從哪同步到哪,要忽略哪些文件,多久同步一次等等
  • rsync:這部分是在sync裏面的,它主要配置rsync自己的一些選項。

如下是兩個官方的配置文檔:

  • 配置Settings:The Configuration File
  • 配置sync和rsync:Config Layer 4: Default Config

/etc/lsyncd.conf中的默認內容沒什麼價值,能夠所有刪除,下邊咱們來說解配置文件要怎麼寫。

default.rsync模式配置文件:
-- 因爲該配置文件其實是lua語言的語法,因此寫註釋要用--,--是lua語言的註釋符號
-- Lsyncd自己的配置

settings {
    -- 指定日誌文件位置
    logfile = "/var/log/lsyncd/lsyncd.log",

    -- 指定狀態文件位置
    statusFile = "/var/log/lsyncd/lsyncd.status",

     -- 是否之後臺的方式運行,注意它是nodaemon,因此是雙重否認,若是填false,意思就是「不要不後臺運行」(即後臺運行),非後臺運行通常用於調試,把rsync的verbose也設置爲true,這樣會把同步的細節輸出到控制檯,方便調試
    nodaemon = false,

    -- 系統inotify指定監聽的變化,什麼事件才同步。CloseWrite表示文件關閉的時候同步(建立文件,修改文件後保存都會觸發CloseWrite事件),能夠是"Modify"、"CloseWrite" (默認) 或"CloseWrite or Modify"。
    inotifyMode = "CloseWrite",

    -- 最大同步進程數(default.rsyncssh模式,則必須設置爲1,這就是rsyncssh模式的缺點了,若是是default.rsync模式則能夠設置大於1,這樣會有多個同步進程,速度更快)
    maxProcesses = 8,
    -- maxProcesses = 1,

    -- 配合下面的delay選項使用,delay單位是秒,當delay時間到了,無論maxDelays設置多少,都會同步,一樣,當maxDelays達到了設定值,無論是否到delay時間,都會同步,即兩個選項有一個知足即會觸發同步,爲了實時同>步,咱們通常設置爲1,表示即便只有一個文件改變也同步
    maxDelays = 1,
}

---- 同步配置default.rsync模式(好比配置從哪同步到哪,忽略哪些文件,多久同步一次等),能夠有多個sync模塊,每一個模塊用於設置一臺目標機器
sync {
    -- 有default.rsync/default.direct/default.rsyncssh三種模式,咱們默認都用default.rsync便可。
    default.rsync,

    -- 同步源目錄(本機某個目錄)
    source = "/data/wwwroot",

    -- 同步目標地址,不一樣同步模式有不一樣寫法,因爲絕大多數狀況都採用rsync同步,因此這裏寫的是rsync的同步地址
    target = "remote@192.168.1.6::wwwroot",

    -- 默認true,容許刪除目錄服務器中的某些文件(即刪除「那些在源服務器中不存在的文件」),可選值有: true/false/startup/running,startup就是隻在啓動lsyncd服務的時候判斷目標服務器中有哪些文件在源服務器中沒有,而後把這些文件刪除,但啓動以後若是目標服務器又新增了文件,這些文件即便在源服務器不存在,也不會被刪除;而running與startup正好相反,是在啓動的時候不會刪除,啓動以後會刪除,true=running+startup,false至關於running和startup都不作。
    -- delete = true,

    -- 哪些文件不一樣步(可用正則))
    exclude = {
        '.**',
        '.git/**',
        '*.bak',
        '*.tmp',
       'runtime/**',
       'cache/**'
    },

    -- 與上邊的maxDelays配合,maxDelays是累計事件數(單位:個),delay是時間(單位:秒),這兩個只要有一個符合條件就會同步一次,但爲了確保實時同步,maxDelays咱們通常設置爲1,也就是隻要有一個文件變化事件,就會同步一次,而delay是比較大的,默認是15。固然,假如咱們把maxDelays設置爲100,那可能15秒到了也沒有達到100個文件變化,但因爲到達時間了,它也會同步。
    delay = 15,

    -- 當init = false時只同步進程啓動之後發生改動事件的文件,原有的目錄即便有差別也不會同步,若是爲true,則啓動後若是源目錄與目標目錄的文件有差別都會同步,默認爲true。
    -- init = false,

    -- rsync的配置(這是default.rsync模式,若是是default.rsyncssh模式,該模塊的配置會有所不一樣)
    rsync = {
        -- rsync可執行文件的絕對路徑
        binary = "/usr/bin/rsync",

        -- 密碼文件路徑(default.rsyncssh模式不須要該項)
        password_file = "/etc/rsyncd.password",

        -- 打包後再同步(注意,打包不等於壓縮,打包便可以壓縮也能夠不壓縮)
        archive = true,

        -- 壓縮後再同步
        compress = false,

        -- 輸出同步信息(因爲是後臺執行,因此不必輸出,若是非後臺執行能夠設置爲true,非後臺執行主要用於調試)
        verbose  = false,

        -- 因爲rsync有很是多的選項(請本身rsync --help查看),部分非主要選項能夠用_extra的方式指定,雙引號引住,逗號分隔(bwlimit中的bw是bandwith,即帶寬,整個意思是帶寬限制,omit-link-times忽略符號連接的修改時間)
        _extra = {"--bwlimit=200", "--omit-link-times"}
    }
}

解釋幾個選項:

  • target = 「remote@192.168.1.6::wwwroot」,192.168.1.6是rsync服務器端的ip,remote是服務器端配置的用戶名,wwwroot是服務器端的模塊名。
  • password_file = 「/etc/rsyncd.password」, rsyncd.password中的內容就是一個字符串(好比:123456,你也不用寫password=123456),是服務器端的密碼(rsync服務器端能夠配置帳號和密碼),需使用命令chmod賦予文件400權限。

exclude再能夠用excludeFrom來代替,這樣就能夠在外部文件單獨寫要排除同步的文件:

excludeFrom = "/etc/lsyncd_exclude.lst",

外部排除同步文件/etc/lsyncd_exclude.lst的寫法:

.svn
Runtime/*
Uploads/*

若某個事件的路徑中的某些片斷匹配這些文本,那麼排除。好比/bin/foo/bar匹配規則foo。
– 若是規則以/開始,那麼只匹配路徑的開始
若是規則以/結束,那麼只匹配路徑的結束
– ? 匹配任何不是/的字符
– *匹配0或屢次非/字符
– **匹配任何字符0或屢次。

rsync服務器端配置

文章開頭已經說過A、B兩臺機各自要安裝什麼,如今A機的操做前面已經講過了,B機的操做,因爲以前寫過文章,這裏就再也不重複,請直接看:rsync的使用

開放端口
rsync的默認端口是873,若是你是CentOS7的firewalld防火牆,能夠用如下方法容許873端口:

firewall-cmd --zone=public --add-port=873/tcp --permanent
firewall-cmd --reload

若是你是本地作實驗以爲防火牆麻煩,也可關閉防火牆:
systemctl stop firewalld

啓動lsync服務
如何啓動的官方文檔:Invoking
lsync配置文件寫好後,就能夠啓動它了,因爲咱們有配置文件,因此啓動方式是:
lsyncd -log Exec /etc/lsyncd.conf

-log Exec表示記錄全部進程的日誌(由於若是maxProcesses大於1就會有多個同步進程)
啓動後,它只輸出了:
21:46:54 Normal: --- Startup, daemonizing ---

查看是否啓動成功:
ps aux | grep lsyncd

如何進程正常運行,能夠看到:
root 5238 7.7 0.6 13348 3340 ? Ss 21:46 0:15 lsyncd /etc/lsyncd.conf

查看log文件,你會看到已經同步了不少文件:
tail -100f /var/log/lsyncd/lsyncd.log

但其實在CentOS7系統中,咱們通常不直接啓動,而是用systemctl命令來啓動:
systemctl start lsyncd

查看啓動狀態:
systemctl status lsyncd

中止:
systemctl stop lsyncd

重啓:
systemctl restart lsyncd

設置開機自啓動:
systemctl enable lsyncd


default.rsyncssh模式配置文件:

相比rsync,主要修改的有如下幾點:

  1. settings中的maxProcesses必須爲1,不然沒法啓動,並報如下錯誤:
    Error: error preparing /etc/lsyncd.conf: /etc/lsyncd.conf:69: default.rsyncssh must have maxProcesses set to 1.
  2. rsync中的password_file去掉(或註釋掉),由於ssh已經不須要用rsync的password來驗證了。
  3. sync添加一個host,格式就是ssh登陸的格式(即:username@<ip_addr>這樣的格式)
  4. 把target改爲targetdir,值的格式就是目標服務器的絕對地址,好比:/data/wwwroot/(最後一個斜槓可要可不要,最好要,由於這樣一看就知道是目錄)。
  5. host指定的ssh登陸用戶須要具備targetdir指定的目錄的權限,若是啓動不了請嘗試用root,而且設置該用戶免密登陸:Linux-使用ssh免密碼登陸,不配置免密碼登陸將會沒法啓動。
  6. 免密碼登陸須要注意:假設你A機使用root啓動lsyncd(其實基本上都得用root),而你host=zhangsan@12.34.56.78(B機),那麼你必須保證A機能在root用戶下ssh 到B機。
  7. host指定ssh用戶,必須與目標文件夾須要的用戶相同,好比不少時候,wwwroot網站目錄咱們都使用www:www這樣的用戶和組,因此你就必須用這個用戶來同步,不然同步後建立出來的文件並非這個權限,權限不對則網站可能會出問題。
-- 因爲該配置文件其實是lua語言的語法,因此寫註釋要用--,--是lua語言的註釋符號
-- Lsyncd自己的配置
settings {
    -- 指定日誌文件位置
    logfile = "/var/log/lsyncd/lsyncd.log",

    -- 指定狀態文件位置
    statusFile = "/var/log/lsyncd/lsyncd.status",

    -- inotify事件模式,什麼事件才同步,CloseWrite表示文件關閉的時候同步(建立文件,修改文件後再關閉(如vim的:wq)都會觸發CloseWrite事件)
    inotifyMode = "CloseWrite",

    -- 最大同步進程數(default.rsyncssh模式必須設置爲1,不然沒法啓動,default.rsync模式能夠設置大於1)
    maxProcesses = 1,

    -- 配合下面的delay選項使用,delay單位是秒,當delay時間到了,無論maxDelays設置多少,都會同步,一樣,當maxDelays達到了設定值,無論是否到delay時間,都會同步,即兩個選項有一個知足即會觸發同步,爲了實時同>步,咱們通常設置爲1,表示即便只有一個文件改變也同步
    maxDelays = 1,

    -- 是否之後臺的方式運行,注意它是nodaemon,因此是雙重否認,若是填false,意思就是「不要不後臺運行」(即後臺運行),非後臺運行通常用於調試,把rsync的verbose也設置爲true,這樣會把同步的細節輸出到控制檯,方便調試
    nodaemon = false,
}

-- 同步配置default.rsync模式(好比配置從哪同步到哪,要忽略哪些文件,多久同步一次等),能夠有多個sync模塊,每一個模塊用於設置一臺目標機器
sync {
    -- 有default.rsync/default.direct/default.rsyncssh三種模式,咱們默認都用default.rsyncssh方式,由於這種方式實際上是最好的。
    default.rsyncssh,

    -- 同步源目錄(本機某個目錄)
    source = "/data/wwwroot/",

    -- 同步目標地址,rsyncssh模式寫法
    host="192.168.1.6",
    targetdir="/data/wwwroot/",

    -- 默認true,容許刪除目錄服務器中的某些文件(即刪除「那些在源服務器中不存在的文件」),可選值有: true/false/startup/running,startup就是隻在啓動lsyncd服務的時候判斷目標服務器中有哪些文件在源服務器中沒有,而後把這些文件刪除,但啓動以後若是目標服務器又新增了文件,這些文件即便在源服務器不存在,也不會被刪除;而running與startup正好相反,是在啓動的時候不會刪除,啓動以後會刪除,true=running+startup,false至關於running和startup都不作。
    -- delete = true,

    -- 哪些文件不一樣步(可用正則))
    exclude = {
        '.**',
        '.git/**',
        '*.bak',
        '*.tmp',
       'runtime/**',
       'cache/**'
    },
    -- 忽略文件路徑規則也可用外部配置文件
    -- excludeFrom = "/etc/lsyncd_exclude.lst",

    -- 與上邊的maxDelays配合,maxDelays是累計事件數(單位:個),delay是時間(單位:秒),這兩個只要有一個符合條件就會同步一次,但爲了確保實時同步,maxDelays咱們通常設置爲1,也就是隻要有一個文件變化事件,就會同步一次,而delay是比較大的,默認是15。固然,假如咱們把maxDelays設置爲100,那可能15秒到了也沒有達到100個文件變化,但因爲到達時間了,它也會同步。
    delay = 15,

    -- 當init = false時只同步進程啓動之後發生改動事件的文件,原有的目錄即便有差別也不會同步,若是爲true,則啓動後若是源目錄與目標目錄的文件有差別,就會同步,咱們固然要設置爲true,默認爲true,因此這個設置能夠不寫,寫在這裏是爲了解釋它。
    -- init = false,

    -- rsyncssh的配置(這是default.rsyncssh模式,若是是default.rsyncssh模式,該模塊的配置會有所不一樣)
    rsync = {
        -- rsync可執行文件的絕對路徑
        binary = "/usr/bin/rsync",

        -- 密碼文件路徑(rsync模式不用該配置,rsyncssh模式才須要該項)
        -- password_file = "/etc/rsyncd.password",

        -- 打包後再同步(注意,打包不等於壓縮,打包便可以壓縮也能夠不壓縮)
        archive = true,

        -- 壓縮後再同步
        compress = true,

        -- 同步符號連接文件
        copy_links = true,

        -- 同步符號連接目錄
        copy_dirlinks = true,

        -- 輸出同步信息(因爲是後臺執行,因此不必輸出,若是非後臺執行能夠設置爲true,非後臺執行主要用於調試)
        verbose  = false,

        -- 因爲rsync有很是多的選項(請本身rsync --help查看),部分非主要選項能夠用_extra的方式指定,雙引號引住,逗號分隔(bwlimit中的bw是bandwith,即帶寬,整個意思是帶寬限制,omit-link-times忽略符號連接的修改時間)
        _extra = {"--bwlimit=200", "--omit-link-times"},

        -- 指定ssh相關參數選項
        rsh = "/usr/bin/ssh -l xiebruce -i /root/.ssh/id_rsa -o StrictHostKeyChecking=no"
    }
}

其中這一句就是用來經過ssh登陸到服務器的:
rsh = "/usr/bin/ssh -l xiebruce -i /root/.ssh/id_rsa -o StrictHostKeyChecking=no"

  • 你平時使用ssh登陸服務器,也許用的是ssh zhangsan@192.168.1.6,最多再加個-p指定一下端口,但其實ssh還有不少選項,好比ssh -l test root@192.168.1.6就表示,我雖然是用root去登陸,但用-l(login的縮寫)指定了登陸的用戶,因此最終會以test用戶進行登陸,而-i(identify的縮寫)則表示指定私鑰(身份認證文件),一般是爲了免密碼登陸服務器,緣由很簡單,這個同步不可能每次讓你輸入密碼,因此須要免密碼登陸。
  • 而-o(option的縮寫)表示選項,ssh有不少選項,能夠用man ssh查到,而每一個選項是什麼意思須要用man ssh_config來查看,「StrictHostKeyChecking」表示嚴格檢查主機的key fingerprint(指紋密鑰),當你首次登陸一臺服務器的時候,它總會有這個提示:
    The authenticity of host '192.168.1.6 (192.168.1.6)' can't be established.
    ECDSA key fingerprint is SHA256:xcDUp3zNlJvhY4fwfwDH1pgOyc5p8Vsr2OjopanEQBw.
    Are you sure you want to continue connecting (yes/no)?

若是你輸入no那就不會登陸,若是你輸入yes,就會登陸,而且把這個「key fingerprint」(指紋密鑰)添加到你終端的ssh配置目錄下的known_hosts文件中,這個文件的位置,對於Mac/Linux電腦,是在~/.ssh/known_hosts,Windows的話則是在C:\Users\用戶名\目錄下。
同理,如今是rsync登陸你的ssh,因此rsync也會存儲這樣的指紋密鑰,若是「StrictHostKeyChecking」設置爲yes,就意味着每次都要嚴格檢查密鑰(就至關於你用終端登陸時,每次都要你輸入一遍yes),這樣顯然是不必的,因此咱們要把它設置爲「StrictHostKeyChecking=no」。


default.direct模式:該模式我沒有測試

sync {
    default.direct,
    source  = "/home/user/src/",
    target  = "/home/user/trg/"
}

同時同步到多臺機

格式以下,每臺目標服務器一個sync模塊便可,每一個sync模塊都像上邊說的那樣寫就行,其實就只是ip不一樣,其餘都同樣:

settings {
    logfile = "/var/log/lsyncd/lsyncd.log",
    inotifyMode = "CloseWrite or Modify",
    -- statusFile = "/var/log/lsyncd/lsyncd.status",
}

--  B服務器配置
sync {
    default.rsync,
    source = "/etc/nginx/",
    target = "rsync://rsync@192.168.1.6:1873/nginx/",
    exclude = { ".*", "*.tmp", "*.swp", "*.bak", "*.log", "*.swx", "*~", "sets/config.json", "listen_local_*" },
    delay = 2,
    init = false,
    rsync = {
        password_file = "/etc/rsyncd.passwd",
        archive = true,
        compress = true,
        verbose = true,
        checksum = true,
        ignore_times = true
    }
}

--  C服務器配置
sync {
    default.rsync,
    source = "/data/web/",
    target = "rsync://rsync@192.168.1.11:1873/web/",
    exclude = { ".*", "*.tmp", "*.swp", "*.bak", "*.log", "*.out", "*/logs/*", "*.swx", "*~" },
    delay = 120,
    init = false,
    rsync = {
        password_file = "/etc/rsyncd.passwd",
        archive = true,
        compress = true,
        verbose = true,
        checksum = true,
        ignore_times = true
    }
}

--  D服務器配置
sync {
    default.rsync,
    source = "/data/script/",
    target = "rsync://rsync@192.168.1.100:1873/script/",
    exclude = { ".*", "*.tmp", "*.swp", "*.bak", "*.log", "*.out", "*/logs/*", "*.swx", "*~" },
    delay = 2,
    init = false,
    rsync = {
        password_file = "/etc/rsyncd.passwd",
        archive = true,
        compress = true,
        verbose = true,
        checksum = true,
        ignore_times = true
    }
}
相關文章
相關標籤/搜索