AcWing
  • 首页
  • 活动
  • 题库
  • 竞赛
  • 应用
  • 其它
    • 题解
    • 分享
    • 商店
    • 问答
  • 吐槽
  • 登录/注册

sp-0125-6-3

作者: 作者的头像   02的Darling吖 ,  2023-01-26 00:55:10 ,  所有人可见 ,  阅读 29


0


注意

  • StratGameService StratGameServiceImpl接口实现 stratGameService 驼峰可以很方便的自动补全

  • Interger.parseInt(data.getFirst(“a_id”))这里明显变黄 根据提示更改点击就可以了

报错

  • 注解没有加

调试方法

  • 为了方便调试 把一个人的天梯分改大一点

  • 修改完数据库但是数据并没有更新 可能是因为有缓存 重启一下就可以了


查看异常 user.get

查询ctrl+f 查询所有users.get的操作 之前都需要加上条件

ctrl+shift+f全局搜

这里针对所有websocket里的users.getid的操作都要加 因为不知道什么时候断开
每次查询都需要判断是否为空


如果一名玩家没有发送退出请求就直接关闭进程
发现玩家不存在时 通知另一名 并不再向此名玩家发送请求
如果发送 就会爆异常 正在匹配 刷新一下 但是人还在匹配持里
在websocket里边 如果断开 查询时就会为空 空的属性没有game的属性
此时就会报异常

ctrl+shift+t恢复关掉的页面


知识点

component
需要加入 autowired就需要加入component


调试

开始匹配发现三个错误

Thread.sleep()缺少Thread
int j=i+1;写成j=0;

报错

403 forbiden 没有封尾
.antMatchers("/pk/start/game/").hasIpAddress("127.0.0.1")
NullPointerException
单击错误信息 
restTemplate可能是空的原因
由于没有加上注解@Component
SpringBoot没有找到Bean树 成功设置respTemplate不为空

微服务调用后端

需要RestTemplate 把config复制过来
注意 这里的Template复制 这里直接复制所有代码

然后在微服务config这里右键粘贴 这里会自动补全路径

MathcingPool里边 注入
private static RestTemplate restTemplate;
@Autowired
public void setRestTemplate(RestTemplate restTemplate){MatchingPool.restTemplate=restTemplate;}

发送

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);

后端接受

写一个方法接受到消息
写一个方法的话 需要写一个service 一个serviceimpl(这里一个注解service) 一个controller
service 下新建pk(StartGameService) service->impl->新建Pk(serviceimpl)
在后端 consumer中的websocket已经有了一个startgame函数 这里改为public static

@Autowired
private StratGameService stratGameService; 注意命名规则
@PostMapping("/pk/start/game")
public String startGame(@RequestParam MultiValueMap<String,String> data){
    Integer aId=Integer.parseInt(Objects.requireNonNull(data.getFirst("a_id")));
    Integer bId=Integer.parseInt(Objects.requireNonNull(data.getFirst("b_id")));
    return stratGameService.startGame(aId,bId);
}

写好controller 放行接口给微服务
.antMatchers("/pk/start/game").hasIpAddress("127.0.0.1")这里增加的一句 后面有ip限制


匹配机制

优先匹配等待时间长的
防止用户流失 越往后加入的用户 越新 越往前的玩家越旧
所以应该从前往后枚举每一名玩家

匹配条件判定

应该是能被a接受 也能被b接受min

两人分差小于等待时间乘10 等待时间最小值乘10

如果使用max 则说明至少一方有需求就匹配

int ratingDelta=Math.abs(a.getRating()-b.getRating());
int waitingTime=Math.min(a.getWaitingTime(),b.getWaitingTime());

return ratingDelta<=waitingTime*10;

sleep(1000)//点击使用trycatch环绕
如果报异常catch写e.printStackTrace();

sleep(1000);
lock.unlock();
try{
    increaseWaitingTime();
    matchPlayers();
}finally {
    lock.unlock();//如果不解锁 就死循环了
}

线程的逻辑
run里边写什么
逻辑是周期性的执行 每次执行都看看玩家有没有执行 如果匹配成功
就移除匹配池然后返回 如果没有匹配 就会一直等待
写一个死循环 然后写一个sleep函数 每一秒执行一遍函数
如果能配队 就配对 不能配队继续等待 每名玩家每等待一秒 就在
waitingtime+1 他未来匹配的阈值可以变宽


