day03.集群部署zookeeper【大數據教程】

一、Nginx/keepalived/lvs的介紹

1.?nginx

1.1.??nginx簡介

Nginx是一個自由、開源、高性能及輕量級的HTTP服務器及反轉代理服務器。Nginx以其高性能、穩定、功能豐富、配置簡單及占用系統資源少而著稱。

Nginx?超越?Apache?的高性能和穩定性,使得國內使用?Nginx?作為?Web?服務器的網站也越來越多.

1.2.?基礎功能?

反向代理加速,簡單的負載均衡和容錯;

1.3.?優勢

1、Nginx專為性能優化而開發,性能是其最重要的考量,?實現上非常注重效率 。有報告表明能支持高達?50,000?個并發連接數。?

2、Nginx具有很高的穩定性。其它HTTP服務器,當遇到訪問的峰值,或者有人惡意發起慢速連接時,也很可能會導致服務器物理內存耗盡頻繁交換,失去響應,只能重啟服務器。

例如當前apache一旦上到200個以上進程,web響應速度就明顯非常緩慢了。而Nginx采取了分階段資源分配技術,使得它的CPU與內存占用率非常低。

3、nginx官方表示保持10,000個沒有活動的連接,它只占2.5M內存,就穩定性而言, nginx比其他代理服務器更勝一籌。

4、Nginx支持熱部署。它的啟動特別容易,?并且幾乎可以做到7*24不間斷運行,即使運行數個月也不需要重新啟動。你還能夠在不間斷服務的情況下,對軟件版本進行進行升級。?

5、Nginx采用C進行編寫,?不論是系統資源開銷還是CPU使用效率都高很多。


1.4.?安裝

見文檔

2.?keepalived

2.1.?簡介

Keepalived的作用是檢測web服務器的狀態,如果有一臺web服務器死機,或工作出現故障,Keepalived將檢測到,并將有故障的web服務器從系統中剔除,當web服務器工作正常后Keepalived自動將web服務器加入到服務器群中,這些工作全部自動完成,不需要人工干涉,需要人工做的只是修復故障的web服務器。


2.2.?作用

主要用作RealServer的健康狀態檢查以及LoadBalance主機和BackUP主機之間failover的實現。

3.?lvs

3.1.?LVS是什么

1、LVS的英文全稱是Linux Virtual Server,即Linux虛擬服務器。

2、它是我們國家的章文嵩博士的一個開源項目。

3.2.?LVS能干什么

1、?LVS主要用于多服務器的負載均衡。

2、它工作在網絡層,可以實現高性能,高可用的服務器集群技術。

3、它可把許多低性能的服務器組合在一起形成一個超級服務器。

4、它配置非常簡單,且有多種負載均衡的方法。

5、它穩定可靠,即使在集群的服務器中某臺服務器無法正常工作,也不影響整體效果。

6、可擴展性也非常好。


3.3.?nginx和lvs作對比的結果:

1、nginx工作在網絡的應用層,主要做反向代理;lvs工作在網絡層,主要做負載均衡。nginx也同樣能承受很高負載且穩定,但負載度和穩定度不及lvs。 ?

2、nginx對網絡的依賴較小,lvs就比較依賴于網絡環境。

3、在使用上,一般最前端所采取的策略應是lvs。?nginx可作為lvs節點機器使用。


3.4.?負載均衡機制

前面我們說了LVS是工作在網絡層。相對于其它負載均衡的解決辦法,它的效率是非常高的。LVS的通過控制IP來實現負載均衡。IPVS是其具體的實現模塊。IPVS的主要作用:安裝在Director Server上面,在Director Server虛擬一個對外訪問的IP(VIP)。用戶訪問VIP,到達Director Server,Director Server根據一定的規則選擇一個Real Server,處理完成后然后返回給客戶端數據。這些步驟產生了一些具體的問題,比如如何選擇具體的Real Server,Real Server如果返回給客戶端數據等等。IPVS為此有三種機制:


1.?VS/NAT(Virtual Server via Network Address Translation),即網絡地址翻轉技術實現虛擬服務器。

當請求來到時,Diretor server上處理的程序將數據報文中的目標地址(即虛擬IP地址)改成具體的某臺Real Server,端口也改成Real Server的端口,然后把報文發給Real Server。Real Server處理完數據后,需要返回給Diretor Server,然后Diretor server將數據包中的源地址和源端口改成VIP的地址和端口,最后把數據發送出去。由此可以看出,用戶的請求和返回都要經過Diretor Server,如果數據過多,Diretor Server肯定會不堪重負。



2.?VS/TUN(Virtual Server via IP Tunneling),即IP隧道技術實現虛擬服務器。

IP隧道(IP tunneling)是將一個IP報文封裝在另一個IP報文的技術,這可以使得目標為一個IP地址的數據報文能被封裝和轉發到另一個IP地址。IP隧道技術亦稱為IP封裝技術(IP encapsulation)。它跟VS/NAT基本一樣,但是Real server是直接返回數據給客戶端,不需要經過Diretor server,這大大降低了Diretor server的壓力。


3.?VS/DR(Virtual Server via Direct Routing),即用直接路由技術實現虛擬服務器。

跟前面兩種方式,它的報文轉發方法有所不同,VS/DR通過改寫請求報文的MAC地址,將請求發送到Real Server,而Real Server將響應直接返回給客戶,免去了VS/TUN中的IP隧道開銷。這種方式是三種負載調度機制中性能最高最好的,但是必須要求Director Server與Real Server都有一塊網卡連在同一物理網段上。


二、Nginx/keepalived/lvs安裝使用

nginx安裝文檔

1.?安裝依賴包

2.?安裝nginx

2.1.?上傳

2.2.?解壓

2.3.?重命名

2.4.?安裝nginx

3.?安裝JDK

3.1.?切換到root用戶:

3.2.?查看以前是不是安裝了openjdk:

3.3.?卸載openjdk:

(其中參數“tzdata-java-2013g-1.el6.noarch”為上面查看中顯示的結果,粘進來就行)

3.4.?安裝sunjdk

3.4.1.?上傳

3.4.2.?解壓

3.4.3.?創建快捷方式?

3.4.4.?配置環境變量

3.4.5.?重新編譯環境變量

4.?安裝tomcat

5.?重新配置nginx

1、cd /usr/local/nginx ?

2、vi /usr/local/nginx/nginx.conf

user ?nobody nobody;?#定義Nginx運行的用戶和用戶組

worker_processes ?4;?#nginx進程數,建議設置為等于CPU總核心數。

error_log ?logs/error.log?info;?#全局錯誤日志定義類型,[ debug | info | notice | warn | error | crit ]

worker_rlimit_nofile 1024;?#一個nginx進程打開的最多文件描述符數目,所以建議與ulimit -n的值保持一致。

pid?logs/nginx.pid;?#進程文件


#工作模式及連接數上限

events {

use epoll;#參考事件模型,use [ kqueue | rtsig | epoll | /dev/poll | select | poll ]; epoll模型是Linux 2.6以上版本內核中的高性能網絡I/O模型

worker_connections ?1024;#單個進程最大連接數(最大連接數=連接數*進程數)

}


#設定http服務器,利用它的反向代理功能提供負載均衡支持

http {

include ??????mime.types;#文件擴展名與文件類型映射表

default_type ?application/octet-stream;#默認文件類型

#設定負載均衡的服務器列表

upstream ?tomcatxxxcom ?{ ?

?????server ??192.168.56.200:8080; ?

server ??192.168.56.201:8080;??

}

#設定日志格式

????log_format ?www_xy_com ?'$remote_addr - $remote_user [$time_local] "$request" '

??????????????????????'$status $body_bytes_sent "$http_referer" '

??????????????????????'"$http_user_agent" "$http_x_forwarded_for"';


sendfile ???????on;#開啟高效文件傳輸模式,sendfile指令指定nginx是否調用sendfile函數來輸出文件,對于普通應用設為?on,如果用來進行下載等應用磁盤IO重負載應用,可設置為off,以平衡磁盤與網絡I/O處理速度,降低系統的負載。注意:如果圖片顯示不正常把這個改成off。

keepalive_timeout ?65; #長連接超時時間,單位是秒


????#gzip ?on;

#設定虛擬主機,默認為監聽80端口

????server {

????????listen ??????80;

server_name ?tomcat.xxx.com;#域名可以有多個,用空格隔開


????????#charset koi8-r;

#設定本虛擬主機的訪問日志

????????access_log ?/data/logs/access.log ?www_xy_com;

#對?"/"?啟用反向代理

???location / {

???proxy_pass ???????http://tomcatxxxcom; ?

???????????????proxy_set_header ??Host ????????????$host; ?

???????????????proxy_set_header ??X-Real-IP ???????$remote_addr; ?

???????????????proxy_set_header ??X-Forwarded-For ?$proxy_add_x_forwarded_for;

????????}


????????#error_page ??500 502 503 504 ?/50x.html;

????????location = /50x.html {

????????????root ??html;

????????}

????}

}


3、創建logs所需要的文件夾/data /logs/

cd /

mkdir –m 755 data

cd data

mkdir –m 755 logs


4、啟動tomcat、nginx。

/usr/local/tomcat/bin/startup.sh

/usr/local/nginx/sbin/nginx


5、修改hosts,加入

192.168.56.99 tomcat.xxx.com


6、訪問http:// tomcat.xxx.com


keepalived安裝文檔

1.?安裝依賴

su - root

yum -y install kernel-devel*

yum -y install openssl-*

yum -y install popt-devel

yum -y install lrzsz

yum -y install openssh-clients

2.?安裝keepalived

2.1.?上傳

1、cd /usr/local

2、rz?–y

3、選擇keepalived安裝文件

2.2.?解壓

tar –zxvf keepalived-1.2.2.tar.gz

2.3.?重命名

mv keepalived-1.2.2 keepalived

2.4.?安裝keepalived

1、cd keepalived

2、執行命令

./configure --prefix=/usr/local/keepalived -enable-lvs-syncd --enable-lvs --with-kernel-dir=/lib/modules/2.6.32-431.el6.x86_64/build

3、編譯

make

4、安裝

make install

2.5.?配置服務和加入開機啟動

cp /usr/local/keepalived/etc/rc.d/init.d/keepalived /etc/init.d/

cp /usr/local/keepalived/etc/sysconfig/keepalived /etc/sysconfig/

mkdir -p /etc/keepalived

cp /usr/local/keepalived/etc/keepalived/keepalived.conf /etc/keepalived/

ln -s /usr/local/keepalived/sbin/keepalived /sbin/

chkconfig keepalived on

2.6.?修改配置文件

1、?vi /etc/keepalived/keepalived.conf

2、詳解:

global_defs {

notification_email {#指定keepalived在發生切換時需要發送email到的對象,一行一個

?????#acassen@firewall.loc

?????#failover@firewall.loc

?????#sysadmin@firewall.loc

???}

notification_email_from Alexandre.Cassen@firewall.loc#指定發件人

#smtp_server 192.168.200.1#指定smtp服務器地址

#smtp_connect_timeout 30 #指定smtp連接超時時間

router_id LVS_DEVEL#運行keepalived機器的一個標識

}


vrrp_instance VI_1 {

state?BACKUP#指定那個為master,那個為backup

interface?eth1#設置實例綁定的網卡

virtual_router_id 51#同一實例下virtual_router_id必須相同

????priority?100#定義優先級,數字越大,優先級越高,備機要小于主

advert_int 1#MASTER與BACKUP負載均衡器之間同步檢查的時間間隔,單位是秒

nopreempt#設置為不搶占,從啟動后主不會自動切換回來,?注:這個配置只能設置在backup主機上,而且這個主機優先級要比另外一臺高


authentication {#設置認證

????????auth_type PASS

????????auth_pass 1111

????}

????virtual_ipaddress{#設置vip

192.168.56.70#虛擬IP

????}

}


