项目实战——匹配系统(下)

news2025/7/19 19:11:33

目录

一、整体梳理

二、创建SpringCloud目录

三、创建子项目

四、实现接口

五、config网关配置

六、放行API

七、封装后端逻辑

八、对接匹配系统

九、修改数据库

十、实现匹配之后的逻辑

十一、线程锁 

十二、匹配函数


上节课知识梳理:

用户浏览器打开之后、两个client表示两个浏览器
Client向云端server发送请求、有http的servercontroller
还有ws的server、前端client会向websocket发请求
发完请求之后、本来应该用微服务实现匹配系统
向matchingsystem发送请求、表示我们加入了一个玩家
matchingsystem匹配完之后向server返回一个结果
当两名玩家匹配完之后、他们就会开一个线程
就会开始在云端执行一次游戏、Game第一步先去创建一个地图
创建完地图之后我们会把地图返回给前端、在前端画出来
通过ws返回、下一步读入两个玩家的操作
也可以用bot来执行、不一定非从前端读进来
如果没有得到某个玩家的输入可以直接结束返回结果
如果两名玩家的下一步操作都获取到的话我们就judge一下
判断一下操作是不是合法、如果不合法的话就结束了
如果合法的话无限循环到下一步

一、整体梳理

微服务
未来bot的执行也是一个微服务、微服务:其实就是这边会有一个独立的程序可以认为
又起了一个新的webserver,又起了一个新的springboot,两个springboot
当我们获取到两名玩家的匹配信息之后、它会向我们的matchingserver后端 发送一个http请求
当我们接收到这个请求之后类似与game它也会开一个单独的线程来做一个匹配
Matching、这个Matching的过程类似的游戏的过程、每隔一秒钟去扫描当前已有的所有玩家
然后判断一下这些玩家能不能相互之间匹配出来、如果可以匹配的话就把结果返回
返回的结果也是通过http、这个就称为一个微服务!我们微服务用的是springcloud
对比Django课微服务实现方式、 我们用的是通过http进行操作两边都是webserver
thrift用的是普通的socket来通信、其他的地方没有任何区别

每个服务是一个独立的程序,每个服务之间可以相互通信。此项目中,BackEnd 和 MatchSystem都是一个微服务!
当BackEnd接收到玩家开始匹配的请求,BackEnd会想MatchSystem发送http请求,通知MatchSystem开始匹配,
MatchSystem会单独开一个线程开始匹配,匹配逻辑为:每秒都会扫描匹配池中的所有玩家,判断玩家能否相互匹配,若能,则发送http请求给BackEnd。此项目使用的微服务技术是Spring Cloud。Spring Cloud技术涉及的很多不只是服务间的相关通信,还包括网关、负载均衡等优化配置,由于项目并发量没那么大,因此未使用这些技术。

二、创建SpringCloud目录

首先我们先创建一个SpringCloud的目录
整个项目的结果会做一个变化
需要创建一个新的父项目用来装两个并列的子项目
加入springweb依赖,这个微服务其实就是两个单独的服务
它可以接收信息也可以发送信息,spring里面我们是用http操作的
也就是创建了两个后端服务器都是通过url来通信的、thrift是通过socket来通信的
会稍微快一些、创建完之后我们需要先配置一下
由于父级项目是没有逻辑的我们可以把src目录删掉、删完之后我们修改一下pom
另外加上springcloud的依赖

 Spring Cloud和Thrift的区别:
Spring Cloud:每个服务都是一个Server,每个Server相互通信使用的是http协议。
Thrift:用Thrift生成的微服务,BackEnd与服务之间的通信为Socket协议,比http快一些。

backendcloud/pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.kob</groupId>
    <artifactId>backendcloud</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>backendcloud</name>
    <description>backendcloud</description>
    <!-- 修改处1 -->
    <packaging>pom</packaging>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- 修改处2 -->
        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-dependencies -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>2021.0.3</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <!-- 修改处3 -->
                <version>2.7.2</version>
            </plugin>
        </plugins>
    </build>

</project>

三、创建子项目

 两个模块,我们需要匹配一些依赖
