LOL根据用户段位进行匹配

昨晚双12通宵值班,无聊的时候玩了几局五子棋,然后对这个匹配的功能产生了兴趣,想了想我们平时玩的LOL 5V5对战的匹配,想用自己的想法简单实现一下这个对战匹配功能。

MatchService接口

public interface MatchService {

    /**
     * 加入到对应的池中
     *
     * @param person
     */
    void join(UserMatch person);

    /**
     * 批量加入到对应的池中(拉好友一起队列)
     *
     * @param persons
     */
    void batchJoin(List<UserMatch> persons);

    /**
     * 匹配出段位相同的几个人,放到本地缓存里
     *
     * @param peopleNumber
     */
    void match(UserMatch person, int peopleNumber);

    /**
     * 查询本地缓存里的数据,返回给前端
     *
     * @param userId
     * @return
     */
    List<UserMatch> findMatch(Long userId, int peopleNumber);

    /**
     * 用户取消队列,要把这个用户从池里删除,其他已经被锁的用户继续匹配
     *
     * @param userId
     */
    void cancel(Long userId);
}

 枚举类

/**
 * 段位枚举
 */
public enum LevelEnum {

    BRONZE("bronze", "青铜"),
    SILVER("silver", "白银"),
    GOLD("gold", "黄金"),
    PLATINUM("platinum", "白金"),
    DIAMONDS("diamonds", "钻石"),
    KING("king", "王者"),
    ;

    private String code;
    private String value;

    public static LevelEnum getByCode(String code) {
        if (StringUtils.isBlank(code)) {
            return null;
        }
        for (LevelEnum level : values()) {
            if (org.apache.commons.lang3.StringUtils.equals(code, level.getCode())) {
                return level;
            }
        }
        return null;
    }

    LevelEnum(String code, String value) {
        this.code = code;
        this.value = value;
    }

    public String getCode() {
        return code;
    }

    public String getValue() {
        return value;
    }
}

UserMatch实体类

@Data
public class UserMatch {

    /**
     * 用户ID
     */
    private Long userId;

    /**
     * 段位
     */
    private LevelEnum level;

    /**
     * 是否锁住,当被用户匹配到之后则锁住,不让其他线程再来找该用户
     */
    private volatile boolean lock = false;
}

用CommonMap当做正在队列中的池

public class CommonMap  {

    public static Map<LevelEnum, List<UserMatch>>  map = new ConcurrentHashMap<>();

}

 匹配成功之后把数据存起来返回给前端,利用本地缓存LoadingCache,这里是配置LoadingCacheConfiguration

@Configuration
public class LoadingCacheConfiguration {

    private static final int EXPIRE_SECONDS = 60;

    @Bean
    public LoadingCache<Long, List<UserMatch>> myCacheStorage() {
        return CacheBuilder.newBuilder().concurrencyLevel(10).maximumSize(3000).expireAfterWrite(EXPIRE_SECONDS, TimeUnit.SECONDS)
                .build(new CacheLoader<Long, List<UserMatch>>() {
                    @Override
                    public List<UserMatch> load(Long name) throws Exception {
                        //在这里可以初始化加载数据的缓存信息,读取数据库中信息或者是加载文件中的某些数据信息
                        return null;
                    }
                });
    }
}

 实现类:MatchServiceImpl

@Service
public class MatchServiceImpl implements MatchService {

    @Resource
    private LoadingCache<Long, List<UserMatch>> cache;

    @Override
    public void join(UserMatch person) {
        if (CommonMap.map.containsKey(person.getLevel())) {
            CommonMap.map.get(person.getLevel()).add(person);
        } else {
            List<UserMatch> list = new ArrayList<>();
            list.add(person);
            CommonMap.map.put(person.getLevel(), list);
        }
    }

    @Override
    public void batchJoin(List<UserMatch> persons) {
        persons.forEach(person -> {
            join(person);
        });
    }

    /**
     * 1、多个用户可以同时去调接口匹配,所以用线程池去处理每一个请求
     * 2、当一个用户被一个线程拿到,就要在池里删除掉(用个锁标识),保证别人不会再拿到这个用户
     * 3、当池里只有一个用户,但是我需要匹配4个用户,那我就要先把符合的用户拿到,再去监听池里满足情况的用户,新加入满足的再拿过来
     * 4、当调取消接口的话,要把拿到的用户全部放在池里去,取消的用户要从池里删除
     *
     * @param person
     * @param peopleNumber
     * @return
     */
    @Override
    public void match(UserMatch person, int peopleNumber) {

        ExecutorUtils.execute(() -> {

            List<UserMatch> newUserList = new ArrayList<>();

            while (newUserList.size() < peopleNumber + 1) {

                try {
                    Thread.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                //该用户已经被其他线程匹配到
                if (person.isLock()) {
                    return;
                }

                //所有这个段位的用户
                List<UserMatch> matchList = CommonMap.map.get(person.getLevel());

                //先过滤掉目前已经锁上的用户
                matchList = matchList.stream().filter(userMatch -> !userMatch.isLock()).collect(Collectors.toList());

                if (matchList.size() == 0 || matchList == null) {
                    continue;
                }

                Random random = new Random();

                //选出peopleNumber个用户出来
                for (int i = 0; i < peopleNumber; i++) {
                    if (matchList.size() == 0 || matchList == null) {
                        continue;
                    }
                    int index = random.nextInt(matchList.size());
                    UserMatch userMatch = matchList.get(index);
                    if (userMatch.isLock()) {
                        continue;
                    }
                    if (newUserList.contains(userMatch)) {
                        userMatch.setLock(true);
                        matchList.remove(index);
                        continue;
                    }
                    userMatch.setLock(true);
                    newUserList.add(userMatch);
                    matchList.remove(index);
                    if (newUserList.size() == peopleNumber + 1) {
                        break;
                    }
                }
            }

            newUserList.forEach(newUser -> {
                cache.put(newUser.getUserId(), newUserList);
            });
        });

    }

    @Override
    public List<UserMatch> findMatch(Long userId, int peopleNumber) {
        try {
            List<UserMatch> userMatches = cache.get(userId);
            if (userMatches != null && userMatches.size() == peopleNumber + 1) {
                return userMatches;
            }
            return null;
        } catch (Exception e) {
            return new ArrayList<>(1);
        }
    }


    /**
     * 木有写,你们可以自己思考下
     * @param person
     */
    @Override
    public void cancel(UserMatch person) {

    }
}

测试controller

@RestController
public class TestMatchController {