virtual_server 192.168.56.70 8080?{

delay_loop 6#健康檢查時間間隔

lb_algo rr #調度算法rr|wrr|lc|wlc|lblc|sh|dh

lb_kind?DR?#負載均衡轉發規則NAT|DR|RUN

????#nat_mask 255.255.255.0#需要驗證

persistence_timeout 1#會話保持時間

protocol TCP#使用的協議


????real_server 192.168.56.201 8080 {

weight 10 #默認為1,0為失效

????????SSL_GET {

url { #檢查url,可以指定多個

??????????????path /

digest ff20ad2481f97b1754ef3e12ecd3a9cc #檢查后的摘要信息

????????????}

????????????url {

??????????????path /mrtg/

??????????????digest 9b3a0c85a887a256d6939da88aabd8cd

????????????}

connect_timeout 3#連接超時時間

nb_get_retry 3#重連次數

delay_before_retry 3#重連間隔時間

????????}

????}


}??

3.?按照上面步驟安裝備機器

注意:備的配置文件不相同。


4.?兩臺機器啟動keepalived:

service keepalived start


5.?驗證

ip a

6.?監控

因為keepalive只能監控機器的死活,所以當軟件死掉后,keepalived仍然不會切換;

所以需要寫一個腳本,監控軟件的死活。

運行wangsf.sh,監控軟件

lvs安裝文檔

1.?安裝lvs應用模塊

1、安裝依賴包:

yum -y install ipvs*

2、驗證本機ip_vs模塊是否加載

[root@client lvs]# grep -i 'ip_vs' /boot/config-2.6.32-431.el6.x86_64

CONFIG_IP_VS=m

CONFIG_IP_VS_IPV6=y

# CONFIG_IP_VS_DEBUG is not set

CONFIG_IP_VS_TAB_BITS=12

CONFIG_IP_VS_PROTO_TCP=y

CONFIG_IP_VS_PROTO_UDP=y

CONFIG_IP_VS_PROTO_AH_ESP=y

CONFIG_IP_VS_PROTO_ESP=y

CONFIG_IP_VS_PROTO_AH=y

CONFIG_IP_VS_PROTO_SCTP=y

CONFIG_IP_VS_RR=m

CONFIG_IP_VS_WRR=m

CONFIG_IP_VS_LC=m

CONFIG_IP_VS_WLC=m

CONFIG_IP_VS_LBLC=m

CONFIG_IP_VS_LBLCR=m

CONFIG_IP_VS_DH=m

CONFIG_IP_VS_SH=m

CONFIG_IP_VS_SED=m

CONFIG_IP_VS_NQ=m

CONFIG_IP_VS_FTP=m

CONFIG_IP_VS_PE_SIP=m

2.?安裝lvs

2.1.?編寫lvs drsrever腳本:

2.1.1.?修改functions權限:

(functions這個腳本是給/etc/init.d里邊的文件使用的(可理解為全局文件)。)

chmod 755 /etc/rc.d/init.d/functions

2.1.2.?創建lvs文件夾

cd /usr/local

mkdir –m 755 lvs

cd /lvs

2.1.3.?編寫腳本

vi ?lvs_dr.sh

#!/bin/bash

#description:start lvs server

echo "1" >/proc/sys/net/ipv4/ip_forward ??#開啟ip轉發

WEB1=192.168.56.200?#真實的webip

WEB2=192.168.56.201?#真實的webip

VIP1=192.168.56.80?#虛擬lvs的ip

/etc/rc.d/init.d/functions??#初始化function

case "$1" in?#第一個參數

start)??#第一個參數是start

echo "start LVS of directorServer"?#打印

/sbin/ifconfig eth0:0 $VIP1 broadcast $VIP1 netmask 255.255.255.255 up?#設置虛擬網絡

/sbin/ipvsadm?–C??#清除內核虛擬服務器表中的所有記錄,清除lvs設置

/sbin/ipvsadm -A -t $VIP1:8080 -s rr?#設置rr模式,輪詢模式

/sbin/ipvsadm -a -t $VIP1:8080 -r $WEB1:8080 –g?#輪詢的機器,-g采用DR模式

/sbin/ipvsadm -a -t $VIP1:8080 -r $WEB2:8080 –g

/sbin/ipvsadm?#啟動lvs

;;

stop)??#如果第一個參數是stop

echo "close LVS directorserver"?#打印

echo "0" >/proc/sys/net/ipv4/ip_forward?#關閉ip轉發

/sbin/ipvsadm?–C??#清除內核虛擬服務器表中的所有記錄

/sbin/ipvsadm?–Z??#虛擬服務表計數器清零(清空當前的連接數量等)

;;

*)?#如果第一個參數是其他任何值

echo "usage:$0 {start|stop}"??#打印:提示輸入start或者stop

exit 1??#退出

esac?#循環結束

2.1.4.?執行腳本

chmod 755 lvs_dr.sh

./lvs-dr.sh ?start

2.1.5.?查看:

ipvsadm –ln

看到上面信息說明ipvsadm啟動成功。

2.2.??編寫lvs realserver腳本

2.2.1.?在web1?和web2機器上修改functions權限:

(functions這個腳本是給/etc/init.d里邊的文件使用的(可理解為全局文件)。)

chmod 755 /etc/rc.d/init.d/functions


2.2.2.?在分別在web1?和web2服務器上創建lvs文件夾:

cd /usr/local

mkdir –m 755 lvs

cd lvs

rz –y

2.2.3.?編寫監本?

vi ?lvs-rs.sh

#!/bin/sh

VIP1=192.168.56.80??#虛擬ip

/etc/rc.d/init.d/functions?#初始化function

case "$1" in??#第一個參數

start)?#如果第一個參數是start

