12、Spring技術棧-Redis Sentinel實現高可用快取叢集方案實戰
Redis Sentinel的分散式特性介紹
Redis Sentinel是一個分散式系統,Sentinel執行在有許多Sentinel程序互相合作的環境下,它本身就是這樣被設計的。有許多Sentinel程序互相合作的優點如下:
- 當多個Sentinel同意一個master不再可用的時候,就執行故障檢測。這明顯降低了錯誤概率。
- 即使並非全部的Sentinel都在工作,Sentinel也可以正常工作,這種特性,讓系統非常的健康。
所有的Sentinels,Redis例項,連線到Sentinel和Redis的客戶端,本身就是一個有著特殊性質的大型分散式系統。在這篇文章中,我將通過例項的形式從部署Redis Sentinel叢集、使用Redis的Master Slave的模式部署Redis叢集來介紹在Spring專案中如何使用Redis Sentinel實現快取系統的高可用。
部署之前瞭解關於Sentinel的基本東西
- 一個健康的叢集部署,至少需要三個Sentinel例項
- 三個Sentinel例項應該被放在失敗獨立的電腦上或虛擬機器中,比如說不同的物理機或者在不同的可用區域上執行的虛擬機器。
- Sentinel + Redis 分散式系統在失敗期間並不確保寫入請求被儲存,因為Redis使用非同步複製。可是有很多部署Sentinel的 方式來讓視窗把丟失寫入限制在特定的時刻,當然也有另外的不安全的方式來部署。
- 客戶端必須支援Sentinel。大多數客戶端庫都支援Sentinel,但並不是全部。
- 沒有高可用的設定是安全的,如果你在你的測試環境沒有經常去測試,或者甚至在生產環境中你也沒有經常去測試,如果Sentinel正常工作。 但是你或許有一個錯誤的配置而僅僅只是在很晚的時候才出現(凌晨3點你的主節點宕掉了)。
- Sentinel,Docker ,其他的網路地址轉換表,埠對映應該很小心的使用:Docker執行埠重新對映,破壞Sentinel自動發現另外的Sentinel程序和一個主節點的從節點列表。在文章的稍後部分檢視更過關於Sentinel和Docker的資訊。
例項介紹
- 本例項通過部署3個Redis Sentinel實現Sentinel例項的高可用。
- 本例項通過部署1個Redis Master例項,3個Slave例項實現Redis快取的高可用。
Redis Master例項和Slave例項部署
可參考Redis主從(Master-Slave)複製(Replication)設定一文第8節Redis主從複製實戰的部署方式部署Master-Slave,但是需要注意的是,本文需要的是一個Master例項,3個Slave例項。
Redis例項資訊如下:
IP | Port | 備註 |
---|---|---|
192.168.199.126 | 6379 | Master |
192.168.199.249 | 6382 | Slave |
192.168.199.249 | 6383 | Slave |
192.168.199.249 | 6384 | Slave |
Master的配置很簡單,我們開啟守護程序即可(演示例項不設定驗證資訊,如有需要自行設定)。
進入redis配置檔案目錄(/data/redis/redis-4.0.1/redis.conf)編輯redis.conf進行如下配置。
daemonize yes
配置Slave
建立Slave配置檔案目錄,拷貝redis.conf到對應目錄並進行配置。
mkdir /data/redis/cluster
cd /data/redis/cluster
mkdir -p 6382
cp /data/redis/redis-4.0.1/redis.conf /data/redis/cluster/6382/redis-6382.conf
mkdir -p 6383
cp /data/redis/redis-4.0.1/redis.conf /data/redis/cluster/6383/redis-6383.conf
mkdir -p 6384
cp /data/redis/redis-4.0.1/redis.conf /data/redis/cluster/6384/redis-6384.conf
3個Slave例項的配置檔案內容,注意修改下面加粗字型部分的內容即可(注意註釋掉的要去掉註釋),其他都相同:
配置選項 | 選項值 | 說明 |
---|---|---|
daemonize | yes | 預設情況下 redis 不是作為守護程序執行的,如果你想讓它在後臺執行,你就把它改成 yes。 |
pidfile | /data/redis/cluster/6382/redis-6382.pid | 當redis作為守護程序執行的時候,預設情況下它會寫一個 pid 到 /var/run/redis.pid 檔案裡面 |
port | 6382 | 監聽埠號,預設為 6379,如果你設為 0 ,redis 將不在 socket 上監聽任何客戶端連線。 |
database | 1 | 設定資料庫的數目。 |
cluster-enabled | no | 啟用或停用叢集 |
cluster-config-file | /data/redis/cluster/6382/nodes-6382.conf | 叢集配置檔案,啟動時自動生成 |
cluster-node-timeout | 15000 | 節點互聯超時時間,單位毫秒 |
cluster-migration-barrier | 1 | 這個引數表示的是,一個主節點在擁有多少個好的從節點的時候就要割讓一個從節點出來。 |
cluster-require-full-coverage | yes | 如果叢集中某些key space沒有被叢集中任何節點覆蓋,叢集將停止接受寫入 |
appendonly | yes | 啟用aof持久化方式 |
dir | /data/redis/cluster/6382/ | 節點資料持久化存放目錄 |
slaveof | 192.168.199.126 6379 | Master節點配置 |
如果配置成功並啟動,通過客戶端工具連線到Master時,輸入如下命令
info replication
將會得到如下結果
# Replication
role:master
connected_slaves:3
slave0:ip=192.168.199.249,port=6384,state=online,offset=1127147,lag=1
slave1:ip=192.168.199.249,port=6383,state=online,offset=1127292,lag=1
slave2:ip=192.168.199.249,port=6382,state=online,offset=1127292,lag=1
master_replid:beba8e13c44c5a4afcf8c82889b524b2f76faa22
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1127292
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:78717
repl_backlog_histlen:1048576
Redis Sentinel部署
3個Redis Sentinel資訊如下
IP | Port | 備註 |
---|---|---|
192.168.199.126 | 26379 | Sentinel |
192.168.199.126 | 26479 | Sentinel |
192.168.199.126 | 26579 | Sentinel |
Redis安裝完成之後,在redis目錄(本例項是redis-4.0.1)下會有一個sentinel.conf檔案,建立sentinel目錄並將該配置檔案拷貝進去重新命名為自定義的sentinel配置檔案。
mkdir /data/redis/sentinel
cp /data/redis/redis-4.0.1/sentinel.conf /data/redis/sentinel/sentinel_26379.conf
cp /data/redis/redis-4.0.1/sentinel.conf /data/redis/sentinel/sentinel_26479.conf
cp /data/redis/redis-4.0.1/sentinel.conf /data/redis/sentinel/sentinel_26579.conf
sentinel_26379.conf配置內容:
protected-mode no
port 26379
sentinel monitor mymaster 192.168.199.249 6384 2
sentinel failover-timeout mymaster 60000
sentinel config-epoch mymaster 1
sentinel leader-epoch mymaster 1
sentinel known-slave mymaster 192.168.199.126 6379
sentinel_26479.conf配置內容:
protected-mode no
port 26479
sentinel monitor mymaster 192.168.199.249 6384 2
sentinel failover-timeout mymaster 60000
sentinel config-epoch mymaster 1
sentinel leader-epoch mymaster 1
sentinel known-slave mymaster 192.168.199.126 6379
sentinel_26579.conf配置內容:
protected-mode no
port 26579
sentinel monitor mymaster 192.168.199.249 6384 2
sentinel failover-timeout mymaster 60000
sentinel config-epoch mymaster 1
sentinel leader-epoch mymaster 1
sentinel known-slave mymaster 192.168.199.126 6379
啟動Redis Master例項
進入Redis Master伺服器Redis bin目錄,執行以下命令:
./redis-server /data/redis/redis-4.0.1/redis.conf
啟動Redis Slave例項
進入Redis Slave伺服器Redis bin目錄,執行以下命令:
./redis-server /data/redis/cluster/6382/redis-6382.conf
./redis-server /data/redis/cluster/6383/redis-6383.conf
./redis-server /data/redis/cluster/6384/redis-6384.conf
啟動Sentinel例項
進入Redis Slave伺服器Redis bin目錄,執行以下命令:
./redis-sentinel /data/redis/sentinel/sentinel_26379.conf
./redis-sentinel /data/redis/sentinel/sentinel_26479.conf
./redis-sentinel /data/redis/sentinel/sentinel_26579.conf
如果啟動時出現如下資訊,表示啟動成功
Maven配置
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.7.5.RELEASE</version>
</dependency>
在config.properties中增加如下配置
#Redis sentinel使用
redis.sentinel1.host=192.168.199.126
redis.sentinel1.port=26379
redis.sentinel2.host=192.168.199.126
redis.sentinel2.port=26479
redis.sentinel3.host=192.168.199.126
redis.sentinel3.port=26579
redis.sentinel.masterName=mymaster
新建spring-redis-sentinel.xml配置檔案,寫入如下資訊
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:util="http://www.springframework.org/schema/util"
xmlns:task="http://www.springframework.org/schema/task" xmlns:cache="http://www.springframework.org/schema/cache"
xmlns:c='http://www.springframework.org/schema/c' xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.0.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.0.xsd
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-3.1.xsd"
default-lazy-init="true">
<!-- 開啟spring cache註解功能 -->
<cache:annotation-driven cache-manager="redisCacheManager" />
<context:annotation-config />
<context:property-placeholder
ignore-unresolvable="true" location="classpath:config.properties" />
<!-- Redis -->
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxTotal" value="${redis.maxTotal}" />
<property name="maxIdle" value="${redis.maxIdle}" />
<property name="maxWaitMillis" value="${redis.maxWait}" />
<property name="testOnBorrow" value="${redis.testOnBorrow}" />
</bean>
<bean id="sentinelConfiguration"
class="org.springframework.data.redis.connection.RedisSentinelConfiguration">
<property name="master">
<bean class="org.springframework.data.redis.connection.RedisNode">
<property name="name" value="${redis.sentinel.masterName}"></property>
</bean>
</property>
<property name="sentinels">
<set>
<bean class="org.springframework.data.redis.connection.RedisNode">
<constructor-arg name="host" value="${redis.sentinel1.host}"></constructor-arg>
<constructor-arg name="port" value="${redis.sentinel1.port}"></constructor-arg>
</bean>
<bean class="org.springframework.data.redis.connection.RedisNode">
<constructor-arg name="host" value="${redis.sentinel2.host}"></constructor-arg>
<constructor-arg name="port" value="${redis.sentinel2.port}"></constructor-arg>
</bean>
<bean class="org.springframework.data.redis.connection.RedisNode">
<constructor-arg name="host" value="${redis.sentinel3.host}"></constructor-arg>
<constructor-arg name="port" value="${redis.sentinel3.port}"></constructor-arg>
</bean>
</set>
</property>
</bean>
<!-- redis伺服器中心 -->
<bean id="jedisConnectionFactory"
class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<constructor-arg name="sentinelConfig" ref="sentinelConfiguration"></constructor-arg>
<constructor-arg name="poolConfig" ref="poolConfig"></constructor-arg>
</bean>
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="jedisConnectionFactory" />
<property name="keySerializer">
<bean
class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
<property name="valueSerializer">
<bean
class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
</property>
</bean>
<!-- redis快取管理器 -->
<bean id="redisCacheManager" class="org.springframework.data.redis.cache.RedisCacheManager">
<constructor-arg name="redisOperations" ref="redisTemplate" />
</bean>
<bean id="redisUtils" class="ron.blog.blog_service.utils.RedisUtils" />
</beans>
在spring-context.xml引入spring-redis-sentinel.xml
<import resource="classpath:spring-redis-sentinel.xml" />
編寫Redis幫助類RedisUtils
public class RedisUtils {
/**
* RedisTemplate是一個簡化Redis資料訪問的一個幫助類,
* 此類對Redis命令進行高階封裝,通過此類可以呼叫ValueOperations和ListOperations等等方法。
*/
@Autowired
private RedisTemplate<Serializable, Object> redisTemplate;
/**
* 批量刪除對應的value
*
* @param keys
*/
public void remove(final String... keys) {
for (String key : keys) {
remove(key);
}
}
/**
* 批量刪除key
*
* @param pattern
*/
public void removePattern(final String pattern) {
Set<Serializable> keys = redisTemplate.keys(pattern);
if (keys.size() > 0)
redisTemplate.delete(keys);
}
/**
* 刪除對應的value
* @param key
*/
public void remove(final String key) {
if (exists(key)) {
redisTemplate.delete(key);
}
}
/**
* 快取是否存在
* @param key
* @return
*/
public boolean exists(final String key) {
return redisTemplate.hasKey(key);
}
/**
* 讀取快取
* @param key
* @return
*/
public Object get(final String key) {
Object result = null;
ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
result = operations.get(key);
return result;
}
/**
*
* @Author Ron
* @param key
* @param hashKey
* @return
*/
public Object get(final String key, final String hashKey){
Object result = null;
HashOperations<Serializable,Object,Object> operations = redisTemplate.opsForHash();
result = operations.get(key, hashKey);
return result;
}
/**
* 寫入快取
*
* @param key
* @param value
* @return
*/
public boolean set(final String key, Object value) {
boolean result = false;
try {
ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
operations.set(key, value);
result = true;
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
*
* @Author Ron
* @param key
* @param hashKey
* @param value
* @return
*/
public boolean set(final String key, final String hashKey, Object value) {
boolean result = false;
try {
HashOperations<Serializable,Object,Object> operations = redisTemplate.opsForHash();
operations.put(key, hashKey, value);
result = true;
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 寫入快取
*
* @param key
* @param value
* @return
*/
public boolean set(final String key, Object value, Long expireTime) {
boolean result = false;
try {
ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
operations.set(key, value);
redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
result = true;
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
}
以上的所有配置,如果不出問題,那麼基本上我們已經集成了Spring與Redis Sentinel,接下來就是在我們的例子中使用。
在我們的Blog服務(BlogService)中,我們自動裝配RedisUtils例項,在進入部落格詳細頁面時,我們首先到Redis中獲取是否存在我們所要檢視的部落格,如果沒有,則從MySQL獲取並寫入Redis。
@Autowired
private BlogContentMapper blogContentMapper;//讀者自行定義
@Autowired
RedisUtils redisUtils;
/**
* @Comment 獲取部落格內容
* @Author Ron
* @Date 2017年10月25日 下午3:05:27
* @return
*/
@Override
public BlogContent getBlog(String bid) {
if(redisUtils.exists(bid)){
logger.info("快取命中部落格"+bid);
return (BlogContent) redisUtils.get(bid);
}else{
logger.info("快取尚未命中部落格"+bid);
BlogContent blogContent = blogContentMapper.selectByPrimaryKey(bid);
redisUtils.set(bid, blogContent);
return blogContent;
}
}
例項演示
我們第一次進入指定部落格頁面時會在控制檯打出快取尚未命中部落格的資訊,第二次進入則會打出快取命中部落格字樣。
部落格詳細頁面:
第一次控制檯打出的資訊:
第二次重新整理頁面是空值臺打出的資訊:
先檢視一下Sentinel資訊:
輸入命令:./redis-cli -p 26379 info Sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=192.168.199.126:6379,slaves=3,sentinels=4
我們可以看到Master的名稱、ip地址、Slave的數量、Sentinel數量。
我們停掉一個Sentinel,在我們的控制檯中就會監控到並出現連線丟失的錯誤日誌,但是我們的系統仍然可用。
重新啟動之後報錯日誌就停止。
我們停掉Master,在Sentinel中就會重新選舉一個Slave為Master,同時在我們的Eclipse的控制檯也可以看到重新選舉的Master;
我們看到系統重新選舉192.168.199.126 6382為Master。
注意:
啟動服務時,可能會報連線Redis Sentinel超時,此時可能是防火牆未關閉造成,使用如下命令關閉防火牆即可:
在CentOS 7中預設使用firewall做為防火牆,下面是啟動&關閉防火牆的命令:
// 啟動firewall
systemctl start firewalld.service
// 關閉firewall
systemctl stop firewalld.service
相關推薦
12、Spring技術棧-Redis Sentinel實現高可用快取叢集方案實戰
Redis Sentinel的分散式特性介紹 Redis Sentinel是一個分散式系統,Sentinel執行在有許多Sentinel程序互相合作的環境下,它本身就是這樣被設計的。有許多Sentinel程序互相合作的優點如下: 當多個Sentinel同意
6.redis sentinel實現高可用讀寫分離
1. redis sentinel 故障轉移的基本原理: 多個sentinel發現並確認master有問題 選舉出一個sentinel作為領導 選出一個slave稱為新的master的slave 通知其他的slave稱為新的master的slave 通
11、Spring技術棧-整合Redis,通過Redis的Master-Slave實現快取資料讀寫分離
1、Redis主從複製(Master-Salve Replication)簡介 Redis 支援簡單且易用的主從複製(master-slave replication)功能, 該功能可以讓從伺服器(slave server)成為主伺服器(master serv
SuSE11環境下Redis+Keepalived實現高可用技術
keep starting pass prot x86_64 configure pid dev 修改 1.Redis配置信息Redis部署使用兩臺服務器,實現Redis+keepalived,提供redis服務高可用,當主redis進程或服務器宕機之後,備redis進程或
redis如何實現高可用【主從複製、哨兵機制】
實現redis高可用機制的一些方法: 保證redis高可用機制需要redis主從複製、redis持久化機制、哨兵機制、keepalived等的支援。 主從複製的作用:資料備份、讀寫分離、分散式叢集、實現高可用、宕機容錯機制等。 redis主從複製原理 首先主從複製
redis+sentinel+keepalived 高可用,可實現多臺並單點訪問
之前有寫過redis+sentinel的哨兵機制主從的切換,這一次多了一個keepalived,是為了能夠方便專案只支援一臺訪問,可是又要高可用的情況下,就可以執行此方案。 本次主要講的就是keepalived的配置,如何才能做到單臺訪問而實現高可用,從而實
Redis+Keepalived實現高可用
redis腳本 itl ive page 出現 IV ping ash d+ 使用redis哨兵可以在主服務器出現故障的時候自動切換主從,但是從服務器的IP不同於原主服務器的IP還需要在客戶端手動修改IP才能生效 下面使用keepalived實現VIP自動漂移
Redis 從下載安裝到 Redis sentinel 的高可用經驗總結
Step 1:Redis的下載安裝 官網下載redis 解壓並安裝: [[email protected] ~]# cd /home/ [[email protected] home]# wget http://download.redis.io/r
Keepalived+Nginx+Redis+Tomcat實現高可用web負載均衡
一、系統環境 作業系統:CentOS 7 tomcat 8.0.47 Nginx 1.12.2 Redis 4.0.2 192.168.124.128 tomcat1+Nginx+Redis 192.168.124.130 tomcat2
redis --redis主從,高可用,叢集
安裝redis,見上一篇這裡寫連結內容 redis主從複製 修改redis主配置檔案 master(server5): #bind 172.25.40.5 protected-mode no slave(server6): #bind 172.25
Spring Boot Redis-Sentinel—實現Redis高可用之哨兵模式
廢話簡論 Redis高可用之哨兵模式它就是,當你的reids掛掉了之後,它可以自己切換到其他redis上.不影響使用者的正常使用. 簡述Sentinel: Sentinel具有四個特點: 監控,通知,自動故障轉移,配置提供者 監控:哨兵不斷的檢查ma
12、python全棧之路-並發編程之多進程
arc 數據傳遞 狀態 for 是否 直接 沒有 交女朋友 pan 十二、並發編程之多進程 http://www.cnblogs.com/linhaifeng/articles/6817679.html 理論:http://www.cnblogs.com/linhaifen
Redis Sentinel實現的機制與原理詳解
過程 正則 發送 進行 還需 生產環境 根據 stat 時間 原文:Redis Sentinel實現的機制與原理詳解序言 Redis-Sentinel是Redis官方推薦的高可用性(HA)解決方案。實際上這意味著你可以使用Sentinel模式創建一個可以不用人為幹預而應對
springboot2整合spring-session-data-redis,實現session共享
1.新增Maven依賴 <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId>
1、spring cloud---feign+Hystrix熔斷器實現(第三章)
Feign-Hystrix 因為熔斷只是作用在服務呼叫這一端,因此我們根據上一篇的示例程式碼只需要改動spring-cloud-consumer專案相關程式碼就可以。因為,Feign中已經依賴了Hystrix所以在maven配置上不用做任何改動。 1、配置檔案 application
7、Spring Boot 與 Redis 集成
asn mos commons 有效期 control push next() warning 小時 1.7 Spring Boot 與 Redis 集成 簡介 繼續上篇的MyBatis操作,詳細介紹在Spring Boot中使用RedisCacheManager作為緩存
7、Spring Boot 與 Redis 整合
1.7 Spring Boot 與 Redis 整合 簡介 繼續上篇的MyBatis操作,詳細介紹在Spring Boot中使用RedisCacheManager作為快取管理器,整合業務於一體。 完整原始碼: Spring-Boot-Demos 1.7.1 建立 spring-boot-r
SpringBoot30 整合Mybatis-Plus、整合Redis、利用Ehcache和Redis分別實現二級快取
1 環境說明 JDK: 1.8 MAVEN: 3. SpringBoot: 2.0.4 2 SpringBoot整合Mybatis-Plus 2.1 建立SpringBoot 利用IDEA建立SpringBoot專案,引入web mysql mybatis-plus lombok
redis主從 + 哨兵(sentinel)+ VIP漂移實現高可用
1. 環境及ip角色說明: 這裡使用三臺伺服器,每臺伺服器上開啟一個redis-server和redis-sentinel服務, redis-server埠為8000,redis-sentinel的埠為6800,修改預設埠是安全的第一步 redis-server: 10.
spring結合redis如何實現資料的快取
1、實現目標 通過redis快取資料。(目的不是加快查詢的速度,而是減少資料庫的負擔) 2、所需jar包 注意:jdies和commons-pool兩個jar的版本是有對應關係的,注意引入jar包是要配對使用,否則將會報錯。因為commons-pooljar的目