叢集機器迴圈呼叫實現,基於SpringBoot
阿新 • • 發佈:2018-11-19
以下的內容純屬理論探討,除非萬不得已最好不要這麼幹,因為會造成叢集的有狀態化和維護困難。
假設一個叢集中有三臺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);
}