黄色网页视频 I 影音先锋日日狠狠久久 I 秋霞午夜毛片 I 秋霞一二三区 I 国产成人片无码视频 I 国产 精品 自在自线 I av免费观看网站 I 日本精品久久久久中文字幕5 I 91看视频 I 看全色黄大色黄女片18 I 精品不卡一区 I 亚洲最新精品 I 欧美 激情 在线 I 人妻少妇精品久久 I 国产99视频精品免费专区 I 欧美影院 I 欧美精品在欧美一区二区少妇 I av大片网站 I 国产精品黄色片 I 888久久 I 狠狠干最新 I 看看黄色一级片 I 黄色精品久久 I 三级av在线 I 69色综合 I 国产日韩欧美91 I 亚洲精品偷拍 I 激情小说亚洲图片 I 久久国产视频精品 I 国产综合精品一区二区三区 I 色婷婷国产 I 最新成人av在线 I 国产私拍精品 I 日韩成人影音 I 日日夜夜天天综合

ZooKeeper的學習與應用

系統 2246 0
?近期大概學習了一下ZooKeeper,本身并沒有深入。LGG嘗試著在虛擬機里面搭了平臺,看了看一些教材,從網上到處看別人的博文并引用之,還請各位大牛們諒解我的剽竊?,F總結例如以下。

1.? ZooKeeper 的學習 與應用

?

1.1.? 概述

?

??? ZooKeeper Apache 在非常多云計算項目中的一個,與 Hadoop 密切相關,這樣的情況導致我一開始覺得 ZooKeeper 的搭建須要 Hadoop 項目作為支持,可是最后發現全然不須要,它是能夠單獨執行的一個項目。

??? 在網上看到了一個非常不錯的關于 ZooKeeper 的介紹:?顧名思義動物園管理員,他是拿來管大象 (Hadoop)? 、?蜜蜂 (Hive)? 、?小豬 (Pig)?? 的管理員,? Apache?Hbase 和? Apache?Solr? 以及 LinkedIn?sensei?? 等項目中都採用到了? Zookeeper 。 ZooKeeper 是一個分布式的,開放源代碼的分布式應用程序協調服務, ZooKeeper 是以 Fast?Paxos 算法為基礎,實現同步服務,配置維護和命名服務等分布式應用。

??? 從介紹能夠看出, ZooKeeper 更傾向于對大型應用的協同維護管理工作。 IBM 則給出了 IBM ZooKeeper 的認知:? Zookeeper? 分布式服務框架是? Apache?Hadoop? 的一個子項目,它主要是用來解決分布式應用中常常遇到的一些數據管理問題,如:統一命名服務、狀態同步服務、集群管理、分布式應用配置項的管理等。

??? 總之,我覺得它的核心詞就是一個單詞,協調。

?

1.2.? ZooKeeper 的特征

?

??? 在 Hadoop 權威指南中看到了關于 ZooKeeper 的一些核心特征,閱讀之后感覺總結的甚是精辟,在這里引用并總結。

?

1.2.1.? 簡易

??? ZooKeeper 的最重要核心就是一個精簡文件系統,提供一些簡單的操作以及附加的抽象(比如排序和通知)。

?

1.2.2.? 易表達

??? ZooKeeper 的原型是一個豐富的集合,它們是一些已建好的塊,能夠用來構建大型的協作數據結構和協議,比如:分布式隊列、分布式鎖以及一組對等體的選舉。

?

1.2.3.? 高可用性

??? ZooKeeper 執行在一些集群上,被設計成可用性較高的,因此應用程序能夠依賴它。 ZooKeeper 能夠幫助你的系統避免單點故障,從而建立一個可靠的應用程序。

?

1.2.4.? 松散耦合

??? ZooKeeper 的交互支持參與者之間并不了解對方。比如: ZooKeeper 能夠被當做一種公共的機制,使得進程彼此不知道對方的存在也能夠相互發現而且交互,對等方可能甚至不是同步的。

