服務(wù)端集群搭建
當單個服務(wù)端節點無法滿足當前業務(wù)吞吐量或者業務(wù)要求時,服務(wù)端将會采用(yòng)集群方式對外提供服務(wù)。URule屬于Web應用(yòng),所以URule服務(wù)端的集群搭建也和傳統的Web應用(yòng)一緻,有(yǒu)一個負載+n個服務(wù)端節點。
接下來我們将提供一種比較簡便的Redis session共享的方式搭建,并借助nginx實現負載均衡。
1. 服務(wù)端集群
2.在集群環境上實現Session共享
關于session共享的方式有(yǒu)多(duō)種,常見的有(yǒu):
(1)通過nginx的ip_hash,根據ip将請求分(fēn)配到對應的服務(wù)器,相當于開啓了會話保持功能(néng)
(2)基于關系型數據庫存儲
(3)基于cookie存儲
(4)服務(wù)器内置的session複制域
(5)基于nosql(memcache、redis都可(kě)以)
實現原理(lǐ)也比較簡單,在所有(yǒu)的請求之前配置一過濾器,在請求之前操作(zuò)session,其實spring-session中(zhōng)真正起作(zuò)用(yòng)的session過濾器是SessionRepositoryFilter。而spring-session集成了redis與mongodb,若啓用(yòng)redis方式隻需要稍加配置即可(kě)實現。
2.1 集成Redis實現Session共享
(1)集成spring-session-data-redis
<!-- 引入redis的集成 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</exclusion>
<exclusion>
<artifactId>lettuce-core</artifactId>
<groupId>io.lettuce</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.6.3</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- 引入 session與redis的集成:若不用(yòng)redis請去掉本依賴 -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
(2)配置類添加(非必須)
@Configuration
//不設置默認30分(fēn)鍾
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 86400*30)
public class RedisSessionConfig {
@Bean("redisTemplate")
@ConfigurationProperties(prefix="spring.redis")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory){
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
//将key的序列化設置成StringRedisSerializer
StringRedisSerializer keySerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(keySerializer);
redisTemplate.setHashKeySerializer(keySerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
@EnableRedisHttpSession:開啓Session共享功能(néng)。使用(yòng)此注解之後Session調用(yòng)會自動通過Redis存儲和獲取。另外,想要達到Session共享的目的,在其他(tā)的系統上隻需要做同樣的配置即可(kě)。 其中(zhōng)maxInactiveIntervalInSeconds參數是設置Session失效時間,在使用(yòng)Redis Session之後,原Spring Boot的server.session.timeout屬性不再生效。
(3)修改Session的儲存方式為(wèi)redis
# session的存儲方式配置為(wèi)redis
spring:
session:
store-type: redis
redis:
host: localhost
port: 6379
database: 1
timeout: 10000
jedis:
pool:
max-active: 50 # 連接池最大連接數(使用(yòng)負值表示沒有(yǒu)限制)
max-wait: 5000 # 連接池中(zhōng)連接用(yòng)完時,新(xīn)的請求等待時間(毫秒(miǎo)),超過該時間抛出異常JedisConnectionException,(默認-1,負值表示沒有(yǒu)限制,不建議使用(yòng)默認值)
max-idle: 10 # 連接池中(zhōng)的最大空閑連接,默認8
min-idle: 2 # 連接池中(zhōng)的最小(xiǎo)空閑連接,默認0
2.2 nginx配置負載均衡示例
nginx的配置文(wén)件如下
upstream nginx-cluster{
server localhost:8080;
server localhost:8081;
}
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://nginx-cluster;
}
}
2.3 測試頁(yè)面
訪問nginx地址http://192.168.1.16:80/,會自動跳由到任一tomcat實例上,通過日志(zhì)可(kě)以發現上述nginx配置的分(fēn)發策略默認是輪詢
2.4 實現規則設計器中(zhōng)複制粘貼的多(duō)實例同步
@Service
public class SessionRedisClipboardStore implements ClipboardStore {
@Resource
private StringRedisTemplate stringRedisTemplate;
@Override
public void set(String key, String value) {
String sessionkey = RequestHolder.getRequest().getSession().getId()+key;
stringRedisTemplate.opsForValue().set(sessionkey,value);
RequestHolder.getRequest().getSession().setAttribute(sessionkey, value);
}
@Override
public String get(String key) {
String sessionkey = RequestHolder.getRequest().getSession().getId()+key;
String val = stringRedisTemplate.opsForValue().get(sessionkey);
if(StringUtils.hasText(val)){
return val;
}
return (String) RequestHolder.getRequest().getSession().getAttribute(sessionkey);
}
@Override
public void remove(String key) {
String sessionkey = RequestHolder.getRequest().getSession().getId()+key;
stringRedisTemplate.delete(sessionkey);
RequestHolder.getRequest().getSession().removeAttribute(sessionkey);
}
}
3.知識包數據同步問題
重點說明:
- 知識包在發布後被編譯成一個rete樹,RETE算法的核心是以‘内存空間換取執行時間’,故而發布後的知識包是直接存儲在jvm虛拟内存中(zhōng),那麽要解決的是各實例内存中(zhōng)知識包數據同步更新(xīn)問題
- 解決這個問題參考文(wén)檔14.6.知識包多(duō)環境部署 · GitBook
- 我們也可(kě)以自行實現PacketPublishListener接口,用(yòng)來攔截發布知識包操作(zuò),将狀态寫入到MQ中(zhōng)來通知其它實例自行加載最新(xīn)的知識包數據
- 另外,知識包數量越多(duō)(體(tǐ)積越大),占用(yòng)的jvm内存空間也越大,那麽我們在生産(chǎn)環境上設置jvm堆内存大小(xiǎo)建議在4g以上
而事實上‘知識包集群環境下内存同步問題與上述session共享并無關聯’,session是由于多(duō)實例造成緩存不同步問題,而知識包是内存不同步造成的,内存比緩存性能(néng)更高。
3.1 多(duō)服務(wù)端集群同步
在集群環境下,由于服務(wù)端會有(yǒu)多(duō)個實例節點,各實例(jvm)之間是物(wù)理(lǐ)隔離的,那麽當在某一個實例上【發布/啓用(yòng)知識包新(xīn)版本】時,就需要立即同步到其它服務(wù)實例的jvm内存中(zhōng),我們可(kě)以在系統管理(lǐ)端中(zhōng)維護集群實例信息來解決這一問題。
更多(duō)配置請查看文(wén)檔3.8.集群管理(lǐ) · GitBook,,都是在集群環境下内存同步引起的一系列問題。
- 集群(8080,8081實例節點維護)
- 知識包數據同步更新(xīn)
在知識包頁(yè)面,【查看當前已發布的知識包】點擊啓用(yòng)後,利用(yòng)http rpc通知各實例從數據庫加載最新(xīn)的知識包數據到jvm内存中(zhōng),會彈出窗口(若事先沒有(yǒu)配置集群節點數據,那就不會彈出如下窗口)自動顯示同步結果,如下图所示:
在上述2個tomcat的控制台可(kě)以看到日志(zhì) Successfully reload file packet package:310
3.2 多(duō)客戶端集群同步
(1)主動推送到客戶端
- 客戶端地址維護
- 查看當前已發布的知識包,在點擊【推送到客戶端】,如下操作(zuò)所示
- 點擊上图“确定”按鈕後,彈出如下图所示界面(若沒有(yǒu)配置在客戶端地址,是不會顯示如下列表)
在客戶端tomcat控制台能(néng)看到以下日志(zhì):
Successfully receive the server side to pushed package:310(fc399b91f72240d89bc6d58fdd50538f)(1.0.3)
(2)被動拉取到客戶端
- 在客戶端(消費方)通過sdk調用(yòng)知識包時,會從指定的服務(wù)器獲取最新(xīn)的知識包,實際上取決于urule.knowledgeUpdateCycle=?的值,即同步策略。
- 用(yòng)postman調用(yòng)客戶端服務(wù)
- 查看控制台,發現了每次運行知識包時都會從服務(wù)端加載最新(xīn)的
3.3 自定義同步方式
重寫PacketPublishListener.java接口,再結合消息中(zhōng)間件MQ來實現消息的發布和訂閱等。
todo