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

ELK日志管理框架介绍

        在小铃铛的毕业设计中涉及到了ELK日志管理框架,在调研期间发现在中文中没有很好的对ELK框架进行介绍的文章,因此拟在本文中进行较为详细的实现的介绍。

理论知识

ELK 框架介绍

ELK 是一个流行的开源日志管理解决方案堆栈,由三个核心组件组成:

ELK框架核心组件
组件名称组件特性
Elasticsearch - 分布式搜索和分析引擎
   - 提供实时搜索和数据分析能力
   - 基于Lucene构建,具有高扩展性
Logstash   - 服务器端数据处理管道
   - 用于收集、解析和转换日志数据
   - 支持多种输入源和输出目标
Kibana - 数据可视化平台
   - 提供丰富的图表和仪表板功能
   - 允许用户交互式地探索数据

随着发展,ELK生态系统也出现了一些变体:

  • EFK:用Fluentd替代Logstash

  • ELK+Beats:加入轻量级数据采集器Beats系列工具

  • Elastic Stack:官方对ELK堆栈的新命名

主要特点

- 实时分析:能够近乎实时地处理和分析数据
- 可扩展性:可以水平扩展以处理大量数据
- 灵活性:支持多种数据源和格式
- 强大的搜索能力:提供全文搜索和结构化搜索

常见应用场景

- 日志集中管理和分析
- 应用程序性能监控
- 安全信息和事件管理(SIEM)
- 业务智能分析

实际应用

小铃铛在系统中使用了ELK Stack框架,数据流如下:

接下来以文章的日志数据的收集为例进行介绍。

Step1.日志模板设计

首先需要设计需要在日志中都收集什么数据,可以自定义模板,写在Logstash的模板配置里,即/templates文件夹下面,例如以下文件:

post-metrics-template.json

{"index_patterns": ["blog-post-metrics-*"],"version": 3,"priority": 400,"template": {"settings": {"number_of_shards": 1,"number_of_replicas": 1,"index.lifecycle.name": "blog-policy","index.lifecycle.rollover_alias": "blog-post-metrics"},"mappings": {"properties": {"@timestamp": {"type": "date"},"log_type": {"type": "keyword"},"service_name": {"type": "keyword"},"level": {"type": "keyword"},"operation": {"type": "keyword"},"post_id": {"type": "long"},"post_title": {"type": "keyword"},"user_id": {"type": "long"},"success": {"type": "boolean"},"duration": {"type": "long"},"error_message": {"type": "text","fields": {"keyword": {"type": "keyword","ignore_above": 256}}},"hostname": {"type": "keyword"},"os_name": {"type": "keyword"},"os_version": {"type": "keyword"},"environment": {"type": "keyword"},"service": {"type": "keyword"},"view_count": {"type": "long"}}}}
}

Step2.设计日志收集时机

根据我个人对日志的理解,后端会在两种情况下打印日志:针对某个位置的类的方法被调用时打印(静态的,使用文件位置定位)和针对类的某方法被调用时打印(动态的,使用注解自主决定)。或许以上的概括不够准确,我将进一步展示。

针对某个位置的类的方法被调用时

        当前AOP设计打印日志的时机是:调用了“com.kitty.blog.application”或“com.kitty.blog.interfaces”文件夹下的函数,并且没有执行“com.kitty.blog.infrastructure.security.filter”下的函数。

(但是以下示例并不是针对收集文章日志写的,看懂原理即可)

