服務端集群搭建

​ 當單個(gè)服務端節點無法滿足當前業務吞吐量或者業務要求時(shí),服務端将會(huì)采用集群方式對外提供服務。URule屬于Web應用,所以URule服務端的集群搭建也和(hé)傳統的Web應用一緻,有(yǒu)一個(gè)負載+n個(gè)服務端節點。

接下來(lái)我們将提供一種比較簡便的Redis session共享的方式搭建,并借助nginx實現負載均衡。

1. 服務端集群

img

2.在集群環境上(shàng)實現Session共享

關于session共享的方式有(yǒu)多(duō)種,常見的有(yǒu):

(1)通(tōng)過nginx的ip_hash,根據ip将請(qǐng)求分配到對應的服務器(qì),相當于開(kāi)啓了會(huì)話(huà)保持功能

(2)基于關系型數(shù)據庫存儲

(3)基于cookie存儲

(4)服務器(qì)內(nèi)置的session複制(zhì)域

(5)基于nosql(memcache、redis都可(kě)以)

實現原理(lǐ)也比較簡單,在所有(yǒu)的請(qǐng)求之前配置一過濾器(qì),在請(qǐng)求之前操作(zuò)session,其實spring-session中真正起作(zuò)用的session過濾器(qì)是SessionRepositoryFilter。而spring-session集成了redis與mongodb,若啓用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的集成:若不用redis請(qǐng)去掉本依賴 -->
<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
</dependency>

(2)配置類添加(非必須)

@Configuration
//不設置默認30分鍾
@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:開(kāi)啓Session共享功能。使用此注解之後Session調用會(huì)自動通(tōng)過Redis存儲和(hé)獲取。另外,想要達到Session共享的目的,在其他的系統上(shàng)隻需要做(zuò)同樣的配置即可(kě)。 其中maxInactiveIntervalInSeconds參數(shù)是設置Session失效時(shí)間(jiān),在使用Redis Session之後,原Spring Boot的server.session.timeout屬性不再生(shēng)效。

(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    # 連接池最大(dà)連接數(shù)(使用負值表示沒有(yǒu)限制(zhì))
        max-wait: 5000    # 連接池中連接用完時(shí),新的請(qǐng)求等待時(shí)間(jiān)(毫秒(miǎo)),超過該時(shí)間(jiān)抛出異常JedisConnectionException,(默認-1,負值表示沒有(yǒu)限制(zhì),不建議使用默認值)
        max-idle: 10      # 連接池中的最大(dà)空(kōng)閑連接,默認8
        min-idle: 2       # 連接池中的最小(xiǎo)空(kōng)閑連接,默認0

2.2 nginx配置負載均衡示例

nginx的配置文件如下

upstream nginx-cluster{
        server localhost:8080;
        server localhost:8081;
    }

 server {
        listen       80;
        server_name  localhost;

        location / {
             proxy_pass http://nginx-cluster;
         }
 }

2.3 測試頁面

訪問nginx地址http://192.168.1.16:80/,會(huì)自動跳(tiào)由到任一tomcat實例上(shàng),通(tōng)過日志(zhì)可(kě)以發現上(shàng)述nginx配置的分發策略默認是輪詢

img

2.4 實現規則設計(jì)器(qì)中複制(zhì)粘貼的多(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.知識包數(shù)據同步問題

重點說明(míng):

  • 知識包在發布後被編譯成一個(gè)rete樹(shù),RETE算(suàn)法的核心是以‘內(nèi)存空(kōng)間(jiān)換取執行(xíng)時(shí)間(jiān)’,故而發布後的知識包是直接存儲在jvm虛拟內(nèi)存中,那(nà)麽要解決的是各實例內(nèi)存中知識包數(shù)據同步更新問題
  • 解決這個(gè)問題參考文檔14.6.知識包多(duō)環境部署 · GitBook
  • 我們也可(kě)以自行(xíng)實現PacketPublishListener接口,用來(lái)攔截發布知識包操作(zuò),将狀态寫入到MQ中來(lái)通(tōng)知其它實例自行(xíng)加載最新的知識包數(shù)據
  • 另外,知識包數(shù)量越多(duō)(體(tǐ)積越大(dà)),占用的jvm內(nèi)存空(kōng)間(jiān)也越大(dà),那(nà)麽我們在生(shēng)産環境上(shàng)設置jvm堆內(nèi)存大(dà)小(xiǎo)建議在4g以上(shàng)

而事實上(shàng)‘知識包集群環境下內(nèi)存同步問題與上(shàng)述session共享并無關聯’,session是由于多(duō)實例造成緩存不同步問題,而知識包是內(nèi)存不同步造成的,內(nèi)存比緩存性能更高(gāo)。

3.1 多(duō)服務端集群同步

在集群環境下,由于服務端會(huì)有(yǒu)多(duō)個(gè)實例節點,各實例(jvm)之間(jiān)是物理(lǐ)隔離的,那(nà)麽當在某一個(gè)實例上(shàng)【發布/啓用知識包新版本】時(shí),就需要立即同步到其它服務實例的jvm內(nèi)存中,我們可(kě)以在系統管理(lǐ)端中維護集群實例信息來(lái)解決這一問題。

更多(duō)配置請(qǐng)查看文檔3.8.集群管理(lǐ) · GitBook,,都是在集群環境下內(nèi)存同步引起的一系列問題。

  • 集群(8080,8081實例節點維護)

img

  • 知識包數(shù)據同步更新

在知識包頁面,【查看當前已發布的知識包】點擊啓用後,利用http rpc通(tōng)知各實例從數(shù)據庫加載最新的知識包數(shù)據到jvm內(nèi)存中,會(huì)彈出窗口(若事先沒有(yǒu)配置集群節點數(shù)據,那(nà)就不會(huì)彈出如下窗口)自動顯示同步結果,如下圖所示:

img

在上(shàng)述2個(gè)tomcat的控制(zhì)台可(kě)以看到日志(zhì) Successfully reload file packet package:310

3.2 多(duō)客戶端集群同步

(1)主動推送到客戶端

  • 客戶端地址維護

img

  • 查看當前已發布的知識包,在點擊【推送到客戶端】,如下操作(zuò)所示

img

  • 點擊上(shàng)圖“确定”按鈕後,彈出如下圖所示界面(若沒有(yǒu)配置在客戶端地址,是不會(huì)顯示如下列表)

img

  • 在客戶端tomcat控制(zhì)台能看到以下日志(zhì):

    Successfully receive the server side to pushed package:310(fc399b91f72240d89bc6d58fdd50538f)(1.0.3)

(2)被動拉取到客戶端

  • 在客戶端(消費方)通(tōng)過sdk調用知識包時(shí),會(huì)從指定的服務器(qì)獲取最新的知識包,實際上(shàng)取決于urule.knowledgeUpdateCycle=?的值,即同步策略。

img

  • 用postman調用客戶端服務

img

  • 查看控制(zhì)台,發現了每次運行(xíng)知識包時(shí)都會(huì)從服務端加載最新的

img

3.3 自定義同步方式

重寫PacketPublishListener.java接口,再結合消息中間(jiān)件MQ來(lái)實現消息的發布和(hé)訂閱等。

todo

results matching ""

    No results matching ""