ZooKeeper配置管理文件


最近在工作中,為了完善公司集群服務的架構,提高可用性,降低運維成本,因此開始學習ZooKeeper。
    至於什么是ZooKeeper?它能做什么?如何安裝ZooKeeper?我就不一一介紹了,類似這些資料網上到處都是。我主要是把在開發過程中,以及個人對ZooKeeper的一些了解記錄下來,大家如果遇到類似場景時,希望我的文章能夠給你提供一些思路。

    我使用的ZooKeeper(以下簡稱:ZK)客戶端是Curator Framework,是Apache的項目,它主要的功能是為ZK的客戶端使用提供了高可用的封裝。在Curator Framework基礎上封裝的curator-recipes,實現了很多經典場景。比如:集群管理(Leader選舉)、共享鎖、隊列、Counter等等。可以總結Curator主要解決以下三類問題:

  • 封裝ZK Client與Server之間的連接處理; 
  • 提供了一套Fluent風格的操作API; 
  • 提供ZK各種應用場景的抽象封裝;

 

   本文主要完成的目標是:Spring PropertyPlaceholderConfigurer配置文件加載器集成ZooKeeper來實現遠程配置讀取。

    配置管理(Configuration Management)。
    在集群服務中,可能都會遇到一個問題:那就是當需要修改配置的時候,必須要對每個實例都進行修改,這是一個很繁瑣的事情,並且易出錯。當然可以使用腳本來解決,但這不是最好的解決辦法。

OK,Let's go!

我們先看看項目結構

 

 

ZooKeeperPropertyPlaceholderConfigurer.java

繼承org.springframework.beans.factory.config.PropertyPlaceholderConfigurer,重寫processProperties(beanFactoryToProcess, props)來完成遠端配置加載的實現

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package org.bigmouth.common.zookeeper.config.spring;
 
import java.io.UnsupportedEncodingException;
import java.util.Properties;
 
import org.apache.commons.lang.StringUtils;
import org.bigmouth.common.zookeeper.config.Config;
import org.bigmouth.common.zookeeper.config.ZooKeeperConfig;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
 
 
public class ZooKeeperPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer {
     
     public static final String PATH = "zoo.paths" ;
 
     @Override
     protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props)
             throws BeansException {
         super .processProperties(beanFactoryToProcess, props);
         
         try {
             fillCustomProperties(props);
             
             System.out.println(props);
         }
         catch (Exception e) {
             // Ignore
             e.printStackTrace();
         }
     }
 
     private void fillCustomProperties(Properties props) throws Exception {
         byte [] data = getData(props);
         fillProperties(props, data);
     }
 
     private void fillProperties(Properties props, byte [] data) throws UnsupportedEncodingException {
         String cfg = new String(data, "UTF-8" );
         if (StringUtils.isNotBlank(cfg)) {
             // 完整的應該還需要處理:多條配置、value中包含=、忽略#號開頭
             String[] cfgItem = StringUtils.split(cfg, "=" );
             props.put(cfgItem[ 0 ], cfgItem[ 1 ]);
         }
     }
 
     private byte [] getData(Properties props) throws Exception {
         String path = props.getProperty(PATH);
         Config config = new ZooKeeperConfig();
         return config.getConfig(path);
     }
 
}

 

 

Config.java
配置操作接口 

?
1
2
3
4
5
6
7
package org.bigmouth.common.zookeeper.config;
 
 
public interface Config {
 
     byte [] getConfig(String path) throws Exception;
}



Startup.java

程序啟動入口

?
1
2
3
4
5
6
7
8
9
10
11
12
package org.bigmouth.common.zookeeper.config;
 
import org.springframework.context.support.ClassPathXmlApplicationContext;
 
 
public class Startup {
 
     public static void main(String[] args) {
         new ClassPathXmlApplicationContext( "classpath:/config/applicationContext.xml" );
     }
 
}

 

ZooKeeperConfig.java

配置操作接口ZooKeeper的實現

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package org.bigmouth.common.zookeeper.config;
 
import org.apache.curator.framework.CuratorFramework;
import org.apache.zookeeper.data.Stat;
 
 
public class ZooKeeperConfig implements Config {
 