@Aspect
@Component
@Slf4j
public class BackendLoggingAspect {@Before("execution(* com.kitty.blog.application..*.*(..))" +" || execution(* com.kitty.blog.interfaces..*.*(..))" +" && !execution(* com.kitty.blog.infrastructure.security.filter..*.*(..))")public void before(JoinPoint joinPoint) {Map<String, Object> logData = new HashMap<>();logData.put("@timestamp", LocalDateTime.now().toInstant(ZoneOffset.UTC).toString());logData.put("log_type", LogConstants.LogType.API_METRICS);logData.put("application", LogConstants.APPLICATION_NAME);logData.put("phase", "method_start");logData.put("class", joinPoint.getTarget().getClass().getName());logData.put("method", joinPoint.getSignature().getName());logData.put("args", Arrays.toString(joinPoint.getArgs()));// 添加标签try {logData.put("host", java.net.InetAddress.getLocalHost().getHostName());logData.put("service", LogConstants.APPLICATION_NAME);logData.put("environment", System.getProperty("spring.profiles.active", "dev"));} catch (Exception e) {log.warn("Failed to add tags to log data", e);}log.info("Method Execution: {}", net.logstash.logback.argument.StructuredArguments.entries(logData));}@After("execution(* com.kitty.blog.application..*.*(..))" +" || execution(* com.kitty.blog.interfaces..*.*(..))" +" && !execution(* com.kitty.blog.infrastructure.security.filter..*.*(..))")public void after(JoinPoint joinPoint) {Map<String, Object> logData = new HashMap<>();logData.put("@timestamp", LocalDateTime.now().toInstant(ZoneOffset.UTC).toString());logData.put("log_type", LogConstants.LogType.API_METRICS);logData.put("application", LogConstants.APPLICATION_NAME);logData.put("phase", "method_end");logData.put("class", joinPoint.getTarget().getClass().getName());logData.put("method", joinPoint.getSignature().getName());// 添加标签try {logData.put("host", java.net.InetAddress.getLocalHost().getHostName());logData.put("service", LogConstants.APPLICATION_NAME);logData.put("environment", System.getProperty("spring.profiles.active", "dev"));} catch (Exception e) {log.warn("Failed to add tags to log data", e);}log.info("Method Execution: {}", net.logstash.logback.argument.StructuredArguments.entries(logData));}@AfterThrowing(pointcut = "execution(* com.kitty.blog.application..*.*(..))" +" || execution(* com.kitty.blog.interfaces..*.*(..))" +" && !execution(* com.kitty.blog.infrastructure.security.filter..*.*(..))", throwing = "ex")public void afterThrowing(JoinPoint joinPoint, Throwable ex) {Map<String, Object> logData = new HashMap<>();logData.put("@timestamp", LocalDateTime.now().toInstant(ZoneOffset.UTC).toString());logData.put("log_type", LogConstants.LogType.ERROR);logData.put("application", LogConstants.APPLICATION_NAME);logData.put("phase", "method_error");logData.put("class", joinPoint.getTarget().getClass().getName());logData.put("method", joinPoint.getSignature().getName());logData.put("error_message", ex.getMessage());logData.put("stack_trace", Arrays.toString(ex.getStackTrace()));// 添加标签try {logData.put("host", java.net.InetAddress.getLocalHost().getHostName());logData.put("service", LogConstants.APPLICATION_NAME);logData.put("environment", System.getProperty("spring.profiles.active", "dev"));} catch (Exception e) {log.warn("Failed to add tags to log data", e);}log.error("Method Execution Error: {}", net.logstash.logback.argument.StructuredArguments.entries(logData));}
}

针对类的某方法被调用时

        当前AOP设计打印日志的时机是:调用了被@LogPostMetrics这个annotation注解过的函数。其中,annotation可以自定义也可以使用预定义的。

@Aspect
@Component
@Slf4j
public class PostMetricsAspect {@Autowiredprivate PostRepository postRepository;@Around("@annotation(com.kitty.blog.common.annotation.LogPostMetrics)")public Object logPostMetrics(ProceedingJoinPoint joinPoint) throws Throwable {long startTime = System.currentTimeMillis();String operation = getOperationType(joinPoint);Long postId = getPostId(joinPoint);try {Object result = joinPoint.proceed();long duration = System.currentTimeMillis() - startTime;recordPostMetrics(operation, postId, true, null, duration);return result;} catch (Exception e) {long duration = System.currentTimeMillis() - startTime;recordPostMetrics(operation, postId, false, e.getMessage(), duration);throw e;}}private String getOperationType(ProceedingJoinPoint joinPoint) {MethodSignature signature = (MethodSignature) joinPoint.getSignature();LogPostMetrics annotation = signature.getMethod().getAnnotation(LogPostMetrics.class);return annotation.value();}private Long getPostId(ProceedingJoinPoint joinPoint) {Object[] args = joinPoint.getArgs();for (Object arg : args) {if (arg instanceof Long) {return (Long) arg;} else if (arg instanceof Integer) {return ((Integer) arg).longValue();} else if (arg instanceof String) {try {return Long.parseLong((String) arg);} catch (NumberFormatException e) {log.warn("Failed to parse post id from string: {}", arg);}}}return null;}private void recordPostMetrics(String operation, Long postId, boolean success, String errorMessage, long duration) {try {Map<String, Object> metrics = new HashMap<>();// 基础字段metrics.put("@timestamp", LocalDateTime.now().toInstant(ZoneOffset.UTC).toString());metrics.put("log_type", LogConstants.LogType.POST_METRICS);metrics.put("service", LogConstants.APPLICATION_NAME);metrics.put("level", "INFO");// 操作信息metrics.put("operation", operation);metrics.put("post_id", postId);if (postId != null) {try {Post post = postRepository.findById(postId.intValue()).orElse(null);if (post != null) {metrics.put("post_title", post.getTitle());metrics.put("user_id", post.getUserId());metrics.put("view_count", post.getViews());} else {metrics.put("post_title", "未找到文章");metrics.put("user_id", "unknown");log.error("文章不存在: {}", postId);}} catch (Exception e) {log.error("获取文章详情失败: {}", e.getMessage(), e);metrics.put("post_title", "获取文章信息失败");metrics.put("user_id", "unknown");}} else {metrics.put("post_title", "无文章ID");metrics.put("user_id", "unknown");log.error("文章ID为空");}metrics.put("success", success);metrics.put("duration", duration);if (errorMessage != null) {metrics.put("error_message", errorMessage);log.error("操作失败: {}", errorMessage);}// 添加标签metrics.put("host", java.net.InetAddress.getLocalHost().getHostName());metrics.put("environment", System.getProperty("spring.profiles.active", "dev"));log.info("Post Metrics: {}",net.logstash.logback.argument.StructuredArguments.entries(metrics));} catch (Exception e) {log.error("记录文章指标失败", e);}}
}

