設計思路
客戶端向服務器發送匹配請求,服務端接收后將客戶端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());
}
}
}
}
