当前位置: 首页 > wzjs >正文

wordpress 数据库查询插件厦门seo推广

wordpress 数据库查询插件,厦门seo推广,外贸营销型网站建站,杭州市建设银行网站文章目录 处理开始匹配/停止匹配请求匹配算法实现匹配器(1)完善匹配器的 TODO 实现匹配器(2)实现 handlerMatch线程安全忙等问题 处理开始匹配/停止匹配请求 实现 handleTestMessage 先从会话中拿到当前玩家的信息解析客户端发来的请求判定请求的类型 如果是 startMatch&…

文章目录

  • 处理开始匹配/停止匹配请求
  • 匹配算法
  • 实现匹配器(1)
    • 完善匹配器的 TODO
  • 实现匹配器(2)
    • 实现 handlerMatch
      • 线程安全
      • 忙等问题

处理开始匹配/停止匹配请求

实现 handleTestMessage

  • 先从会话中拿到当前玩家的信息
  • 解析客户端发来的请求
  • 判定请求的类型
    • 如果是 startMatch,则把用户加入到匹配队列
    • 如果是 stopMatch,则把用户对象从匹配队列中删除
  • 此处需要实现一个匹配器对象,来处理匹配的实际逻辑
@Override  
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {  // 实现处理开始匹配请求和处理停止匹配请求  User user = (User) session.getAttributes().get("user");  // 获取到客户端给服务器发送的数据  String payload = message.getPayload();  // 当前这个数据载荷是一个 JSON 格式的字符串,就需要把它转换成 Java 对象,MatchRequest  MatchRequest request = objectMapper.readValue(payload, MatchRequest.class);  MatchResponse response = new MatchResponse();  if (response.getMessage().equals("startMatch")) {  // 进入匹配队列  // TODO 先创建一个类,来表示匹配队列,把当前用户给加进去  // 把玩家信息放入匹配队列之后,就可以返回一个响应给客户端了  response.setOk(true);  response.setMessage("startMatch");  } else if (response.getMessage().equals("stopMatch")){  // 退出匹配队列  // TODO 先创建一个类表示匹配队列,把当前用户从队列中移除  // 移除之后,就可以返回一个响应给客户端了  response.setOk(true);  response.setMessage("stopMatch");  } else {  // 非法情况  response.setOk(false);  response.setReason("非法的匹配请求!");  }  
}

匹配算法

目标:从待匹配的玩家中,选出分数尽量相近的玩家

把整个所有的玩家,按照分数,划分为三类:

  1. Normal: socre < 2000
  2. High: score >= 2000 && score < 3000
  3. VeryHigh: score >= 3000
    给这三个等级,分配三个不同的队列。根据当前玩家的分数,来把这个玩家的用户信息,放到对应的队列里面

接下来再搞一个专门的线程,去不停地扫描这个匹配队列。只要队列里面的元素 (匹配的玩家) 凑成了一对,就把这对玩家取出来,放到一个游戏房间中

  • 当前的匹配实现,比较粗糙,只是简单的搞了三个段位的队列
  • 如果想要匹配的更加精确,就可以多搞几个队列

实现匹配器(1)

创建 game.Matcher

  • Matcher 中创建三个队列(按上面分类)
  • 提供 add 方法,供 MatchAPI 类来调用,用来把玩家加入匹配队列
  • 提供 remove 方法,供 MatchAPI 类来调用,用来把玩家移出匹配队列
  • 同时 Matcher 找那个要记录 OnlineUserManager,来获取到玩家的 session
package org.example.java_gobang.game;  import org.example.java_gobang.model.User;  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.stereotype.Component;  import java.util.LinkedList;  
import java.util.Queue;  // 这个类表示”匹配器“,这个类负责完成整个匹配功能  
@Component  
public class Matcher {  // 创建三个匹配队列  private Queue<User> normalQueue = new LinkedList<>();  private Queue<User> highQUeue = new LinkedList<>();  private Queue<User> veryHighQueue = new LinkedList<>();  // 将用户在线状态这个管理器引入,方便我们后续随时能够得到相应玩家的会话信息,也可以判断用户的在线状态  @Autowired  private OnlineUserManager onlineUserManager;  // 操作匹配队列的方法:  // 把玩家放到匹配队列中  public void  add(User user) {  if (user.getScore() < 2000) {  normalQueue.offer(user);  System.out.println("把玩家 " + user.getUsername() + " 加入到了 normalQueue 中!");  } else if (user.getScore() >= 2000 && user.getScore() < 3000) {  highQUeue.offer(user);  System.out.println("把玩家 " + user.getUsername() + " 加入到了 highQueue 中!");  }else {  veryHighQueue.offer(user);  System.out.println("把玩家 " + user.getUsername() + " 加入到了 veryHighQueue 中!");  }  }  // 当玩家点击停止匹配的时候,就需要把玩家从匹配队列中删除  public void remove(User user) {  if (user.getScore() < 2000) {  normalQueue.remove(user);  System.out.println("把玩家 " + user.getUsername() + " 从 normalQueue 中删除了!");  } else if (user.getScore() >= 2000 && user.getScore() < 3000) {  highQUeue.remove(user);  System.out.println("把玩家 " + user.getUsername() + " 从 highQueue 中删除了!");  }else {  veryHighQueue.remove(user);  System.out.println("把玩家 " + user.getUsername() + " 从 veryHighQueue 中删除了!");  }  }  
}