        该自定义的注解在使用时和预定义的一样,只需要在目标方法上写@LogPostMetrics即可。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogPostMetrics {String value() default "";
}

Step3.用Logback类规定部分数据

        在logback-spring.xml主要规定了日志以天为单位会重新创建一个新的文件,便于管理。        

<?xml version="1.0" encoding="UTF-8"?>
<configuration><timestamp key="CURRENT_DATE" datePattern="yyyy-MM-dd"/><property name="LOG_PATH" value="./logs"/><property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"/><!-- 控制台输出 --><appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>${LOG_PATTERN}</pattern></encoder></appender><!-- 博客文章指标日志 --><appender name="POST_METRICS_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>${LOG_PATH}/blog-post-metrics-${CURRENT_DATE}.json</file><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><fileNamePattern>${LOG_PATH}/blog-post-metrics-%d{yyyy.MM.dd}.json</fileNamePattern><maxHistory>7</maxHistory></rollingPolicy><encoder class="net.logstash.logback.encoder.LogstashEncoder"><customFields>{"application":"blog-system","log_type":"post-metrics"}</customFields><timestampPattern>yyyy-MM-dd'T'HH:mm:ss.SSSZZ</timestampPattern><includeMdcData>true</includeMdcData><includeContext>true</includeContext><fieldNames><tags>[ignore]</tags><hostname>host</hostname></fieldNames><provider class="net.logstash.logback.composite.loggingevent.LogstashMarkersJsonProvider"/><provider class="net.logstash.logback.composite.loggingevent.MdcJsonProvider"/></encoder></appender><!-- 日志配置 --><root level="INFO"><appender-ref ref="CONSOLE"/><appender-ref ref="ERROR_FILE"/></root><logger name="com.kitty.blog.common.aspect.PostMetricsAspect" level="INFO" additivity="false"><appender-ref ref="POST_METRICS_FILE"/><appender-ref ref="CONSOLE"/></logger>
</configuration>

Step4.ELK环境搭建

现在已经配置好了日志文件将存储的位置,我们需要保证Filebeat可以读取到,比如说下面是我的Docker环境里的Filebeat配置。

docker-compose.yml

