設計思路
客戶端向服務器發送匹配請求,服務端接收后將客戶端Session放入匹配隊列中,匹配完成時通知用戶。
使用觀察者設計模式可以實現這個功能。
代碼
觀察者代碼:
Java util 包擁有Observer 接口,這里根據實際應用自定義參數 Map。
import java.util.Map; public interface Observer { public void update(Map<String, String> map); }
被觀察者接口:
import java.util.List; public interface Observerable { public void registerObserver(MatchNode o); public void removeObserver(MatchNode o); public void notifyObserver(List<MatchNode> matchNodes); }
匹配節點:
每個節點存放用戶數據,用於進行匹配和查找。
import java.util.Map; public class MatchNode implements Observer { private Long userId; private Long sessionId; private Long ruleId; public Long getUserId() { return userId; } public void setUserId(Long userId) { this.userId = userId; } public Long getSessionId() { return sessionId; } public void setSessionId(Long sessionId) { this.sessionId = sessionId; } public Long getRuleId() { return ruleId; } public void setRuleId(Long ruleId) { this.ruleId = ruleId; } @Override public void update(Map<String, String> map) { } }
匹配線程
使用被觀察接口 registerObserver 和 registerObserver 可以對匹配隊列中的用戶進行管理,當匹配成功后,會將兩個匹配成功的用戶移除匹配集合,並對匹配成功的用戶發送通知。
使用單例模式意味着可以在任意位置獲取匹配線程的狀態,在程序初始化時啟動匹配線程即可。
用戶在匹配成功時,由匹配線程生成一個 groupId 用於標記這對用戶,可以通過groupId 在后續功能開發中可以起到查詢的作用。
package common.thread; import cn.hutool.core.util.IdUtil; import common.util.RandomTable; import lombok.extern.slf4j.Slf4j; import java.util.*; import java.util.stream.Collectors; @Slf4j public class MatchQueueThread implements Runnable, Observerable { List<MatchNode> matchNodes; @Override public void registerObserver(MatchNode o) { matchNodes.add(o); } @Override public void removeObserver(MatchNode o) { if (!matchNodes.isEmpty()) matchNodes.remove(o); } @Override public void notifyObserver(List<MatchNode> matchNodes) { //從匹配隊列中移除 this.matchNodes.removeAll(matchNodes); String groupId = IdUtil.randomUUID(); Map<String, String> dataMap = new HashMap<>(); dataMap.put("groupId", groupId); dataMap.put("tables", RandomTable.randomTableSerial()); matchNodes.forEach(matchNode -> { matchNode.update(dataMap); }); } //單例 private static class SingletonClassInstance { private static final MatchQueueThread instance = new MatchQueueThread(); } private MatchQueueThread() { matchNodes = new ArrayList<>(); } public static MatchQueueThread getInstance() { return SingletonClassInstance.instance; } public int getMatchCount() { return matchNodes.size(); } /** * | * 匹配邏輯 */ @Override public void run() { Map<Long, List<MatchNode>> result = matchNodes.parallelStream() .collect(Collectors.groupingBy(MatchNode::getRuleId)); for (Map.Entry<Long, List<MatchNode>> group : result.entrySet()) { if (group.getValue().size() > 1) { List<MatchNode> matchNodes = group.getValue().subList(0, 2); notifyObserver(matchNodes); log.info("匹配成功 : " + matchNodes.get(0).getUserId() + " " + matchNodes.get(1).getUserId()); } } } }