MatchingPool实现
存放用户

private static List<Player>  players=new ArrayList<>();
这里我们不考虑线程安全不安全 我们会自己手动加锁 把他变成安全的

异步是不是顺序执行的 我切回一个线程执行 然后再切到另外一边执行
同步顺序 加上锁 一个是加入玩家的线程 一个是匹配
需要加锁

lock.lock();
try{players.add(new Player(userId,rating,0));
}finally {
    lock.unlock();
}

删除不太好删除
先创建一个列表 把没有删除的加进去

List<Player> newPlayer=new ArrayList<>();
for(Player player:players){
    if(!player.getUserId().equals(userId)){
        newPlayer.add(player);
    }
}
players=newPlayer;

注意

需要使用lambok才能使用注解
player

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Player {
    private Integer userId;
    private Integer rating;
    private Integer waitingTime;//等待时间
}

MatchignPool继承自Thread
需要重写run方法
放到service里边 全局只有一个线程
所以定义成静态的
public final static MatchingPool matchingPool=new MatchingPool();什么时候启动呢
可以选择一个喜欢的地方启动 在sp启动的时候启动
MatchingServiceImpl.matchingPool.start();在项目启动之前启动 放在主函数之前


impl下新建utils存取相关
Player
MatchingPool


进行到前端发送一个请求 从socket里边 然后发送给matchingSystem
我们是在matchingSystem里边获取的

收到所有用户之后 放到一个池子里边 开一个新线程 每隔一秒钟扫描一遍数组 将能够匹配的人匹配到一起
匹配两名分值比较接近的玩家 随着时间越来越大 允许玩家之间的分值相差越来越大


发送给后端

private final static String addPlayerUrl="http://127.0.0.1:3001/player/add/";
private final static String removePlayerUrl="http://127.0.0.1:3001/player/remove/";
先把url定义成常量 
System.out.println("start matching");
MultiValueMap<String,String> data=new LinkedMultiValueMap<>();
data.add("user_id",this.user.getId().toString());//注意这里的id需要转化
data.add("rating",this.user.getRating().toString());
restTemplate.postForObject(addPlayerUrl,data,String.class);//这里的String.class是返回值的类
java的反射机制

remove

private void stopMatching(){
System.out.println("stop matching");
MultiValueMap<String,String> data=new LinkedMultiValueMap<>();
data.add("user_id",this.user.getId().toString());
restTemplate.postForObject(addPlayerUrl,data,String.class);

}

运行一下本进程 出现enable 点一下就可以 构建的后端在服务里边


这里的rating是放在bot身上还是用户身上
应该是都是可以的

这里放在用户身上 通过不同修改bot获取自身id

重新连接数据库
root+密码 kob 连接 连接成功后刷新出kob
添加rating一列 默认值1500 刷新一下
bot删除rating一列 点击rating后 上方 - 号 减掉 刷新一下
旧的ui

然后修改pojo bot移动到user

然后修改一下构造函数 在新建user时实例user

在service的impl(接口实现) register接口

bot也改一下 修改bot时 将rating去掉


先定义一个静态变量

private static RestTemplate restTemplate;    
@Autowired
public void setRestTemplate(RestTemplate restTemplate){WebSocketServer.restTemplate=restTemplate};
//这个自动执行 

原理
如果我们加了Autowired的话 就会看看这个service接口是不是有一个唯一的加了注解Bean的函数
和他对应 如果有 就调用一下这个函数 把返回值赋值过来


打通匹配服务和后端

comsumer下的websocket创建函数 startGame把startMatching后的东西都剪切过来

把matchpoo相关的都删除

springboot向后端发请求

如果希望在当前的component使用Service需要 先把他加一个注解bean
在config下新建类 RestTemplateConfig类 加入注解@Configuration
想取得谁 加一个Bean注解

@Configuration
public class RestTemplateConfig {
    @Bean
    public RestTemplate getRestTemplate(){
        return  new RestTemplate();//可以让两个springboot之间进行通信
    }
}

未来使用它的时候 就加一个@Autowired就可以


添加后端进入总项目

在backendcloud下新建项目 backend 高级设置kob.backend
创建 删除src (这里我的没有同步 可能有问题) 把上次那个src复制过来
粘贴到idea项目树的刚刚那个位置 然后把原来的pom打开 把dependency全部粘贴过来到backend的Pom
删除没有用的依赖thymeleaf 点开maven重新加载一下 如果在构建->同步出现对号接成功了