echo "start LVS of realserver"?#打印

/sbin/ifconfig lo:0 $VIP1 broadcast $VIP1 netmask 255.255.255.255 up?#設置虛擬網絡

echo "1" >/proc/sys/net/ipv4/conf/lo/arp_ignore?#定義接收到ARP請求時的響應級別

echo "2" >/proc/sys/net/ipv4/conf/lo/arp_announce?#定義將自己的地址向外通告時的級別

echo "1" >/proc/sys/net/ipv4/conf/all/arp_ignore

echo "2" >/proc/sys/net/ipv4/conf/all/arp_announce

;;

stop)?#如果第一個參數是stop

/sbin/ifconfig lo:0 down??#停止網卡

echo "close lvs dirctorserver"?#打印

echo "0" >/proc/sys/net/ipv4/conf/lo/arp_ignore?#定義接收到ARP請求時的響應級別

echo "0" >/proc/sys/net/ipv4/conf/lo/arp_announce?#定義將自己的地址向外通告時的級別

echo "0" >/proc/sys/net/ipv4/conf/all/arp_ignore

echo "0" >/proc/sys/net/ipv4/conf/all/arp_announce

;;

*)

echo "usage:$0{start|stop}"

exit 1

esac

2.2.4.?啟動在web1?和web2機器上lvs:

chmod 755 lvs-rs.sh

./lvs-rs.sh start


3.?設置dr機器上設置連接超時值(秒)?

ipvsadm --set 1 1 1

4.?關閉

./lvs-rs.sh stop

./lvs-dr.sh stop

三、Nginx/keepalived/lvs集群安裝

1.?安裝tomcat

2.?安裝nginx

配置文件和之前的一樣

user ?nobody nobody;?#定義Nginx運行的用戶和用戶組

worker_processes ?4;?#nginx進程數,建議設置為等于CPU總核心數。

error_log ?logs/error.log?info;?#全局錯誤日志定義類型,[ debug | info | notice | warn | error | crit ]

worker_rlimit_nofile 1024;?#一個nginx進程打開的最多文件描述符數目,所以建議與ulimit -n的值保持一致。

pid?logs/nginx.pid;?#進程文件


#工作模式及連接數上限

events {

use epoll;#參考事件模型,use [ kqueue | rtsig | epoll | /dev/poll | select | poll ]; epoll模型是Linux 2.6以上版本內核中的高性能網絡I/O模型

worker_connections ?1024;#單個進程最大連接數(最大連接數=連接數*進程數)

}


#設定http服務器,利用它的反向代理功能提供負載均衡支持

http {

include ??????mime.types;#文件擴展名與文件類型映射表

default_type ?application/octet-stream;#默認文件類型

#設定負載均衡的服務器列表

upstream ?tomcatxxxcom ?{ ?

?????server ??192.168.56.200:8080; ?

server ??192.168.56.201:8080;??

}

#設定日志格式

????log_format ?www_xy_com ?'$remote_addr - $remote_user [$time_local] "$request" '

??????????????????????'$status $body_bytes_sent "$http_referer" '

??????????????????????'"$http_user_agent" "$http_x_forwarded_for"';


sendfile ???????on;#開啟高效文件傳輸模式,sendfile指令指定nginx是否調用sendfile函數來輸出文件,對于普通應用設為?on,如果用來進行下載等應用磁盤IO重負載應用,可設置為off,以平衡磁盤與網絡I/O處理速度,降低系統的負載。注意:如果圖片顯示不正常把這個改成off。

keepalive_timeout ?65; #長連接超時時間,單位是秒


????#gzip ?on;

#設定虛擬主機,默認為監聽80端口

????server {

????????listen ??????80;

server_name ?tomcat.xxx.com;#域名可以有多個,用空格隔開


????????#charset koi8-r;

#設定本虛擬主機的訪問日志

????????access_log ?/data/logs/access.log ?www_xy_com;

#對?"/"?啟用反向代理

???location / {

???proxy_pass ???????http://tomcatxxxcom; ?

???????????????proxy_set_header ??Host ????????????$host; ?

???????????????proxy_set_header ??X-Real-IP ???????$remote_addr; ?

???????????????proxy_set_header ??X-Forwarded-For ?$proxy_add_x_forwarded_for;

????????}


????????#error_page ??500 502 503 504 ?/50x.html;

????????location = /50x.html {

????????????root ??html;

????????}

????}

}

3.?安裝lvs

lvs-dr.sh:和之前對比,變化之處就是vip和轉發的端口。

#!/bin/bash

#description:start lvs server

echo "1" >/proc/sys/net/ipv4/ip_forward


WEB1=192.168.56.200

WEB2=192.168.56.201


VIP1=192.168.56.90


/etc/rc.d/init.d/functions


case "$1" in

start)

echo "start LVS of directorServer"

#set the Virtual address and sysctl parameter

/sbin/ifconfig eth1:0 $VIP1 broadcast $VIP1 netmask 255.255.255.255 up

#clear ipvs table

/sbin/ipvsadm -C


#set LVS

#web apache or tomcat

/sbin/ipvsadm -A -t $VIP1:80 -s rr

/sbin/ipvsadm -a -t $VIP1:80 -r $WEB1:80 ?-g

/sbin/ipvsadm -a -t $VIP1:80 -r $WEB2:80 ?-g


#run LVS

/sbin/ipvsadm

;;


stop)

echo "close LVS directorserver"

echo "0" >/proc/sys/net/ipv4/ip_forward


/sbin/ipvsadm -C


/sbin/ipvsadm -Z


;;

*)

echo "usage:$0 {start|stop}"

exit 1

esac

lvs-rs.sh:與之前的不同在于修改了vip

#!/bin/sh

#description start realserver

#chkconfig 235 26 26

VIP1=192.168.56.90

/etc/rc.d/init.d/functions

case "$1" in

start)


echo "start LVS of realserver"

/sbin/ifconfig lo:0 $VIP1 broadcast $VIP1 netmask 255.255.255.255 up


echo "1" >/proc/sys/net/ipv4/conf/lo/arp_ignore

echo "2" >/proc/sys/net/ipv4/conf/lo/arp_announce

echo "1" >/proc/sys/net/ipv4/conf/all/arp_ignore

echo "2" >/proc/sys/net/ipv4/conf/all/arp_announce

;;

stop)

/sbin/ifconfig lo:0 down

echo "close lvs dirctorserver"

echo "0" >/proc/sys/net/ipv4/conf/lo/arp_ignore

echo "0" >/proc/sys/net/ipv4/conf/lo/arp_announce

echo "0" >/proc/sys/net/ipv4/conf/all/arp_ignore

echo "0" >/proc/sys/net/ipv4/conf/all/arp_announce

;;

*)

echo "usage:$0{start|stop}"

exit 1

esac

4.?安裝keepalived

注意:在用keepalived做tomcat和nginx的熱備時,需要加入realserver的配置。但是做lvs的熱備則不需要配置realserver,因為keepalived有lvs的配置參數。


backup

! Configuration File for keepalived


global_defs {

???notification_email {

?????#acassen@firewall.loc

?????#failover@firewall.loc

?????#sysadmin@firewall.loc

???}

???notification_email_from Alexandre.Cassen@firewall.loc

???#smtp_server 192.168.200.1

???#smtp_connect_timeout 30

???router_id LVS_DEVEL

}


vrrp_instance VI_1 {

????state BACKUP

????interface eth1

lvs_sync_daemon_inteface eth1

????virtual_router_id 51

????priority 100

nopreempt

????advert_int 1

????authentication {

????????auth_type PASS

????????auth_pass 1111

????}

????virtual_ipaddress {

????????192.168.56.90

????}

}


virtual_server 192.168.56.90 80 {

????delay_loop 6

????lb_algo rr

????lb_kind DR

????#nat_mask 255.255.255.0

????persistence_timeout 1

????protocol TCP

}

master

! Configuration File for keepalived


global_defs {

???notification_email {

?????#acassen@firewall.loc

?????#failover@firewall.loc

?????#sysadmin@firewall.loc

???}

???notification_email_from Alexandre.Cassen@firewall.loc

???#smtp_server 192.168.200.1

???#smtp_connect_timeout 30

???router_id LVS_DEVEL

}


vrrp_instance VI_1 {

????state MASTER

????interface eth1

lvs_sync_daemon_inteface eth1

????virtual_router_id 51

????priority 200


????advert_int 1

????authentication {

????????auth_type PASS

????????auth_pass 1111

????}

????virtual_ipaddress {

????????192.168.56.90

????}

}


virtual_server 192.168.56.90 80 {

????delay_loop 6

????lb_algo rr

????lb_kind DR

????#nat_mask 255.255.255.0

????persistence_timeout 1

????protocol TCP


?}


四、Zookeeper

1.?Zookeeper概念簡介:

Zookeeper是一個分布式協調服務;就是為用戶的分布式應用程序提供協調服務

A、zookeeper是為別的分布式程序服務的

B、Zookeeper本身就是一個分布式程序(只要有半數以上節點存活,zk就能正常服務)

C、Zookeeper所提供的服務涵蓋:主從協調、服務器節點動態上下線、統一配置管理、分布式共享鎖、統一名稱服務……

D、雖然說可以提供各種服務,但是zookeeper在底層其實只提供了兩個功能:

管理(存儲,讀取)用戶程序提交的數據;

并為用戶程序提供數據節點監聽服務;


Zookeeper常用應用場景:


Zookeeper集群的角色: ?Leader?和 ?follower ?(Observer)

只要集群中有半數以上節點存活,集群就能提供服務


2.?zookeeper集群機制

半數機制:集群中半數以上機器存活,集群可用。

zookeeper適合裝在奇數臺機器上!!!

3.?安裝

3.1.安裝

3.1.1.?機器部署

安裝到3臺虛擬機上

安裝好JDK

3.1.2.?上傳

上傳用工具。

3.1.3.?解壓

su –?hadoop(切換到hadoop用戶)

tar -zxvf zookeeper-3.4.5.tar.gz(解壓)

3.1.4.?重命名

mv zookeeper-3.4.5 zookeeper(重命名文件夾zookeeper-3.4.5為zookeeper)

3.1.5.?修改環境變量

1、su?–?root(切換用戶到root)

2、vi /etc/profile(修改文件)

3、添加內容:

export ZOOKEEPER_HOME=/home/hadoop/zookeeper

export PATH=$PATH:$ZOOKEEPER_HOME/bin

4、重新編譯文件:

source /etc/profile

5、注意:3臺zookeeper都需要修改

6、修改完成后切換回hadoop用戶:

su - hadoop

3.1.6.?修改配置文件

1、用hadoop用戶操作

cd zookeeper/conf

cp zoo_sample.cfg?zoo.cfg

2、vi zoo.cfg

3、添加內容:

dataDir=/home/hadoop/zookeeper/data

dataLogDir=/home/hadoop/zookeeper/log

server.1=slave1:2888:3888 (主機名,?心跳端口、數據端口)

server.2=slave2:2888:3888

server.3=slave3:2888:3888

4、創建文件夾:

cd?/home/hadoop/zookeeper/

mkdir -m 755 data

mkdir -m 755 log

5、在data文件夾下新建myid文件,myid的文件內容為:

cd data

vi myid

添加內容:略

3.1.7.?將集群下發到其他機器上

scp -r /home/hadoop/zookeeper hadoop@slave2:/home/hadoop/

scp -r /home/hadoop/zookeeper hadoop@slave3:/home/hadoop/

3.1.8.?修改其他機器的配置文件

到slave2上:修改myid為:2

到slave3上:修改myid為:3

3.1.9.?啟動(每臺機器)

zkServer.sh start