     @Override
     public byte [] getConfig(String path) throws Exception {
         CuratorFramework client = ZooKeeperFactory.get();
         if (!exists(client, path)) {
             throw new RuntimeException( "Path " + path + " does not exists." );
         }
         return client.getData().forPath(path);
     }
     
     private boolean exists(CuratorFramework client, String path) throws Exception {
         Stat stat = client.checkExists().forPath(path);
         return !(stat == null );
     }
 
}

 

ZooKeeperFactory.java

管理ZooKeeper客戶端連接

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package org.bigmouth.common.zookeeper.config;
 
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
 
public class ZooKeeperFactory {
 
     public static final String CONNECT_STRING = "172.16.3.42:2181,172.16.3.65:2181,172.16.3.24:2181" ;
     
     public static final int MAX_RETRIES = 3 ;
 
     public static final int BASE_SLEEP_TIMEMS = 3000 ;
 
     public static final String NAME_SPACE = "cfg" ;
 
     public static CuratorFramework get() {
         RetryPolicy retryPolicy = new ExponentialBackoffRetry(BASE_SLEEP_TIMEMS, MAX_RETRIES);
         CuratorFramework client = CuratorFrameworkFactory.builder()
                 .connectString(CONNECT_STRING)
                 .retryPolicy(retryPolicy)
                 .namespace(NAME_SPACE)
                 .build();
         client.start();
         return client;
     }
}

 

applicationContext.xml

配置加載器使用我們自己創建的ZooKeeperPropertyPlaceholderConfigurer,因為它重寫了processProperties方法。這個方法里會去讀取遠程配置。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<? xml version = "1.0" encoding = "UTF-8" ?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
< beans >
     
     < bean class = "org.bigmouth.common.zookeeper.config.spring.ZooKeeperPropertyPlaceholderConfigurer" >
         < property name = "systemPropertiesModeName" value = "SYSTEM_PROPERTIES_MODE_OVERRIDE" />
         < property name = "ignoreResourceNotFound" value = "true" />
         < property name = "locations" >
             < list >
                 < value >classpath:application.properties</ value >
             </ list >
         </ property >
     </ bean >
     
</ beans >

 

application.properties

項目配置文件,里面除了配置ZooKeeper服務器地址和讀取的節點以外,其他所有的配置都應該保存在ZooKeeper中。

?
1
zoo.paths=/properties

 

設置ZooKeeper數據

登錄ZooKeeper中為節點 /cfg/properties 添加一條配置項:

如圖所示:我創建了一個節點 /cfg/properties 並設置內容為:jdbc.driver=org.postgresql.Driver

運行Startup.java

OK 了,zoo.paths是本地application.properties文件中的,jdbc.driver是遠程ZooKeeper服務器中的。

 

項目中需要依賴的jar包

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
< dependency >
     < groupId >commons-lang</ groupId >
     < artifactId >commons-lang</ artifactId >
     < version >2.4</ version >
</ dependency >
< dependency >
     < groupId >org.springframework</ groupId >
     < artifactId >spring-core</ artifactId >
     < version >3.0.3.RELEASE</ version >
</ dependency >
< dependency >
     < groupId >org.springframework</ groupId >
     < artifactId >spring-context</ artifactId >
     < version >3.0.3.RELEASE</ version >
</ dependency >
< dependency >
     < groupId >org.springframework</ groupId >
     < artifactId >spring-tx</ artifactId >
     < version >3.0.3.RELEASE</ version >
</ dependency >
< dependency >
     < groupId >org.springframework</ groupId >
     < artifactId >spring-context-support</ artifactId >
     < version >3.0.3.RELEASE</ version >
</ dependency >
 
<!-- ZooKeeper -->
< dependency >
     < groupId >org.apache.zookeeper</ groupId >
     < artifactId >zookeeper</ artifactId >
     < version >3.4.6</ version >
</ dependency >
< dependency >
     < groupId >org.apache.curator</ groupId >
     < artifactId >curator-framework</ artifactId >
     < version >2.4.2</ version >
</ dependency >
< dependency >
     < groupId >org.apache.curator</ groupId >
     < artifactId >curator-recipes</ artifactId >
     < version >2.4.2</ version >
</ dependency >


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM