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

Kafka在Vue和Spring Boot中的使用实例

Kafka在Vue和Spring Boot中的使用实例

一、项目概述

本项目演示了如何在Vue前端和Spring Boot后端中集成Kafka,实现实时消息的发送和接收,以及数据的实时展示。
后端实现:springboot配置、kafka配置、消息模型和仓库、消息服务和消费者、websocket配置、REST api控制器
前端实现:vue项目创建、websocket客户端配置、api服务、消息聊天组件、统计图表组件、主页面和路由配置

1.1 功能特点

  • 前端实时发送消息到Kafka
  • 后端接收Kafka消息并处理
  • 后端发送消息到Kafka
  • 前端实时接收并展示Kafka消息
  • 消息历史记录展示
  • 消息统计图表展示

1.2 技术栈

  • 前端:Vue 3 + Element Plus + ECharts
  • 后端:Spring Boot 2.7.x + Spring Kafka
  • 消息中间件:Apache Kafka
  • 数据库:MySQL (存储消息历史)

二、环境准备

2.1 安装Kafka

# 下载Kafka
wget https://downloads.apache.org/kafka/3.5.1/kafka_2.13-3.5.1.tgz# 解压
tar -xzf kafka_2.13-3.5.1.tgz# 启动Zookeeper
bin/zookeeper-server-start.sh config/zookeeper.properties# 启动Kafka
bin/kafka-server-start.sh config/server.properties

2.2 创建Topic

# 创建消息主题
bin/kafka-topics.sh --create --bootstrap-server localhost:9092 \--replication-factor 1 --partitions 3 --topic chat-messages# 创建通知主题
bin/kafka-topics.sh --create --bootstrap-server localhost:9092 \--replication-factor 1 --partitions 3 --topic notifications

三、后端实现

3.1 添加依赖

<!-- pom.xml -->
<dependencies><!-- Spring Boot Starter --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- Spring Kafka --><dependency><groupId>org.springframework.kafka</groupId><artifactId>spring-kafka</artifactId></dependency><!-- MySQL --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><!-- Lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency>
</dependencies>

3.2 配置Kafka

# application.yml
spring:kafka:bootstrap-servers: localhost:9092producer:key-serializer: org.apache.kafka.common.serialization.StringSerializervalue-serializer: org.springframework.kafka.support.serializer.JsonSerializerconsumer:group-id: chat-groupauto-offset-reset: earliestkey-deserializer: org.apache.kafka.common.serialization.StringDeserializervalue-deserializer: org.springframework.kafka.support.serializer.JsonDeserializerproperties:spring.json.trusted.packages: "com.example.kafkademo.model"datasource:url: jdbc:mysql://localhost:3306/kafka_demo?useSSL=false&serverTimezone=UTCusername: rootpassword: passworddriver-class-name: com.mysql.cj.jdbc.Driverjpa:hibernate:ddl-auto: updateshow-sql: true

3.3 创建消息模型

// Message.java
package com.example.kafkademo.model;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import javax.persistence.*;
import java.time.LocalDateTime;@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "messages")
public class Message {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String sender;private String content;private LocalDateTime timestamp;@PrePersistpublic void prePersist() {if (timestamp == null) {timestamp = LocalDateTime.now();}}
}

3.4 创建消息仓库

// MessageRepository.java
package com.example.kafkademo.repository;import com.example.kafkademo.model.Message;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;import java.util.List;public interface MessageRepository extends JpaRepository<Message, Long> {List<Message> findTop50ByOrderByTimestampDesc();@Query("SELECT m.sender, COUNT(m) FROM Message m GROUP BY m.sender")List<Object[]> countMessagesBySender();
}

3.5 创建Kafka配置类