    @Resource
    private MatchService matchService;

    @RequestMapping("/testMatch")
    public void testMatch() {

        List<UserMatch> persons = new ArrayList<>();
        UserMatch person = new UserMatch();
        person.setUserId(123L);
        person.setLevel(LevelEnum.getByCode("bronze"));
        persons.add(person);

        UserMatch person1 = new UserMatch();
        person1.setUserId(234L);
        person1.setLevel(LevelEnum.getByCode("bronze"));
        persons.add(person1);

        UserMatch person2 = new UserMatch();
        person2.setUserId(345L);
        person2.setLevel(LevelEnum.getByCode("bronze"));
        persons.add(person2);

        UserMatch person3 = new UserMatch();
        person3.setUserId(000L);
        person3.setLevel(LevelEnum.getByCode("gold"));
        persons.add(person3);

        UserMatch person4 = new UserMatch();
        person4.setUserId(111L);
        person4.setLevel(LevelEnum.getByCode("gold"));
        persons.add(person4);

        UserMatch person5 = new UserMatch();
        person5.setUserId(222L);
        person5.setLevel(LevelEnum.getByCode("gold"));
        persons.add(person5);

        matchService.batchJoin(persons);

    }

    @RequestMapping("/testMatch1")
    public void testMatch1() {

        List<UserMatch> persons = new ArrayList<>();
        UserMatch person = new UserMatch();
        person.setUserId(1231L);
        person.setLevel(LevelEnum.getByCode("bronze"));
        persons.add(person);

        UserMatch person1 = new UserMatch();
        person1.setUserId(2341L);
        person1.setLevel(LevelEnum.getByCode("bronze"));
        persons.add(person1);

        UserMatch person2 = new UserMatch();
        person2.setUserId(3451L);
        person2.setLevel(LevelEnum.getByCode("bronze"));
        persons.add(person2);

        UserMatch person3 = new UserMatch();
        person3.setUserId(0001L);
        person3.setLevel(LevelEnum.getByCode("gold"));
        persons.add(person3);

        UserMatch person4 = new UserMatch();
        person4.setUserId(1111L);
        person4.setLevel(LevelEnum.getByCode("gold"));
        persons.add(person4);

        UserMatch person5 = new UserMatch();
        person5.setUserId(2221L);
        person5.setLevel(LevelEnum.getByCode("gold"));
        persons.add(person5);

        matchService.batchJoin(persons);

    }

    @RequestMapping("/match")
    public void match(Long userId, String code) {
        UserMatch person = new UserMatch();
        person.setUserId(userId);
        person.setLevel(LevelEnum.getByCode(code));
        matchService.match(person, 4);
    }

    @RequestMapping("/find")
    public List<UserMatch> find(Long userId, int peopleNumber) {
        return matchService.findMatch(userId, peopleNumber);
    }

}

1、启动项目

2、http://localhost:8080/testMatch 先初始化几条数据进行,代码里是三个黄铜、三个黄金的用户

3、http://localhost:8080/match?userId=123&code=bronze 当用户点击 LOL里开始队列时,调join接口(这里我们用第一步的初始化数据代替了)和match接口 (你可以debug)

4、第三步的时候可以发现,我要匹配四个黄铜队友,但是第二步里初始化的池中只有3个用户,所以代码里会继续对池里的数据进行实时查询(你依然可以debug,这样观察的更明显);

5、http://localhost:8080/testMatch1 执行这个接口,再往池中新增几个刚刚点击开始队列的用户,断点f9,然后这些新用户会被上面第四步的线程读取到,再把符合条件的两个黄铜用户拉过来,凑成了五个人,放进本地缓存里。

6、http://localhost:8080/find?userId=123&peopleNumber=4 前端点击队列之后,就立即轮训该接口获取匹配到的用户。

我的测试结果为:

这个是我即兴的一次撸码,代码写的简单或不完善还请多担待,只是为了增加对事情的思考。谢谢~

发布了165 篇原创文章 · 获赞 103 · 访问量 39万+

猜你喜欢

转载自blog.csdn.net/qq_33101675/article/details/103517658