matchingsystem本质上也是一个springboot、我们需要将springboot的一些依赖加进去
迁移一下Springweb、然后我们点开我们的src然后我们来实现一下我们的匹配系统
匹配系统中我们需要实现几个接口呢、我们需要实现两个接口
第一个是添加一个玩家addplayer、第二个是removeplayer
有两个springboot每一个springboot都需要占用一个独立的端口
我们需要先把matchingsystem这个服务器配置一下端口、接下里java里创建两个接口

配置端口
在matchingsystem -> src -> main -> resources右键新建文件, 文件名为:application.properties:

server.port=3001

四、实现接口

接下来在java里创建两个接口
创建接口的话我们就需要创建一个controller
定义两个接口、然后我们实现这两个接口、然后我们创建一个controller、注入接口
因为涉及到对玩家的修改、所以用post请求
MultiValueMap、是每一个关键字对应一个列表value、在这里需要加上一个权限控制

matchingsystem/service/MatchingService.java:

package com.kob.matchingsystem.service;

public interface MatchingService {

    String addPlayer(Integer userId, Integer rating);

    String removePlayer(Integer userId);
}

matchingsystem/service/impl/MatchingServiceImpl.java:

package com.kob.matchingsystem.service.impl;

import com.kob.matchingsystem.service.MatchingService;
import org.springframework.stereotype.Service;

@Service
public class MatchingServiceImpl implements MatchingService {

    @Override
    public String addPlayer(Integer userId, Integer rating) {
        System.out.println("Add Player: " + userId + " " + rating);
        return "add player success";
    }

    @Override
    public String removePlayer(Integer userId) {
        System.out.println("Remove Player: " + userId);
        return "Remove player success";
    }
}

 matchingsystem/controller/MatchingController.java:

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;

    // 参数不能使用普通map,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);
    }
}

五、config网关配置

加上依赖
需要对链接加一个权限设置,只能被我们的后端服务器访问
可以通过ip地址来判断、只允许本地来访问,
写完之后写一下SpringBoot的入口
一个软件中可以使用多个springboot项目
add报405我们应该是post类型但是我们写的是get类型所以访问不了报405
remove是403是因为我们将add放行了,并没有将我们的remove链接放行
所以我们的权限是起到作用了

matchingsystem/config/SecurityConfig.java:

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;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @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();
    }
}

 matchingsystem/MatchSystemApplication.java:

package com.kob.matchingsystem;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class MatchSystemApplication {
    public static void main(String[] args) {
        SpringApplication.run(MatchSystemApplication.class, args);
    }
}

六、放行API

 这样我们的matchingserver的两个api就写完了
写完之后我们还需要对接一下我们前面的服务、我们还需要创建一个新的子项目
把我们前面的后端逻辑装起来

七、封装后端逻辑

我们还需要创建一个新的子项目
把我们前面的后端逻辑装起来,删掉src
把上节课的src复制过来、pom的话也把之前的pom复制过来即可

封装StartGame逻辑:

backend/consumer/WebSocketServer.java:

package com.kob.backend.consumer;

// 1.删除以下两个包
import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArraySet;

@Component
// url链接:ws://127.0.0.1:3000/websocket/**
@ServerEndpoint("/websocket/{token}")  // 注意不要以'/'结尾
public class WebSocketServer {
    ...

    // 2.删除matchingPool变量

    ...

    @OnClose
    public void onClose() {
        // 关闭链接
        System.out.println("disconnected!");
        if(this.user != null) {
            users.remove(this.user.getId());
            // 3. 删除取消匹配逻辑
        }
    }

    // 4.抽取匹配成功后的逻辑为一个函数
    public static void startGame(Integer aId, Integer bId) {
        User a = userMapper.selectById(aId), b = userMapper.selectById(bId);
        Game game = new Game(13, 14, 20, a.getId(), b.getId());
        game.createMap();
        // 一局游戏一个线程,会执行game类的run方法
        game.start();

        users.get(a.getId()).game = game;
        users.get(b .getId()).game = game;

        JSONObject respGame = new JSONObject();
        // 玩家的id以及横纵信息
        respGame.put("a_id", game.getPlayerA().getId());
        respGame.put("a_sx", game.getPlayerA().getSx());
        respGame.put("a_sy", game.getPlayerA().getSy());
        respGame.put("b_id", game.getPlayerB().getId());
        respGame.put("b_sx", game.getPlayerB().getSx());
        respGame.put("b_sy", game.getPlayerB().getSy());
        respGame.put("map", game.getG());

        // 发送给A的信息
        JSONObject respA = new JSONObject();
        respA.put("event", "start-matching");
        respA.put("opponent_username", b.getUsername());
        respA.put("opponent_photo", b.getPhoto());
        respA.put("game", respGame);
        // 通过userId取出a的连接,给A发送respA
        users.get(a.getId()).sendMessage(respA.toJSONString());

        // 发送给B的信息
        JSONObject respB = new JSONObject();
        respB.put("event", "start-matching");
        respB.put("opponent_username", a.getUsername());
        respB.put("opponent_photo", a.getPhoto());
        respB.put("game", respGame);
        // 通过userId取出b的连接,给B发送respB
        users.get(b.getId()).sendMessage(respB.toJSONString());
    }

    // 5.删除原先匹配逻辑,把匹配的逻辑交给另一个服务,服务成功后再调用startGame函数开始游戏逻辑
    private void startMatching() {
        System.out.println("start matching!");
    }

    // 6.删除取消匹配逻辑
    private void stopMatching() {
        System.out.println("stop matching");
    }

    ...
}

 

 

八、对接匹配系统

我们需要让我们前面的链接对接到我们的匹配系统
需要打通这一步、我们之前实现了一个傻瓜式的匹配
只要有两个人就会匹配到一起、首先为了方便我们可以把匹配的逻辑封装成函数
把mathingpool所有的相关操作放到我们的匹配系统中、开始的时候我们应该在我们的
matchingserver发送我们的请求、表示我们传一个玩家过去
向后端发请求SpringBoot也有一个工具、如果我们需要在当前的component用到service的话
就把它先加个注解bean、想取得谁加一个bean注解然后获取就可以了
当我们需要用到某个东西的时候、就定一个它的configuration
加一个注解bean、返回它的实例然后就可以了
定义完之后、未来我们用它的时候就可以加一个Autowired就可以自动注入进来
加了Autowired的话
他就会去看一下他就会去看一下这个service这个接口是不是有唯一的加了注解bean的函数和它对应
如果有的话就调用一下这个函数把它的返回赋过来
RestTemplate:是一个工具可以来两个进程间进行通信
我们这里需要向后端发送一个请求

 

九、修改数据库

天梯积分应该放到user还是bot上?
其实都行习惯把天梯分放到个人身上、把rating从bot拿到user里面
在pojo里改一下配置\还有service层里改一下

backend/pojo/User.java:

package com.kob.backend.pojo;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    @TableId(type = IdType.AUTO)
    private Integer id;
    private String username;
    private String password;
    private String photo;
    private Integer rating;
}

 backend/pojo/Bot.java::


package com.kob.backend.pojo;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Bot {
    @TableId(type = IdType.AUTO)
    private Integer id;
    private Integer userId;
    private String title;
    private String description;
    private String content;
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone="Asia/Shanghai")
    private Date createtime;
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone="Asia/Shanghai")
    private Date modifytime;
}

 backend/service/impl/user/account/RegisterServiceImpl.java:

package com.kob.backend.service.impl.user.account;

...

@Service
public class RegisterServiceImpl implements RegisterService {
    ...

    @Override
    public Map<String, String> register(String username, String password, String confirmedPassword) {
        ...

        // 修改处:增加rating为1500的分数
        User user = new User(null, username, encodedPassword, photo, 1500);

        ...
    }
}

 backend/service/impl/user/bot/AddServiceImpl.java:

package com.kob.backend.service.impl.user.bot;

...

@Service
public class AddServiceImpl implements AddService {
    @Autowired
    private BotMapper botMapper;

    @Override
    public Map<String, String> add(Map<String, String> data) {
        ...

        Bot bot = new Bot(null, user.getId(), title, description, content, now, now);

        ...
    }
}

 

 

十、实现匹配之后的逻辑

当前已经实现浏览器端向云端发送了一个请求
我们在websocket当中接收到这个请求之后,它会把我们的请求再发送给我们的匹配系统
我们刚才看到的输出是在匹配系统中看到的输出、一个添加一个取消两个操作
下一步就是要实现我们匹配之后的逻辑了、收到请求之后的逻辑
接收到一个请求之后会将当前匹配的所有用户放到一个池子
然后我们开一个额外的新线程、是每隔一秒去扫描一遍数组
将能够匹配的人匹配到一起、匹配的时候、匹配两名分值比较相近的玩家
随着时间的推移、两名玩家允许的分差可以越来越大可能一开始a和b分值比较大
这两个人是不能匹配到一块的,但是随着时间推移我们发现没有其他玩家可以跟这两名玩家匹配
我们就可以逐步放大两名玩家的一个匹配范围、直到两名玩家都可以匹配到一块为止
具体就是、每位玩家有一个分值rating
第0秒的时候这个玩家只能匹配分差在十以内的玩家、第二秒的时候就可以匹配分差在20以内的玩家

创建一个类来存储玩家
匹配池是多线程类要继承自Thread
MatchingPool里面要把所有用户存起来

加上BackEnd发送给matchingsystem服务的添加玩家和删除玩家的请求:

backend/consumer/WebSocketServer.java:


package com.kob.backend.consumer;

...

import org.springframework.web.client.RestTemplate;
import org.springframework.util.MultiValueMap;
import org.springframework.util.LinkedMultiValueMap;

@Component
// url链接:ws://127.0.0.1:3000/websocket/**
@ServerEndpoint("/websocket/{token}")  // 注意不要以'/'结尾
public class WebSocketServer {
    private static RestTemplate restTemplate;
    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/";

    ...

    @Autowired
    public void setRestTemplate(RestTemplate restTemplate) {
        WebSocketServer.restTemplate = restTemplate;
    }

    private void startMatching() {
        System.out.println("start matching!");
        MultiValueMap<String, String> data = new LinkedMultiValueMap<>();
        data.add("user_id", this.user.getId().toString());
        data.add("rating", this.user.getRating().toString());
        restTemplate.postForObject(addPlayerUrl, data, String.class);
    }

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

    ...
}

 

 

十一、线程锁 

这里不需要用线程安全的列表
因为这里会手动加锁、不管线程安全不安全
我们会把它变成安全、异步就是不是按照顺序执行的
会切换线程、顺序是乱的、我们需要两个线程范围我们的player
一个是匹配线程一个是我们传入参数的线程
这是我们添加一名玩家以及删除一名玩家

 

十二、匹配函数

我们需要写几个匹配函数
如何去匹配玩家呢?
我们可以开一个bool数组用来判断当前还剩下哪些人、从前往后去枚举 每一个玩家
对于这个玩家我们需要去看一下哪个玩家和他匹配、如果有人可以和他匹配
我们就不匹配了、越老的玩家越具有优先选择权
分差能被ab接受\我们只需要判断我们的分差能不能被a和b等待时间的最小值*10就可以了

Player.java:

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

 matchingsystem/service/impl/MatchingServiceImpl.java:

package com.kob.matchingsystem.service.impl;

...

import com.kob.matchingsystem.service.impl.utils.MatchingPool;

@Service
public class MatchingServiceImpl implements MatchingService {
    public static final 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";
    }
}

 

 我们需要是调用sendresult函数的话
已经在匹配池匹配好了、server里需要能接收这个消息
写一个方法能够接收这个信息、写完之后对接口放行
就可以在sendresult中调用、用到RestTemplate

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/35989.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

天津专升本报名时的报名点

2023年天津专升本报名区县及应届生、往届生报名点 天津市在校大三应届生报名点选自己的专科院校&#xff0c;往届生及在外省就读的应届生选户籍所在区招办。 退役士兵从天津入伍的选择学校作为报名点&#xff0c;外省市入伍的选择区招办。

slambook2(ch2)—— Ubuntu20.04 使用cmake + make自动化编译过程

slambook2&#xff08;ch2&#xff09;—— Ubuntu20.04 使用cmake make自动化编译过程主函数库函数libhello.cpplibhello.hCMakeLists.txt编译重新编译vim加行号主函数 main.cpp 库函数 libhello.cpp libhello.h CMakeLists.txt 编译 mkdir build && cd build c…

ASEMI肖特基二极管MBR30100CT特征,MBR30100CT应用

编辑-Z ASEMI肖特基二极管MBR30100CT参数&#xff1a; 型号&#xff1a;MBR30100CT 最大重复峰值反向电压&#xff08;VRRM&#xff09;&#xff1a;100V 最大平均正向整流输出电流&#xff08;IF&#xff09;&#xff1a;30A 峰值正向浪涌电流&#xff08;IFSM&#xff0…

通达信l1l2行情接口-十档行情有哪些优势?

据提供系统或用户编制的条件选股公式进行选股选定一个条件选股公式或多个组合条件后&#xff0c;计算机自动帮您选出当时或历史上某一段时间内满足条件的所有股票十档行情 英文&#xff0c;列在行情下载显示窗口&#xff0c;同时可保留成板块。 那通达信l1l2行情接口-十档行情…

map容器(20221125)

一、map/multimap容器 1、map基本概念 map中所有元素都是pair&#xff1b; pair第一个元素为key&#xff08;键值&#xff09;,起到索引的作用&#xff0c;第二个元素为value(实值)&#xff1b; 所有元素会根据元素的键值&#xff08;key&#xff09;自动排序。 map/multi…

论文阅读笔记 | 三维目标检测——PartA2算法

如有错误&#xff0c;恳请指出。 文章目录0. 前言与补充知识1. 背景2. 相关工作3. 网络结构3.1 Part-aware stageAnchor-free Proposal GenerationAnchor-based Proposal GenerationDiscussion Two Proposal Generation Strategies3.2 Part-aggregation stageROI-aware feature…

ORB-SLAM2 ---- Frame::ComputeBoW函数(TrackReferenceKeyFrame调用版)

目录 1.函数作用 2.什么是BowVec和FeatVec 3.代码 3.1 Frame::ComputeBoW解释 3.2 transform主函数&#xff1a;将一幅图像所有的特征点转化为BowVector和FeatureVector 3.3 transform&#xff1a;将描述子转化为Word id&#xff0c; Word weight&#xff0c;节点所属的…

[毕业设计]机器学习水域检测标注算法

前言 &#x1f4c5;大四是整个大学期间最忙碌的时光,一边要忙着备考或实习为毕业后面临的就业升学做准备,一边要为毕业设计耗费大量精力。近几年各个学校要求的毕设项目越来越难,有不少课题是研究生级别难度的,对本科同学来说是充满挑战。为帮助大家顺利通过和节省时间与精力投…

uniapp里接入lottie-miniprogram详细指南

包工头&#xff1a;小张啊&#xff0c;我们页面里那几个gif也太low了&#xff0c;你能不能追求远大点。ui妹子&#xff1a;软件推荐可以用lottie实现。我&#xff1a;这玩意我耍过&#xff0c;交给我了。 牛逼已经吹出去了&#xff0c;开干&#xff0c; 遇到问题有&#xff0…

基于MxNet实现目标检测-YoloV3【附部分源码及模型】

文章目录前言目标检测发展史及意义一、数据集的准备1.标注工具的安装2.数据集的准备3.标注数据4.解释xml文件的内容二、网络结构的介绍三、代码实现0.工程目录结构如下1.导入库2.配置GPU/CPU环境3.数据加载器4.模型构建YoloV3-tinyYoloV35.模型训练1.学习率设置2.优化器设置3.损…

XSS绕过安全狗WAF

今天继续给大家介绍渗透测试相关知识&#xff0c;本文主要内容是XSS绕过安全狗WAF。 一、测试环境搭建 我们使用Vmware虚拟机搭建靶场环境。在Vmware虚拟机上&#xff0c;安装有PHPStudy&#xff0c;如下所示&#xff1a; 然后安装安全狗WAF&#xff0c;安全狗WAF有一系列的…

深度学习入门(五十二)计算机视觉——风格迁移

深度学习入门&#xff08;五十二&#xff09;计算机视觉——风格迁移前言计算机视觉——风格迁移课件样式迁移易于CNN的样式迁移教材1 方法2 阅读内容和风格图像3 预处理和后处理4 抽取图像特征5 定义损失函数5.1 内容损失5.2 风格损失5.3 全变分损失5.4 损失函数6 初始化合成图…

【瑞萨RA4M2】开发环境搭建和点灯指南

【瑞萨RA4系列开发板体验】开发环境搭建和新手点灯指南 文章目录【瑞萨RA4系列开发板体验】开发环境搭建和新手点灯指南一、简单开箱二、芯片简介三、开发环境搭建2.1 安装FSP(RASC)2.2 安装Keil MDK2.3 安装RA4M2 Keil Pack2.4 安装RFP(瑞萨烧录工具)三、新手点灯指南3.1 创建…

hoops编程指南:04.4用户交互突出显示

user interaction highlighting 1.突出显示 在执行选择之后&#xff0c;通常需要向用户提供关于所选内容的视觉反馈。例如&#xff0c;场景可能包含由多个几何体表示的飞机机翼的图片。然而&#xff0c;HOOPS Visualize对飞机机翼这一独特概念一无所知。因此&#xff0c;如果…

ES6 入门教程 28 异步遍历器 28.1 同步遍历器的问题 28.2 异步遍历的接口 28.3 for await...of

ES6 入门教程 ECMAScript 6 入门 作者&#xff1a;阮一峰 本文仅用于学习记录&#xff0c;不存在任何商业用途&#xff0c;如侵删 文章目录ES6 入门教程28 异步遍历器28.1 同步遍历器的问题28.2 异步遍历的接口28.3 for await...of28 异步遍历器 28.1 同步遍历器的问题 Itera…

【教学类-16-02】20221125《世界杯七巧板A4整页-随机参考图七巧板 3份一页》(大班)

效果展示&#xff1a; 单页效果 多页效果 预设样式&#xff1a; 背景需求&#xff1a; 2022年11月24日&#xff0c;大1班随机抽取的9位幼儿制作了9张拼图&#xff0c;发现以下三个问题&#xff1a; 1、粉红色辅助纸选择量多——9份作业有4位幼儿的七巧板人物是粉红色的 2、…

【计算机网络】以太网供电PoE - Power over Ethernet

.5BG? ?: J^ ~P YG: ~5PY^ 5&Y^ .#&J. 7&G7^. ~##?. :Y##PY?!~^:... .5#Y^ .7P&&&##BBBBB#B^ …

神经网络和深度学习-均方误差Mean Square Error

均方误差Mean Square Error 测量预测值Ŷ与某些真实值匹配程度。MSE 通常用作回归问题的损失函数。 由单个样本训练损失来推导出整个训练集的MSE MSE1n∑i1n(Yi−Y^i)2\mathrm{MSE}\frac{1}{n} \sum_{i1}^{n}\left(Y_{i}-\hat{Y}_{i}\right)^{2} MSEn1​i1∑n​(Yi​−Y^i​)…

02. Docker安装记录卸载

notice: 本文所有内容参考文档&#xff0c;具体没有任何价值 Linux&#xff08;CentOS 7 &#xff09; 1. 安装 查看系统信息&#xff1a; # 系统版本是3.0以上的&#xff1b; [rootVM-8-4-centos /]# uname -r 3.10.0-1160.76.1.el7.x86_64 [rootVM-8-4-centos /]# cat /et…

nrComm Lib组件以及串行通信任务的类

nrComm Lib组件以及串行通信任务的类 nrCommLib被描述为VCL例程的一组Delphi组件以及串行通信任务的类。该库能够帮助用户和开发人员访问不同的设备&#xff0c;包括数据和语音调制解调器、条形码扫描仪、蓝牙、人机接口设备、串行端口、USB、GSM、GPS、LPT SS等。它能够为几乎…