运行项目

将main函数改名为MatchingSystemlApplication

@SpringBootApplication
SpringApplication.run(MatchingSystemApplication.class,args);
函数体运行代码

此时提示 在服务工具窗口管理多个SpringBoot运行 点击使用服务

访问端口号3001 发现是可以访问的 WhitelableErrorPage

访问player/add/报错405 player/remove/爆403

这里是因为仅仅放行了add 但是并没有放行remove


MatchingServier按理说只能接受后端
的请求 不能接受浏览器的请求 如果可以 有可能通过这个链接攻击服务器

加上权限控制 添加依赖spring-secrity 复制以前的代码

config复制SecurityConfig的 删除jwt相关 留下configure函数

删除http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
去除无关引入变灰得得引用项

.antMatchers("/player/add/").hasIpAddress("127.0.0.1")只允许本地访问


实现接口
加入注解@service 继承接口MatchingSystem alt+insert 实现方法 输出调试信息
sout user+rating return "addPalyerSuccess" 具体逻辑先不实现

创建controller

controller下创建类MatchingController添加注解@RestController 注入接口@Authwired
private MatchingService matchingservice 因为涉及到数据的修改 所以用Post
@PostMapping("/player/add/")
public String addPlayer(@RequestParam MultiValueMap<String,String> data)两个点 一个requestParam
一个Multivaluemap

Integer userId=Integer.parseInt(data.getFirst("user_id"));注意这里爆黄色感叹号 悬浮点击选项即可
Integer rating=Integer.parseInt(data.getFirst("rating"));
return matchingService.addPlayer(userId,rating);
MultiValue是一个关键字对应一个列表value

新建接口

创建controller service软件包
service下有impl接口的实现 service定义接口类 MatchingSystem在impl下实现


配置新项目

删除src目录 pom desciption下加入标签 <packaging>pom</packaging>

加入springcloud依赖 创建子项目 backendcloud创建模块 新建模块(空项目) matchingsystem

java,maven,1.8 高级设置加入组id.matchingsystem

添加git? 取消 不再询问 matchingsystem本质上也是一个springboot 也是需要添加依赖的

所以复制springcloud下的依赖 剪切出来<dependencies>下所有标签 放到子目录的pom里边

复制完之后点一下刷新 同步成功就可以了 占用一个端口 配置

在resources下新建文件application.properties->server.port=3001web是3000


匹配系统的逻辑
client 发送请求给 servier(websocket和http controller) 本来使用微服务实现matching system 成功匹配后
返回结果给servier 开一个线程给game ->map 返回前端->每次读取玩家操作(也可以是bot) ->没有输入就超时了->
操作合法 继续读取操作

本节课实现微服务 matchingSystem 后来bot1和Bot2的执行也是微服务 微服务是一个独立的程序
(理解为新开了一个springBoot) 会向matchingSystem发送http请求 Ms接收到后开一个单独的线程
(每隔一秒钟扫描一下所有玩家 判断一下这些玩家能不能相互匹配出来) 成功就返回http 这里实现
用的是spring cloud网关 注册 负载 均衡 (并发性不大)都不会遇到 显得不那么的SpringCloud

spring的微服务是使用http来实现的 结果返回是传一些url 传一些参数回来(Django是用thift通信的
使用普通的socket协议稍微快一些) 玩家特别多要分配服务器

springboot共有两个子项目 一个后端一个匹配系统

新建项目 spring项目 名称:backendcloud 组 com.kob jdk1.8java8 加入springweb依赖

可以使用start.aliyun.com


redis带了解


sp-0125-6-3 微服务

客户端向服务器两端通信 生成游戏地图

表头

Idea操 作 指 南:https://www.acwing.com/blog/content/25456/

每次都要点击的网址:https://www.acwing.com/blog/content/28250/

G i t B a s h : https://www.acwing.com/blog/content/22768/

WindowsIdea :https://www.acwing.com/blog/content/23868/

本节课讲义https://www.acwing.com/file_system/file/content/whole/index/content/6325603/

0 评论

你确定删除吗?
1024
x

© 2018-2023 AcWing 版权所有  |  京ICP备17053197号-1
用户协议  |  常见问题  |  联系我们
AcWing
请输入登录信息
更多登录方式: 微信图标 qq图标
请输入绑定的邮箱地址
请输入注册信息