创建cloud父组件,并将项目改为主服务+微服务MatchingSystem
This commit is contained in:
+15
@@ -0,0 +1,15 @@
|
||||
package com.kob.matchingsystem;
|
||||
|
||||
import com.kob.matchingsystem.service.impl.MatchingServiceImpl;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class MatchingSystemApplication {
|
||||
public static void main(String[] args) {
|
||||
// 启动 MatchingSystem 服务之前启动 MatchingPool 线程
|
||||
MatchingServiceImpl.matchingPool.start(); // 启动匹配线程
|
||||
|
||||
SpringApplication.run(MatchingSystemApplication.class, args);
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package com.kob.matchingsystem.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
@Configuration
|
||||
public class RestTemplateConfig {
|
||||
@Bean
|
||||
public RestTemplate getRestTemplate() {
|
||||
return new RestTemplate();
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
// 链接访问权限控制
|
||||
package com.kob.matchingsystem.config;
|
||||
|
||||
/*
|
||||
主要作用:放行登录、注册等接口
|
||||
*/
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
|
||||
// TODO: 2023/2/19 WebSecurityConfigurerAdapter 已被弃用,注意替换
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
/*
|
||||
.antMatchers("/user/account/token/", "/user/account/register/").permitAll()
|
||||
用于配置公开链接
|
||||
.hasIpAddress("127.0.0.1") 用于限制访问的IP地址, 127.0.0.1 意为只允许本地访问
|
||||
*/
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.csrf().disable()
|
||||
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||||
.and()
|
||||
.authorizeRequests()
|
||||
.antMatchers("/player/add/", "/player/remove/").hasIpAddress("127.0.0.1")
|
||||
.antMatchers(HttpMethod.OPTIONS).permitAll()
|
||||
.anyRequest().authenticated();
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package com.kob.matchingsystem.controller;
|
||||
|
||||
import com.kob.matchingsystem.service.MatchingService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@RestController
|
||||
public class MatchingController {
|
||||
// 注入接口
|
||||
@Autowired
|
||||
private MatchingService matchingService;
|
||||
|
||||
// 涉及到对数据的修改,使用 POST 请求
|
||||
// MultiValueMap 每个关键字对应一个列表的值, Map 每个关键字只能对应一个单值
|
||||
@PostMapping("/player/add/")
|
||||
public String addPlayer(@RequestParam MultiValueMap<String, String> data) {
|
||||
Integer userId = Integer.parseInt(Objects.requireNonNull(data.getFirst("user_id")));
|
||||
Integer rating = Integer.parseInt(Objects.requireNonNull(data.getFirst("rating")));
|
||||
return matchingService.addPlayer(userId, rating);
|
||||
}
|
||||
|
||||
@PostMapping("/player/remove/")
|
||||
public String removePlayer(@RequestParam MultiValueMap<String, String> data) {
|
||||
Integer userId = Integer.parseInt(Objects.requireNonNull(data.getFirst("user_id")));
|
||||
return matchingService.removePlayer(userId);
|
||||
}
|
||||
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// 定义接口
|
||||
package com.kob.matchingsystem.service;
|
||||
|
||||
public interface MatchingService {
|
||||
// 给匹配池添加一名玩家
|
||||
String addPlayer(Integer userId, Integer rating);
|
||||
|
||||
// 从匹配池删除一名玩家
|
||||
String removePlayer(Integer userId);
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
// 实现接口
|
||||
package com.kob.matchingsystem.service.impl;
|
||||
|
||||
import com.kob.matchingsystem.service.MatchingService;
|
||||
import com.kob.matchingsystem.service.impl.utils.MatchingPool;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class MatchingServiceImpl implements MatchingService {
|
||||
// MatchingPool 全局只有一个线程,所以这里定义成静态
|
||||
public final static MatchingPool matchingPool = new MatchingPool();
|
||||
|
||||
@Override
|
||||
public String addPlayer(Integer userId, Integer rating) {
|
||||
System.out.println("add player: " + userId + " " + rating);
|
||||
// 向匹配池添加一名玩家
|
||||
matchingPool.addPlayer(userId, rating);
|
||||
return "add player success";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String removePlayer(Integer userId) {
|
||||
System.out.println("remove player: " + userId);
|
||||
// 从匹配池移除一名玩家
|
||||
matchingPool.removePlayer(userId);
|
||||
return "remove player success";
|
||||
}
|
||||
}
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
// 用来维护线程
|
||||
package com.kob.matchingsystem.service.impl.utils;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
@Component
|
||||
public class MatchingPool extends Thread {
|
||||
// 把所有用户存下来
|
||||
private static List<Player> playerList = new ArrayList<>();
|
||||
// 定义一个锁, Reentrant Lock 是可重入锁
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
// 定义微服务传值 URL
|
||||
private static final String startGameUrl = "http://127.0.0.1:3000/pk/start/";
|
||||
// 定义 RestTemplate 用来微服务数据通信
|
||||
private static RestTemplate restTemplate;
|
||||
|
||||
@Autowired
|
||||
private void setRestTemplate(RestTemplate restTemplate) {
|
||||
MatchingPool.restTemplate = restTemplate;
|
||||
}
|
||||
|
||||
public void addPlayer(Integer userId, Integer rating) {
|
||||
lock.lock();
|
||||
try {
|
||||
// 一开始匹配等待时间是 0
|
||||
playerList.add(new Player(userId, rating, 0));
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void removePlayer(Integer userId) {
|
||||
lock.lock();
|
||||
try {
|
||||
// 先创建一个新列表,将没有删的用户先存下来
|
||||
List<Player> newPlayerList = new ArrayList<>();
|
||||
// 枚举所有的 player 并存入 newPlayerList
|
||||
for (Player player : playerList) {
|
||||
// 如果当前 player 的 id 不等于要删除的用户 id,则表示此用户保留,将要保留的用户信息存入新列表
|
||||
// 否则不存入新列表
|
||||
if (!player.getUserId().equals(userId))
|
||||
newPlayerList.add(player);
|
||||
}
|
||||
// 将删除用户后的新 newPlayerList 赋值给 playerList
|
||||
playerList = newPlayerList;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
// 将所有玩家的等待时间 +1
|
||||
private void increaseWaitingTime() {
|
||||
for (Player player : playerList) {
|
||||
player.setWaitingTime(player.getWaitingTime() + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// 判断两名玩家是否匹配
|
||||
private boolean checkMatched(Player a, Player b) {
|
||||
int ratingDelta = Math.abs(a.getRating() - b.getRating()); // 两名玩家的天梯积分之差
|
||||
// a b 两名玩家的等待时间最小值
|
||||
// min 是两方任意一方接受等待时间就可以匹配, max 是两方都接受的匹配
|
||||
int minWaitingTime = Math.min(a.getWaitingTime(), b.getWaitingTime());
|
||||
// 匹配结果可被 a 接受: a b 的分差小于等于 a和b 最小等待时间乘以 10
|
||||
// 匹配结果可被 b 接受: a b 的分差小于等于 a和b 最小等待时间乘以 10
|
||||
return ratingDelta <= minWaitingTime * 10;
|
||||
}
|
||||
|
||||
// 返回 a 和 b 的匹配结果
|
||||
private void sendResult(Player a, Player b) {
|
||||
MultiValueMap<String, String> data = new LinkedMultiValueMap<>();
|
||||
data.add("a_id", a.getUserId().toString());
|
||||
data.add("b_id", b.getUserId().toString());
|
||||
restTemplate.postForObject(startGameUrl, data, String.class);
|
||||
}
|
||||
|
||||
// 尝试匹配所有玩家
|
||||
private void matchPlayers() {
|
||||
//TODO 后端调试
|
||||
System.out.println("match players: " + playerList.toString());
|
||||
// havaMatched 表示玩家已经匹配过了
|
||||
boolean[] haveMatched = new boolean[playerList.size()];
|
||||
for (int i = 0; i < playerList.size(); i++) {
|
||||
if (haveMatched[i]) continue; // 如果当前枚举到的玩家已经匹配过了,则跳过改玩家
|
||||
for (int j = i + 1; j < playerList.size(); j++) { // j 从 i+1 开始枚举
|
||||
if (haveMatched[j]) continue;
|
||||
// 如果 i 和 j 都没有匹配,则将 a 和 b 玩家取出来
|
||||
Player a = playerList.get(i);
|
||||
Player b = playerList.get(j);
|
||||
// 判断 a 和 b 能否匹配:如果匹配,则将结果返回,并将 a 和 b 的位置置为 true
|
||||
if (checkMatched(a, b)) {
|
||||
haveMatched[i] = haveMatched[j] = true; // 置为已匹配
|
||||
sendResult(a, b); // 返回匹配玩家结果
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 匹配成功后,需要将匹配过的玩家从匹配池移除
|
||||
List<Player> newPlayerList = new ArrayList<>();
|
||||
for (int i = 0; i < playerList.size(); i++) {
|
||||
// 如果当前枚举到的玩家没有匹配,则将该玩家存入新的 list
|
||||
if (!haveMatched[i])
|
||||
newPlayerList.add(playerList.get(i));
|
||||
}
|
||||
// 将新的 newPlayerList 重新赋值 playerList
|
||||
playerList = newPlayerList;
|
||||
}
|
||||
|
||||
// 实现线程
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
lock.lock();
|
||||
try {
|
||||
// 每隔一秒钟调用“等待时间增加”函数
|
||||
increaseWaitingTime();
|
||||
// 尝试匹配玩家
|
||||
matchPlayers();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
// 如果捕获到异常,则输出异常并终止运行
|
||||
e.printStackTrace();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// 存储玩家
|
||||
package com.kob.matchingsystem.service.impl.utils;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class Player {
|
||||
private Integer userId;
|
||||
private Integer rating;
|
||||
private Integer waitingTime; // 等待时间
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
server.port=3001
|
||||
Reference in New Issue
Block a user