完善匹配器的 TODO

MatchAPI 类中创建 Matcher 对象

@Autowired  
private Matcher matcher;

然后完善 TODO 部分的逻辑
image.png|333

  • 上面的红框改为:macher.add(user)
  • 下面的红框改为:macher.remove(user)

针对连接异常和连接关闭的两个方法,我们也要进行相应处理

  • 当在匹配的时候,突然连接关闭/断开了,相应的匹配就要停止了
  • 停止匹配逻辑为:matcher.remove(user); image.png|324

实现匹配器(2)

修改 game.Matcher,实现匹配逻辑

Matcher 的构造方法中,创建一个线程,使用该线程扫描每个队列,把每个队列的头两个元素取出来,匹配到一组中

// 匿名内部内的方式,继承 Thread 类  
public Matcher() {  // 创建三个线程,分别针对这三个匹配队列,进行操作  Thread t1 = new Thread() {  @Override  public void run() {  // 扫描 normalQueue            while (true) {  handlerMatch(normalQueue);  }  }  };  t1.start();  Thread t2 = new Thread() {  @Override  public void run() {  // 扫描 highQueue            while (true) {  handlerMatch(highQUeue);  }  }  };  t2.start();  Thread t3 = new Thread() {  @Override  public void run() {  // 扫描 veryHighQueue            while (true) {  handlerMatch(veryHighQueue);  }  }  };
}

实现 handlerMatch

