1. 程式人生 > >叢集機器迴圈呼叫實現,基於SpringBoot

叢集機器迴圈呼叫實現,基於SpringBoot

以下的內容純屬理論探討,除非萬不得已最好不要這麼幹,因為會造成叢集的有狀態化和維護困難。

假設一個叢集中有三臺tomcat,在發生某件事情(比如介面呼叫)的時候我需要首先在本機做某件事情,如果做不了,就呼叫下一臺機器,如此往復,直到最後一臺。

我們可以將tomcat定義成為一個環,每臺儲存自己的標識和下一臺的標識,呼叫的時候帶上已經經過的節點,如果下一個節點已經在已經經過的節點中,說明就是最後一臺了,不能再往下呼叫。

/**
 * @author xiongshiyan at 2018/10/24 , contact me with email [email protected] or phone 15208384257
 */
public interface NodeNext {
    /**
     * 往下執行
     * @param payload 需要傳送的內容
     * @param crossNodes 已經經過的node
     */
    String next(String payload , Set<String> crossNodes) throws IOException;
}
/**
 * @author xiongshiyan at 2018/10/24 , contact me with email [email protected] or phone 15208384257
 */
@Component
public class DefaultNodeNext implements NodeNext {
    @Autowired
    private SmartHttpClient smartHttpClient;
    @Autowired
    private Node node;

    @Override
    public String next(String payload , Set<String> crossNodes) throws IOException{
        //如果所有經過的節點包含本節點的下個節點,那麼就不幹什麼
        if(crossNodes.contains(node.getNextName())){
            return "";
        }

        if(null == node.getName()){
            return "";
        }


        Request request = Request.of(node.getUrl());
        Map<String , Object> map = new HashMap<>(2);
        map.put(Node.PAYLOAD, payload);

        crossNodes.add(node.getName());
        //新增本地的名字
        map.put(Node.CROSS_NODES , crossNodes);
        request.setBody(JsonUtil.serializeMap(map));

        return smartHttpClient.post(request).getBody();
    }
}
/**
 * 一個tomcat節點,實現環形呼叫的資料結構,一個節點儲存本節點名和下一個節點名,呼叫的時候帶上已經經過的節點,這樣就能知道何時結束
 * @author xiongshiyan at 2018/10/24 , contact me with email [email protected] or phone 15208384257
 */
@ConfigurationProperties(prefix = "spring.circle.send")
@PropertySource(value = "file:/mnt/install/tomcat/circle.properties" , ignoreResourceNotFound = true)
@Component
public class Node {
    public static final String PAYLOAD = "payload";
    public static final String CROSS_NODES = "crossNodes";

    private String name;
    private String nextName;
    private String url;

    ...
}

主要的邏輯都在DefaultNextNode中,實現了判斷是否應該結束和新增本地節點。Node代表一個節點,有自己的標識名字,下一個節點的標識名字,和訪問的URL。為了做到每個tomcat中程式碼的不修改,這個配置需要放到每個機器的相同位置。

spring.circle.send.name=node1
spring.circle.send.nextName=node2
spring.circle.send.url=http://xxxxxxxx1:8080/api/circle/receive

 

spring.circle.send.name=node2
spring.circle.send.nextName=node3
spring.circle.send.url=http://xxxxxxxx2:8080/api/circle/receive

 

spring.circle.send.name=node3
spring.circle.send.nextName=node1
spring.circle.send.url=http://xxxxxxxx3:8080/api/circle/receive

 

需要在每個tomca中新增一個controller來接收上一臺的請求,並作出處理和判斷。

@Autowired
    private NodeNext nodeNext;
    @Autowired
    private NodeCurrent nodeCurrent;

    @PostMapping(value = "/receive")
    public String receive(@RequestBody String json) throws Exception{
        boolean jsonObject = JsonUtil.isJsonObject(json);
        if(!jsonObject){
            return null;
        }

        JSONObject object = new JSONObject(json);
        String payload = object.getString(Node.PAYLOAD);

        boolean canDo = nodeCurrent.doWithPayLoad(payload);
        if(canDo){
            return "";
        }


        JsonArray crossNodes = object.getJsonArray(Node.CROSS_NODES);

        int size = crossNodes.size();
        Set<String> nodes = new HashSet<>(size);
        for (int i = 0; i < size; i++) {
            nodes.add(crossNodes.getString(i));
        }
        return nodeNext.next(payload , nodes);

    }

本地處理的方法介面為NodeCurrent,需要返回是否能處理的標識。

public interface NodeCurrent {
    /**
     * 當前節點需要做的事情
     * @param payload 接收到的資料
     * @return 當前是否能做
     */
    boolean doWithPayLoad(String payload);
}