??? 這一特點我感覺最能體如今集群的部署啟動過程中。像 Hadoop 當把配置文件寫好之后,然后執行啟動腳本,則 251 , 241 , 242 中作為集群的虛擬機是同步啟動的,也就是 DataNode NameNode , TaskTracker ,以及 JobTracker 的啟動并執行時在一次啟動過程中啟動的,就是執行一次啟動腳本文件,則都啟動起來??墒? ZooKeeper 的啟動過程卻不是這種。我在 251 , 241 , 242 部署了 ZooKeeper 集群,并進行啟動,則啟動的過程是這種:首先 ssh 251 然后啟動,這時候 251 的集群節點啟動起來,可是控制臺一直報錯,大概的含義就是沒有檢測到其它兩個結點。接著分別 ssh 241 , 242 ,分別啟動集群中的剩下的結點,當 241 啟動起來時,回到 251 查看,發現報錯的信息降低,意思是僅僅差一個結點。當 251 , 241 , 242 三臺server的結點所有啟動起來,則三臺的server的控制臺打印出正常的信息。

?

1.2.5.? ZooKeeper 是一個庫

??? ZooKeeper 提供了一個開源的、共享的運行存儲,以及通用協作的方法,分擔了每一個程序猿寫通用協議的負擔。隨著時間的推移,人們能夠添加和改進這個庫來滿足自己的需求。

?

1.3.? Zookeeper 基本知識

?

??? 在這一小結,我介紹關于 ZooKeeper 的一些基本理論知識,以便對 ZooKeeper 有一個基本感性的認識吧,因為學習的時間不長,有些的認識可能是比較片面的,之后假設有了更深層次的認識,會補充于之后的月總結中。

?

1.3.1.? 層次化的名字空間

??? ZooKeeper 的整個名字空間的結構是層次化的,和一般的 Linux 文件系統結構非常相似,一顆非常大的樹。這也就是 ZooKeeper 的數據結構情況。名字空間的層次由斜杠 / 來進行切割,在名稱空間里面的每個結點的名字空間唯一由這個結點的路徑來確定。

3.1?ZooKeeper 的層次化名字空間

??? 每個節點擁有自身的一些信息,包含:數據、數據長度、創建時間、改動時間等等。從這樣一類既含有數據,又作為路徑表標示的節點的特點中,能夠看出, ZooKeeper 的節點既能夠被看做是一個文件,又能夠被看做是一個文件夾,它同一時候具有二者的特點。為了便于表達,今后我們將使用 Znode 來表示所討論的 ZooKeeper 節點。

?

1.3.2.? Znode

??? Znode 維護著數據、 ACL access?control?list ,訪問控制列表)、時間戳等交換版本等數據結構,它通過對這些數據的管理來讓緩存生效而且令協調更新。每當 Znode 中的數據更新后它所維護的版本將添加,這很類似于數據庫中計數器時間戳的操作方式。

另外 Znode 還具有原子性操作的特點:命名空間中,每個 Znode 的數據將被原子地讀寫。讀操作將讀取與 Znode 相關的全部數據,寫操作將替換掉全部的數據。除此之外,每個節點都有一個訪問控制列表,這個訪問控制列表規定了用戶操作的權限。

??? ZooKeeper 中相同存在暫時節點。這些節點與 session 同一時候存在,當 session 生命周期結束,這些暫時節點也將被刪除。暫時節點在某些場合也發揮著很關鍵的數據。

1.3.3.? Watch 機制

??? Watch 機制就和單詞本身的意思一樣,看??词裁矗吭敿殎碇v就是某一個或者一些 Znode 的變化。官方給出的定義:一個 Watch 事件是一個一次性的觸發器,當被設置了 Watch 的數據發生了改變的時候,則server將這個改變發送給設置了 Watch 的client,以便通知它們。

Watch 機制主要有下面三個特點:

1? 一次性的觸發器( one-time?trigger

??? 當數據改變的時候,那么一個 Watch 事件會產生而且被發送到client中??墒莄lient僅僅會收到一次這種通知,假設以后這個數據再次發生改變的時候,之前設置 Watch 的client將不會再次收到改變的通知,由于 Watch 機制規定了它是一個一次性的觸發器。

2? 發送給client

??? 這個表明了 Watch 的通知事件是從server發送給client的,是異步的,這就表明不同的client收到的 Watch 的時間可能不同,可是 ZooKeeper 有保證:當一個client在看到 Watch 事件之前是不會看到結點數據的變化的。比如: A=3 ,此時在上面設置了一次 Watch ,假設 A 突然變成 4 了,那么client會先收到 Watch 事件的通知,然后才會看到 A=4

3 被設置 Watch 的數據

??? 這表明了一個結點能夠變換的不同方式。一個 Znode 變化方式有兩種,結點本身數據的變化以及結點孩子的變化。因此 Watch 也能夠設置為這個 Znode 的結點數據,當然也能夠設置為 Znode 結點孩子。

?

1.3.4.? ACL 訪問控制列表

??? 這是另外一個和 Linux 操作系統很相似的地方, ZooKeeper 使用 ACL 來控制對旗下 Znode 結點們的訪問。 ACL 的實現和 Linux 文件系統的訪問權限十分類似:它通過設置權限為來表明是否同意對一個結點的相關內容的改變。

??? 可是與傳統 Linux 機制不太同樣,一個結點的數據沒有類似“擁有者,組用戶,其它用戶”的概念,在 ZooKeeper 中, ACL 通過設置 ID 以及與其關聯的權限來完畢訪問控制的。 ACL 的權限組成語法是:

(scheme:expression,?perms)

前者表明設置的 ID ,逗號后面表示的是 ID 相關的權限,比如:

? ( ip:172.16.16.1 ,?READ)

指明了 IP 地址為如上的用戶的權限為僅僅讀。

?

下面列舉下面 ACL 所具有的權限

CREATE :表明你能夠創建一個 Znode 的子結點。

READ :你能夠得到這個結點的數據以及列舉該結點的子結點情況。

WRITE :設置一個結點的數據。

DELETE :能夠刪除一個結點

ADMIN :對一個結點設置權限。

?

1.4.? ZooKeeper 的部署以及簡單使用

?

??? 要想使用 ZooKeeper ,首先就要把它部署在server上跑起來,就想 Apache , Tomcat FtpServer 等server一樣。 ZooKeeper 的部署方式主要有三種,單機模式、偽集群模式、集群模式。事實上剩下的兩種模式都是集群模式的特殊情況。

?

1.4.1.? 主要的環境變量配置

??? Java 大型的項目中,環境變量的配置非常重要,假設沒有非常好的配置環境變量的話,甚至項目連啟動都是難事。

export?ZOOKEEPER_HOME=/ home /zookeeper-3.3. 3

export?PATH=$PATH:$ZOOKEEPER_HOME/bin:$ZOOKEEPER_HOME/conf

?

??? 1.4.2.? ZooKeeper 的單機模式部署

ZooKeeper 的單機模式一般是用來高速測試client應用程序的,在實際過程中不可能是單機模式。單機模式的配置也比較簡單。

l? 編寫配置文件 zoo.cfg

??? zookeeper-3.3.3/conf 目錄以下就是要編寫配置文件的位置了。在目錄以下新建一個文件 zoo.cfg 。 ZooKeeper 的執行默認是讀取 zoo.cfg 文件中面的內容的。以下是一個最簡單的配置文件的例子:

tickTime=2000

dataDir=/var/zookeeper

clientPort=2181

??? 在這個文件里,我們須要指定? dataDir? 的值,它指向了一個文件夾,這個文件夾在開始的時候須要為空。以下是每一個參數的含義:

tickTime? :基本事件單元,以毫秒為單位。這個時間是作為? Zookeeper? server之間或client與server之間維持心跳的時間間隔,也就是每一個? tickTime? 時間就會發送一個心跳。?

dataDir? :存儲內存中數據庫快照的位置,顧名思義就是? Zookeeper? 保存數據的文件夾,默認情況下, Zookeeper? 將寫數據的日志文件也保存在這個文件夾里。?

clientPort? :這個port就是client連接? Zookeeper? server的port, Zookeeper? 會監聽這個port,接受client的訪問請求。

??? 使用單機模式時用戶須要注意:這樣的配置方式下沒有? ZooKeeper? 副本,所以假設? ZooKeeper? server出現問題,? ZooKeeper? 服務將會停止。

l? 執行執行腳本

??? 在 zookeeper-3.3.3/bin 目錄以下執行 zkServer.sh 就可以,執行完成之后則 ZooKeeper 服務變啟動起來。

./ zkServer.sh?start

??? 腳本默認調用 zoo.cfg 里面的配置,因此程序正常啟動。

?

1.4.3.? ZooKeeper 的集群模式部署

??? ZooKeeper 的集群模式下,多個 Zookeeper server在工作前會選舉出一個 Leader ,在接下來的工作中這個被選舉出來的 Leader 死了,而剩下的 Zookeeper server會知道這個 Leader 死掉了,在活著的 Zookeeper 集群中會繼續選出一個 Leader ,選舉出 Leader 的目的是為了能夠在分布式的環境中保證數據的一致性。如圖所看到的:

3.2?ZooKeeper 集群模式圖

l? 確認集群server的數量

??? 因為 ZooKeeper 集群中,會有一個 Leader 負責管理和協調其它集群server,因此server的數量通常都是單數,比如 3 , 5 , 7... 等,這樣 2n+1 的數量的server就能夠同意最多 n 臺server的失效。

l? 編寫配置文件

配置文件須要在每臺server中都要編寫,下面是一個配置文件的樣本:

#?Filename?zoo.cfg

tickTime=2000

dataDir=/var/zookeeper/

clientPort=2181

initLimit=5

syncLimit=2

server.1= 202.115.36.251 :2888:3888

server.2= 202.115.36.241 :2888:3888

server.3= 202.115.36.242 :2888:3888

??? initLimit :這個配置項是用來配置? Zookeeper? 接受client(這里所說的client不是用戶連接? Zookeeper? server的client,而是? Zookeeper? server集群中連接到? Leader? 的? Follower? server)初始化連接時最長能忍受多少個心跳時間間隔數。當已經超過? 10? 個心跳的時間(也就是? tickTime )長度后? Zookeeper? server還沒有收到client的返回信息,那么表明這個client連接失敗??偟臅r間長度就是? 5*2000=10? 。

??? syncLimit :這個配置項標識? Leader? 與? Follower? 之間發送消息,請求和應答時間長度,最長不能超過多少個? tickTime? 的時間長度,總的時間長度就是? 2*2000=4? 秒?

??? server.A=B C D :當中? A? 是一個數字,表示這個是第幾號server; B? 是這個server的? ip? 地址; C? 表示的是這個server與集群中的? Leader? server交換信息的port; D? 表示的是萬一集群中的? Leader? server掛了,須要一個port來又一次進行選舉,選出一個新的? Leader ,而這個port就是用來運行選舉時server相互通信的port。假設是偽集群的配置方式,因為? B? 都是一樣,所以不同的? Zookeeper? 實例通信port號不能一樣,所以要給它們分配不同的port號。

l? 創建 myid 文件

??? 除了改動? zoo.cfg? 配置文件,集群模式下還要配置一個文件? myid ,這個文件在? dataDir? 文件夾下,這個文件中面就僅僅有一個數據就是? A? 的值, Zookeeper? 啟動時會讀取這個文件,拿到里面的數據與? zoo.cfg? 里面的配置信息比較從而推斷究竟是那個? server 。

l? 執行執行腳本

??? 和單機模式下的執行方式基本同樣,值得注意的地方就是要分別在不同server上執行一次,比如分別在 251 , 241 , 242 上執行:

./ zkServer.sh?start

這樣才干使得整個集群啟動起來。

?

1.4.4.? ZooKeeper 的集群偽分布

??? 事實上在企業中式不會存在的,另外為了測試一個client程序也沒有必要存在,僅僅有在物質條件比較匱乏的條件下才會存在的模式。

集群偽分布模式就是在單機下模擬集群的 ZooKeeper 服務,在一臺機器上面有多個 ZooKeeper JVM 同一時候執行。

l? 確認集群偽server的數量

??? 2n+1 ,和之前的集群分布同樣。

l? 編寫配置文件

??? 在 /conf 目錄新建三個配置文件, zoo1.cfg , zoo2.cfg 以及 zoo3.cfg 。配置文件分別例如以下編寫:

Zoo1.cfg

tickTime=2000

initLimit=10

syncLimit=5

dataDir=/root/hadoop-0.20.2/zookeeper-3.3.1/d_1

clientPort=2181

server.1=localhost:2887:3887

server.2=localhost:2888:3888

server.3=localhost:2889:3889

Zoo2.cfg

tickTime=2000

initLimit=10

syncLimit=5

dataDir=/root/hadoop-0.20.2/zookeeper-3.3.1/d_ 2

clientPort=218 2

server.1=localhost:2887:3887

server.2=localhost:2888:3888

server.3=localhost:2889:3889

Zoo3.cfg

tickTime=2000

initLimit=10

syncLimit=5

dataDir=/root/hadoop-0.20.2/zookeeper-3.3.1/d_ 3

clientPort=218 3

server.1=localhost:2887:3887

server.2=localhost:2888:3888

server.3=localhost:2889:3889

??? 因為三個服務都在同一臺電腦上,因此這里要保證地址的唯一性,因此要特別注意 IP 地址和port號不要互相沖突,以免影響程序的正確運行。

l? 創建 myid 文件

??? 這個同集群模式部署,在各自的目錄以下創建。

l? 執行執行腳本

??? 因為全部的配置文件都在 /conf 目錄以下,因此要運行三次,并且要加文件名稱的參數,不然會默認運行 zoo.cfg 這個文件,例如以下:

./ zkServer.sh?start?zoo1.cfg

./ zkServer.sh?start?zoo2.cfg

./ zkServer.sh?start?zoo3.cfg

運行完畢后,將完畢 ZooKeeper 的集群偽分布的啟動。

?

1.4.5.? 通過 ZooKeeper 命令行工具訪問 ZooKeeper

??? ZooKeeper 命令行工具類似于 Linux shell 環境,只是功能肯定不及 shell 啦,可是使用它我們能夠簡單的對 ZooKeeper 進行訪問,數據創建,數據改動等操作。

當啟動? ZooKeeper? 服務成功之后,輸入下述命令,連接到? ZooKeeper? 服務:

zkCli.sh?–server? 202.115.36.251 :2181

連接成功后,系統會輸出? ZooKeeper? 的相關環境以及配置信息,并在屏幕輸出“? Welcome?to?ZooKeeper? ”等信息。

命令行工具的一些簡單操作例如以下:

1? )使用? ls? 命令來查看當前? ZooKeeper? 中所包括的內容:

[zk:?202.115.36.251:2181(CONNECTED)?1]?ls?/

2? )創建一個新的? znode? ,使用? create?/zk?myData? 。這個命令創建了一個新的? znode? 節點“? zk? ”以及與它關聯的字符串:

[zk:?202.115.36.251:2181(CONNECTED)?2]?create?/zk?"myData"

3? )我們執行? get? 命令來確認? znode? 是否包括我們所創建的字符串:

[zk:?202.115.36.251:2181(CONNECTED)?3]?get?/zk

4? )以下我們通過? set? 命令來對? zk? 所關聯的字符串進行設置:

[zk:?202.115.36.251:2181(CONNECTED)?4]?set?/zk?"zsl"

5? )以下我們將剛才創建的? znode? 刪除:

[zk:?202.115.36.251:2181(CONNECTED)?5]?delete?/zk

?

1.4.6.? 使用 API 來訪問 ZooKeeper

??? API 訪問 ZooKeeper 才是client基本的使用手段,通過在client編寫豐富多彩的程序,來達到對 ZooKeeper 的利用。這里給出一個簡單的樣例:(深入的還沒能力給出啊,樣例是從網上找的非常清晰明了)

1.?import?java.io.IOException;

2.

3.?import?org.apache.zookeeper.CreateMode;

4.?import?org.apache.zookeeper.KeeperException;

5.?import?org.apache.zookeeper.Watcher;

6.?import?org.apache.zookeeper.ZooDefs.Ids;

7.?import?org.apache.zookeeper.ZooKeeper;

8.

9.?public?class?demo?{

10.?????//? 會話超時時間,設置為與系統默認時間一致

11.?????private?static?final?int?SESSION_TIMEOUT=30000;

12.????

13.?????//? 創建? ZooKeeper? 實例

14.?????ZooKeeper?zk;

15.????

16.?????//? 創建? Watcher? 實例

17.?????Watcher?wh=new?Watcher(){

18.????????????public?void?process(org.apache.zookeeper.WatchedEvent?event)

19.????????????{

20.????????????????????System.out.println(event.toString());

21.????????????}

22.?????};

23.????

24.?????//? 初始化? ZooKeeper? 實例

25.?????private?void?createZKInstance()?throws?IOException

26.?????{?????????????

27.????????????zk=new?ZooKeeper("localhost:2181",demo.SESSION_TIMEOUT,this.wh);

28.???????????

29.?????}

30.????

31.?????private?void?ZKOperations()?throws?IOException,InterruptedException,KeeperException

32.?????{

33.????????????System.out.println("\n1.? 創建? ZooKeeper? 節點? (znode? :? zoo2,? 數據:? myData2? ,權限:? OPEN_ACL_UNSAFE? ,節點類型:? Persistent");

34.????????????zk.create("/zoo2","myData2".getBytes(),?Ids.OPEN_ACL_UNSAFE,?CreateMode.PERSISTENT);

35.???????????

36.????????????System.out.println("\n2.? 查看是否創建成功:? ");

37.????????????System.out.println(new?String(zk.getData("/zoo2",false,null)));

38.???????????????????????????

39.????????????System.out.println("\n3.? 改動節點數據? ");

40.????????????zk.setData("/zoo2",?"shenlan211314".getBytes(),?-1);

41.???????????

42.????????????System.out.println("\n4.? 查看是否改動成功:? ");

43.????????????System.out.println(new?String(zk.getData("/zoo2",?false,?null)));

44.???????????????????????????

45.????????????System.out.println("\n5.? 刪除節點? ");

46.????????????zk.delete("/zoo2",?-1);

47.???????????

48.????????????System.out.println("\n6.? 查看節點是否被刪除:? ");

49.????????????System.out.println("? 節點狀態:? ["+zk.exists("/zoo2",?false)+"]");

50.?????}

51.????

52.?????private?void?ZKClose()?throws??InterruptedException

53.?????{

54.????????????zk.close();

55.?????}

56.????

57.?????public?static?void?main(String[]?args)?throws?IOException,InterruptedException,KeeperException?{

58.????????????demo?dm=new?demo();

59.????????????dm.createZKInstance(?);

60.????????????dm.ZKOperations();

61.????????????dm.ZKClose();

62.?????}

63.}

??? 此類包括兩個基本的 ?ZooKeeper? 函數,分別為 ?createZKInstance? ()和 ?ZKOperations? ()。當中 ?createZKInstance? ()函數負責對 ?ZooKeeper? 實例 ?zk? 進行初始化。 ?ZooKeeper? 類有兩個構造函數,我們這里使用 ? “ ?ZooKeeper? ?String?connectString,? , ?int?sessionTimeout,? , ?Watcher?watcher? 對其進行初始化。因此,我們須要提供初始化所需的,連接字符串信息,會話超時時間,以及一個 ?watcher? 實例。 ?17? 行到 ?23? 行代碼,是程序所構造的一個 ?watcher? 實例,它可以輸出所發生的事件。

??? ZKOperations? ()函數是我們所定義的對節點的一系列操作。它包含:創建 ?ZooKeeper? 節點( ?33? 行到 ?34? 行代碼)、查看節點( ?36? 行到 ?37? 行代碼)、改動節點數據( ?39? 行到 ?40? 行代碼)、查看改動后節點數據( ?42? 行到 ?43? 行代碼)、刪除節點( ?45? 行到 ?46? 行代碼)、查看節點是否存在( ?48? 行到 ?49? 行代碼)。另外,須要注意的是:在創建節點的時候,須要提供節點的名稱、數據、權限以及節點類型。此外,使用 ?exists? 函數時,假設節點不存在將返回一

?null? 值。

?

1.5.? 小結

??? 對于 ZooKeeper 的認識眼下處在比較淺顯的狀態,了解到了主要的服務的部署以及大概 ZooKeeper 的工作原理。非常多東西都是僅僅懂得皮毛,如今可以深深地感受到“僅僅有結合詳細的應用才干使你對一個東西有較深的了解”這句話的深刻含義了。

ZooKeeper的學習與應用


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!?。?/p>

發表我的評論
最新評論 總共0條評論