private void handlerMatch(Queue<User> matchQueue) {  try {  // 1. 检测队列中元素个数是否达到 2        if (matchQueue.size() < 2) {  return;  }  // 2. 尝试从队列中取出两个玩家  User player1 = matchQueue.poll();  User player2 = matchQueue.poll();  System.out.println("匹配两个玩家:" + player1.getUsername() + ", " + player2.getUsername());  // 3. 获取到玩家的 websocket 的会话(目的是为了告诉玩家,你排到了)  WebSocketSession session1 = onlineUserManager.getFromGameHall(player1.getUserId());  WebSocketSession session2 = onlineUserManager.getFromGameHall(player2.getUserId());  // 理论上来说,匹配队列中的玩家一定是在线的状态  // 因为前面的逻辑中进行了处理,当玩家断开连接的时候就把玩家从匹配队列中移除了  // 但是此处仍然要进行一次判定(更稳妥)  if (session1 == null) {  // 如果玩家1 现在不在线了,就把玩家2 重新放回匹配队列中  matchQueue.offer(player2);  return;  }  if (session2 == null) {  // 如果玩家2 现在不在线了,就把玩家1 重新放回匹配队列中  matchQueue.offer(player1);  return;  }  // 当前能否排到两个玩家是同一个用户的情况吗?一个玩家入队列了两次  // 理论上也不存在  // 1) 如果玩家下线,就会将玩家移出匹配队列  // 2) 又禁止了玩家多开  // 但是仍然在这里多进行一次判定,以免前面的逻辑出现 bug 的时候带来严重的后果  if (session1 == session2) {  // 把其中一个玩家放回匹配队列  matchQueue.offer(player1);  return;  }  // 4. TODO 把这两个玩家放到一个游戏房间中  // 5. 给玩家反馈信息:你匹配到对手了  //    通过 websocket 返回一个 message 为 ‘matchSuccess’ 这样的响应  //    此时是要给两个玩家都返回”匹配成功“这样的信息,所以要返回两次  MatchResponse response1 = new MatchResponse();  response1.setOk(true);  response1.setMessage("matchSuccess");  String json1 = objectMapper.writeValueAsString(response1);  session1.sendMessage(new TextMessage(json1));  MatchResponse response2 = new MatchResponse();  response2.setOk(true);  response2.setMessage("matchSuccess");  String json2 = objectMapper.writeValueAsString(response2);  session1.sendMessage(new TextMessage(json2));  }catch (IOException e) {  e.printStackTrace();  }  
}

线程安全

我们主要涉及到要操作 normalQueuehighQueueveryHighQueue 这三个队列,而这三个队列本身就是在多个线程中进行的

  • 使用到多线程代码的时候,一定要时刻注意“线程安全”问题

此处我们使用 synchornized 进行加锁

  • 指定一个“锁对象”
  • 到底针对谁进行加锁
  • 只有多个线程在尝试针对同一个锁对象进行加锁的时候,才会有互斥效果
  • 此处进行加锁的时候,要明确
    • 如果多个线程访问的是不同的队列,就不会涉及到线程安全问题
    • 必须是多个线程操作同一个队列,才需要加锁
  • 因此在加锁的时候选取的锁对象,就是 normalQueuehighQueueveryHighQueue 这三个队列对象本身

image.png image.png

  • addremove 里面的这些对队列的操作进行加锁操作image.png|325

还要把 Macher 类中的 handlerMatch 这个方法全部加锁image.png

忙等问题

如果当前匹配队列中,只有一个元素,或者没有元素,会出现什么效果?
image.png

  • 在这个代码中,就会出现 handlerMatch 已进入方法就快速返回,然后再次进入方法…
  • 循环速度飞快,但是却没有什么实质的意义,这个过程中 CPU 占用率会非常高
  • 这个就是我们说的——忙等

如何解决?

  1. 在调用完 handlerMatch 之后,加上个 sleep(500) 合理吗

这个方法可以,但是不是很完美

  • 当有玩家匹配到了之后,可能要 500ms 之后才能真正得到匹配的返回结果
  • 通过 sleep 是难以两全其美的,要么就得让玩家多等,要么就得让 CPU 多转
  1. 这里我们更好地选择就是使用 wait/notify
  • 在扫描线程中,使用 wait 来等待
  • 当真正有玩家进入匹配队列之后,就调用 notify 来唤醒

添加 waitnotify
image.png

  • handlerMatch 方法里面,当元素未达到 2 的时候,让其进行等待
  • 当三个队列里面任意一个队列有元素加进来了,就进行通知

队列的初始情况可能是 0,如果往队列中添加一个元素,这个时候,让然是不能进行后续的匹配操作,因此在 handlerMatch 方法里面,元素未达到 2 的时候,使用 while 循环检查时更合理的image.png|368

http://www.dtcms.com/wzjs/279994.html

相关文章:

  • 网站怎么放到服务器seo项目分析
  • 建设旅游网站的市场分析网站推广优化的原因
  • 绍兴网络推广公司福州百度网站排名优化
  • 西宁市建设网站价格低莱芜seo
  • 青岛网站模板建站合肥关键词排名工具
  • 网站自身seo优化怎么做人力资源和社会保障部
  • 网站开发时间进度有哪些免费推广网站
  • 商城手机网站建设百度推广平台
  • 福州婚庆网站建设哪个公司比较专业湖南企业网站建设
  • 网站建设客服成都网络推广公司
  • 钦州网站建推广方案如何写
  • 做电影种子下载网站违法吗公司企业员工培训
  • 音乐网站建设策划书智慧教育
  • app营销策略模板关键词快速优化排名软件
  • 网站后台怎样登陆什么软件比百度搜索好
  • 设计宝藏资源站福州百度分公司
  • 企业做网站需要注意什么问题徐州关键词优化平台
  • 九度互联网站建设以图搜图百度识图
  • 怎么用自己的服务器做网站什么叫做网络营销
  • 西安做网站的公司在哪怎么查询百度收录情况
  • 河北斯皮尔网站建设广告联盟有哪些平台
  • 深圳市建设注册中心网站磁力链搜索引擎入口
  • 1688网站建设与维护杭州百度seo优化
  • 建设网站应该加什么服务器百度seo多少钱一个月
  • 做网站需要哪些人站内推广有哪些具体方式
  • 南昌网站建设网络营销和传统营销的区别有哪些
  • 电子化业务管理与网站建设友情链接软件
  • 网站模板系统提高百度快速排名
  • 大连网站建设哪里好博客网站登录入口
  • 外海赌博如何做网站的推广买链接