1. 程式人生 > >【java小程式】zookeeper監聽並自動更新

【java小程式】zookeeper監聽並自動更新

開發需求背景,在開發小程式視訊時(springboot後端),需要一個後臺管理系統(ssm後端),並且這兩個系統是部署在不同的伺服器上,當管理人員通過短視訊後臺管理背景音樂的上傳和刪除,需要小程式端的伺服器能通過監聽能自動下載背景音樂。這裡我們就通過zookeeper中介軟體,springboot監聽並且下載。
###內容目錄

文章目錄

什麼是Zookeeper?

1、中介軟體,提供協調服務。
2、作用於分散式系統,發揮其優勢,可以為大資料服務。
3、支援java,提供java和c語言的客戶端api。

什麼是分散式系統?

1、很多臺計算機組成一個整體,一個整體一致對外並且處理同一請求。
2、內部的每臺計算機都可以相互通訊。
3、客戶端到服務端的一次請求到響應結束會經歷多臺計算機。

windows10 單機安裝zookeeper

1、在安裝zookeeper之前,需要先安裝JDK,並進行環境配置。
2、下載zookeeper解壓。在bin目錄下, zkServer.cmd是zookeeper的啟動指令碼。

在你執行啟動指令碼之前,還有幾個基本的配置項需要配置一下,Zookeeper 的配置檔案在 conf 目錄下,這個目錄下有 zoo_sample.cfg 和 log4j.properties,你需要做的就是將 zoo_sample.cfg 改名為 zoo.cfg,因為 Zookeeper 在啟動時會找這個檔案作為預設配置檔案。

常用的zk java客戶端

1、zk原生API

缺點:超時重連,不支援自動,需要手動操作。Watch註冊一次後會失效,不支援遞迴的建立節點。

2、zkClient (很久沒有更新)

3、apache curator

apche的開源專案,解決watcher的註冊一次就失效的問題,api更加簡單方便。

支援程式碼塊,並解決常見的程式碼塊換行不正確,特別是iPone、iPad上不能滾動的問題;
解決把內容貼上到公眾號時,圖片、或樣式丟失的問題;

Curator 建立zk客戶端的步驟

1、建立重試策略 - retryPolicy
2、建立客戶端 - client
3、 初始化客戶端,加入到spring容器,專案啟動載入的時候執行。

zookeeepr程式碼實現

1、zookeeper需要匯入的pom依賴

	<!-- 引入zookeeper -->
		<dependency>
			<groupId>org.apache.curator</groupId>
			<artifactId>curator-framework</artifactId>
			<version>4.0.0</version>
		</dependency>
		<dependency>
			<groupId>org.apache.zookeeper</groupId>
			<artifactId>zookeeper</artifactId>
			<version>3.4.11</version>
		</dependency>
		<dependency>
			<groupId>org.apache.curator</groupId>
			<artifactId>curator-recipes</artifactId>
			<version>4.0.0</version>
		</dependency>

2、後臺管理ssm進行zookeeper的spring容器的配置

<description>zookeeper 放入spring容器,專案啟動載入的時候就建立和zk的連線</description>
	
	<!-- 建立重連策略 -->
	<bean id="retryPolicy" class="org.apache.curator.retry.ExponentialBackoffRetry">
		<!-- 每次重試連線的等待時間 -->
		<constructor-arg index="0" value="1000"></constructor-arg>
		<!-- 設定的重連的次數 -->
		<constructor-arg index="1" value="5"></constructor-arg>
	</bean>
	
	<!-- 建立zookeeper客戶端 -->
	<bean id="client" class="org.apache.curator.framework.CuratorFrameworkFactory" 
		factory-method="newClient" init-method="start">
		<constructor-arg index="0" value="192.168.1.210:2181"></constructor-arg>
		<constructor-arg index="1" value="10000"></constructor-arg>
		<constructor-arg index="2" value="10000"></constructor-arg>
		<constructor-arg index="3" ref="retryPolicy"></constructor-arg>
	</bean>
	
	<!-- 客戶端配置 -->
	<!-- init-method="init" 不使用zk的話,僅僅只是測試,為了方便可以把這個方法暫時拿掉 -->
	<bean id="ZKCurator" class="com.imooc.web.util.ZKCurator" init-method="init">
		<constructor-arg index="0" ref="client"></constructor-arg>
	</bean>

3、後臺管理zookeeper的客戶端工具類