3.1.10.?查看集群狀態

1、?jps(查看進程)

2、?zkServer.sh status(查看集群狀態,主從信息)

4.?zookeeper結構和命令

4.1.?zookeeper特性

1、Zookeeper:一個leader,多個follower組成的集群

2、全局數據一致:每個server保存一份相同的數據副本,client無論連接到哪個server,數據都是一致的

3、分布式讀寫,更新請求轉發,由leader實施

4、更新請求順序進行,來自同一個client的更新請求按其發送順序依次執行

5、數據更新原子性,一次數據更新要么成功,要么失敗

6、實時性,在一定時間范圍內,client能讀到最新數據

4.2.?zookeeper數據結構

1、層次化的目錄結構,命名符合常規文件系統規范(見下圖)

2、每個節點在zookeeper中叫做znode,并且其有一個唯一的路徑標識

3、節點Znode可以包含數據和子節點(但是EPHEMERAL類型的節點不能有子節點,下一頁詳細講解)

4、客戶端應用可以在節點上設置監視器(后續詳細講解)

4.3.?數據結構的圖

?4.4.?節點類型

1、Znode有兩種類型:

短暫(ephemeral)(斷開連接自己刪除)

持久(persistent)(斷開連接不刪除)

2、Znode有四種形式的目錄節點(默認是persistent?)

PERSISTENT

PERSISTENT_SEQUENTIAL(持久序列/test0000000019?)

EPHEMERAL

EPHEMERAL_SEQUENTIAL

3、創建znode時設置順序標識,znode名稱后會附加一個值,順序號是一個單調遞增的計數器,由父節點維護

4、在分布式系統中,順序號可以被用于為所有的事件進行全局排序,這樣客戶端可以通過順序號推斷事件的順序

4.5.?zookeeper命令行操作

運行zkCli.sh–server?進入命令行工具


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

#監聽這個節點的變化,當另外一個客戶端改變/zk時,它會打出下面的

#WATCHER::

#WatchedEvent state:SyncConnected type:NodeDataChanged path:/zk

[zk: localhost:2181(CONNECTED) 4] get /zk watch

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

6、刪除節點:rmr

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


4.6.?zookeeper-api應用

4.6.1.?基本使用

?org.apache.zookeeper.Zookeeper是客戶端入口主類,負責建立與server的會話

它提供了表1?所示幾類主要方法:

功能描述

create在本地目錄樹中創建一個節點

delete刪除一個節點

exists測試本地是否存在目標節點

get/set data從目標節點上讀取?/?寫數據

get/set ACL獲取?/?設置目標節點訪問控制列表信息

get children檢索一個子節點上的列表

sync等待要被傳送的數據

表?1?:?ZooKeeper API?描述

4.6.2.?demo增刪改查

public class SimpleDemo {

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

private static final int SESSION_TIMEOUT = 30000;

// 創建 ZooKeeper 實例

ZooKeeper zk;

// 創建 Watcher 實例

Watcher wh = new Watcher() {

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

{

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

}

};

// 初始化 ZooKeeper 實例

private void createZKInstance() throws IOException

{

zk = new ZooKeeper("weekend01:2181", SimpleDemo.SESSION_TIMEOUT, this.wh);

}

private void ZKOperations() throws IOException, InterruptedException, KeeperException

{

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

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

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

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

System.out.println("/n3. 修改節點數據 ");

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

System.out.println("/n4. 查看是否修改成功: ");

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

System.out.println("/n5. 刪除節點 ");

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

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

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

}

private void ZKClose() throws InterruptedException

{

zk.close();

}

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

SimpleDemo dm = new SimpleDemo();

dm.createZKInstance();

dm.ZKOperations();

dm.ZKClose();

}

}


Zookeeper的監聽器工作機制



監聽器是一個接口,我們的代碼中可以實現Wather這個接口,實現其中的process方法,方法中即我們自己的業務邏輯


監聽器的注冊是在獲取數據的操作中實現:

getData(path,watch?)監聽的事件是:節點數據變化事件

getChildren(path,watch?)監聽的事件是:節點下的子節點增減變化事件


4.7.?zookeeper應用案例(分布式應用HA||分布式鎖)

4.7.1?實現分布式應用的(主節點HA)及客戶端動態更新主節點狀態

某分布式系統中,主節點可以有多臺,可以動態上下線

任意一臺客戶端都能實時感知到主節點服務器的上下線



A、客戶端實現

public class AppClient {

private String groupNode = "sgroup";

private ZooKeeper zk;

private Stat stat = new Stat();

private volatile List serverList;


/**

?* 連接zookeeper

?*/

public void connectZookeeper() throws Exception {

zk

= new ZooKeeper("localhost:4180,localhost:4181,localhost:4182", 5000, new Watcher() {

public void process(WatchedEvent event) {

// 如果發生了"/sgroup"節點下的子節點變化事件, 更新server列表, 并重新注冊監聽

if (event.getType() == EventType.NodeChildrenChanged

&& ("/" + groupNode).equals(event.getPath())) {

try {

updateServerList();

} catch (Exception e) {

e.printStackTrace();

}

}

}

});


updateServerList();

}


/**

?* 更新server列表

?*/

private void updateServerList() throws Exception {

List newServerList = new ArrayList();


// 獲取并監聽groupNode的子節點變化

// watch參數為true, 表示監聽子節點變化事件.

// 每次都需要重新注冊監聽, 因為一次注冊, 只能監聽一次事件, 如果還想繼續保持監聽, 必須重新注冊

List subList = zk.getChildren("/" + groupNode, true);

for (String subNode : subList) {

// 獲取每個子節點下關聯的server地址

byte[] data = zk.getData("/" + groupNode + "/" + subNode, false, stat);

newServerList.add(new String(data, "utf-8"));

}


// 替換server列表

serverList = newServerList;


System.out.println("server list updated: " + serverList);

}


/**

?* client的工作邏輯寫在這個方法中

?* 此處不做任何處理, 只讓client sleep

?*/

public void handle() throws InterruptedException {

Thread.sleep(Long.MAX_VALUE);

}


public static void main(String[] args) throws Exception {

AppClient ac = new AppClient();

ac.connectZookeeper();


ac.handle();

}

}