// KafkaConfig.java
package com.example.kafkademo.config;import com.example.kafkademo.model.Message;
import org.apache.kafka.clients.admin.AdminClientConfig;
import org.apache.kafka.clients.admin.NewTopic;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
import org.springframework.kafka.core.*;
import org.springframework.kafka.support.serializer.JsonDeserializer;
import org.springframework.kafka.support.serializer.JsonSerializer;import java.util.HashMap;
import java.util.Map;@Configuration
public class KafkaConfig {@Value("${spring.kafka.bootstrap-servers}")private String bootstrapServers;@Beanpublic KafkaAdmin kafkaAdmin() {Map<String, Object> configs = new HashMap<>();configs.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);return new KafkaAdmin(configs);}@Beanpublic NewTopic chatTopic() {return new NewTopic("chat-messages", 3, (short) 1);}@Beanpublic NewTopic notificationTopic() {return new NewTopic("notifications", 3, (short) 1);}@Beanpublic ProducerFactory<String, Message> producerFactory() {Map<String, Object> configProps = new HashMap<>();configProps.put(org.apache.kafka.clients.producer.ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);configProps.put(org.apache.kafka.clients.producer.ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, org.apache.kafka.common.serialization.StringSerializer.class);configProps.put(org.apache.kafka.clients.producer.ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class);return new DefaultKafkaProducerFactory<>(configProps);}@Beanpublic KafkaTemplate<String, Message> kafkaTemplate() {return new KafkaTemplate<>(producerFactory());}@Beanpublic ConsumerFactory<String, Message> consumerFactory() {Map<String, Object> props = new HashMap<>();props.put(org.apache.kafka.clients.consumer.ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);props.put(org.apache.kafka.clients.consumer.ConsumerConfig.GROUP_ID_CONFIG, "chat-group");props.put(org.apache.kafka.clients.consumer.ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, org.apache.kafka.common.serialization.StringDeserializer.class);props.put(org.apache.kafka.clients.consumer.ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, JsonDeserializer.class);props.put(JsonDeserializer.TRUSTED_PACKAGES, "com.example.kafkademo.model");return new DefaultKafkaConsumerFactory<>(props, new org.apache.kafka.common.serialization.StringDeserializer(),new JsonDeserializer<>(Message.class));}@Beanpublic ConcurrentKafkaListenerContainerFactory<String, Message> kafkaListenerContainerFactory() {ConcurrentKafkaListenerContainerFactory<String, Message> factory = new ConcurrentKafkaListenerContainerFactory<>();factory.setConsumerFactory(consumerFactory());return factory;}
}

3.6 创建消息服务