  filebeat:image: elastic/filebeat:8.11.1user: rootvolumes:- ./config/filebeat/filebeat.yml:/usr/share/filebeat/filebeat.yml- ../../../logs:/var/log/blog:ro- /var/lib/docker/containers:/var/lib/docker/containers:ro- /var/run/docker.sock:/var/run/docker.sock:rodepends_on:logstash:condition: service_healthyenvironment:- ENVIRONMENT=dev- ELASTIC_USERNAME=elastic- ELASTIC_PASSWORD=123456networks:- elk-networkcommand: >bash -c "chmod go-w /usr/share/filebeat/filebeat.yml &&chown root:root /usr/share/filebeat/filebeat.yml &&chmod 0644 /usr/share/filebeat/filebeat.yml &&filebeat -e -strict.perms=false"

filebeat.yml 

filebeat.inputs:
- type: logenabled: truepaths:- /var/log/blog/blog-api-metrics-*.jsonjson.keys_under_root: truejson.add_error_key: truejson.ignore_decoding_error: truefields:app: blog-systemenvironment: ${ENVIRONMENT:-dev}log_type: api-metricsfields_under_root: true- type: logenabled: truepaths:- /var/log/blog/blog-error-*.jsonjson.keys_under_root: truejson.add_error_key: truejson.ignore_decoding_error: truefields:app: blog-systemenvironment: ${ENVIRONMENT:-dev}log_type: errorfields_under_root: true- type: logenabled: truepaths:- /var/log/blog/blog-post-metrics-*.jsonjson.keys_under_root: truejson.add_error_key: truejson.ignore_decoding_error: truefields:app: blog-systemenvironment: ${ENVIRONMENT:-dev}log_type: post-metricsfields_under_root: true- type: logenabled: truepaths:- /var/log/blog/blog-system-metrics-*.jsonjson.keys_under_root: truejson.add_error_key: truejson.ignore_decoding_error: truefields:app: blog-systemenvironment: ${ENVIRONMENT:-dev}log_type: system-metricsfields_under_root: true- type: logenabled: truepaths:- /var/log/blog/blog-user-activity-*.jsonjson.keys_under_root: truejson.add_error_key: truejson.ignore_decoding_error: truefields:app: blog-systemenvironment: ${ENVIRONMENT:-dev}log_type: user-activityfields_under_root: trueprocessors:- add_host_metadata: ~- add_docker_metadata: ~output.logstash:hosts: ["logstash:5000"]loadbalance: truebulk_max_size: 2048logging.level: info
logging.to_files: true
logging.files:path: /var/log/filebeatname: filebeat.logrotateeverybytes: 10485760  # 10MBkeepfiles: 7permissions: 0644setup.ilm.enabled: false
setup.template.enabled: false

那么现在日志已经被收集到Filebeat里,Logstash的配置如下。

docker-compose.yml

  logstash:image: logstash:8.11.1depends_on:elasticsearch:condition: service_healthy  # 添加健康检查依赖ports:- "5000:5000"volumes:- ./config/logstash/logstash.yml:/usr/share/logstash/config/logstash.yml:ro # 标记为只读- ./config/logstash/logstash.conf:/usr/share/logstash/pipeline/logstash.conf:ro- ./config/logstash/templates:/usr/share/logstash/templates:roenvironment:- "LS_JAVA_OPTS=-Xms256m -Xmx256m"- ELASTICSEARCH_USERNAME=elastic- ELASTICSEARCH_PASSWORD=123456networks:- elk-networkhealthcheck:test: ["CMD-SHELL", "curl -s -f http://localhost:9600 || exit 1"]interval: 30stimeout: 10sretries: 3start_period: 40s

logstash.conf

input {beats {port => 5000host => "0.0.0.0"}
}filter {json {source => "message"skip_on_invalid_json => truetarget => "parsed_json"}# 处理 host 字段mutate {add_field => {"hostname" => "%{[host][name]}""os_name" => "%{[host][os][name]}""os_version" => "%{[host][os][version]}"}remove_field => ["host"]}# 将 host、environment 和 service 作为独立字段处理mutate {add_field => {"environment" => "%{[parsed_json][environment]}""service" => "%{[parsed_json][service]}"}remove_field => ["tags"]}# 调试输出当前事件ruby {code => "require 'logger'logger = Logger.new(STDOUT)logger.info('Current event:')logger.info(event.to_hash.inspect)"}# 根据log_type设置目标索引if [log_type] == "user-activity" {mutate {add_field => { "[@metadata][index]" => "blog-user-activity-%{+YYYY.MM.dd}" }add_field => { "[@metadata][template_name]" => "user-activity-template" }}} else if [log_type] == "post-metrics" {mutate {add_field => { "[@metadata][index]" => "blog-post-metrics-%{+YYYY.MM.dd}" }add_field => { "[@metadata][template_name]" => "post-metrics-template" }}# Extract view_count from parsed JSONif [parsed_json][view_count] {mutate {add_field => { "view_count" => "%{[parsed_json][view_count]}" }}mutate {convert => { "view_count" => "integer" }}}}else if [log_type] == "api-metrics" {mutate {add_field => { "[@metadata][index]" => "blog-api-metrics-%{+YYYY.MM.dd}" }add_field => { "[@metadata][template_name]" => "blog-template" }}} else if [log_type] == "error" {mutate {add_field => { "[@metadata][index]" => "blog-error-logs-%{+YYYY.MM.dd}" }add_field => { "[@metadata][template_name]" => "blog-template" }}} else if [log_type] == "system-metrics" {mutate {add_field => { "[@metadata][index]" => "blog-system-metrics-%{+YYYY.MM.dd}" }add_field => { "[@metadata][template_name]" => "blog-template" }}}# 如果没有匹配到任何类型,添加到默认索引if ![@metadata][index] {mutate {add_field => { "[@metadata][index]" => "blog-unknown-1" }add_field => { "[@metadata][template_name]" => "blog" }add_field => { "error_message" => "Unknown log_type: %{[log_type]}" }}}# 确保时间戳格式正确date {match => [ "@timestamp", "ISO8601" ]target => "@timestamp"timezone => "Asia/Shanghai"}# 移除不需要的字段mutate {remove_field => ["message", "tags", "beat", "input", "prospector", "agent"]}
}output {if [@metadata][index] {if [@metadata][template_name] == "post-metrics-template" {elasticsearch {hosts => ["elasticsearch:9200"]user => "${ELASTICSEARCH_USERNAME}"password => "${ELASTICSEARCH_PASSWORD}"index => "%{[@metadata][index]}"template => "/usr/share/logstash/templates/post-metrics-template.json"template_name => "post-metrics-template"template_overwrite => truedata_stream => false}} else if [@metadata][template_name] == "user-activity-template" {elasticsearch {hosts => ["elasticsearch:9200"]user => "${ELASTICSEARCH_USERNAME}"password => "${ELASTICSEARCH_PASSWORD}"index => "%{[@metadata][index]}"template => "/usr/share/logstash/templates/user-activity-template.json"template_name => "user-activity-template"template_overwrite => truedata_stream => false}} else if [@metadata][template_name] == "blog-template" {elasticsearch {hosts => ["elasticsearch:9200"]user => "${ELASTICSEARCH_USERNAME}"password => "${ELASTICSEARCH_PASSWORD}"index => "%{[@metadata][index]}"template => "/usr/share/logstash/templates/blog-template.json"template_name => "blog-template"template_overwrite => truedata_stream => false}}}
}

logstash.yml 

http.host: "0.0.0.0"
xpack.monitoring.enabled: falsepath.config: /usr/share/logstash/pipeline
path.logs: /var/log/logstashpipeline.workers: 2
pipeline.batch.size: 125
pipeline.batch.delay: 50queue.type: memory
queue.max_bytes: 1024mblog.level: info

接下来存储到Elasticsearch里,如果需要额外功能需自己下载插件,例如中文分词器。

docker-compose.yml

请确定暴露出来一个账号和密码使得Logstash和Kibana可以访问。当然,账号可以不是同一个,但是应至少给Logstash的用户配有写入权限,Kibana的用户配有读出权限。

  elasticsearch:image: elasticsearch:8.11.1environment:- discovery.type=single-node- "ES_JAVA_OPTS=-Xms512m -Xmx512m"- xpack.security.enabled=true- ELASTIC_PASSWORD=123456- bootstrap.memory_lock=true- cluster.name=docker-cluster- network.host=0.0.0.0- xpack.security.transport.ssl.enabled=false- xpack.security.http.ssl.enabled=false- "ELASTIC_USERNAME=elastic"- KIBANA_SYSTEM_PASSWORD=kibana123ports:- "9201:9200"- "9300:9300"volumes:- elasticsearch_data:/usr/share/elasticsearch/data- ./config/elasticsearch/plugins/analysis-ik:/usr/share/elasticsearch/plugins/analysis-ik- ./config/elasticsearch/setup:/usr/share/elasticsearch/setupnetworks:- elk-networkhealthcheck:test: ["CMD-SHELL", "curl -s -u elastic:123456 http://localhost:9200/_cluster/health || exit 1"]interval: 30stimeout: 10sretries: 5start_period: 60sulimits:  # 添加系统限制memlock:soft: -1hard: -1nofile:soft: 65536hard: 65536

接下来配置Kibana:

docker-compose.yml

  kibana:image: kibana:8.11.1depends_on:elasticsearch:condition: service_healthyports:- "5601:5601"environment:- ELASTICSEARCH_HOSTS=http://elasticsearch:9200- ELASTICSEARCH_USERNAME=kibana_system- ELASTICSEARCH_PASSWORD=kibana123- SERVER_NAME=kibana- SERVER_HOST=0.0.0.0- XPACK_REPORTING_ENABLED=falsehealthcheck:test: ["CMD-SHELL", "curl -s -I http://localhost:5601 | grep -q 'HTTP/1.1 302 Found'"]interval: 10stimeout: 10sretries: 120networks:- elk-network

Kibana的默认访问地址是http://localhost:5601,管理员用户和密码是:elastic和123456。此时,你就可以使用可视化的方式来进行分析数据并管理集群状态等操作了。

相关文章:

ELK日志管理框架介绍

在小铃铛的毕业设计中涉及到了ELK日志管理框架&#xff0c;在调研期间发现在中文中没有很好的对ELK框架进行介绍的文章&#xff0c;因此拟在本文中进行较为详细的实现的介绍。 理论知识 ELK 框架介绍 ELK 是一个流行的开源日志管理解决方案堆栈&#xff0c;由三个核心组件组…...

【Linux】sed 命令详解及使用样例:流式文本编辑器

【Linux】sed 命令详解及使用样例&#xff1a;流式文本编辑器 引言 sed 是 Linux/Unix 系统中一个强大的流式文本编辑器&#xff0c;名称来源于 “Stream EDitor”&#xff08;流编辑器&#xff09;。它允许用户在不打开文件的情况下对文本进行筛选和转换&#xff0c;是命令行…...

机器学习:聚类算法及实战案例

本文目录&#xff1a; 一、聚类算法介绍二、分类&#xff08;一&#xff09;根据聚类颗粒度分类&#xff08;二&#xff09;根据实现方法分类 三、聚类流程四、K值的确定—肘部法&#xff08;一&#xff09;SSE-误差平方和&#xff08;二&#xff09;肘部法确定 K 值 五、代码重…...

预览pdf(url格式和blob格式)

<template><div class"pdf-container"><div v-if"loading" class"loading-state"><a-spin size"large" /></div><div v-else-if"error" class"loading-state">加载失败&…...

【p2p、分布式,区块链笔记 MESH】 论文阅读 Thread/OpenThread Low-Power Wireless Multihop Net

paperauthorThread/OpenThread: A Compromise in Low-Power Wireless Multihop Network Architecture for the Internet of ThingsHyung-Sin Kim, Sam Kumar, and David E. Culler 目录 引言RPL 标准设计目标与架构设计选择与特性shortcomIngs of RPL设计选择的反面影响sImulta…...

for AC500 PLCs 3ADR025003M9903的安全说明

1安全说明 必须遵守特殊的环境条件(例如&#xff0c;由于爆炸性物质、重污染或腐蚀影响的危险区域)。必须在指定的技术数据和系统数据范围内处理和操作设备。该装置不含可维修部件&#xff0c;不得打开。除非另有规定&#xff0c;否则操作过程中必须关闭可拆卸的盖子。拒绝对不…...

moon游戏服务器-demo运行

下载地址 https://github.com/sniper00/MoonDemo redis安装 Redis-x64-3.0.504.msi 服务器配置文件 D:\gitee\moon_server_demo\serverconf.lua 貌似不修改也可以的&#xff0c;redis不要设置密码 windows编译 安装VS2022 Community 下载premake5.exe放MoonDemo\server\moon 双…...

前端(vue)学习笔记(CLASS 7):vuex

vuex概述 vuex是一个vue的状态管理工具&#xff0c;状态就是数据 大白话&#xff1a;vuex是一个插件&#xff0c;可以帮我们管理vue通用的数据&#xff08;多组件共享的数据&#xff09; 场景 1、某个状态在很多个组件来使用&#xff08;个人信息&#xff09; 2、多个组件…...

[特殊字符] 在 React Native 项目中封装 App Icon 一键设置命令(支持参数与默认路径)

📦 前置依赖 使用的是社区维护的 CLI 工具: @bam.tech/react-native-make它扩展了 react-native 命令,支持 set-icon 功能。 安装: yarn add -D "@bam.tech/react-native-make"🧠 封装目标 我们希望能够通过以下方式调用: # 默认使用 ./icon.png yarn …...

基于深度学习(Unet和SwinUnet)的医学图像分割系统设计与实现:超声心脏分割

基于深度学习的医学图像分割系统设计与实现 摘要 本文提出了一种基于深度学习的医学图像分割系统,该系统采用U-Net和Swin-Unet作为核心网络架构,实现了高效的医学图像分割功能。系统包含完整的训练、验证和推理流程,并提供了用户友好的图形界面。实验结果表明,该系统在医…...

Qt学习及使用_第1部分_认识Qt---学习目的及技术准备

前言 学以致用,通过QT框架的学习,一边实践,一边探索编程的方方面面. 参考书:<Qt 6 C开发指南>(以下称"本书") 标识说明:概念用粗体倾斜.重点内容用(加粗黑体)---重点内容(红字)---重点内容(加粗红字), 本书原话内容用深蓝色标识,比较重要的内容用加粗倾…...

如何把本地服务器变成公网服务器?内网ip网址转换到外网连接访问

​ 内网IP只能在本地内部网络连接访问&#xff0c;当本地搭建服务器部署好相关网站或应用后&#xff0c;在局域网内可以通过内网IP访问&#xff0c;但在外网是无法直接访问异地内网IP端口应用的&#xff0c;只有公网IP和域名才能实现互联网上的访问。那么需要如何把本地服务器变…...

Java+Access综合测评系统源码分享:含论文、开题报告、任务书全套资料

JAVAaccess综合测评系统毕业设计 一、系统概述 本系统采用Java Swing开发前端界面&#xff0c;结合Access数据库实现数据存储&#xff0c;专为教育机构打造的综合测评解决方案。系统包含学生管理、题库管理、在线测评、成绩分析四大核心模块&#xff0c;实现了测评流程的全自…...

湖北理元理律师事务所:债务咨询中的心理支持技术应用

债务危机往往伴随心理崩溃。世界卫生组织研究显示&#xff0c;长期债务压力下抑郁症发病率提升2.3倍。湖北理元理律师事务所将心理干预技术融入法律咨询&#xff0c;构建“法律方案心理支持”的双轨服务模型。 一、债务压力下的心理危机图谱 通过对服务对象的追踪发现&#x…...

时间序列预测:LSTM与Prophet对比实验

时间序列预测&#xff1a;LSTM与Prophet对比实验 系统化学习人工智能网站&#xff08;收藏&#xff09;&#xff1a;https://www.captainbed.cn/flu 文章目录 时间序列预测&#xff1a;LSTM与Prophet对比实验摘要引言实验设计1. 数据集选择2. 实验流程 模型架构对比1. LSTM架…...

阿里云域名怎么绑定

阿里云服务器绑定域名全攻略&#xff1a;一步步轻松实现网站“零”障碍上线&#xff01; 域名&#xff0c;您网站在云端的“身份证”&#xff01; 在数字化浪潮中&#xff0c;拥有一个属于自己的网站或应用&#xff0c;是个人展示、企业运营不可或缺的一环。而云服务器&#x…...

能上Nature封面的idea!强化学习+卡尔曼滤波

2025深度学习发论文&模型涨点之——强化学习卡尔曼滤波 强化学习&#xff08;Reinforcement Learning, RL&#xff09;与卡尔曼滤波&#xff08;Kalman Filtering, KF&#xff09;的交叉研究已成为智能控制与状态估计领域的重要前沿方向。 强化学习通过试错机制优化决策策…...

Linux网桥实战手册:从基础配置到虚拟化网络深度优化

一、网桥基础操作全解析 1. 网桥生命周期管理 创建网桥的两种方式&#xff1a; # 传统brctl工具&#xff08;需安装bridge-utils&#xff09; brctl addbr br0 echo BRIDGEbr0 > /etc/sysconfig/network-scripts/ifcfg-br0# 现代iproute2工具链 ip link add name br0 typ…...

Design Theory and Method of Complex Products: A Review

abstract 摘要 Design is a high-level and complex thinking activity of human beings, using existing knowledge and technology to solve problems and create new things. With the rise and development of intelligent manufacturing, design has increasingly reflec…...

yaffs2目录搜索上下文数据结构struct yaffsfs_dirsearchcontext yaffsfs_dsc[] 详细解析

1. 目录搜索上下文&#xff08;Directory Search Context&#xff09; struct yaffsfs_dirsearchcontext 是 YAFFS2 文件系统中用于 目录遍历操作 的核心数据结构&#xff0c;专门管理 readdir() 等目录操作的状态。 结构体定义&#xff08;典型实现&#xff09; struct yaf…...

Markdown基础(1.2w字)

1. Markdown基础 这次就没目录了&#xff0c;因为md格式太乱了写示例&#xff0c;展示那些都太乱了&#xff0c;导致目录很乱。 &#xff08;我是XX&#xff0c;出现了很多错误&#xff0c;有错误和我说&#xff09; 1.1 Markdown简介 Markdown是一种轻量级的标记语言&#…...

LabVIEW与PLC液压泵测控系统

针对液压泵性能测试场景&#xff0c;采用LabVIEW与西门子 PLC 控制系统&#xff0c;构建高精度、高可靠性的智能测控系统。通过选用西门子 PLC、NI 数据采集卡、施耐德变频电机等&#xff0c;结合LabVIEW 强大的数据处理与界面开发能力&#xff0c;实现液压泵压力、流量、转速等…...

SQL-labs通关(level1-22)

SQL-labs靶场详解 靶场下载 靶场下载地址 关卡 level1联合注入 用order by语句来查询字段数 顺便提一下&#xff0c;使用联合注入语句union select也可以查询字段数&#xff0c;在不能使用order by的情况下&#xff0c;可以使用union select来查询字段数。这里我们通过查询…...

【HarmonyOS5】UIAbility组件生命周期详解:从创建到销毁的全景解析

⭐本期内容&#xff1a;【HarmonyOS5】UIAbility组件生命周期详解&#xff1a;从创建到销毁的全景解析 &#x1f3c6;系列专栏&#xff1a;鸿蒙HarmonyOS&#xff1a;探索未来智能生态新纪元 文章目录 前言生命周期全景图详细状态解析与最佳实践&#x1f3ac; Create状态&#…...

在命令行直接执行可以执行成功,加入crontab定时任务执行shell脚本不成功失败的问题解决方法

今天遇到在命令行直接执行可以执行成功&#xff0c;加入crontab定时任务执行shell脚本却不成功失败的问题&#xff0c;踩坑了很长时间 记录下我的解决方法 原来我的定时任务填写方式: [roottao ~]# crontab -l */10 * * * * /bin/sh /search/index.sh >>/dev/null 2&g…...

c++ 静态成员变量

Student.h头文件内容&#xff1a; #pragma once #include <string> using namespace std;class Student { public:string name;int score;static int totalScore; // 静态局部变量声明Student(string name, int score);~Student();void print() const; };Student.cpp源文…...

分布式爬虫代理IP使用技巧

最近我们讨论的是分布式爬虫如何使用代理IP。在我们日常的分布式爬虫系统中&#xff0c;多个爬虫节点同时工作&#xff0c;每个节点都需要使用代理IP来避免被目标网站封禁。怎么解决代理IP问题显得尤为重要。 我们知道在分布式爬虫中使用代理IP是解决IP封禁、提高并发能力和实…...

数据分析之OLTP vs OLAP

数据处理系统主要有两种基本方法&#xff1a;一种注重数据操作(增删查改)&#xff0c;另一种注重商业智能数据分析。 这两种系统是&#xff1a; 联机事务处理&#xff08;OLTP&#xff09; 联机分析处理&#xff08;OLAP&#xff09; Power BI专为与OLAP系统兼容而构建&…...

Flask音频处理:构建高效的Web音频应用指南

引言 在当今多媒体丰富的互联网环境中&#xff0c;音频处理功能已成为许多Web应用的重要组成部分。无论是音乐分享平台、语音识别服务还是播客应用&#xff0c;都需要强大的音频处理能力。Python的Flask框架因其轻量级和灵活性&#xff0c;成为构建这类应用的理想选择。 本文…...

powershell 安装 .netframework3.5

在 PowerShell 中安装 .NET Framework 3.5 可以通过几种不同的方法实现&#xff0c;取决于你的操作系统版本。以下是几种常见的方法&#xff1a; 方法1&#xff1a;使用 DISM 命令 对于 Windows 10 和 Windows 8.1&#xff0c;你可以使用 DISM&#xff08;Deployment Image Se…...