Zookeeper4:Java客戶端、應用場景以及實現、第三方客戶端curator工具包

文章目錄

    • Java連接Zookeeper服務端
      • 依賴
      • 代碼使用
    • 應用場景
      • 統一命名服務
      • 統一配置管理
      • 統一集群管理
      • 服務器節點動態上下線
        • 理解
        • 實現
          • 模擬服務提供者【客戶端代碼】-注冊服務
          • 模擬服務消費者【客戶端代碼】-獲取服務信息進行請求消費
      • 軟負載均衡
      • 分布式鎖
        • 理解
        • 實現
      • 生產集群安裝N臺機器合適
    • 第三方基于zookeeper的包
      • curator
        • 依賴
        • 代碼

Java連接Zookeeper服務端

文檔: https://zookeeper.apache.org/doc/r3.9.1/javaExample.html

依賴

依賴

  <dependencies><!-- https://mvnrepository.com/artifact/org.apache.zookeeper/zookeeper --><dependency><groupId>org.apache.zookeeper</groupId><artifactId>zookeeper</artifactId><version>3.9.1</version></dependency><!-- https://mvnrepository.com/artifact/org.dromara.hutool/hutool-all --><dependency><groupId>org.dromara.hutool</groupId><artifactId>hutool-all</artifactId><version>6.0.0-M11</version></dependency><!-- https://mvnrepository.com/artifact/junit/junit --><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version></dependency><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-api</artifactId><version>5.10.2</version></dependency><!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.30</version><scope>provided</scope></dependency></dependencies>

代碼使用

Java代碼

public class ZkClient {@Test@SneakyThrowspublic void test1() {String connectString = "192.168.19.107:2181"; // zookeeper服務端信息int sessionTimeout = 2000; // 連接最大時長(毫秒)ZooKeeper zooKeeperClient = new ZooKeeper(connectString, sessionTimeout, new Watcher() {public void process(WatchedEvent event) {Console.log("服務端推送給客戶端的監聽事件信息 == {}", event);}});// 監聽節點數據的變化 === 等價于get -w 命令try {String s = zooKeeperClient.create("/test", "testData".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);Console.log("創建節點成功:{}", s);} catch (Exception e) {Console.log("創建節點失敗:{}", e.getMessage());}// 啟動監聽增刪子節點的變化,然后在前面【Watcher】能收到監聽事件 === 等價于ls -w 命令List<String> children = zooKeeperClient.getChildren("/test", true);Console.log("Zookeeper服務端當前/test所有的子節點名字:{}", children);//啟動節點的狀態信息變化 === 等價于stat -w 命令Stat statInfo = zooKeeperClient.exists("/test", true);Console.log("Zookeeper服務端當前/test節點的狀態信息:{}" , statInfo);//程序永遠不結束while (true) {try {Thread.sleep(1000); // 暫停1秒鐘} catch (InterruptedException e) {e.printStackTrace();}}}}

1. 前提背景
在這里插入圖片描述

2. 開始執行代碼
在這里插入圖片描述

3. 命令增加節點
在這里插入圖片描述

4. Java客戶端監聽到的消息
在這里插入圖片描述

應用場景

統一命名服務

對應用、服務同意命名便于識別,比如一個對外服務接口的多集群,則需要統一的管理同一服務的所有IP

在這里插入圖片描述

統一配置管理

  • 場景:
  1. 一般要求一個集群中,所有節點的配置信息是一致的,比如Kafka集群。
  2. 對配置文件修改后,希望能夠快速同步到各個節點上
  3. 實現:配置信息寫入到Zookeeper一個節點中,客戶端監聽這個節點即可

在這里插入圖片描述

統一集群管理

  • 場景:
  1. 分布式環境,實時掌握每個節點狀態是必要的
  2. 實現: 節點信息寫入ZooKeeper_上的一個ZNode。客戶端監聽這個ZNode可獲取它的實時狀態變化

在這里插入圖片描述

服務器節點動態上下線

理解

特點: 客戶端能實時洞察到服務器上下線的變化

在這里插入圖片描述

實現

前提: 運行代碼前自行在Zookeeper客戶端創建/service節點【create /service “service”】,因為zookeeper創建子節點前必須有父節點,否則創建子節點失敗

在這里插入圖片描述

模擬服務提供者【客戶端代碼】-注冊服務
public class ServiceProviderZkClient {private static String connecting = StrUtil.join(StrUtil.COMMA,"192.168.19.107:2181","192.168.19.108:2181","192.168.19.109:2181");private static Integer timeout = 2000;@SneakyThrowspublic static void main(String[] args) {Arrays.asList("application1","application2","application3").stream().parallel().forEach(applicationName -> {serviceRegister(applicationName);});}@SneakyThrowspublic static void serviceRegister(String applicationName) {ZooKeeper zooKeeper = new ZooKeeper(connecting, timeout, new Watcher() {public void process(WatchedEvent event) {Console.log("服務端推送的監聽信息:{}", event);}});String zookeeperPath = StrUtil.format("/service/{}", applicationName);byte[] zookeeperPathData = Convert.toPrimitiveByteArray(StrUtil.format("{}應用的IP地址等信息", applicationName));String newNodePath = zooKeeper.create(zookeeperPath, zookeeperPathData, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);Console.log("【{}】在線" , applicationName);//程序永遠不結束while (true) {try {Thread.sleep(1000); // 暫停1秒鐘} catch (InterruptedException e) {e.printStackTrace();}}}}
模擬服務消費者【客戶端代碼】-獲取服務信息進行請求消費
public class ServiceProviderConsumerZkClient {private static String connecting = StrUtil.join(StrUtil.COMMA,"192.168.19.107:2181","192.168.19.108:2181","192.168.19.109:2181");private static Integer timeout = 2000;private static AtomicReference children = new AtomicReference(ListUtil.of());private static ZooKeeper zooKeeper = null;@SneakyThrowspublic static void main(String[] args) {zooKeeper = new ZooKeeper(connecting, timeout, new Watcher() {public void process(WatchedEvent event) {Console.log("服務端推送的監聽信息:{}", event);//每次收到監聽通知消息,同步服務在線狀態getServiceNode();}});//獲取在線中的提供提供者getServiceNode();while (true) {String targetServiceName = "application2";//在線的服務真實路徑String targetServiceNodeName = CollUtil.emptyIfNull((List<String>)children.get()).stream().filter(childrenPath -> StrUtil.contains(childrenPath, targetServiceName)).findFirst().orElse(null);String targetServiceNamePath = StrUtil.format("/service/{}" , targetServiceNodeName);boolean targetServiceNameExistFlag = StrUtil.isNotBlank(targetServiceNodeName);if(targetServiceNameExistFlag) {//獲取服務的配置信息進行服務調用 == 節點里面一般包含當前服務提供者http,端口等等信息String nodeData = Convert.toStr(zooKeeper.getData(targetServiceNamePath, false, null));Console.log("【{}】第三方服務上線,調用接口成功", targetServiceName);}else {Console.log("【{}】第三方服務未上線,調用接口失敗" , targetServiceName);}ThreadUtil.sleep(5000);}}@SneakyThrowspublic static void getServiceNode() {children.set(zooKeeper.getChildren("/service", true));Console.log("系統中的服務提供者節點:{}" , children.get());}}

服務提供者進行注冊服務時
在這里插入圖片描述

服務消費者進行消費時
在這里插入圖片描述

軟負載均衡

特點: 在Zookeepert中記錄每臺服務器的訪問數,讓訪問數最少的服務器去處理最新的客戶端請求

在這里插入圖片描述

分布式鎖

理解

概念: 分布式系統中能保證多個進程有序地進行訪問臨界資源的鎖,拿到鎖的進程才可以訪問資源,否則一直排隊等待鎖

在這里插入圖片描述

實現
public class DistributedLock {private static String connecting = StrUtil.join(StrUtil.COMMA, "192.168.19.107:2181", "192.168.19.108:2181", "192.168.19.109:2181");private static Integer timeout = 2000;private static ZooKeeper zooKeeper;private static String parentPath = "/DistributedLock";private static Map<String, CountDownLatch> threadIdToCountDownLatchMap = MapUtil.newSafeConcurrentHashMap();static {init();}@SneakyThrowsprivate static void init() {zooKeeper = connectZooKeeper();// 創建鎖父節點String realParentPath = zooKeeper.create(parentPath, Convert.toPrimitiveByteArray(parentPath), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);// 監聽父節點下子節點增刪的變化// List<String> sonNodeNames = getParentSortedNodes(true);}@SneakyThrowsprivate static ZooKeeper connectZooKeeper() {ZooKeeper connectedZooKeeper = new ZooKeeper(connecting, timeout, new Watcher() {@Overridepublic void process(WatchedEvent event) {Console.log("zookeeper收到服務端監聽通知消息:{}", event);String dealNodePath = event.getPath();Event.EventType eventType = event.getType();if (eventType == Event.EventType.NodeDeleted) {String nodeBelongThreadId = StrUtil.subBefore(StrUtil.subAfter(dealNodePath, "/", true), "_", false);Console.log("收到刪除節點通知,釋放線程等待 == {}", nodeBelongThreadId);// 釋放鎖CountDownLatch countDownLatch = threadIdToCountDownLatchMap.get(nodeBelongThreadId);if (countDownLatch != null) {countDownLatch.countDown();threadIdToCountDownLatchMap.remove(nodeBelongThreadId);}}}});return connectedZooKeeper;}@SneakyThrowspublic static List<String> getParentSortedNodes(Boolean watchFlag) {List<String> sonNodeNames = CollUtil.emptyIfNull(zooKeeper.getChildren(parentPath, true)).stream().sorted(CompareUtil::compare).collect(Collectors.toList());return sonNodeNames;}/*** 獲取鎖*/@SneakyThrowspublic static void acquireLock() {// 當前鎖的節點前綴String nodeNamePrefix = Thread.currentThread().getId() + "_";// 當前鎖的節點完整領前綴String absolutenodeNamePathPrefix = StrUtil.format("{}/{}", parentPath, nodeNamePrefix);// 完整的前綴String realAbsolutenodeNamePath = zooKeeper.create(absolutenodeNamePathPrefix, Convert.toPrimitiveByteArray("absolutenodeNamePathPrefix"), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);// 獲取當前父路徑下的所有節點List<String> sonNodeNames = getParentSortedNodes(false);if (CollUtil.size(sonNodeNames) == 1) { // 獲取鎖Console.log("【子元素為1】獲取鎖【{}】",realAbsolutenodeNamePath);return;} else {String firstNodeName = CollUtil.getFirst(sonNodeNames);if (StrUtil.startWith(firstNodeName, nodeNamePrefix)) {Console.log("【當前子節點在排序后序列為第一個】獲取鎖【{}】",realAbsolutenodeNamePath);return;} else {// 一直等待直到有機會獲取鎖CountDownLatch countDownLatch = new CountDownLatch(1);// 監聽該節點的前一個節點的增刪變化int currentNodeIndex = CollUtil.indexOf(sonNodeNames, nodeName -> StrUtil.endWith(realAbsolutenodeNamePath, nodeName));String previousNodeName = CollUtil.get(sonNodeNames, currentNodeIndex - 1);String previousNodePath = StrUtil.format("{}/{}", parentPath, previousNodeName);zooKeeper.getData(previousNodePath, true, null);threadIdToCountDownLatchMap.put(StrUtil.subBefore(previousNodeName,"_", false), countDownLatch);countDownLatch.await();Console.log("獲取鎖【{}】", realAbsolutenodeNamePath);}}}/*** 釋放鎖*/@SneakyThrowspublic static void releaseLock() {String currentThreadId = Convert.toStr(Thread.currentThread().getId());// CountDownLatch countDownLatch = threadIdToCountDownLatchMap.get(currentThreadId);// if (ObjUtil.isNull(countDownLatch)) {//     Console.log("當前線程并沒有等待鎖的操作");//     return;// }// 當前鎖的節點前綴String nodeNamePrefix = currentThreadId + "_";String realNodeName = getParentSortedNodes(false).stream().filter(nodeName -> StrUtil.startWith(nodeName, nodeNamePrefix)).findFirst().orElse(null);if (StrUtil.isBlank(realNodeName)) {Console.log("當前線程并未有獲取鎖的操作");return;}String completeNodePath = StrUtil.format("{}/{}", parentPath, realNodeName);zooKeeper.delete(completeNodePath, -1);Console.log("釋放鎖【{}】", completeNodePath);}public static void main(String[] args) {String s = StrUtil.subAfter("fsd/fdsfsdfds", "/", true);Console.log(s);}
}

?
?

public class App2Test {@Test@SneakyThrowspublic void test3() {Bean publicBean = new Bean();List<Thread> threadGroup = ListUtil.of();for (int i = 0; i < 10; i++) {Thread newThread = new Thread(() -> {DistributedLock.acquireLock();// 隨機等待try {Thread.sleep(RandomUtil.randomInt(1000, 3000));} catch (InterruptedException e) {throw new RuntimeException(e);}publicBean.num = ++publicBean.num;Console.log("【{}】:數+1處理 == {}", Thread.currentThread().getId(), publicBean.num);DistributedLock.releaseLock();});newThread.start();threadGroup.add(newThread);}for (Thread runThread : threadGroup) {// 等待線程運行完runThread.join();}Console.log("公共數據最終的結果:{}", publicBean.num);Assert.equals(publicBean.num, 10);}
}

在這里插入圖片描述

生產集群安裝N臺機器合適

特點: 好處提高可靠性、壞處數據同步有延遲

案例(生產經驗)
10臺服務器:3個ZK
20臺服務器:5個ZK
100臺服務器:11個zk
200臺服務器:11臺zk

第三方基于zookeeper的包

curator

官網: https://curator.apache.org/docs/about
?
入門教程: https://curator.apache.org/docs/getting-started/

依賴
    <!-- https://mvnrepository.com/artifact/org.apache.curator/curator-framework --><dependency><groupId>org.apache.curator</groupId><artifactId>curator-framework</artifactId><version>5.6.0</version></dependency><!-- https://mvnrepository.com/artifact/org.apache.curator/curator-recipes --><dependency><groupId>org.apache.curator</groupId><artifactId>curator-recipes</artifactId><version>5.6.0</version></dependency><!-- https://mvnrepository.com/artifact/org.apache.curator/curator-client --><dependency><groupId>org.apache.curator</groupId><artifactId>curator-client</artifactId><version>5.6.0</version></dependency>
代碼

發現: 運行代碼可見curator的分布式鎖的原理跟前面自己實現的邏輯差不多,都是通過增、刪子節點,然后監控前一個節點被刪釋放鎖的邏輯原理去做的

public class OtherTest {@Test@SneakyThrowspublic void test2() {String connectString = "192.168.19.107:2181,192.168.19.108:2181,192.168.19.109:2181";RetryOneTime retryOneTime = new RetryOneTime(2000);CuratorFramework curatorFramework = CuratorFrameworkFactory.newClient(connectString, 60 * 1000 * 10, 15 * 1000 * 10, retryOneTime);curatorFramework.start();//獲取某父目錄旗下的親兒子節點名字信息List<String> sonNodeNames = curatorFramework.getChildren().forPath("/");Console.log(sonNodeNames);// 分布式鎖InterProcessMutex interProcessMutexLock = new InterProcessMutex(curatorFramework, "/CuratorLock");App2Test.Bean publicBean = new App2Test.Bean();List<Thread> threadGroup = ListUtil.of();for (int i = 0; i < 5; i++) {Thread newThread = new Thread(() -> {try {interProcessMutexLock.acquire();// 隨機等待try {Thread.sleep(RandomUtil.randomInt(1000, 3000));} catch (InterruptedException e) {throw new RuntimeException(e);}publicBean.num = ++publicBean.num;Console.log("【{}】:數+1處理 == {}", Thread.currentThread().getId(), publicBean.num);}catch (Exception e) {}finally {try {interProcessMutexLock.release();}catch (Exception e) {}}});newThread.start();threadGroup.add(newThread);}for (Thread runThread : threadGroup) {// 等待線程運行完runThread.join();}Console.log("公共數據最終的結果:{}", publicBean.num);Assert.equals(publicBean.num, 5);}}

在這里插入圖片描述

在這里插入圖片描述

?
剛興趣的同行可以進群溝通交流,內置機器人供大家愉快

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/716033.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/716033.shtml
英文地址,請注明出處:http://en.pswp.cn/news/716033.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Java中的Collection

Collection Collection 集合概述和使用 Collection集合概述 是單例集合的頂層接口,它表示一組對象,這些對象也稱為Collection的元素 JDK 不提供此接口的任何直接實現.它提供更具體的子接口(如Set和List)實現 創建Collection集合的對象 多態的方式 具體的實現類ArrayList C…

leetcode - 71. Simplify Path

Description Given a string path, which is an absolute path (starting with a slash ‘/’) to a file or directory in a Unix-style file system, convert it to the simplified canonical path. In a Unix-style file system, a period ‘.’ refers to the current di…

MATLAB環境下基于熵的聲納圖像分割算法

聲納圖像作為準確獲取水下信息的重要途徑之一&#xff0c;在國防、軍事、工程等方面發揮著巨大作用。然而&#xff0c;由于水聲信道的復雜多變和聲波本身的傳播損失&#xff0c;聲納圖像往往呈現出分辨率和對比度不高、噪聲干擾嚴重、目標輪廓模糊等特點。 聲納圖像的分割指的…

FCIS 2023網絡安全創新大會:洞察前沿技術,探索安全新境界(附大會核心PPT下載)

隨著信息技術的飛速發展&#xff0c;網絡安全問題日益凸顯&#xff0c;成為全球關注的焦點。作為網絡安全領域的重要盛會&#xff0c;FCIS 2023網絡安全創新大會如期而至&#xff0c;匯聚了全球網絡安全領域的頂尖專家、學者、企業家和政策制定者&#xff0c;共同探討網絡安全的…

備戰藍橋杯————差分數組1

引言 一、差分數組 什么是差分數組 差分數組的作用 Java代碼實現差分數組 二、 區間加法 題目描述 代碼與解題思路 總結 引言 在數字世界的海洋中&#xff0c;數據是構建和優化算法的基石。然而&#xff0c;當我們面對需要頻繁進行區間操作的數組時&#xff0c;傳統的逐元素…

ABAP - SALV教程10 添加可編輯checkbox列

幾乎所有的功能報表都會有那么一個選擇列&#xff0c;問了業務顧問&#xff0c;業務顧問說是用戶不習慣使用報表原生的選擇模式。效果圖SALV的選擇列是通過將列設置成checkbox_hotspot樣式&#xff0c;注冊單擊事件完成勾選功能的。完成步驟 將SEL列設置成checkbox_hotspot樣式…

【筆記】OpenHarmony和HarmonyOS區別及應用開發簡介

一、概念 OpenHarmony(OH) &#xff1a; OpenAtom OpenHarmonyHarmonyOS(HO)&#xff1a;開發 | 華為開發者聯盟 (huawei.com) HO當前最高是3.1&#xff0c;在華為mate 60上面也是。關于4.0、5.0和next這類版本說法都是面向用戶的&#xff0c;不是開發人員。對于程序員&#…

Springboot 項目讀取yaml的配置文件信息給靜態方法使用,以及通過配置 ResourceBundle 類讀取config.properties

讀取yaml 的配置文件 配置文件信息 iot_saas_tenement:user_id: 7........8d9bprivate_key: MII.......qQbj_url: http://4.....5:8088project_name: iot_s.......rojectdevice_name: te.....ice 創建一個類 ProxyProperties 讀取配置文件信息&#xff0c;并對外提供get方法 …

內存的檢測與排查

內存&#x1f40e;的檢測與排查 文章目錄 內存&#x1f40e;的檢測與排查查殺Java Web filter型內存馬0x01 內存馬簡歷史0x02 查殺思路0x03 內存馬的識別0x04 內存馬的查殺 查殺Java Web filter型內存馬 0x01 內存馬簡歷史 其實內存馬由來已久&#xff0c;早在17年n1nty師傅的…

QT6 libModbus 用于ModbusTcp客戶端讀寫服務端

雖然在以前的文章中多次描述過,那么本文使用開源庫libModbus,可得到更好的性能&#xff0c;也可移植到各種平臺。 性能&#xff1a;讀1次和寫1次約各用時2ms。 分別創建了讀和寫各1個連接指針&#xff0c;用于讀100個寄存器和寫100個寄存器&#xff0c;讀寫分離。 客戶端&am…

物聯網與智慧城市:科技驅動下的城市智能化升級之路

一、引言 隨著科技的不斷進步和城市化進程的加速&#xff0c;物聯網與智慧城市的結合已經成為推動城市智能化升級的關鍵力量。物聯網技術以其強大的連接和數據處理能力&#xff0c;為智慧城市的建設提供了無限可能。本文旨在探討物聯網如何助力智慧城市的構建&#xff0c;以及…

SLAM ORB-SLAM2(21)基礎矩陣的計算和評分

SLAM ORB-SLAM2&#xff08;21&#xff09;基礎矩陣的計算和評分 1. 前言2. 基礎矩陣2.1. 對級約束2.2. 推導2.3. 計算原理 3. ComputeF214. CheckFundamental 1. 前言 在 《SLAM ORB-SLAM2&#xff08;20&#xff09;查找基礎矩陣》 中了解到 查找基礎矩陣主要過程&#xff1…

web基礎03-JavaScript

目錄 一、JavaScript基礎 1.變量 2.輸出 3.變量提升 4.區塊 5.JavaScript數據類型 6.查看數值類型 7.undefined和null 8.布爾值 9.和的區別 10.算數/三元/比較/邏輯/賦值運算符 11.特殊字符 12.字符串 &#xff08;1&#xff09;獲取字符串長度 &#xff08;2&am…

備戰藍橋杯Day21 - 堆排序的內置模塊+topk問題

一、內置模塊 在python中&#xff0c;堆排序已經設置好了內置模塊&#xff0c;不想自己寫的話可以使用內置模塊&#xff0c;真的很方便&#xff0c;但是堆排序算法的底層邏輯最好還是要了解并掌握一下的。 使用heapq模塊的heapify()函數將列表轉換為堆&#xff0c;然后使用he…

41、網絡編程/TCP.UDP通信模型練習20240301

一、編寫基于TCP的客戶端實現以下功能&#xff1a; 通過鍵盤按鍵控制機械臂&#xff1a;w(紅色臂角度增大)s&#xff08;紅色臂角度減小&#xff09;d&#xff08;藍色臂角度增大&#xff09;a&#xff08;藍色臂角度減小&#xff09;按鍵控制機械臂 1.基于TCP服務器的機械臂…

Java 創建對象有哪幾種方式

1. 使用new關鍵字&#xff1a;這是最常見和最簡單的創建對象的方式。你可以通過這種方式調用任意的構造函數&#xff0c;無論是無參的還是有參數的構造函數。 例如&#xff1a; Student stu new Student 2. 使用Class類的newInstance方法&#xff08;反射&#xff09; 這種…

Python3零基礎教程之數學運算專題進階

大家好,我是千與編程,今天已經進入我們Python3的零基礎教程的第十節之數學運算專題進階。上一次的數學運算中我們介紹了簡單的基礎四則運算,加減乘除運算。當涉及到數學運算的 Python 3 刷題使用時,進階課程包含了許多重要的概念和技巧。下面是一個簡單的教程,涵蓋了一些常…

勒索軟件類型

勒索軟件類型 加密勒索軟件 它使個人文件和文件夾&#xff08;文檔、電子表格、圖片和視頻&#xff09;被加密。受感染的文件被加密后會被刪除&#xff0c;用戶通常會在當下無法使用的文件的文件夾中看到一個包含付款說明的文本文件。當您嘗試打開其中一個加密文件時,您才可能…

Tomcat負載均衡、動靜分離

目錄 引言 實驗圖解 1.實驗環境搭建 2.部署Nginx服務器及配置靜態頁面Web服務 3.部署Tomcat服務及配置動態頁面Web服務 4.實驗驗收 動態頁面 靜態頁面 引言 tomcat服務既可以處理動態頁面&#xff0c;也可以處理靜態頁面&#xff1b;但其處理靜態頁面的速度遠遠不如…

Oracle SQL優化概念之集群因子解析

導讀 本文介紹一個Oracle 數據庫SQL優化的一個基本概念【集群因子】&#xff0c;理解了此概念&#xff0c;有助于對Oracle數據庫進行SQL優化。 1. 集群因子名詞解析 集群因子&#xff08;ClusteringFactor&#xff09;是如果通過一個索引掃描一張表&#xff0c;需要訪問的表的數…