// MessageService.java
package com.example.kafkademo.service;import com.example.kafkademo.model.Message;
import com.example.kafkademo.repository.MessageRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;import java.util.List;@Service
@RequiredArgsConstructor
public class MessageService {private final KafkaTemplate<String, Message> kafkaTemplate;private final MessageRepository messageRepository;public void sendMessage(Message message) {// 发送消息到KafkakafkaTemplate.send("chat-messages", message);// 保存消息到数据库messageRepository.save(message);}public void sendNotification(String content) {Message notification = new Message();notification.setSender("系统");notification.setContent(content);kafkaTemplate.send("notifications", notification);}public List<Message> getRecentMessages() {return messageRepository.findTop50ByOrderByTimestampDesc();}public List<Object[]> getMessageStats() {return messageRepository.countMessagesBySender();}
}

3.7 创建Kafka消费者

// KafkaConsumer.java
package com.example.kafkademo.kafka;import com.example.kafkademo.model.Message;
import lombok.extern.slf4j.Slf4j;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Component;@Component
@Slf4j
public class KafkaConsumer {private final SimpMessagingTemplate messagingTemplate;public KafkaConsumer(SimpMessagingTemplate messagingTemplate) {this.messagingTemplate = messagingTemplate;}@KafkaListener(topics = "chat-messages", groupId = "chat-group")public void listenChatMessages(Message message) {log.info("收到聊天消息: {}", message);// 通过WebSocket发送消息到前端messagingTemplate.convertAndSend("/topic/messages", message);}@KafkaListener(topics = "notifications", groupId = "chat-group")public void listenNotifications(Message message) {log.info("收到通知: {}", message);// 通过WebSocket发送通知到前端messagingTemplate.convertAndSend("/topic/notifications", message);}
}

3.8 创建WebSocket配置

// WebSocketConfig.java
package com.example.kafkademo.config;import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {@Overridepublic void configureMessageBroker(MessageBrokerRegistry config) {config.enableSimpleBroker("/topic");config.setApplicationDestinationPrefixes("/app");}@Overridepublic void registerStompEndpoints(StompEndpointRegistry registry) {registry.addEndpoint("/ws").setAllowedOrigins("http://localhost:8080").withSockJS();}
}

3.9 创建控制器

// MessageController.java
package com.example.kafkademo.controller;import com.example.kafkademo.model.Message;
import com.example.kafkademo.service.MessageService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;import java.util.List;@RestController
@RequestMapping("/api/messages")
@RequiredArgsConstructor
@CrossOrigin(origins = "http://localhost:8080")
public class MessageController {private final MessageService messageService;@PostMappingpublic ResponseEntity<Message> sendMessage(@RequestBody Message message) {messageService.sendMessage(message);return ResponseEntity.ok(message);}@GetMappingpublic ResponseEntity<List<Message>> getRecentMessages() {return ResponseEntity.ok(messageService.getRecentMessages());}@GetMapping("/stats")public ResponseEntity<List<Object[]>> getMessageStats() {return ResponseEntity.ok(messageService.getMessageStats());}
}

四、前端实现

4.1 创建Vue项目

# 创建Vue项目
npm create vue@latest kafka-vue-demo# 进入项目目录
cd kafka-vue-demo# 安装依赖
npm install# 安装额外依赖
npm install element-plus axios sockjs-client @stomp/stompjs echarts

4.2 配置WebSocket客户端

// src/utils/websocket.js
import SockJS from 'sockjs-client';
import { Stomp } from '@stomp/stompjs';class WebSocketClient {constructor() {this.stompClient = null;this.connected = false;}connect() {const socket = new SockJS('http://localhost:8081/ws');this.stompClient = Stomp.over(socket);this.stompClient.connect({}, this.onConnected, this.onError);}onConnected = () => {this.connected = true;console.log('WebSocket连接成功');// 订阅消息主题this.stompClient.subscribe('/topic/messages', this.onMessageReceived);this.stompClient.subscribe('/topic/notifications', this.onNotificationReceived);}onError = (error) => {console.error('WebSocket连接错误:', error);this.connected = false;}onMessageReceived = (payload) => {const message = JSON.parse(payload.body);console.log('收到消息:', message);// 触发消息接收事件window.dispatchEvent(new CustomEvent('messageReceived', { detail: message }));}onNotificationReceived = (payload) => {const notification = JSON.parse(payload.body);console.log('收到通知:', notification);// 触发通知接收事件window.dispatchEvent(new CustomEvent('notificationReceived', { detail: notification }));}disconnect() {if (this.stompClient) {this.stompClient.disconnect();this.connected = false;}}
}export default new WebSocketClient();

4.3 创建API服务

// src/api/messageApi.js
import axios from 'axios';const API_URL = 'http://localhost:8081/api';export default {sendMessage(message) {return axios.post(`${API_URL}/messages`, message);},getRecentMessages() {return axios.get(`${API_URL}/messages`);},getMessageStats() {return axios.get(`${API_URL}/messages/stats`);}
};

4.4 创建消息组件

<!-- src/components/MessageChat.vue -->
<template><div class="message-chat"><el-card class="chat-container"><template #header><div class="card-header"><h2>实时聊天</h2></div></template><div class="message-list" ref="messageList"><div v-for="message in messages" :key="message.id" class="message-item" :class="{ 'message-self': message.sender === username }"><div class="message-sender">{{ message.sender }}</div><div class="message-content">{{ message.content }}</div><div class="message-time">{{ formatTime(message.timestamp) }}</div></div></div><div class="message-input"><el-inputv-model="newMessage"placeholder="输入消息..."@keyup.enter="sendMessage"><template #append><el-button type="primary" @click="sendMessage">发送</el-button></template></el-input></div></el-card></div>
</template><script>
import { ref, onMounted, onUnmounted, nextTick } from 'vue';
import { ElMessage } from 'element-plus';
import messageApi from '../api/messageApi';
import websocket from '../utils/websocket';export default {name: 'MessageChat',props: {username: {type: String,required: true}},setup(props) {const messages = ref([]);const newMessage = ref('');const messageList = ref(null);// 加载历史消息const loadMessages = async () => {try {const response = await messageApi.getRecentMessages();messages.value = response.data;scrollToBottom();} catch (error) {console.error('加载消息失败:', error);ElMessage.error('加载消息失败');}};// 发送消息const sendMessage = async () => {if (!newMessage.value.trim()) return;const message = {sender: props.username,content: newMessage.value,timestamp: new Date()};try {await messageApi.sendMessage(message);newMessage.value = '';} catch (error) {console.error('发送消息失败:', error);ElMessage.error('发送消息失败');}};// 处理接收到的消息const handleMessageReceived = (event) => {const message = event.detail;messages.value.push(message);scrollToBottom();};// 处理接收到的通知const handleNotificationReceived = (event) => {const notification = event.detail;ElMessage.info(notification.content);};// 滚动到底部const scrollToBottom = async () => {await nextTick();if (messageList.value) {messageList.value.scrollTop = messageList.value.scrollHeight;}};// 格式化时间const formatTime = (timestamp) => {if (!timestamp) return '';const date = new Date(timestamp);return date.toLocaleTimeString();};onMounted(() => {loadMessages();websocket.connect();window.addEventListener('messageReceived', handleMessageReceived);window.addEventListener('notificationReceived', handleNotificationReceived);});onUnmounted(() => {websocket.disconnect();window.removeEventListener('messageReceived', handleMessageReceived);window.removeEventListener('notificationReceived', handleNotificationReceived);});return {messages,newMessage,messageList,sendMessage,formatTime};}
};
</script><style scoped>
.message-chat {height: 100%;display: flex;flex-direction: column;
}.chat-container {height: 100%;display: flex;flex-direction: column;
}.card-header {display: flex;justify-content: space-between;align-items: center;
}.message-list {flex: 1;overflow-y: auto;padding: 10px;margin-bottom: 10px;height: 400px;
}.message-item {margin-bottom: 10px;padding: 10px;border-radius: 5px;background-color: #f5f7fa;max-width: 80%;
}.message-self {margin-left: auto;background-color: #ecf5ff;
}.message-sender {font-weight: bold;margin-bottom: 5px;
}.message-time {font-size: 12px;color: #909399;text-align: right;margin-top: 5px;
}.message-input {margin-top: 10px;
}
</style>

4.5 创建统计图表组件

<!-- src/components/MessageStats.vue -->
<template><div class="message-stats"><el-card><template #header><div class="card-header"><h2>消息统计</h2></div></template><div class="chart-container" ref="chartContainer"></div></el-card></div>
</template><script>
import { ref, onMounted, onUnmounted } from 'vue';
import { ElMessage } from 'element-plus';
import * as echarts from 'echarts';
import messageApi from '../api/messageApi';export default {name: 'MessageStats',setup() {const chartContainer = ref(null);let chart = null;// 加载统计数据const loadStats = async () => {try {const response = await messageApi.getMessageStats();const stats = response.data;const senders = stats.map(item => item[0]);const counts = stats.map(item => item[1]);updateChart(senders, counts);} catch (error) {console.error('加载统计数据失败:', error);ElMessage.error('加载统计数据失败');}};// 更新图表const updateChart = (senders, counts) => {if (!chart) return;const option = {title: {text: '用户消息数量统计'},tooltip: {trigger: 'axis',axisPointer: {type: 'shadow'}},xAxis: {type: 'category',data: senders},yAxis: {type: 'value'},series: [{name: '消息数量',type: 'bar',data: counts}]};chart.setOption(option);};// 初始化图表const initChart = () => {if (chartContainer.value) {chart = echarts.init(chartContainer.value);loadStats();}};// 处理窗口大小变化const handleResize = () => {if (chart) {chart.resize();}};onMounted(() => {initChart();window.addEventListener('resize', handleResize);});onUnmounted(() => {if (chart) {chart.dispose();}window.removeEventListener('resize', handleResize);});return {chartContainer};}
};
</script><style scoped>
.message-stats {height: 100%;
}.chart-container {height: 400px;
}
</style>

4.6 创建主页面

<!-- src/App.vue -->
<template><div class="app-container"><el-container><el-header><h1>Kafka实时聊天演示</h1><div class="user-info" v-if="!username"><el-input v-model="inputUsername" placeholder="请输入用户名" /><el-button type="primary" @click="login">登录</el-button></div><div class="user-info" v-else><span>欢迎, {{ username }}</span><el-button type="danger" @click="logout">退出</el-button></div></el-header><el-main v-if="username"><el-row :gutter="20"><el-col :span="16"><MessageChat :username="username" /></el-col><el-col :span="8"><MessageStats /></el-col></el-row></el-main><el-main v-else class="login-container"><el-card class="login-card"><h2>欢迎使用Kafka实时聊天</h2><p>请输入用户名开始聊天</p></el-card></el-main></el-container></div>
</template><script>
import { ref } from 'vue';
import MessageChat from './components/MessageChat.vue';
import MessageStats from './components/MessageStats.vue';export default {name: 'App',components: {MessageChat,MessageStats},setup() {const username = ref('');const inputUsername = ref('');const login = () => {if (inputUsername.value.trim()) {username.value = inputUsername.value;inputUsername.value = '';}};const logout = () => {username.value = '';};return {username,inputUsername,login,logout};}
};
</script><style>
.app-container {height: 100vh;
}.el-header {background-color: #409eff;color: white;display: flex;justify-content: space-between;align-items: center;padding: 0 20px;
}.user-info {display: flex;align-items: center;gap: 10px;
}.login-container {display: flex;justify-content: center;align-items: center;
}.login-card {width: 400px;text-align: center;padding: 20px;
}.el-main {padding: 20px;
}
</style>

4.7 配置路由

// src/router/index.js
import { createRouter, createWebHistory } from 'vue-router';
import App from '../App.vue';const routes = [{path: '/',name: 'Home',component: App}
];const router = createRouter({history: createWebHistory(import.meta.env.BASE_URL),routes
});export default router;

4.8 配置主入口

// src/main.js
import { createApp } from 'vue';
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';
import App from './App.vue';
import router from './router';const app = createApp(App);app.use(ElementPlus);
app.use(router);app.mount('#app');

五、运行项目

5.1 启动后端

# 进入后端项目目录
cd kafka-spring-demo# 启动Spring Boot应用
./mvnw spring-boot:run

5.2 启动前端

# 进入前端项目目录
cd kafka-vue-demo# 启动开发服务器
npm run dev

5.3 访问应用

打开浏览器,访问 http://localhost:8080 即可使用应用。

六、功能演示

6.1 发送和接收消息

  1. 打开多个浏览器窗口,分别以不同用户名登录
  2. 在任意窗口发送消息,所有窗口都能实时接收到消息
  3. 消息会显示发送者、内容和时间

6.2 消息统计

  1. 发送多条消息后,统计图表会显示每个用户发送的消息数量
  2. 图表会自动更新,反映最新的统计数据

6.3 系统通知

  1. 后端可以发送系统通知,所有用户都能收到
  2. 通知会以Element Plus的消息提示形式显示

七、扩展功能

7.1 添加消息类型

// 在Message类中添加消息类型字段
private String type; // 消息类型:text, image, file等

7.2 添加消息已读状态

// 在Message类中添加已读状态字段
private boolean read;

7.3 添加私聊功能

// 在Message类中添加接收者字段
private String receiver; // 私聊接收者,为空表示群聊

7.4 添加消息搜索功能

// 在MessageRepository中添加搜索方法
List<Message> findByContentContainingOrSenderContaining(String content, String sender);

八、总结

本项目演示了如何在Vue前端和Spring Boot后端中集成Kafka,实现实时消息的发送和接收。通过WebSocket和Kafka的结合,我们实现了一个功能完善的实时聊天应用。

8.1 技术要点

  • Kafka消息的发送和接收
  • WebSocket实时通信
  • Vue组件化开发
  • Spring Boot后端服务
  • 数据可视化

8.2 项目亮点

  • 实时消息推送
  • 消息历史记录
  • 消息统计图表
  • 系统通知功能
  • 响应式设计

8.3 后续优化方向

  • 添加消息加密
  • 实现消息撤回功能
  • 添加文件上传功能
  • 优化移动端适配
  • 添加用户认证和授权

相关文章:

Kafka在Vue和Spring Boot中的使用实例

Kafka在Vue和Spring Boot中的使用实例 一、项目概述 本项目演示了如何在Vue前端和Spring Boot后端中集成Kafka&#xff0c;实现实时消息的发送和接收&#xff0c;以及数据的实时展示。 后端实现&#xff1a;springboot配置、kafka配置、消息模型和仓库、消息服务和消费者、we…...

JSON 是什么?通俗详解

**JSON 是什么&#xff1f;通俗详解** --- ### **1. 一句话总结** **JSON&#xff08;JavaScript Object Notation&#xff09;** 是一种轻量级的 **数据交换格式**&#xff0c;就像“数据的快递包装盒”&#xff0c;用来在不同系统之间 **传递和存储信息**&#xff0c;简单易…...

Flutter:Flutter SDK版本控制,fvm安装使用

1、首先已经安装了Dart&#xff0c;cmd中执行 dart pub global activate fvm2、windows配置系统环境变量 fvm --version3、查看本地已安装的 Flutter 版本 fvm releases4、验证当前使用的 Flutter 版本&#xff1a; fvm flutter --version5、切换到特定版本的 Flutter fvm use …...

碰一碰发视频源头开发技术服务商

碰一碰发视频系统 随着短视频平台的兴起&#xff0c;用户的创作与分享需求日益增长。而如何让视频分享更加便捷、有趣&#xff0c;则成为各大平台优化的重点方向之一。抖音作为国内领先的短视频平台&#xff0c;在2023年推出了“碰一碰”功能&#xff0c;通过近距离通信技术实…...

Oracle 23ai Vector Search 系列之4 VECTOR数据类型和基本操作

文章目录 Oracle 23ai Vector Search 系列之4 VECTOR数据类型和基本操作VECTOR 数据类型基本语法Vector 维度限制和向量大小向量存储格式&#xff08;DENSE vs SPARSE&#xff09;1. DENSE存储2. SPARSE存储3. 内部存储与空间计算 Oracle VECTOR数据类型的声明格式VECTOR基本操…...

Java面试38-Dubbo是如何动态感知服务下线的?

首先&#xff0c;Dubbo默认采用Zookeeper实现服务注册与服务发现&#xff0c;就是多个Dubbo服务之间的通信地址&#xff0c;是使用Zookeeper来维护的。在Zookeeper上&#xff0c;会采用树形结构的方式来维护Dubbo服务提供端的协议地址&#xff0c;Dubbo服务消费端会从Zookeeper…...

C++day8

思维导图 牛客练习 练习 #include <iostream> #include <cstring> #include <cstdlib> #include <unistd.h> #include <sstream> #include <vector> #include <memory> using namespace std; class user{ public: …...

MySQL的进阶语法8(SQL优化——insert、主键、order by、group by、limit、count和update)

目录 一、插入数据 1.1 insert 1.2 大批量插入数据 二、主键优化 2.1 数据组织方式 2.2 页分裂 2.2.1 主键顺序插入效果 2.2.2 主键乱序插入效果 2.3 页合并 2.4 索引设计原则 三、order by优化 3.1 执行以下两条语句&#xff08;无索引&#xff09; 3.2 创建索引…...

STM32 基础2

STM32中断响应过程 1、中断源发出中断请求。 2、判断处理器是否允许中断&#xff0c;以及该中断源是否被屏蔽。 3、中断优先级排队。 4、处理器暂停当前程序&#xff0c;保护断点地址和处理器的当前状态&#xff0c;根据中断类型号&#xff0c;查找中断向量表&#xff0c;转到…...

前端单页应用性能优化全指南:从加载提速到极致体验

一、SPA性能瓶颈深度剖析 1.1 核心性能指标解读 指标健康阈值测量工具优化方向FCP (首次内容渲染)< 1.8sLighthouse资源加载优化TTI (可交互时间)< 3.5sWebPageTestJavaScript优化LCP (最大内容渲染)< 2.5sChrome DevTools渲染性能优化CLS (布局偏移)< 0.1PageSp…...

自然语言处理利器NLTK:从入门到核心功能解析

文章目录 一、NLP领域的基石工具包二、NLTK核心模块全景解析1 数据获取与预处理2 语言特征发现3 语义与推理 三、设计哲学与架构优势1 四维设计原则2 性能优化策略 四、典型应用场景1 学术研究2 工业实践 五、生态系统与未来演进 一、NLP领域的基石工具包 自然语言工具包&…...

简述Unity对多线程的支持限制和注意事项

Unity是一个以单线程为核心设计的游戏引擎&#xff0c;其主线程负责渲染、物理模拟、脚本更新&#xff08;如Update和FixedUpdate&#xff09;等核心功能。虽然Unity允许开发者使用C#的多线程功能&#xff08;如System.Threading命名空间&#xff09;来创建和管理线程&#xff…...

Mysql 中有哪些日志结构?

在 MySQL 中&#xff0c;日志文件是非常重要的&#xff0c;它们用于记录数据库的各类活动&#xff0c;帮助管理员进行监控、调试、恢复、以及优化数据库性能。MySQL 提供了几种类型的日志&#xff0c;每种日志都有其特定的用途。以下是 MySQL 中常见的几种日志类型&#xff1a;…...

【第2月 day17】Matplotlib 新手设计的直方图与饼图学习内容

以下是专为Python新手设计的直方图与饼图学习内容&#xff0c;包含基础知识、代码演示及注意事项&#xff1a; 一、直方图&#xff08;Histogram&#xff09; 1. 直方图的作用 展示数据分布情况&#xff08;如年龄分布、成绩分布&#xff09;观察数据集中趋势、离散程度 2. …...

使用Docker安装及使用最新版本的Jenkins

1. 拉取镜像 通过Windows powerShell执行命令行&#xff08;2选1&#xff09;&#xff1a; -- 长期支持版 docker pull jenkins/jenkins:lts-- 最新版 docker pull jenkins/jenkins:latest 2. 创建并执行容器 你可以通过以下命令来运行Jenkins容器&#xff0c;执行命令&…...

在Spring Boot中配置数据库连接

今天我们要谈谈如何在Spring Boot项目中配置数据库连接。我们会创建两个Java类&#xff1a;DatabaseProperties.java和DataSourceConfig.java&#xff0c;并在我们的应用程序中注入这些配置。让我们一起乘风破浪&#xff0c;开始这段编码之旅吧&#xff01; 目录 创建DatabaseP…...

Tiktok 关键字 视频及评论信息爬虫(2) [2025.04.07]

&#x1f64b;‍♀️Tiktok APP的基于关键字检索的视频及评论信息爬虫共分为两期&#xff0c;希望对大家有所帮助。 第一期&#xff1a;基于关键字检索的视频信息爬取 第二期见下文。 1.Node.js环境配置 首先配置 JavaScript 运行环境&#xff08;如 Node.js&#xff09;&…...

关于深度学习中内部协变量偏移问题小记

内部协变量偏移问题 内部协变量偏移&#xff08;Internal Covariate Shift&#xff0c;简称ICS&#xff09;是深度学习中一个重要的概念&#xff0c;用来描述神经网络在训练过程中&#xff0c;各层输入分布发生变化的现象。这种分布偏移会导致训练不稳定、收敛变慢甚至失败。2…...

15-产品经理-维护需求

一、提研发需求 在产品–研发需求列表页&#xff0c;点击“提研发需求”按钮&#xff0c; 在提研发需求页面&#xff0c;可以选择已有的计划。也可以在计划页面里进行关联。 未编辑完的需求可以点击【存为草稿】按钮&#xff0c;保存为草稿状态&#xff0c;待编辑完成再选择提…...

JVM基础架构:内存模型×Class文件结构×核心原理剖析

&#x1f680;前言 “为什么你的Java程序总在半夜OOM崩溃&#xff1f;为什么某些代码性能突然下降&#xff1f;一切问题的答案都在JVM里&#xff01; 作为Java开发者&#xff0c;如果你&#xff1a; 对OutOfMemoryError束手无策看不懂GC日志里的神秘数字好奇.class文件如何变…...

js前端对时间进行格式处理

时间格式处理 通过js前端&#xff0c;使用dayjs库进行格式化 安装dayjs库 npm install dayjs 封装成日期格式化工具类 formatter.ts // 导入 dayjs&#xff0c;先安装依赖 npm install dayjs import dayjs from "dayjs"; import utc from "dayjs/plugin/utc…...

如何拿到iframe中嵌入的游戏数据

在 iframe 中嵌入的游戏数据是否能被获取&#xff0c;取决于以下几个关键因素&#xff1a; 1. 同源策略 浏览器的同源策略是核心限制。如果父页面和 iframe 中的内容同源&#xff08;即协议、域名和端口号完全相同&#xff09;&#xff0c;那么可以直接通过 JavaScript 访问 …...

Chrome 135 版本新特性

Chrome 135 版本新特性 一、Chrome 135 版本浏览器更新 ** 1. 第三方托管账户注册迁移到 OIDC 授权码流程** Chrome 135 将账户注册的登录页面从营销网站迁移到动态网站&#xff0c;同时也将 OpenID Connect (OIDC) 的隐式流程迁移到授权码流程。这样做的目的是进一步提升第…...

基于milvus安装--可视化工具 attu

说明:Attu是Milvus的可视化工具,用于方便地管理和查询向量数据。 1.安装docker 并配置国内镜像 配置国内镜像加速: 1.修改 /etc/docker/daemon.json,设置 registry mirror: sudo vim /etc/docker/daemon.json {"registry-mirrors": ["https://docker.1m…...

【Vue-组件】学习笔记

目录 <<回到导览组件1.项目1.1.Vue Cli1.2.项目目录1.3.运行流程1.4.组件的组成1.5.注意事项 2.组件2.1.组件注册2.2.scoped样式冲突2.3.data是一个函数2.4.props详解2.5.data和prop的区别 3.组件通信3.1.父子通信3.1.1.父传子&#xff08;props&#xff09;3.1.2.子传父…...

[蓝桥杯青少年组省赛 2024] 通关游戏的最少能量值

在 Python 中&#xff0c;可以通过以下步骤实现二维数组的输入&#xff0c;并根据数组的第一个元素进行排序&#xff1a; 读取输入&#xff1a;使用 input() 函数读取输入&#xff0c;并将其转换为整数。创建二维数组&#xff1a;将输入的任务的启动能量值和消耗能量值存储为二…...

预测函数控制(PFC)——理论、应用与实践

目录 预测函数控制(PFC)——理论、应用与实践一、引言二、预测函数控制的基本原理1. PFC 的核心思想2. 数学建模与公式推导3. 优势与局限优势局限三、典型案例分析案例一:一维动态系统控制案例描述分析案例二:温度调节系统案例描述分析案例三:机器人轨迹跟踪控制案例描述分…...

(PROFINET 转 EtherCAT)EtherCAT/Ethernet/IP/Profinet/ModbusTCP协议互转工业串口网关

型号 协议转换通信网关 PROFINET 转 EtherCAT MS-GW31 概述 MS-GW31 是 PROFINET 和 EtherCAT 协议转换网关&#xff0c;为用户提供两种不同通讯协议的 PLC 进行数据交互的解决方案&#xff0c;可以轻松容易将 EtherCAT 网络接入 PROFINET 网络中&#xff0c;方便扩展&…...

关于sqlsugar实体多层List映射的问题

如上图所示&#xff0c;当一个主表&#xff08;crm_fina_pay_req&#xff09;的子表list<文件附件关系表>&#xff08; List<crm_fina_payreq_evidofpay_relation> &#xff09;中&#xff0c;还包含有sysfile&#xff08;SysFile SysFiles&#xff09;类型的文件信…...

STM32 HAL库 CANFD配置工具

用法说明&#xff1a; 该工具适用于STM32HAL库&#xff0c;可一键生成CANFD的HAL库配置代码。计算依据为HAL库&#xff0c;并参考ZLG标准。 软件界面&#xff1a; 仓库地址&#xff1a; HAL CANFD Init Gen: 适用于STM32控制器的HAL库 版本说明&#xff1a; V1.2.0 &#x…...