import org.apache.curator.framework.CuratorFramework;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.ZooDefs.Ids;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ZKCurator {

	// zk客戶端
	private CuratorFramework client = null;	
	final static Logger log = LoggerFactory.getLogger(ZKCurator.class);
	
	public ZKCurator(CuratorFramework client) {
		this.client = client;
	}
	/**
	   初始化呼叫
	*/
	public void init() {
		//使用admin名稱空間,方便zookeeper節點目錄區分。
		client = client.usingNamespace("admin");
		
		try {
			// 判斷在admin名稱空間下是否有bgm節點  /admin/bmg
			if (client.checkExists().forPath("/bgm") == null) {
				/**
				 * 對於zk來講,有兩種型別的節點:
				 * 持久節點: 當你建立一個節點的時候,這個節點就永遠存在,除非你手動刪除
				 * 臨時節點: 你建立一個節點之後,會話斷開,會自動刪除,當然也可以手動刪除
				 */
				client.create().creatingParentsIfNeeded()
					.withMode(CreateMode.PERSISTENT)		// 節點型別:持久節點
					.withACL(Ids.OPEN_ACL_UNSAFE)			// acl:匿名許可權
					.forPath("/bgm");
				log.info("zookeeper初始化成功...");
				
				log.info("zookeeper伺服器狀態:{}", client.isStarted());
			}
		} catch (Exception e) {
			log.error("zookeeper客戶端連線、初始化錯誤...");
			e.printStackTrace();
		}
	}
	
	/**
	 * @Description: 增加或者刪除bgm,向zk-server建立子節點,供小程式後端監聽
	 *  forPath的兩個引數:第一個:表示建立的目錄節點,第二個表示該節點下要儲存的資料。
	 */
	public void sendBgmOperator(String bgmId, String operObj) {
		try {
			
			client.create().creatingParentsIfNeeded()
				.withMode(CreateMode.PERSISTENT)		// 節點型別:持久節點
				.withACL(Ids.OPEN_ACL_UNSAFE)			// acl:匿名許可權
				.forPath("/bgm/" + bgmId, operObj.getBytes());  //     bgm/bgmId 子節點
		} catch (Exception e) { 
			e.printStackTrace();
		}
	}
	
}

3、後臺管理–列舉類

package com.imooc.enums;

public enum BGMOperatorTypeEnum {
	
	ADD("1", "新增bgm"),				
	DELETE("2", "刪除bgm");		
	
	public final String type;
	public final String value;
	
	BGMOperatorTypeEnum(String type, String value){
		this.type = type;
		this.value = value;
	}
	
	public String getUserType() {
		return type;
	}  
	
	public String getValue() {
		return value;
	} 
	
	public static String getValueByKey(String key) {
		for (BGMOperatorTypeEnum type : BGMOperatorTypeEnum.values()) {
			if (type.getUserType().equals(key)) {
				return type.value;
			}
		}
		return null;
	}
	
}

4、後臺管理對bgm進行新增和刪除時候,將資料儲存在zookeeper節點上。

@Override
	public void addBgm(Bgm bgm) {
		String bgmId = sid.nextShort();
		bgm.setId(bgmId);
		bgmMapper.insert(bgm);
		
		Map<String, String> map = new HashMap<>();
		map.put("operType", BGMOperatorTypeEnum.ADD.type);
		map.put("path", bgm.getPath());
		
		zkCurator.sendBgmOperator(bgmId, JsonUtils.objectToJson(map));
	}
	
@Override
	public void deleteBgm(String id) {
		Bgm bgm = bgmMapper.selectByPrimaryKey(id);
		
		bgmMapper.deleteByPrimaryKey(id);
		
		Map<String, String> map = new HashMap<>();
		map.put("operType", BGMOperatorTypeEnum.DELETE.type);
		map.put("path", bgm.getPath());
		
		zkCurator.sendBgmOperator(id, JsonUtils.objectToJson(map));
		
	}

5、小程式端後臺監聽,並進行刪除或新增操作。

package com.imooc;

import java.io.File;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Map;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.cache.PathChildrenCache;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.imooc.cofig.ResourceConfig;
import com.imooc.enums.BGMOperatorTypeEnum;
import com.imooc.utils.JsonUtils;

@Component
public class ZKCuratorClient {

	// zk客戶端
	private CuratorFramework client = null;	
	final static Logger log = LoggerFactory.getLogger(ZKCuratorClient.class);

//	@Autowired
//	private BgmService bgmService;
	
//	public static final String ZOOKEEPER_SERVER = "192.168.1.210:2181";
	
	@Autowired
	private ResourceConfig resourceConfig;
	