B、服務器端實現

public class AppServer {

private String groupNode = "sgroup";

private String subNode = "sub";


/**

?* 連接zookeeper

?* @param address server的地址

?*/

public void connectZookeeper(String address) throws Exception {

ZooKeeper?zk = new ZooKeeper(

"localhost:4180,localhost:4181,localhost:4182",

5000, new Watcher() {

public void process(WatchedEvent event) {

// 不做處理

}

});

// 在"/sgroup"下創建子節點

// 子節點的類型設置為EPHEMERAL_SEQUENTIAL, 表明這是一個臨時節點, 且在子節點的名稱后面加上一串數字后綴

// 將server的地址數據關聯到新創建的子節點上

String createdPath = zk.create("/" + groupNode + "/" + subNode, address.getBytes("utf-8"),

Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);

System.out.println("create: " + createdPath);

}

/**

?* server的工作邏輯寫在這個方法中

?* 此處不做任何處理, 只讓server sleep

?*/

public void handle() throws InterruptedException {

Thread.sleep(Long.MAX_VALUE);

}

public static void main(String[] args) throws Exception {

// 在參數中指定server的地址

if (args.length == 0) {

System.err.println("The first argument must be server address");

System.exit(1);

}

AppServer as = new AppServer();

as.connectZookeeper(args[0]);

as.handle();

}

}


4.7.2分布式共享鎖的簡單實現

客戶端A

public class DistributedClient {

????// 超時時間

????private static final int SESSION_TIMEOUT = 5000;

????// zookeeper server列表

????private String hosts = "localhost:4180,localhost:4181,localhost:4182";

????private String groupNode = "locks";

????private String subNode = "sub";


????private ZooKeeper zk;

????// 當前client創建的子節點

????private String thisPath;

????// 當前client等待的子節點

????private String waitPath;


????private CountDownLatch latch = new CountDownLatch(1);


????/**

?????* 連接zookeeper

?????*/

????public void connectZookeeper() throws Exception {

????????zk = new ZooKeeper(hosts, SESSION_TIMEOUT, new Watcher() {

????????????public void process(WatchedEvent event) {

????????????????try {

????????????????????// 連接建立時, 打開latch, 喚醒wait在該latch上的線程

????????????????????if (event.getState() == KeeperState.SyncConnected) {

????????????????????????latch.countDown();

????????????????????}


????????????????????// 發生了waitPath的刪除事件

????????????????????if (event.getType() == EventType.NodeDeleted && event.getPath().equals(waitPath)) {

????????????????????????doSomething();

????????????????????}

????????????????} catch (Exception e) {

????????????????????e.printStackTrace();

????????????????}

????????????}

????????});


????????// 等待連接建立

????????latch.await();


????????// 創建子節點

????????thisPath = zk.create("/" + groupNode + "/" + subNode, null, Ids.OPEN_ACL_UNSAFE,

????????????????CreateMode.EPHEMERAL_SEQUENTIAL);


????????// wait一小會, 讓結果更清晰一些

????????Thread.sleep(10);


????????// 注意, 沒有必要監聽"/locks"的子節點的變化情況

????????List childrenNodes = zk.getChildren("/" + groupNode, false);


????????// 列表中只有一個子節點, 那肯定就是thisPath, 說明client獲得鎖

????????if (childrenNodes.size() == 1) {

????????????doSomething();

????????} else {

????????????String thisNode = thisPath.substring(("/" + groupNode + "/").length());

????????????// 排序

????????????Collections.sort(childrenNodes);

????????????int index = childrenNodes.indexOf(thisNode);

????????????if (index == -1) {

????????????????// never happened

????????????} else if (index == 0) {

????????????????// inddx == 0, 說明thisNode在列表中最小, 當前client獲得鎖

????????????????doSomething();

????????????} else {

????????????????// 獲得排名比thisPath前1位的節點

????????????????this.waitPath = "/" + groupNode + "/" + childrenNodes.get(index - 1);

????????????????// 在waitPath上注冊監聽器, 當waitPath被刪除時, zookeeper會回調監聽器的process方法

????????????????zk.getData(waitPath, true, new Stat());

????????????}

????????}

????}


????private void doSomething() throws Exception {

????????try {

????????????System.out.println("gain lock: " + thisPath);

????????????Thread.sleep(2000);

????????????// do something

????????} finally {

????????????System.out.println("finished: " + thisPath);

????????????// 將thisPath刪除, 監聽thisPath的client將獲得通知

????????????// 相當于釋放鎖

????????????zk.delete(this.thisPath, -1);

????????}

????}


????public static void main(String[] args) throws Exception {

????????for (int i = 0; i < 10; i++) {

????????????new Thread() {

????????????????public void run() {

????????????????????try {

????????????????????????DistributedClient dl = new DistributedClient();

????????????????????????dl.connectZookeeper();

????????????????????} catch (Exception e) {

????????????????????????e.printStackTrace();

????????????????????}

????????????????}

????????????}.start();

????????}


????????Thread.sleep(Long.MAX_VALUE);

????}

}

?分布式多進程模式實現:

public class DistributedClientMy {


// 超時時間

private static final int SESSION_TIMEOUT = 5000;

// zookeeper server列表

private String hosts = "spark01:2181,spark02:2181,spark03:2181";

private String groupNode = "locks";

private String subNode = "sub";

private boolean haveLock = false;


private ZooKeeper zk;

// 當前client創建的子節點

private volatile String thisPath;


/**

?* 連接zookeeper

?*/

public void connectZookeeper() throws Exception {

zk = new ZooKeeper("spark01:2181", SESSION_TIMEOUT, new Watcher() {

public void process(WatchedEvent event) {

try {


// 子節點發生變化

if (event.getType() == EventType.NodeChildrenChanged && event.getPath().equals("/" + groupNode)) {

// thisPath是否是列表中的最小節點

List childrenNodes = zk.getChildren("/" + groupNode, true);

String thisNode = thisPath.substring(("/" + groupNode + "/").length());

// 排序

Collections.sort(childrenNodes);

if (childrenNodes.indexOf(thisNode) == 0) {

doSomething();

thisPath = zk.create("/" + groupNode + "/" + subNode, null, Ids.OPEN_ACL_UNSAFE,

CreateMode.EPHEMERAL_SEQUENTIAL);

}

}

} catch (Exception e) {

e.printStackTrace();

}

}

});


// 創建子節點

thisPath = zk.create("/" + groupNode + "/" + subNode, null, Ids.OPEN_ACL_UNSAFE,

CreateMode.EPHEMERAL_SEQUENTIAL);


// wait一小會, 讓結果更清晰一些

Thread.sleep(new Random().nextInt(1000));


// 監聽子節點的變化

List childrenNodes = zk.getChildren("/" + groupNode, true);


// 列表中只有一個子節點, 那肯定就是thisPath, 說明client獲得鎖

if (childrenNodes.size() == 1) {

doSomething();

thisPath = zk.create("/" + groupNode + "/" + subNode, null, Ids.OPEN_ACL_UNSAFE,

CreateMode.EPHEMERAL_SEQUENTIAL);

}

}


/**

?* 共享資源的訪問邏輯寫在這個方法中

?*/

private void doSomething() throws Exception {

try {

System.out.println("gain lock: " + thisPath);

Thread.sleep(2000);

// do something

} finally {

System.out.println("finished: " + thisPath);

// 將thisPath刪除, 監聽thisPath的client將獲得通知

// 相當于釋放鎖

zk.delete(this.thisPath, -1);

}

}


public static void main(String[] args) throws Exception {

DistributedClientMy dl = new DistributedClientMy();

dl.connectZookeeper();

Thread.sleep(Long.MAX_VALUE);

}

}


5.?zookeeper原理

Zookeeper雖然在配置文件中并沒有指定master和slave

但是,zookeeper工作時,是有一個節點為leader,其他則為follower

Leader是通過內部的選舉機制臨時產生的


?5.1.?zookeeper的選舉機制(全新集群paxos)

以一個簡單的例子來說明整個選舉的過程.

假設有五臺服務器組成的zookeeper集群,它們的id從1-5,同時它們都是最新啟動的,也就是沒有歷史數據,在存放數據量這一點上,都是一樣的.假設這些服務器依序啟動,來看看會發生什么.

1)?服務器1啟動,此時只有它一臺服務器啟動了,它發出去的報沒有任何響應,所以它的選舉狀態一直是LOOKING狀態

2)?服務器2啟動,它與最開始啟動的服務器1進行通信,互相交換自己的選舉結果,由于兩者都沒有歷史數據,所以id值較大的服務器2勝出,但是由于沒有達到超過半數以上的服務器都同意選舉它(這個例子中的半數以上是3),所以服務器1,2還是繼續保持LOOKING狀態.

3)?服務器3啟動,根據前面的理論分析,服務器3成為服務器1,2,3中的老大,而與上面不同的是,此時有三臺服務器選舉了它,所以它成為了這次選舉的leader.

4)?服務器4啟動,根據前面的分析,理論上服務器4應該是服務器1,2,3,4中最大的,但是由于前面已經有半數以上的服務器選舉了服務器3,所以它只能接收當小弟的命了.

5)?服務器5啟動,同4一樣,當小弟.

5.2.?非全新集群的選舉機制(數據恢復)

那么,初始化的時候,是按照上述的說明進行選舉的,但是當zookeeper運行了一段時間之后,有機器down掉,重新選舉時,選舉過程就相對復雜了。

需要加入數據id、leader id和邏輯時鐘。

數據id:數據新的id就大,數據每次更新都會更新id。

Leader id:就是我們配置的myid中的值,每個機器一個。

邏輯時鐘:這個值從0開始遞增,每次選舉對應一個值,也就是說: ?如果在同一次選舉中,那么這個值應該是一致的?; ?邏輯時鐘值越大,說明這一次選舉leader的進程更新.

選舉的標準就變成:

1、邏輯時鐘小的選舉結果被忽略,重新投票

2、統一邏輯時鐘后,數據id大的勝出

3、數據id相同的情況下,leader id大的勝出

根據這個規則選出leader。

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,572評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,071評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,409評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,569評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,360評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,895評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,979評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,123評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,643評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,559評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,742評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,250評論 5 356
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,981評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,363評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,622評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,354評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,707評論 2 370

推薦閱讀更多精彩內容

  • 一、高可用集群 (一)提升系統高可用性的解決方案:冗余(redundant) 工作模式active/passive...
    哈嘍別樣閱讀 1,739評論 2 5
  • 1 Zookeeper概述# ZooKeeper是一個為分布式應用所設計的分布的、開源的協調服務,它主要是用來解決...
    七寸知架構閱讀 7,366評論 0 101
  • 我愿這座大地上開滿鮮艷的花朵。我愿這片廣闊的藍天上充滿潔白的云朵. 我愿這廣闊的海面上能夠沙鷗翔集 我愿每一個在外...
    潘碧瑩I閱讀 190評論 0 0
  • 風吹亂了你的頭發 遮住了我觀望的眼睛 墻上斑駁的海報 散落掉了你當時的樣子 只是時光還是溫暖如春天柔軟的被子 夜里...
    宋禾初閱讀 143評論 0 0
  • 1月3日,是2018年第三天,朋友圈被支付寶年度賬單大肆攻占。作為中國目前最大的虛擬賬戶平臺,大數據統計一下,輕而...
    琥珀水晶閱讀 359評論 0 1