Ehcache 集羣改造-----增加手動發現方式自動增加減機器機制

上一篇文章說了ehcache基於RMI手動發現集羣的搭建 https://blog.csdn.net/java_ying/article/details/103071571
但是手動發現有很多限制,比如不能動態增加機器,使用中有很多不方便
所以就根據ehcache的機制 加了一個輪詢功能 類似心跳機制 去檢查當前生效的鏈接

放代碼前,先說一下思路:
ehcache 手動發現機制,在項目啓動時 會讀取xml配置文件的值,然後放到一個map裏面。如下
在這裏插入圖片描述
操作是這個類操作的
在這裏插入圖片描述
就是簡單的map操作而已
map裏面 的鍵就是url 類似 //127.0.0.1:40009/articleCache 這樣,值是一個date 我們暫時不用關心

RMICacheManagerPeerProvider 這個類提供了兩個方法,分別是註冊和反註冊

/**
     * Register a new peer
     *
     * @param rmiUrl
     */
    public abstract void registerPeer(String rmiUrl);

    /**
     * Unregisters a peer
     *
     * @param rmiUrl
     */
    public final synchronized void unregisterPeer(String rmiUrl) {
        peerUrls.remove(rmiUrl);
    }

我們就可以利用這兩個方法來操作它
上文提到過 每次同步內容的時候 他都會遍歷這個map 然後如果發送消息不成功會很浪費時間
我們就做一個監測功能,當我們配置文件的值不生效的時候 就unregisterPeer,如果生效了就registerPeer

這個provider 可以從CacheManager中拿到。放代碼

package com.hqjl.communityserv.thread;

import com.hqjl.communityserv.bean.vo.SocketParam;
import com.hqjl.communityserv.cache.EhcacheManager;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.distribution.CacheManagerPeerProvider;
import net.sf.ehcache.distribution.RMICacheManagerPeerProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

import java.net.Socket;
import java.util.*;

/**
 * @author chunying
 * @Date: 2019/11/14 0014
 */
@Configuration
@PropertySource(value= {"classpath:eh.properties"}, encoding = "utf-8")
public class RMIUrlsThread{

    @Value("${rmiurls}")
    private String rmiurls;

    private static final Map<Integer, String> urls = new HashMap<>();
    private static final Map<Integer, SocketParam> paramMap = new HashMap<>();
    private static final String RMI_URLS = "rmiUrls";
    private static Boolean flag = false;

    public void loadProps() {
        int key = 0;
        String[] split = rmiurls.split("\\|");
        //這裏面根據url 取出IP 和port用於測試連接是否可用
        for (String s : split) {
            urls.put(key, s);
            String substring = s.substring(2);
            String[] splitS1 = substring.split("\\:");
            String IP = splitS1[0];
            String s2 = splitS1[1];
            String[] s2Split = s2.split("\\/");
            String port = s2Split[0];
            SocketParam socketParam = new SocketParam(IP, Integer.parseInt(port));
            paramMap.put(key, socketParam);
            key++;
        }

    }

    public void thread() {
        new Thread(){
            @Override
           public void run() {
               loadProps();
               //這裏面獲取CacheManager 很簡單 我沒有放這個類。 不會的話看我前一篇文章
               CacheManager cacheManager = EhcacheManager.getCacheManager();
               Map<String, CacheManagerPeerProvider> cacheManagerPeerProviders = cacheManager.getCacheManagerPeerProviders();
               cacheManagerPeerProviders.forEach((k,v)-> {
                   RMICacheManagerPeerProvider provider = (RMICacheManagerPeerProvider)v;
                   while(true) {
                       urls.forEach((key, url)-> {
                           SocketParam socketParam = paramMap.get(key);
                           Socket socket = null;
                           String IP = socketParam.getIp();
                           Integer port = socketParam.getPort();
                           try {
                               //測試連接是否可用
                               socket = new Socket(IP, port);
                               socket.close();
                               provider.registerPeer(url);
                           }catch(Exception e) {
                               provider.unregisterPeer(url);
                           }
                       });
                       System.out.println("執行一次");
                       try {
                           Thread.sleep(10000);
                       } catch (InterruptedException e) {
                           e.printStackTrace();
                       }
                   }

               });
           }
        }.start();

    }

    public static void main(String[] args) {

    }
}


package com.hqjl.communityserv.bean.vo;

/**
 * @author chunying
 * @Date: 2019/11/15 0015
 */
public class SocketParam {

    private String ip;
    private Integer port;

    public SocketParam(){}

    public SocketParam(String ip, Integer port) {
        this.ip = ip;
        this.port = port;
    }

    public String getIp() {
        return ip;
    }

    public void setIp(String ip) {
        this.ip = ip;
    }

    public Integer getPort() {
        return port;
    }

    public void setPort(Integer port) {
        this.port = port;
    }
}

eh.properties中的值就是我copy了一份 xml中rmiurl的值 比較懶 不想解析xml。。。
這個方法會在項目啓動完成後立刻執行,首先把properties中值讀出來,放進我們準備好的map,
然後還有一個map 是初次加載時 解析出地址中的IP 和 port ,因爲String 的相關操作還是有點耗時的,最好一次操作,一直使用。兩個map的key是相同的 用的時候用key做關聯。

thread方法,新建了一個不死的線程,裏面先通過EhcacheManager 取出 RMICacheManagerPeerProvider
while(true) 保證一直運行,測試連接是否可用 使用java.net的socket 如果通了 說明 連接的機器在運行,那麼就註冊
如果沒通,說明掛了或者沒啓動,直接反註冊。 切記 記得close();
Sleep()方法 是心跳間隔時間,自由發揮即可。

好了 這是一個不完整的博客,這樣操作雖然可以實現想要的功能,但是會有一個問題:
我們看這個Map 注意是個SynchronizedMap 這個東西是帶操作鎖的,一個只有一個線程可以操作他,如果在遍歷時有更新操作,那麼他會報錯的。所以要處理這個問題,還要繼續跟蹤ehcache的同步機制,是怎麼處理異常的。
後續會繼續更新~

------------------------------------------------------------------分割線-------------------------------------------------------------------------------------
後續:準備把ehcache源碼搞下來,把遍歷過程和讀寫過程加讀寫鎖。最近莫得時間,有時間更改後上代碼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章