	public void init() {
		
		if (client != null) {
			return;
		}
		
		// 重試策略
		RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 5);
		// 建立zk客戶端
		client = CuratorFrameworkFactory.builder().connectString(resourceConfig.getZookeeperServer())
				.sessionTimeoutMs(10000).retryPolicy(retryPolicy).namespace("admin").build();
		// 啟動客戶端
		client.start();
		
		try {
			//測試 從這個(admin/bgm/18052674D26HH3X4)節點下獲取資料
//		    String testNodeData = new String(client.getData().forPath("/bgm/18052674D26HH3X4"));
//			log.info("測試的節點資料為: {}", testNodeData);
			addChildWatch("/bgm");  //監聽bgm下的子節點
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}
	//事件監聽
	public void addChildWatch(String nodePath) throws Exception {
		
		final PathChildrenCache cache = new PathChildrenCache(client, nodePath, true);
		cache.start();
		cache.getListenable().addListener(new PathChildrenCacheListener() {
			
			@Override
			public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) 
					throws Exception {
				
				if (event.getType().equals(PathChildrenCacheEvent.Type.CHILD_ADDED)) {
					log.info("監聽到事件 CHILD_ADDED");
					
					// 1. 從資料庫查詢bgm物件,獲取路徑path
					String path = event.getData().getPath();
					String operatorObjStr = new String(event.getData().getData()); //節點型別
					Map<String, String> map = JsonUtils.jsonToPojo(operatorObjStr, Map.class);
					String operatorType = map.get("operType");
					String songPath = map.get("path");
					
//					String arr[] = path.split("/");
//					String bgmId = arr[arr.length - 1];
					//在刪除的時候,有可能bgm這條記錄已經被後臺管理刪除,這裡再查詢就查不到記錄。
//					Bgm bgm = bgmService.queryBgmById(bgmId);
//					if (bgm == null) {
//						return;
//					}
					
					// 1.1 bgm所在的相對路徑
//					String songPath = bgm.getPath();
					
					// 2. 定義儲存到本地的bgm路徑
//					String filePath = "C:\\imooc_videos_dev" + songPath;
					String filePath = resourceConfig.getFileSpace() + songPath;
					
					// 3. 定義下載的路徑(播放url)
					String arrPath[] = songPath.split("\\\\");
					String finalPath = "";
					// 3.1 處理url的斜槓以及編碼
					for(int i = 0; i < arrPath.length ; i ++) {
						if (StringUtils.isNotBlank(arrPath[i])) {
							finalPath += "/";
							finalPath += URLEncoder.encode(arrPath[i], "UTF-8") ;
						}
					}
//					String bgmUrl = "http://192.168.1.2:8080/mvc" + finalPath;
					String bgmUrl = resourceConfig.getBgmServer() + finalPath;
					
					if (operatorType.equals(BGMOperatorTypeEnum.ADD.type)) {
						// 下載bgm到spingboot伺服器
						URL url = new URL(bgmUrl);
						File file = new File(filePath);
						FileUtils.copyURLToFile(url, file);
						client.delete().forPath(path);
					} else if (operatorType.equals(BGMOperatorTypeEnum.DELETE.type)) {
						File file = new File(filePath);
						FileUtils.forceDelete(file);
						client.delete().forPath(path);
					}
				}
			}
		});
	}
	
}

6、小程式後端,springboot對ZKCuratorClient進行bean的配置。

@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {

    @Bean(initMethod="init")
	public ZKCuratorClient zkCuratorClient() {
		return new ZKCuratorClient();
	}
}

7、小程式後端,springboot進行自定義資原始檔配置。

@ConfigurationProperties(prefix=“com.imooc”)中的com.imooc是對resource.properties檔案中com.imooc.zookeeperServer的com.imooc的統一定義。

@Configuration //定義配置檔案
@ConfigurationProperties(prefix="com.imooc") // 定義字首。
@PropertySource("classpath:resource.properties")  //指定資原始檔目錄
public class ResourceConfig {

	private String zookeeperServer;
	private String bgmServer;
	private String fileSpace;

	public String getZookeeperServer() {
		return zookeeperServer;
	}
	public void setZookeeperServer(String zookeeperServer) {
		this.zookeeperServer = zookeeperServer;
	}
	public String getBgmServer() {
		return bgmServer;
	}
	public void setBgmServer(String bgmServer) {
		this.bgmServer = bgmServer;
	}
	public String getFileSpace() {
		return fileSpace;
	}
	public void setFileSpace(String fileSpace) {
		this.fileSpace = fileSpace;
	}
}

資原始檔:resource.properties

com.imooc.zookeeperServer=192.168.1.210:2181
com.imooc.bgmServer=http://192.168.1.2:8080/mvc
com.imooc.fileSpace=C:\\imooc_videos_dev