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

云服务器部署WebSocket项目

WebSocket是一种在单个TCP连接上进行全双工通信的协议,其设计的目的是在Web浏览器和Web服务器之间进行实时通信(实时Web)

WebSocket协议的优点包括:

1. 更高效的网络利用率:与HTTP相比,WebSocket的握手只需要一次,之后客户端和服务器端可以直接交换数据

2. 实时性更高:WebSocket的双向通信能够实现实时通信,无需等待客户端或服务器端的响应

3. 更少的通信量和延迟:WebSocket可以发送二进制数据,而HTTP只能发送文本数据,并且WebSocket的消息头比HTTP更小

项目内容

1.WebSocketConfig

表示这是一个配置类,可以定义 Spring Bean

Spring 会扫描该类并将其中定义的 @Bean 方法返回的对象注册到应用上下文中

@Bean 方法

serverEndpointExporter 方法用来创建并注册一个 ServerEndpointExporter 实例

ServerEndpointExporter 是 Spring 提供的一个类,用于自动注册基于 Java 标准的 WebSocket 端点(由 @ServerEndpoint 注解标注的类)

它负责将 @ServerEndpoint 注解标记的 WebSocket 类注册到容器中

ServerEndpointExporter 的作用:

当应用运行在 Spring Boot 容器中时,ServerEndpointExporter 会扫描所有带有@ServerEndpoint 注解的类,并将其注册为 WebSocket 端点,适用于嵌入式的 Servlet 容器(如 Tomcat),如果使用的是独立的 Servlet 容器(如外部的Tomcat),则不需要配置 ServerEndpointExporter

package com.qcby.chatroom1117.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;@Configuration
public class WebSocketConfig {@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}
}

2.ChatController

获取在线用户列表:

  • 调用 WebSocketServer.getWebSocketSet() 获取所有在线用户
  • 如果用户的 sid 不是 "admin",则添加到返回列表中

管理员发送消息:

  • 使用 @RequestParam 获取请求中的 sid(目标用户 ID)和 message(消息内容)
  • 调用 WebSocketServer.sendInfo 向指定用户发送消息
package com.qcby.chatroom1117.controller;import com.qcby.chatroom1117.server.WebSocketServer;
import org.springframework.web.bind.annotation.*;import java.io.IOException;
import java.util.ArrayList;
import java.util.List;@RestController
@RequestMapping("/api/chat")
public class ChatController {/*** 获取在线用户列表,不包含管理员*/@GetMapping("/online-users")public List<String> getOnlineUsers() {List<String> sidList = new ArrayList<>();for (WebSocketServer server : WebSocketServer.getWebSocketSet()) {//排除管理员if (!server.getSid().equals("admin")) {sidList.add(server.getSid());}}return sidList;}/*** 管理员发送消息给指定用户*/@PostMapping("/send")public void sendMessageToUser(@RequestParam String sid, @RequestParam String message) throws IOException {WebSocketServer.sendInfo(message, sid);}}

3.WebSocketServer

  • @OnOpen: 客户端连接时执行的操作,维护连接集合并记录用户的 sid
  • @OnClose: 客户端断开时从集合中移除,更新在线用户数
  • @OnMessage: 接收客户端消息,解析后发送到指定用户
  • sendMessage: 服务端向客户端单独发送消息
  • sendInfo: 群发或向指定客户端发送消息
  • getOnlineCount: 获取当前在线连接数
  • addOnlineCount & subOnlineCount: 管理在线人数的计数
  • @OnError: 捕获 WebSocket 连接中的异常,记录日志以便排查
package com.qcby.chatroom1117.server;import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArraySet;/*** WebSocket 服务端*/
@Component
@Slf4j
@Service
@ServerEndpoint("/api/websocket/{sid}")
public class WebSocketServer {//当前在线连接数private static int onlineCount = 0;//存放每个客户端对应的 WebSocketServer 对象private static final CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<>();//用户信息private Session session;//当前用户的 sidprivate String sid = "";//JSON解析工具private static final ObjectMapper objectMapper = new ObjectMapper();/*** 连接建立成功调用的方法*/@OnOpenpublic void onOpen(Session session, @PathParam("sid") String sid) {this.session = session;this.sid = sid;webSocketSet.add(this); //加入集合addOnlineCount(); //在线数加1try {sendMessage("conn_success");log.info("有新窗口开始监听: " + sid + ", 当前在线人数为: " + getOnlineCount());} catch (IOException e) {log.error("WebSocket IO Exception", e);}}/*** 连接关闭调用的方法*/@OnClosepublic void onClose() {webSocketSet.remove(this); //从集合中删除subOnlineCount(); //在线数减1log.info("释放的 sid 为:" + sid);log.info("有一连接关闭!当前在线人数为 " + getOnlineCount());}/*** 收到客户端消息后调用的方法*/@OnMessagepublic void onMessage(String message, Session session) {log.info("收到来自窗口 " + sid + " 的信息: " + message);//解析消息中的 targetSidString targetSid;String msgContent;try {Map<String, String> messageMap = objectMapper.readValue(message, Map.class);targetSid = messageMap.get("targetSid");msgContent = messageMap.get("message");} catch (IOException e) {log.error("消息解析失败", e);return;}//构造消息Map<String, String> responseMap = new HashMap<>();responseMap.put("sourceSid", sid);responseMap.put("message", msgContent);String jsonResponse;try {jsonResponse = objectMapper.writeValueAsString(responseMap);} catch (IOException e) {log.error("JSON 序列化失败", e);return;}//按 targetSid 发送消息for (WebSocketServer item : webSocketSet) {try {if (targetSid.equals(item.sid)) {item.sendMessage(jsonResponse);break; //找到目标用户后不再继续发送}} catch (IOException e) {log.error("消息发送失败", e);}}}/*** 判断是否是管理员*/private boolean isAdmin(String sid) {return "admin_sid".equals(sid);}/*** 发生错误时调用的方法*/@OnErrorpublic void onError(Session session, Throwable error) {log.error("发生错误", error);}/*** 实现服务器主动推送*/public void sendMessage(String message) throws IOException {this.session.getBasicRemote().sendText(message);}/*** 群发自定义消息*/public static void sendInfo(String message, @PathParam("sid") String sid) throws IOException {log.info("推送消息到窗口 " + sid + ",推送内容: " + message);for (WebSocketServer item : webSocketSet) {try {if (sid == null) {item.sendMessage(message); //推送给所有人} else if (item.sid.equals(sid)) {item.sendMessage(message); //推送给指定 sid}} catch (IOException e) {log.error("推送消息失败", e);}}}public static synchronized int getOnlineCount() {return onlineCount;}public static synchronized void addOnlineCount() {WebSocketServer.onlineCount++;}public static synchronized void subOnlineCount() {WebSocketServer.onlineCount--;}public static CopyOnWriteArraySet<WebSocketServer> getWebSocketSet() {return webSocketSet;}public String getSid() {return this.sid;}
}

