Zookeeper和Curator-Framework實踐系列之: 配置管理
看過Zookeeper相關文件後都知道它可以實現分散式叢集的配置管理,本文以一個簡單的例項來演示它是如何實現的並工作的。
情景需要,簡單理解為下圖:
一個web叢集,需要通過zk來控制叢集的日誌輸出級別,比如管理員需要在生產環境下檢視一下DEBUG日誌,他可以臨時將叢集的日誌輸出級別改為DEBUG,獲取他想要的資訊後還要將級別調回到INFO或者ERROR級別。
今天的主角是Curator-Framework,它的存在使得我們操作ZK變得簡單有趣起來!
環境,框架,工具
- Zookeeper叢集
- Zookeeper API
- Curator-Framework
- Spring-MVC
- Logback
- Maven + IDEA
POM
依賴關係和jetty外掛
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>3.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>3.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.5</version>
<exclusions>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>2.0.1-incubating</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>2.0.1-incubating</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.0.13</version>
</dependency>
<dependency>
<version>1.7.5</version>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.8.5</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>zookeeper-configuration</finalName>
<plugins>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>8.1.7.v20120910</version>
<configuration>
<stopKey>pbase</stopKey>
<stopPort>8080</stopPort>
</configuration>
</plugin>
</plugins>
</build>
WEB.XML
這個不用解釋了,地球人都知道
<web-app>
<display-name>Archetype Created Web Application</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath*:/applicationContext.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>springServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:/spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
Spring 配置
applicationContext.xml
<context:component-scan base-package="cn.bg">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- Curator的FactoryBean,Spring啟動時建立Curator例項。 -->
<bean id="zookeeperFactoryBean" class="cn.bg.zk.core.ZookeeperFactoryBean" lazy-init="false">
<property name="zkConnectionString" value="hadoopmaster:2181"/>
<!-- 設定zookeeper的事件監聽者,本例是一個logback日誌級別znode監聽器 -->
<property name="listeners">
<list>
<bean class="cn.bg.zk.configuration.LogbackLevelListener">
<constructor-arg value="/zk_test/logbacklevel"/>
</bean>
</list>
</property>
</bean>
spring-mvc.xml
<context:component-scan base-package="cn.bg">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<mvc:annotation-driven></mvc:annotation-driven>
Zookeeper配置管理實現相關類
ZookeeperFactoryBean.java
在Spring Context載入過程中建立Zookeeper連結對像並設定觸發監聽,通過Curator.
package cn.bg.zk.core;
public class ZookeeperFactoryBean implements FactoryBean<CuratorFramework>, InitializingBean, DisposableBean {
private Logger logger = LoggerFactory.getLogger(this.getClass());
private CuratorFramework zkClient;
//設定Zookeeper啟動後需要呼叫的監聽或者,或者需要做的初始化工作。
public void setListeners(List<IZKListener> listeners) {
this.listeners = listeners;
}
private List<IZKListener> listeners;
//設定ZK連結串
public void setZkConnectionString(String zkConnectionString) {
this.zkConnectionString = zkConnectionString;
}
private String zkConnectionString;
@Override
public CuratorFramework getObject() {
return zkClient;
}
@Override
public Class<?> getObjectType() {
return CuratorFramework.class;
}
@Override
public boolean isSingleton() {
return true;
}
@Override
public void destroy() throws Exception {
zkClient.close();
}
//建立ZK連結
@Override
public void afterPropertiesSet(){
//1000 是重試間隔時間基數,3 是重試次數
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
zkClient = createWithOptions(zkConnectionString, retryPolicy, 2000, 10000);
registerListeners(zkClient);
zkClient.start();
}
/**
* 通過自定義引數建立
*/
public CuratorFramework createWithOptions(String connectionString, RetryPolicy retryPolicy, int connectionTimeoutMs, int sessionTimeoutMs)
{
return CuratorFrameworkFactory.builder()
.connectString(connectionString)
.retryPolicy(retryPolicy)
.connectionTimeoutMs(connectionTimeoutMs)
.sessionTimeoutMs(sessionTimeoutMs)
.build();
}
//註冊需要監聽的監聽者對像.
private void registerListeners(CuratorFramework client){
client.getConnectionStateListenable().addListener(new ConnectionStateListener() {
@Override
public void stateChanged(CuratorFramework client, ConnectionState newState) {
logger.info("CuratorFramework state changed: {}", newState);
if(newState == ConnectionState.CONNECTED || newState == ConnectionState.RECONNECTED){
for(IZKListener listener : listeners){
listener.executor(client);
logger.info("Listener {} executed!", listener.getClass().getName());
}
}
}
});
client.getUnhandledErrorListenable().addListener(new UnhandledErrorListener() {
@Override
public void unhandledError(String message, Throwable e) {
logger.info("CuratorFramework unhandledError: {}", message);
}
});
}
}
監聽事件介面
所有需要在ZK客戶端連結成功後需要做的事件,需要實現這個介面,由上面的ZookeeperFactoryBean統一排程。
package cn.bg.zk.core;
import org.apache.curator.framework.CuratorFramework;
public interface IZKListener {
void executor(CuratorFramework client);
}
Logback監聽實現
這裡主要用到Curator的NodeCache類,它的主要功能是用來監聽znode本身的變化,並可以獲取當前值,而且會自動重複監聽,簡化了原生API開發的繁瑣過程。
package cn.bg.zk.configuration;
public class LogbackLevelListener implements IZKListener {
//獲取logback例項
Logger log = (Logger) LoggerFactory.getLogger(this.getClass());
private String path;
//Logback日誌級別ZNode
public LogbackLevelListener(String path) {
this.path = path;
}
@Override
public void executor(CuratorFramework client) {
//使用Curator的NodeCache來做ZNode的監聽,不用我們自己實現重複監聽
final NodeCache cache = new NodeCache(client, path);
cache.getListenable().addListener(new NodeCacheListener() {
@Override
public void nodeChanged() throws Exception {
byte[] data = cache.getCurrentData().getData();
//設定日誌級別
if (data != null) {
String level = new String(data);
Logger logger = (Logger) LoggerFactory.getLogger("root");
Level newLevel = Level.fromLocationAwareLoggerInteger(Integer.parseInt(level));
logger.setLevel(newLevel);
System.out.println("Setting logback new level to :" + newLevel.levelStr);
}
}
});
try {
cache.start(true);
} catch (Exception e) {
log.error("Start NodeCache error for path: {}, error info: {}", path, e.getMessage());
}
}
}
通過WEB端檢視當前日誌級別
寫一個controller來讀取logback的日誌級別
MainController.java
package cn.bg.controller;
@Controller
public class MainController {
@RequestMapping("/")
@ResponseBody
public String logbackLevel() throws Exception {
Logger logger = (Logger) LoggerFactory.getLogger("root");
String levelStr = logger.getLevel().levelStr;
return levelStr;
}
}
執行
在ZK叢集端啟動zkCli建立/zk_test/logbacklevel的znode,設定值為10,或20在控制檯來檢視logback的日誌輸出情況,logback日誌級別資料表示如下:
TRACE_INT = 0
DEBUG_INT = 10
INFO_INT = 20
WARN_INT = 30
ERROR_INT = 40
到此就實現了一個簡單的ZK配置管理情境,有了Curator-Framework後一切變的簡單起來,我們主要精力只要解決業務相關的的需要,而ZK相關的實現由Curator來解決。
下一篇介紹Curator分散式佇列實現。
轉載: http://www.cnblogs.com/xguo/archive/2013/06/10/3130589.html
相關文章
- 基於zookeeper實現統一配置管理
- Nacos配置管理最佳實踐
- 採用spring zookeeper 實現簡單的配置管理Spring
- Hadoop大資料實戰系列文章之ZookeeperHadoop大資料
- dart系列之:和null說再見,null使用最佳實踐DartNull
- dart系列之:集合使用最佳實踐Dart
- ZooKeeper系列(3)--基於ZooKeeper實現主從協作
- kubernetes實踐之三十五:Pod配置管理ConfigMap
- dart系列之:dart程式碼最佳實踐Dart
- SpringBoot之Dubbo和Zookeeper整合Spring Boot
- ZooKeeper 系列(二)—— Zookeeper單機環境和叢集環境搭建
- ZooKeeper系列(三)
- ZooKeeper系列(四)
- 持續輸出面試題系列之ZooKeeper篇面試題
- springboot+dubbo+zookeeper微服務實踐demoSpring Boot微服務
- Redis、Zookeeper實現分散式鎖——原理與實踐Redis分散式
- 服務發現與配置管理高可用最佳實踐
- 分散式鎖之Zookeeper實現分散式
- ZooKeeper 系列(一)—— ZooKeeper核心概念詳解
- Zookeeper系列一:Zookeeper基礎命令操作
- HDFS系列之DataNode磁碟管理解析及實踐!
- 訊息中介軟體Kafka+Zookeeper叢集簡介、部署和實踐Kafka
- Go語言分散式系統配置管理實踐--go archaiusGo分散式AI
- 使用Java和Consul實現服務配置管理Java
- 【Kubernetes系列】第8篇 CI/CD之全流程實踐
- Spring Security系列之極速入門與實踐教程Spring
- dart系列之:手寫Library,Library編寫最佳實踐Dart
- [實踐系列]Babel原理Babel
- [實踐系列] 前端路由前端路由
- springcloud之hystrix原理和實踐總結SpringGCCloud
- 從內部自用到對外服務,配置管理的演進和設計優化實踐優化
- ZooKeeper 避坑實踐:如何調優 jute.maxbuffer
- 【zookeeper之七】Zookeeper客戶端客戶端
- ZooKeeper系列(4):ZooKeeper的配置檔案詳解
- ZooKeeper系列(2):ZooKeeper命令列工具zkCli.sh命令列
- RxJava2系列實踐之倒數計時功能(三)RxJava
- GitOps 應用實踐系列 - Argo CD 上手實踐GitGo
- 【RxSwift 實踐系列 2/3】thinking in Rx- Create和DriveSwiftThinking
- SpringBoot 實踐系列-整合 RocketMQSpring BootMQ