 4.admin页面

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>管理员端 - 聊天窗口</title><style>* {margin: 0;padding: 0;box-sizing: border-box;}body {font-family: Arial, sans-serif;display: flex;height: 100vh;margin: 0;background-color: #f4f7fc;color: #333;}/* 左侧在线用户列表 */#onlineUsersContainer {width: 250px;padding: 20px;background-color: #fff;border-right: 1px solid #ddd;box-shadow: 2px 0 5px rgba(0, 0, 0, 0.1);overflow-y: auto;}#onlineUsers {list-style-type: none;padding: 0;margin-top: 20px;}#onlineUsers li {padding: 10px;cursor: pointer;border-radius: 5px;transition: background-color 0.3s ease;}#onlineUsers li:hover {background-color: #e9f1fe;}#onlineUsers li.selected {background-color: #d0e7fe;}/* 右侧聊天窗口 */#chatBox {flex: 1;display: flex;flex-direction: column;padding: 20px;background-color: #fff;}#messages {border: 1px solid #ddd;height: 500px;overflow-y: scroll;margin-bottom: 20px;padding: 15px;background-color: #f9f9f9;border-radius: 10px;box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.1);}.message {padding: 10px;margin: 8px 0;border-radius: 10px;max-width: 80%;line-height: 1.6;word-wrap: break-word;}.message-right {background-color: #dcf8c6;text-align: right;margin-left: auto;}.message-left {background-color: #f1f0f0;text-align: left;margin-right: auto;}#messageInput {width: 80%;padding: 12px;border-radius: 25px;border: 1px solid #ccc;margin-right: 10px;font-size: 16px;transition: border-color 0.3s ease;}#messageInput:focus {border-color: #007bff;outline: none;}button {padding: 12px 20px;border-radius: 25px;border: 1px solid #007bff;background-color: #007bff;color: white;cursor: pointer;font-size: 16px;transition: background-color 0.3s ease;}button:hover {background-color: #0056b3;}h3 {font-size: 18px;color: #333;margin-bottom: 20px;}#onlineUsers li.unread {font-weight: bold;color: red;}@media (max-width: 768px) {#onlineUsersContainer {width: 100%;padding: 15px;}#chatBox {padding: 15px;}#messageInput {width: calc(100% - 100px);}button {width: 80px;}}</style>
</head>
<body>
<div id="onlineUsersContainer"><h3>在线用户</h3><ul id="onlineUsers"></ul>
</div>
<div id="chatBox"><h3>聊天窗口</h3><div id="messages"></div><div style="display: flex;"><input id="messageInput" type="text" placeholder="请输入消息"><button onclick="sendMessage()">发送</button></div>
</div><script>let websocket;const sid = "admin";let currentUserSid = null; //当前聊天对象的sidlet chatHistory = {}; //用于存储每个用户的聊天记录//页面加载时初始化window.onload = () => {connectWebSocket();getOnlineUsers(); //页面加载时刷新在线用户列表restoreSelectedUser(); //恢复选中的用户};function connectWebSocket() {websocket = new WebSocket("ws://localhost:8080/api/websocket/" + sid);websocket.onopen = () => {console.log("连接成功,管理员ID:" + sid);};websocket.onmessage = (event) => {try {let data;if (event.data.startsWith("{") && event.data.endsWith("}")) {data = JSON.parse(event.data); // 如果是有效的 JSON 格式,进行解析} else {// 如果是无效的 JSON(比如 "conn_success" 这样的字符串),进行处理console.log("接收到非 JSON 消息:", event.data);return;}const { sourceSid, message } = data;if (sourceSid) {//初始化聊天记录存储if (!chatHistory[sourceSid]) {chatHistory[sourceSid] = [];}//存储对方的消息chatHistory[sourceSid].push({ sender: 'left', message });//如果消息来源是当前聊天对象,更新聊天窗口if (sourceSid === currentUserSid) {displayMessages();} else {//消息来源不是当前对象,提示未读消息notifyUnreadMessage(sourceSid);}}} catch (e) {console.error("解析消息失败", e);}};websocket.onclose = () => {console.log("连接关闭");};websocket.onerror = (error) => {console.error("WebSocket发生错误", error);};}function notifyUnreadMessage(userSid) {const userListItems = document.querySelectorAll("#onlineUsers li");userListItems.forEach(item => {if (item.textContent === userSid) {item.classList.add("unread"); //添加未读消息样式}});}//清除未读消息提示function clearUnreadMessage(userSid) {const userListItems = document.querySelectorAll("#onlineUsers li");userListItems.forEach(item => {if (item.textContent === userSid) {item.classList.remove("unread");}});}function sendMessage() {const message = document.getElementById("messageInput").value;if (!currentUserSid) {alert("请选择一个用户进行聊天!");return;}if (message.trim() !== "") {websocket.send(JSON.stringify({ targetSid: currentUserSid, message }));chatHistory[currentUserSid] = chatHistory[currentUserSid] || [];chatHistory[currentUserSid].push({ sender: 'right', message });document.getElementById("messageInput").value = '';displayMessages();}}//显示当前用户的聊天记录function displayMessages() {const messagesDiv = document.getElementById("messages");messagesDiv.innerHTML = "";if (currentUserSid && chatHistory[currentUserSid]) {chatHistory[currentUserSid].forEach(msg => {const messageDiv = document.createElement("div");messageDiv.classList.add("message", msg.sender === 'right' ? "message-right" : "message-left");messageDiv.textContent = msg.message;messagesDiv.appendChild(messageDiv);});}scrollToBottom();}function scrollToBottom() {const messagesDiv = document.getElementById("messages");messagesDiv.scrollTop = messagesDiv.scrollHeight;}//获取在线用户列表(不包括管理员)function getOnlineUsers() {fetch("/api/chat/online-users").then(response => response.json()).then(users => {const userList = document.getElementById("onlineUsers");userList.innerHTML = "";users.forEach(user => {if (user !== "admin") {const li = document.createElement("li");li.textContent = user;li.onclick = () => selectUser(user, li);userList.appendChild(li);}});});}//选择用户进行聊天function selectUser(user, liElement) {//清除所有选中状态const userListItems = document.querySelectorAll("#onlineUsers li");userListItems.forEach(item => item.classList.remove("selected"));//高亮显示当前选中的用户liElement.classList.add("selected");if (currentUserSid !== user) {currentUserSid = user;//清除未读消息提示clearUnreadMessage(user);//显示与该用户的聊天记录displayMessages();//保存当前选中的用户localStorage.setItem('selectedUserSid', user);}scrollToBottom();}//恢复选中的用户function restoreSelectedUser() {const savedUserSid = localStorage.getItem('selectedUserSid');if (savedUserSid) {currentUserSid = savedUserSid;const userListItems = document.querySelectorAll("#onlineUsers li");userListItems.forEach(item => {if (item.textContent === savedUserSid) {item.classList.add("selected");}});displayMessages();}}
</script>
</body>
</html>

5.user页面

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>用户端 - 聊天窗口</title><style>* {margin: 0;padding: 0;box-sizing: border-box;}body {font-family: Arial, sans-serif;background-color: #f0f4f8;display: flex;justify-content: center;align-items: center;min-height: 100vh;}#chatBox {position: fixed;bottom: 10px;right: 10px;width: 400px;height: 500px;background-color: #ffffff;border-radius: 8px;padding: 20px;box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);background: linear-gradient(to top right, #f9f9f9, #e9eff7);display: flex;flex-direction: column;max-height: 80vh;}#chatBox h3 {font-size: 20px;margin-bottom: 15px;color: #333;text-align: center;}#messages {flex: 1;border: 1px solid #ddd;padding: 15px;overflow-y: auto;background-color: #f9f9f9;border-radius: 8px;margin-bottom: 15px;font-size: 14px;color: #333;line-height: 1.5;box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.1);}.message {padding: 10px;margin: 5px 0;border-radius: 8px;max-width: 80%;word-wrap: break-word;}.message-right {background-color: #dcf8c6;text-align: right;margin-left: auto;}.message-left {background-color: #f1f0f0;text-align: left;margin-right: auto;}#inputWrapper {display: flex;width: 100%;}#messageInput {width: calc(100% - 80px);padding: 12px;border-radius: 25px;border: 1px solid #ccc;margin-right: 10px;font-size: 16px;transition: border-color 0.3s ease;}#messageInput:focus {border-color: #007bff;outline: none;}button {padding: 12px 20px;border-radius: 25px;border: 1px solid #007bff;background-color: #007bff;color: white;cursor: pointer;font-size: 16px;transition: background-color 0.3s ease;width: 60px;display: inline-flex;align-items: center;justify-content: center;}button:hover {background-color: #0056b3;}@media (max-width: 768px) {#chatBox {width: 100%;bottom: 20px;padding: 15px;}#messageInput {width: calc(100% - 100px);}button {width: 70px;padding: 10px;}}</style><script>let websocket;const sid = Math.random().toString(36).substring(2, 15); //用户端的sidconst isAdmin = false; //这是用户端,管理员标识为falsefunction connectWebSocket() {websocket = new WebSocket("ws://localhost:8080/api/websocket/" + sid);websocket.onopen = () => {console.log("连接成功,用户ID:" + sid);document.getElementById("messages").innerHTML += `<div class="message-left">连接成功,您的ID是:${sid}</div>`;};websocket.onmessage = (event) => {try {let data;// 检查消息是否是有效的 JSONif (event.data && event.data.startsWith("{")) {data = JSON.parse(event.data);const { targetSid, message, sourceSid } = data;// 确保消息是发送给当前用户的if (sourceSid === "admin" || targetSid === sid) {document.getElementById("messages").innerHTML += `<div class="message-left">${message}</div>`;scrollToBottom();}} else {// 如果不是 JSON 格式,可以直接处理其他类型的消息document.getElementById("messages").innerHTML += `<div class="message-left">${event.data}</div>`;scrollToBottom();}} catch (e) {console.error("解析消息失败", e);}};websocket.onclose = () => {console.log("连接关闭");};websocket.onerror = (error) => {console.error("WebSocket发生错误", error);};}function sendMessage() {const message = document.getElementById("messageInput").value;const targetSid = "admin"; //目标为管理员if (message.trim() !== "") {websocket.send(JSON.stringify({ targetSid, message }));document.getElementById("messages").innerHTML += `<div class="message-right">${message}</div>`;document.getElementById("messageInput").value = '';scrollToBottom();}}function scrollToBottom() {const messagesDiv = document.getElementById("messages");messagesDiv.scrollTop = messagesDiv.scrollHeight;}connectWebSocket();</script>
</head>
<body>
<div id="chatBox"><h3>用户聊天窗口</h3><div id="messages"></div><div id="inputWrapper"><input id="messageInput" type="text" placeholder="请输入消息"><button onclick="sendMessage()">发送</button></div>
</div>
</body>
</html>

项目部署

1.准备云服务器

2.在服务器上安装jdk

(1)yum install -y java-1.8.0-openjdk-devel.x86_64

(2)输入java -version查看已安装的jdk版本

3.在服务器上安装tomcat

(1)sudo wget https://archive.apache.org/dist/tomcat/tomcat-9/v9.0.75/bin/apache-tomcat-9.0.75.tar.gz

(2)解压后进入到文件目录,启动

3.修改项目

(1)修改pom文件

添加打包方式:

添加tomcat和websocket依赖:

        <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-tomcat</artifactId><scope>provided</scope> <!-- 提示该依赖已由外部服务器提供 --></dependency><dependency><groupId>javax.websocket</groupId><artifactId>javax.websocket-api</artifactId><version>1.1</version></dependency>

添加插件:

            <plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-war-plugin</artifactId><configuration><failOnMissingWebXml>false</failOnMissingWebXml></configuration></plugin>

(2)修改启动类

package com.qcby.chatroom1117;import javafx.application.Application;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;@SpringBootApplication
public class ChatRoom1117Application extends SpringBootServletInitializer {public static void main(String[] args) {SpringApplication.run(ChatRoom1117Application.class, args);}@Override  //这个表示使用外部的tomcat容器protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {// 注意这里要指向原先用main方法执行的启动类return builder.sources(ChatRoom1117Application.class);}}

(3)修改前端代码

4.打包

先执行clean,再执行install

5.上传war包到tomcat文件夹的webapp目录下

6.重新启动tomcat,访问

用户端 - 聊天窗口icon-default.png?t=O83Ahttp://47.96.252.224:8080/chatroom1117-0.0.1-SNAPSHOT/user

管理员端 - 聊天窗口icon-default.png?t=O83Ahttp://47.96.252.224:8080/chatroom1117-0.0.1-SNAPSHOT/admin

 

至此,部署完成

相关文章:

云服务器部署WebSocket项目

WebSocket是一种在单个TCP连接上进行全双工通信的协议&#xff0c;其设计的目的是在Web浏览器和Web服务器之间进行实时通信&#xff08;实时Web&#xff09; WebSocket协议的优点包括&#xff1a; 1. 更高效的网络利用率&#xff1a;与HTTP相比&#xff0c;WebSocket的握手只…...

林业产品智能推荐引擎:Spring Boot篇

1 绪论 1.1 选题背景 网络技术和计算机技术发展至今&#xff0c;已经拥有了深厚的理论基础&#xff0c;并在现实中进行了充分运用&#xff0c;尤其是基于计算机运行的软件更是受到各界的关注。计算机软件可以针对不同行业的营业特点以及管理需求&#xff0c;设置不同的功能&…...

【C++】LeetCode:LCR 077. 排序链表

题干 LCR 077. 排序链表 给定链表的头结点 head &#xff0c;请将其按 升序 排列并返回 排序后的链表 。 解法&#xff1a;归并排序 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(null…...

git教程

文章目录 简介&#xff1a;使用教程&#xff1a;&#xff08;1&#xff09;安装git&#xff1a;&#xff08;2&#xff09;设置用户名和邮箱作为标识符&#xff1a;&#xff08;3&#xff09;建立本地仓库&#xff1a;本地仓库作用&#xff1a;&#xff08;1&#xff09;将文件…...

报表工具功能对比:免费易上手的山海鲸报表 vs 庞大用户群体的Tableau

在数据报表与分析领域&#xff0c;随着大数据技术的不断发展和企业数字化转型的深入&#xff0c;市面上涌现出了众多报表工具&#xff0c;为用户提供多元化的选择。对于企业数据分析师、IT人员及管理层来说&#xff0c;选择一款适合自己的报表工具至关重要。本文将从多个角度对…...

鸿蒙原生应用开发及部署:首选华为云,开启HarmonyOS NEXT App新纪元

目录 前言 HarmonyOS NEXT&#xff1a;下一代操作系统的愿景 1、核心特性和优势 2、如何推动应用生态的发展 3、对开发者和用户的影响 华为云服务在鸿蒙原生应用开发中的作用 1、华为云ECS C系列实例 &#xff08;1&#xff09;全维度性能升级 &#xff08;2&#xff…...

CSS之3D转换

三维坐标系 三维坐标系其实就是指立体空间&#xff0c;立体空间是由3个轴共同组成的。 x轴:水平向右注意:x右边是正值&#xff0c;左边是负值 y轴:垂直向下注意:y下面是正值&#xff0c;上面是负值 z轴:垂直屏幕注意:往外面是正值&#xff0c;往里面是负值 3D移动 translat…...

uni-app初学笔记:文件路径与作用

components:可复用的组件pages:页面&#xff08;可见/不可见&#xff09;static:静态资源&#xff0c;存放图片视频等 &#xff08;相当于vue项目的 assets&#xff09;mainjs:Vue初始化入口文件App.vue:应用配置&#xff0c;用来配置App全局样式以及监听pages.json :配置页面路…...

子组件中$emit和update更新传递变量

vue2.6之后才可以使用update更新&#xff0c;vue2.6以下版本使用input和v-model 需求描述&#xff1a;蒙层上展示弹窗&#xff0c;弹窗点击关闭&#xff0c;需要向父传递关闭的信息 方法1&#xff0c;简便直接传递变量visible&#xff08;或者不改名isModalVisible也是可以的…...

浅谈Python库之lxml

一、基本介绍 lxml 是一个用 Python 编写的库&#xff0c;它提供了对 XML 和 HTML 文档的解析和操作功能。它使用 C 语言编写的 libxml2 和 libxslt 库作为后端&#xff0c;因此解析速度非常快&#xff0c;并且能够处理大型文档。lxml 支持 XPath 和 XSLT&#xff0c;这使得它在…...

spring boot框架漏洞复现

spring - java开源框架有五种 Spring MVC、SpringBoot、SpringFramework、SpringSecurity、SpringCloud spring boot版本 版本1: 直接就在根下 / 版本2:根下的必须目录 /actuator/ 端口:9093 spring boot搭建 1:直接下载源码打包 2:运行编译好的jar包:actuator-testb…...

IDEA插件CamelCase,快速转变命名格式

在IDEA上大小写转换的快捷键是 CtrlshitU 其它的格式转换的快捷键是 shitaltu 安装方法&#xff1a; file-settings-plugins-在marketplace搜索“CamelCase”-点击安装。 安装成功设置后&#xff0c;重新打开idea 下载完成后 点击 Apply 和OK 此刻就可以选中命名 并使用快捷…...

Elasticsearch中的节点(比如共20个),其中的10个选了一个master,另外10个选了另一个master,怎么办?

大家好&#xff0c;我是锋哥。今天分享关于【Elasticsearch中的节点&#xff08;比如共20个&#xff09;&#xff0c;其中的10个选了一个master&#xff0c;另外10个选了另一个master&#xff0c;怎么办&#xff1f;】面试题。希望对大家有帮助&#xff1b; Elasticsearch中的节…...

Spring Boot 集成 Knife4j 的 Swagger 文档

在开发微服务应用时&#xff0c;API 文档的生成和维护是非常重要的一环。Swagger 是一个非常流行的 API 文档工具&#xff0c;可以帮助我们自动生成 RESTful API 的文档&#xff0c;并提供了一个友好的界面供开发者测试 API。本文将介绍如何在 Spring Boot 项目中集成 Knife4j …...

C# 创建快捷方式文件和硬链接文件

C# 创建快捷方式文件和硬链接文件 引言什么是快捷方式什么是硬链接文件硬链接与快捷方式不同 实现创建快捷方式文件实现创建硬链接文件小结 引言 什么是快捷方式 平常我们最常window桌面上点击的左下角带小箭头的文件就是快捷方式了&#xff0c;大家都很熟悉它。快捷方式是Wi…...

Linux高阶——1123—服务器基础服务器设备服务器基础能力

目录 1、服务器基础 1、服务器基本概述 2、服务器设计之初解决的问题 网络穿透 网络数据设备间的收发 3、服务器的类型C/S、B/S 2、服务器设备 将自己的服务器软件部署上线 3、代理服务器负载均衡&#xff0c;以及地址绑定方式 4、服务器的基础能力 1、服务器基础 1…...

LabVIEW串口通讯速度

LabVIEW串口通讯能达到的速度 LabVIEW支持高效的串口通讯&#xff0c;通过优化设置&#xff0c;理论上可以实现每次接收一个字节时达到1ms甚至更短的周期。不过&#xff0c;实际性能会受到以下因素的限制&#xff1a; ​ 波特率&#xff08;Baud Rate&#xff09;&#xff1a;…...

Jmeter中的监听器

3&#xff09;监听器 1--查看结果树 用途 调试测试计划&#xff1a;查看每个请求的详细信息&#xff0c;帮助调试和修正测试计划。分析响应数据&#xff1a;查看服务器返回的响应数据&#xff0c;验证请求是否成功。检查错误&#xff1a;识别和分析请求失败的原因。 配置步骤…...

缺失的第一个正数(java)

题目描述&#xff1a; 给你一个未排序的整数数组 nums &#xff0c;请你找出其中没有出现的最小的正整数。 请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,0] 输出&#xff1a;3 解释&#xff1a;范围 […...

跨部门文件共享安全:平衡协作与风险的关键策略

在现代企业中&#xff0c;跨部门协作已成为推动业务发展的关键因素。然而&#xff0c;随着信息的自由流动和共享&#xff0c;文件安全风险也随之增加。如何在促进跨部门协作的同时&#xff0c;确保文件共享的安全性&#xff0c;成为了一个亟待解决的问题。 一、明确文件分类与…...

一键AI换脸软件,支持表情控制,唇形同步Facefusion-3.0.0发布!支持N卡和CPU,一键启动包

嗨,小伙伴们!还记得小编之前介绍的FaceFusion 2.6.1吗?今天给大家带来超级exciting的消息 —— FaceFusion 3.0.0闪亮登场啦! &#x1f31f; 3.0.0版本更新 &#x1f3d7;️ 全面重构:修复了不少小虫子,运行更稳定,再也不怕突然罢工啦! &#x1f600; Live Portrait功能:新增…...

我要成为算法高手-递归篇

目录 题目1&#xff1a;汉诺塔题目2&#xff1a;合并两个有序链表题目3&#xff1a;反转链表题目4&#xff1a;两两交换链表中的结点题目5&#xff1a;Pow(x,n) 题目1&#xff1a;汉诺塔 面试题 08.06. 汉诺塔问题 - 力扣&#xff08;LeetCode&#xff09; 解题思路&#xff1…...

Git 提交的相对引用

Git 提交的相对引用 在 Git 中&#xff0c;使用 ~ 和 ^ 符号可以帮助你更灵活地引用提交历史中的特定提交。以下是这些符号的具体用法和示例&#xff1a; 1. ~&#xff08;波浪号&#xff09; ~ 符号用于指向上一个或多个父提交。它总是沿着第一个父提交的链向上追溯。 HEA…...

国内首家! 阿里云人工智能平台 PAI 通过 ITU 国际标准测评

近日&#xff0c;阿里云人工智能平台 PAI 顺利通过中国信通院组织的 ITU-T AICP-GA&#xff08;Technical Specification for Artificial Intelligence Cloud Platform&#xff1a;General Architecture&#xff09;国际标准和《智算工程平台能力要求》国内标准一致性测评&…...

CDAF / PDAF 原理 | PDAF、CDAF 和 LAAF 对比 | 图像清晰度评价指标

注&#xff1a;本文为 “CDAF / PDAF 原理 | PDAF、CDAF 和 LAAF 对比 | 图像清晰度评价指标” 几篇相关文章合辑。 文章中部分超链接、图片异常受引用之前的原文所限。 相机自动对焦原理 TriumphRay 于 2020-01-16 18:59:41 发布 凸透镜成像原理 这一部分大家中学应该就学过…...

小米C++ 面试题及参考答案下(120道面试题覆盖各种类型八股文)

指针和引用的区别?怎么实现的? 指针和引用有以下一些主要区别。 从概念上来说,指针是一个变量,它存储的是另一个变量的地址。可以通过指针来间接访问所指向的变量。例如,我们定义一个整型指针int *p;,它可以指向一个整型变量的内存地址。而引用是一个别名,它必须在定义的…...

WPF异步UI交互功能的实现方法

前面的文章我们提及过&#xff0c;异步UI的基础实现。基本思路主要是开启新的UI线程&#xff0c;并通过VisualTarget将UI线程上的Visual(即RootVisual)连接到主线程上的UI上即可渲染显示。 但是&#xff0c;之前的实现访问是没有交互能力的&#xff0c;视觉树上的UI并不能实现…...

2024 java大厂面试复习总结(一)(持续更新)

10年java程序员&#xff0c;2024年正好35岁&#xff0c;2024年11月公司裁员&#xff0c;记录自己找工作时候复习的一些要点。 java基础 hashCode()与equals()的相关规定 如果两个对象相等&#xff0c;则hashcode一定也是相同的两个对象相等&#xff0c;对两个对象分别调用eq…...

TCP/IP学习笔记

TCP\IP从实际应用的五层结构开始&#xff0c;自顶而下的去分析每一层。 TCP/IP五层架构概述 学术上面是TCP/IP四层架构&#xff0c;OSI/ISO是七层架构&#xff0c;实际中使用的是TCP/IP五层架构。 数据链路层 ICMP数据包分析 Wireshark抓包分析ICMP协议_wireshark抓ping包分析…...

基于IPMI的服务器硬件监控指标解读

在现代化数据中心中&#xff0c;服务器的稳定运行对于保障业务连续性至关重要。为了实时掌握服务器的健康状况&#xff0c;运维团队需要借助高效的监控工具。监控易作为一款功能强大的监控软件&#xff0c;支持使用IPMI&#xff08;Intelligent Platform Management Interface&…...

相亲交友小程序项目介绍

一、项目背景 在当今快节奏的社会生活中&#xff0c;人们忙于工作和事业&#xff0c;社交圈子相对狭窄&#xff0c;寻找合适的恋爱对象变得愈发困难。相亲交友作为一种传统而有效的社交方式&#xff0c;在现代社会依然有着巨大的需求。我们的相亲交友项目旨在为广大单身人士提…...

Day3 洛谷Day3 1161+1179+1200+1304

零基础洛谷刷题记录 Day1 2024.11.18 Day2 2024.11.25 Day3 2024.11.26 文章目录 零基础洛谷刷题记录1161&#xff1a;题目描述1161&#xff1a;解题代码1161&#xff1a;学习成果1179&#xff1a;题目描述&#xff08;成功写出&#xff09;1179&#xff1a;解题代码1179&…...

【通俗理解】ELBO(证据下界)——机器学习中的“情感纽带”

【通俗理解】ELBO&#xff08;证据下界&#xff09;——机器学习中的“情感纽带” 关键词提炼 #ELBO #证据下界 #变分推断 #机器学习 #潜变量模型 #KL散度 #期望 #对数似然 第一节&#xff1a;ELBO的类比与核心概念【尽可能通俗】 ELBO&#xff0c;即证据下界&#xff0c;在…...

Vue: computed 计算属性

在Vue中&#xff0c;computed属性是用于计算和返回基于其他响应式数据的值的功能。 适合在模板中使用&#xff0c;因为能够根据依赖的数据自动更新。 当依赖的数据变化时&#xff0c;computed属性会重新计算&#xff0c;并且会将结果缓存&#xff0c;以提高性能。 computed的…...

【自动化Selenium】Python 网页自动化测试脚本(上)

目录 1、Selenium介绍 2、Selenium环境安装 3、创建浏览器、设置、打开 4、打开网页、关闭网页、浏览器 5、浏览器最大化、最小化 6、浏览器的打开位置、尺寸 7、浏览器截图、网页刷新 8、元素定位 9、元素交互操作 10、元素定位 &#xff08;1&#xff09;ID定位 &…...

数据库命令规范、数据库基本设计规范

所有数据库对象名称必须使用小写字母并用下划线分割 所有数据库对象名称禁止使用mysql保留关键字&#xff08;如果表名中包含关键字查询时&#xff0c;需要将其用单引号括起来&#xff09; 数据库对象的命名要能做到见名识意&#xff0c;并且最后不要超过32个字符 临时库表必…...

php常用伪协议整理

前言 欢迎来到我的博客 个人主页:北岭敲键盘的荒漠猫-CSDN博客 本文整理php常见的伪协议 php伪协议介绍 直观点&#xff0c;就是php可以识别的协议。 类似于我们访问网站的http协议&#xff0c;我们用浏览器访问我们自己本地文件的file协议等。 php可以识别这些协议&#xf…...

Redis与MySQL如何保证数据一致性

Redis与MySQL如何保证数据一致性 简单来说 该场景主要发生在读写并发进行时&#xff0c;才会发生数据不一致。 主要流程就是要么先操作缓存&#xff0c;要么先操作Redis&#xff0c;操作也分修改和删除。 一般修改要执行一系列业务代码&#xff0c;所以一般直接删除成本较低…...

NIO三大组件

现在互联网环境下&#xff0c;分布式系统大相径庭&#xff0c;而分布式系统的根基在于网络编程&#xff0c;而netty恰恰是java领域的网络编程的王者&#xff0c;如果要致力于并发高性能的服务器程序、高性能的客户端程序&#xff0c;必须掌握netty网络编程。 NIO基础 NIO是从ja…...

智能呼叫中心是什么?

智能呼叫中心是什么&#xff1f; 作者&#xff1a;开源智能呼叫中心系统 FreeIPCC&#xff0c;Github地址&#xff1a;https://github.com/lihaiya/freeipcc 智能呼叫中心是指运用人工智能、大数据分析等技术&#xff0c;对来电进行智能分析和处理的客户服务中心。以下是对智能…...

LSTM原理解读与实战

在RNN详解及其实战中&#xff0c;简单讨论了为什么需要RNN这类模型、RNN的具体思路、RNN的简单实现等问题。同时&#xff0c;在文章结尾部分我们提到了RNN存在的梯度消失问题&#xff0c;及之后的一个解决方案&#xff1a;LSTM。因此&#xff0c;本篇文章主要结构如下&#xff…...

24.11.26 神经网络 参数初始化

神经网络 感知神经网络 神经网络&#xff08;Neural Networks&#xff09;是一种模拟人脑神经元网络结构的计算模型&#xff0c;用于处理复杂的模式识别、分类和预测等任务 生物学&#xff1a; 人脑可以看做是一个生物神经网络&#xff0c;由众多的神经元连接而成 树突&#…...

51单片机从入门到精通:理论与实践指南(一)

单片机在智能控制领域的应用已非常普遍&#xff0c;发展也很迅猛&#xff0c;学习和使用单片机的人员越来越多。虽然新型微控制器在不断推出&#xff0c;但51单片机价格低廉、易学易用、性能成熟&#xff0c;在家电和工业控制中有一定的应用&#xff0c;而且学好了51单片机&…...

wordpress获取文章总数、分类总数、tag总数等

在制作wordpress模板的时候会要调用网站的文章总数分类总数tag总数等这个数值&#xff0c;如果直接用count查询数据库那就太过分了。好在wordpress内置了一些标签可以直接获取到这些数值&#xff0c;本文整理了一些常用的wordpress网站总数标签。 文章总数 <?php $count_…...

Tcon技术和Tconless技术介绍

文章目录 TCON技术&#xff08;传统时序控制器&#xff09;定义&#xff1a;主要功能&#xff1a;优点&#xff1a;缺点&#xff1a; TCONless技术&#xff08;无独立时序控制器&#xff09;定义&#xff1a;工作原理&#xff1a;优点&#xff1a;缺点&#xff1a; TCON与TCONl…...

WinFrom调用webapi接口另一个方法及其应用实例

1.调用接口方法 代码如下&#xff1a; public class WebAPI{#region WebAPI调用 public async Task<string> Call_Webapi(string Url, string Json) //url传入的是接口名称&#xff0c;json传入的是接口参数{string responseBody string.Empty; //responseBod…...

上海乐鑫科技一级代理商飞睿科技,ESP32-C61高性价比WiFi6芯片高性能、大容量

在当今快速发展的物联网市场中&#xff0c;无线连接技术的不断进步对智能设备的性能和能效提出了更高要求。为了满足这一需求&#xff0c;乐鑫科技推出了ESP32-C61——一款高性价比的Wi-Fi 6芯片&#xff0c;旨在为用户设备提供更出色的物联网性能&#xff0c;并满足智能设备连…...

【机器学习】数据集合集!

本文将为您介绍经典、热门的数据集&#xff0c;希望对您在选择适合的数据集时有所帮助。 1 privacy 更新时间&#xff1a;2024-11-26 访问地址: GitHub 描述&#xff1a; 此存储库包含 TensorFlow Privacy&#xff08;一种 Python&#xff09;的源代码 库&#xff0c;其中包…...

GitLab使用操作v1.0

1.前置条件 Gitlab 项目地址&#xff1a;http://******/req Gitlab账户信息&#xff1a;例如 001/******自己的分支名称&#xff1a;例如 001-master&#xff08;注&#xff1a;master只有项目创建者有权限更新&#xff0c;我们只能更新自己分支&#xff0c;然后创建合并请求&…...

企业后端多租户管理平台

1 简介 此系统在企业后端管理系统上进行的更改&#xff0c;用于快速开发租户管理平台。项目中详细的功能请查看文章&#xff1a;企业后端系统通用模版_后端模板-CSDN博客 支持多租户&#xff0c;支持多租户切换&#xff0c;支持多租户数据隔离&#xff0c;支持多租户数据同步等…...