disruptor-spring-boot-start启动器

news2024/9/27 16:53:07

文章目录

  • 一. Disruptor简介
    • 1.简介
    • 2.Disruptor官方文档及项目地址
    • 3.原理图
  • 二. disruptor-spring-boot-start启动器使用教程
    • 1.项目中引入依赖如下
      • 1.1 gitee坐标
      • 1.2 github坐标
    • 2.启动类上加入如下注解
    • 3.使用Demo
      • 3.1. DisruptorEventHandler类
      • 3.2. DisruptorBizListener类
      • 3.3. DisruptorHandlerImpl类
      • 3.4. DisruptorController类
  • 三.总结
    • 好文分享

一. Disruptor简介

1.简介

  Disruptor是一种高性能的并发编程框架,它由LMAX Exchange公司开发,并于2011年开源。Disruptor旨在解决传统多线程编程中的性能瓶颈和并发问题,特别适用于需要高度并发处理的场景。

   Disruptor采用了一种称为"无锁编程"的机制,通过使用环形缓冲区(Ring Buffer)和事件驱动的方式实现高效的消息传递和处理。它的核心思想是将消息(事件)在生产者和消费者之间进行无锁的、高效的交换,以减少线程间的竞争和上下文切换。

   在Disruptor中,所有的事件都被放置在一个环形缓冲区中,生产者将事件写入缓冲区,而消费者则从缓冲区中读取事件进行处理。这种设计避免了线程间的锁竞争,使得多个线程可以同时进行读写操作,从而提高了整体的处理能力。

2.Disruptor官方文档及项目地址

gitHub项目地址:
https://github.com/LMAX-Exchange/disruptor
官方文档地址:
https://lmax-exchange.github.io/disruptor/#_read_this_first
user-guide:
https://lmax-exchange.github.io/disruptor/user-guide/index.html
changelog:
https://lmax-exchange.github.io/disruptor/changelog.html

3.原理图

img

二. disruptor-spring-boot-start启动器使用教程

1.项目中引入依赖如下

1.1 gitee坐标

<dependency>
    <groupId>io.gitee.bigbigfeifei</groupId>
    <artifactId>disruptor-spring-boot-start</artifactId>
    <version>1.0</version>
</dependency>

1.2 github坐标

<dependency>
    <groupId>io.github.bigbigfeifei</groupId>
    <artifactId>disruptor-spring-boot-start</artifactId>
    <version>1.0</version>
</dependency>

如果没有依赖到disruptor的依赖,需要在项目中引入以下依赖即可:

<dependency>
   <groupId>com.lmax</groupId>
   <artifactId>disruptor</artifactId>
   <version>3.4.4</version>
</dependency>

  需要注意的是3.4.4是支持JDK8的最有一个3.x的版本
  最新的4.0.0及以上版本需要最小JDK11及以上版本(Minimum Java version now 11)

2.启动类上加入如下注解

  启动类上加上@EnableZlfDisruptor注解

3.使用Demo

  使用超级之简单,拓展性也很好,几行代码就搞定了,减少代码量

3.1. DisruptorEventHandler类

消费者处理类接口实现

package org.example.service.impl;

import com.alibaba.fastjson.JSON;
import com.lmax.disruptor.EventHandler;
import com.zlf.event.DisruptorEvent;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class DisruptorEventHandler implements EventHandler<DisruptorEvent> {

    @Override
    public void onEvent(DisruptorEvent event, long sequence, boolean endOfBatch) throws Exception {
        log.info("DisruptorEventHandler.event:{}", JSON.toJSONString(event));
        //这里测试用,抛异常会被CustomExceptionHandler处理(CustomExceptionHandler会发送springBoot的CustomExceptionHandlerEvent事件,业务监听处理该事件就可以了)
        throw new RuntimeException(JSON.toJSONString(event));
    }
}

  建议消费者的handler处理中最好加上try/catch, 否则,若没有异常没有设置CustomExceptionHandler自定义的异常处理的话, 该消费者的handler线程就不会执行了,这里是需要注意的地方, 还有一个需要注意的地方就是,分开使用DisruptorService中的接口自己组合实现, disruptor创建之后,先要设置好handler相关消费者处理的handlers,且disruptor的start方法只能触发一次, 否则,重复启动disruptor会报错,所以可以直接使用DisruptorService中提供的createAddHandlerStart1方法, 该方法是在设置好了disruptor相关的参数之后, 初始化disruptor之后设置disruptor相关的handler(只需要业务自己实现DisruptorHandler接口:可以自定义消费者的handlers的处理类链路) 且只触发启动一次。

3.2. DisruptorBizListener类

  消费者异常handler处理有异常发送CustomExceptionHandlerEvent的springBoot事件监听,解耦处理

package org.example.service.impl;

import com.alibaba.fastjson.JSON;
import com.zlf.event.CustomExceptionHandlerEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Component
@Slf4j
public class DisruptorBizListener {

    @EventListener
    public void disruptorEventListener(CustomExceptionHandlerEvent event) {
        log.info("DisruptorBizListener.disruptorEventListener.event:{}", JSON.toJSONString(event));

    }
}

3.3. DisruptorHandlerImpl类

package org.example.service.impl;

import com.lmax.disruptor.dsl.Disruptor;
import com.zlf.handler.ClearingEventHandler;
import com.zlf.handler.CustomExceptionHandler;
import com.zlf.handler.DisruptorHandler;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class DisruptorHandlerImpl implements DisruptorHandler {

    @Override
    public void buildHandler(String key, Disruptor disruptor) {
        log.info("===key:{}自定义handler处理链开始=====", key);
        disruptor.handleExceptionsWith(new CustomExceptionHandler());
        disruptor.handleEventsWith(new DisruptorEventHandler())
                .then(new ClearingEventHandler());
        log.info("===key:{}自定义handler处理链结束=====", key);
    }
}

3.4. DisruptorController类

package org.example.controller;

import cn.hutool.core.lang.Tuple;
import com.lmax.disruptor.dsl.ProducerType;
import com.zlf.builder.CustomThreadBuilder;
import com.zlf.builder.ThreadPoolExecutorBuilder;
import com.zlf.builder.WaitStrategyBuilder;
import com.zlf.dto.DisruptorCreate;
import com.zlf.enums.BlockingQueueTypeEnum;
import com.zlf.enums.DisruptorCreateMethodEnum;
import com.zlf.enums.RejectedPolicyTypeEnum;
import com.zlf.enums.WaitStrategyEnum;
import com.zlf.factory.CustomThreadFactory;
import com.zlf.factory.ThreadPoolExecutorFactory;
import com.zlf.service.DisruptorService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.example.service.impl.DisruptorHandlerImpl;
import org.example.service.impl.ThreadPoolService;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Objects;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 一:初始化disruptor姿势
 * https://blog.csdn.net/2401_84048205/article/details/137853949
 *
 * 二:初始化的一些方式:
 * 1. 静态代码块
 *    static {
 *
 *     }
 * 2. 构造方法或构造方法注入
 *     public DisruptorController(){
 *
 *     }
 * 3.@PostConstruct注解
 *     @PostConstruct
 *     public void init() {
 *          //执行构造方法前执行
 *     }*
 * 3. 实现InitializingBean 接口(bean初始化属性设置之后调用afterPropertiesSet)
 * 4. 实现CommandLineRunner或ApplicationRunner接口
 *   在run方法中初始化disruptor也是可以的
 *   https://www.freexyz.cn/dev/47789.html
 * 5. 非静态代码块中
 *     {}
 * 6. 监听springBoot容器启动完成Event
 *
 * 三:静态变量、静态代码块、非静态代码块、构造方法的执行顺序
 * 1. 执行顺序:先父类子类,
 * 2. 静态的东西属于类:类加载的时候只执行一次
 * 3. 非静态代码块、构造方法属于实例,
 * 4. 当类new的时候先执行非静态代码块(非静态代码块按从上到下顺序执行,跟书写位置没有关系,可以写类的任意地方)
 * 6. 后执行构造方法
 * 类的实例可以调用类的静态方法、不推荐这种方式
 */
@Slf4j
@RestController
@RequestMapping("disruptor")
public class DisruptorController implements InitializingBean, ApplicationContextAware {

    private ApplicationContext applicationContext;

    @Autowired
    private DisruptorService disruptorService;

    private DisruptorHandlerImpl disruptorHandler = new DisruptorHandlerImpl();

    private static final ThreadPoolExecutor executor1 = ThreadPoolService.getInstance();

    private static final ThreadFactory threadFactory = new CustomThreadFactory(CustomThreadBuilder.builder()
            .name("test888-thread-factory").isDaemon(Boolean.TRUE).build());

    private static final ThreadPoolExecutor executor2 = new ThreadPoolExecutorFactory(
            ThreadPoolExecutorBuilder.builder()
                    .blockingQueueTypeEnum(BlockingQueueTypeEnum.ARRAY_BLOCKING_QUEUE)
                    .defaultCoreSize(100)
                    .keepAliveTime(60)
                    .unit(TimeUnit.SECONDS)
                    .maxQueueSize(800)
                    .rejectedPolicyTypeEnum(RejectedPolicyTypeEnum.SYNC_PUT_QUEUE_POLICY)
                    .threadFactory(new CustomThreadFactory(CustomThreadBuilder.builder()
                            .name("test666-thread-factory").isDaemon(Boolean.TRUE)
                            .build()))
                    .build()).createThreadPoolExecutor();

    private static String KEY0;

    private static String KEY1;

    private static String KEY2;

    private static String KEY3;

    private static String KEY4;

    private static String KEY5;

    private static String KEY6;

    private static String KEY7;

    /**
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        //方法pushEvent0的disruptor初始化启动方法
        disruptorInitStart0();

        //方法pushEvent1的disruptor初始化启动方法
        disruptorInitStart1();

        //方法pushEvent2的disruptor初始化启动方法
        disruptorInitStart2();

        //方法pushEvent3的disruptor初始化启动方法
        disruptorInitStart3();

        //方法pushEvent4的disruptor初始化启动方法
        disruptorInitStart4();

        //方法pushEvent5的disruptor初始化启动方法
        disruptorInitStart5();

        //方法pushEvent6的disruptor初始化启动方法
        disruptorInitStart6();

        //方法pushEvent7的disruptor初始化启动方法
        disruptorInitStart7();
    }

    private Tuple disruptorInitStart0() {
        Tuple tuple0 = disruptorService.buildKey(DisruptorCreateMethodEnum.CREATE0, "test0");
        DisruptorCreateMethodEnum.createDcbMaps(tuple0);
        KEY0 = tuple0.get(1);
        return disruptorService.createAddHandlerStart1(KEY0, disruptorHandler);
    }

    private Tuple disruptorInitStart1() {
        Tuple tuple1 = disruptorService.buildKey(DisruptorCreateMethodEnum.CREATE1, "test1");
        DisruptorCreateMethodEnum.createDcbMaps(tuple1);
        KEY1 = tuple1.get(1);
        return disruptorService.createAddHandlerStart1(KEY1, disruptorHandler);
    }

    private Tuple disruptorInitStart2() {
        Tuple tuple2 = disruptorService.buildKey(DisruptorCreateMethodEnum.CREATE2, "test2");
        DisruptorCreateMethodEnum.createDcbMaps(tuple2);
        KEY2 = tuple2.get(1);
        return disruptorService.createAddHandlerStart1(KEY2, disruptorHandler);
    }

    private Tuple disruptorInitStart3() {
        Tuple tuple3 = disruptorService.buildKey(DisruptorCreateMethodEnum.CREATE3, "test3");
        DisruptorCreateMethodEnum.createDcbMaps(tuple3);
        KEY3 = tuple3.get(1);
        return disruptorService.createAddHandlerStart1(KEY3, disruptorHandler);
    }

    private Tuple disruptorInitStart4() {
        Tuple tuple4 = disruptorService.buildKey(DisruptorCreateMethodEnum.CREATE4, "test4");
        DisruptorCreateMethodEnum.createDcbMaps(tuple4);
        KEY4 = tuple4.get(1);
        return disruptorService.createAddHandlerStart1(KEY4, disruptorHandler);
    }

    private Tuple disruptorInitStart5() {
        Tuple tuple5 = disruptorService.buildKey(DisruptorCreateMethodEnum.CREATE4, "test5");
        WaitStrategyBuilder waitStrategyBuilder5 = DisruptorCreateMethodEnum.createWaitStrategyMaps(tuple5);
        waitStrategyBuilder5.setWaitStrategyEnum(WaitStrategyEnum.YIELD);
        DisruptorCreateMethodEnum.createExecutorMaps(tuple5, executor1);
        DisruptorCreate disruptorCreate5 = DisruptorCreateMethodEnum.createDcbMaps(tuple5);
        disruptorCreate5.setProducerType(ProducerType.MULTI);
        KEY5 = tuple5.get(1);
        return disruptorService.createAddHandlerStart1(KEY5, disruptorHandler);
    }

    private Tuple disruptorInitStart6() {
        Tuple tuple6 = disruptorService.buildKey(DisruptorCreateMethodEnum.CREATE4, "test6");
        WaitStrategyBuilder waitStrategyBuilder6 = DisruptorCreateMethodEnum.createWaitStrategyMaps(tuple6);
        waitStrategyBuilder6.setWaitStrategyEnum(WaitStrategyEnum.YIELD);
        DisruptorCreateMethodEnum.createExecutorMaps(tuple6, executor2);
        DisruptorCreate disruptorCreate6 = DisruptorCreateMethodEnum.createDcbMaps(tuple6);
        disruptorCreate6.setProducerType(ProducerType.MULTI);
        KEY6 = tuple6.get(1);
        return disruptorService.createAddHandlerStart1(KEY6, disruptorHandler);
    }

    private Tuple disruptorInitStart7() {
        Tuple tuple7 = disruptorService.buildKey(DisruptorCreateMethodEnum.CREATE3, "test7");
        WaitStrategyBuilder waitStrategyBuilder7 = DisruptorCreateMethodEnum.createWaitStrategyMaps(tuple7);
        waitStrategyBuilder7.setWaitStrategyEnum(WaitStrategyEnum.YIELD);
        DisruptorCreateMethodEnum.createThreadFactoryMaps(tuple7, threadFactory);
        DisruptorCreate disruptorCreate7 = DisruptorCreateMethodEnum.createDcbMaps(tuple7);
        disruptorCreate7.setProducerType(ProducerType.MULTI);
        KEY7 = tuple7.get(1);
        return disruptorService.createAddHandlerStart1(KEY7, disruptorHandler);
    }

    @GetMapping("/disruptorInitStart")
    public String disruptorInitStart(int index) {
        if (Objects.isNull(index)) {
            return "index不为空!";
        }
        if (0 == index) {
            disruptorInitStart0();
        } else if (1 == index) {
            disruptorInitStart1();
        } else if (2 == index) {
            disruptorInitStart2();
        } else if (3 == index) {
            disruptorInitStart3();
        } else if (4 == index) {
            disruptorInitStart4();
        } else if (5 == index) {
            disruptorInitStart5();
        } else if (6 == index) {
            disruptorInitStart6();
        } else if (7 == index) {
            disruptorInitStart7();
        }
        return "ok";
    }

    @GetMapping("/showdown")
    public String showdown(String key) {
        if (StringUtils.isEmpty(key)) {
            return "key不为空!";
        }
        Tuple tuple = disruptorService.getKeyMaps().get(key);
        if (Objects.nonNull(tuple) && StringUtils.isNotBlank(tuple.get(1))) {
            disruptorService.shutdown(tuple.get(1));
        }
        return "ok";
    }

    /**
     * 关闭方法1
     */
    @GetMapping("/destroy")
    public void destroy() {
        // 在需要关闭容器的时候调用
        SpringApplication.exit(applicationContext);
    }

    /**
     * 关闭方法2
     */
    @GetMapping("/destroy2")
    public void destroy2() {
        ConfigurableApplicationContext context = (ConfigurableApplicationContext) applicationContext;
        context.close();
    }


    @GetMapping("/pushEvent0")
    public String pushEvent0() {
        disruptorService.pushEvent0(KEY0, "你好,disruptor0");
        return "ok";
    }

    @GetMapping("/pushEvent1")
    public String pushEvent1() {
        disruptorService.pushEvent0(KEY1, "你好,disruptor1");
        return "ok";
    }


    @GetMapping("/pushEvent2")
    public String pushEvent2() {
        disruptorService.pushEvent0(KEY2, "你好,disruptor2");
        return "ok";
    }


    @GetMapping("/pushEvent3")
    public String pushEvent3() {
        disruptorService.pushEvent0(KEY3, "你好,disruptor3");
        return "ok";
    }


    @GetMapping("/pushEvent4")
    public String pushEvent4() {
        disruptorService.pushEvent0(KEY4, "你好,disruptor4");
        return "ok";
    }

    @GetMapping("/pushEvent5")
    public String pushEvent5() {
        disruptorService.pushEvent0(KEY5, "你好,disruptor5");
        return "ok";
    }

    @GetMapping("/pushEvent6")
    public String pushEvent6() {
        disruptorService.pushEvent0(KEY6, "你好,disruptor6");
        return "ok";
    }

    @GetMapping("/pushEvent7")
    public String pushEvent7() {
        disruptorService.pushEvent0(KEY7, "你好,disruptor7");
        return "ok";
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

三.总结

  使用该start启动器可以简化Disruptor的使用,拓展性好,代码量少, 上面的Disruptor初始化的代码可以和pushEvent发送消息的代码写在一个方法里面, 但是建议不写一起,分开可以做到解耦的目的,提高接口性能, 使用Disruptor原生的那些api足够了,其它Disruptor的api使用姿势都不推荐去搞, 专注业务即可,该start可以自定义Disruptor的相关参数,还可以自定义线程池的执行器, 根据自己的需求来自定义即可(建议不要自定义太多,否则造成对象创建泛滥导致内存溢出), 所以一般自定义一个全局静态的就可以了,参数可以根据业务调整即可, 该start将java原有的线程池和线程池的相关队列等和Disruptor很好的结合到了一起, 可以发挥二者各自的优势,给业务带来很大的性能提升,网上我还发现一个Disruptor的一个start:

https://github.com/hiwepy/disruptor-spring-boot-starter

  我把他的源码拉下来看了一下,发现写的花里胡哨的,太过于炫技了,华而不实, 搞了一条handler的链路,责任链模式的使用,我看他是照抄了spingMvc的那个实现的,其实完全没有必要的, 因为Disruptor的handler的链路实现相当的灵活,所以搞成自定义的就好了,他那个starter里面只可以搞 一个Disruptor,而我写的这个可以根据自己的业务来自定义Disruptor,从使用的角度上然Disruptor的 使用变得非常简单,代码量也降低了,拓展性也很好,事半功倍,所以说在制造轮子的时候,需要多方面的去考虑, 可以从简化的角度、拓展性的角度、适用场景的角度,尽量从多方面多角度等去设计考虑实现, 我写的这个disruptor-spring-boot-start启动器是原创独一无二超级好用的一个disruptor的启动器了, SeaTunnel里面也用到了Disruptor的,之前网上说Disruptor处理600w订单数据, Disruptor的设计思想确实是牛逼的。

好文分享

https://developer.aliyun.com/article/1409939
https://www.cnblogs.com/konghuanxi/p/17324988.html
https://blog.csdn.net/weixin_43996530/article/details/132721172
https://blog.csdn.net/qq_39939541/article/details/131508396
https://www.cnblogs.com/konghuanxi/p/17303118.html

  关于Disruptor的原理、源码等可以看上面的好文和Disruptor的官方项目地址及文档,关于它是如何解决CPU缓存伪共享问题的?在下面这篇文章中有讲的:

https://developer.aliyun.com/article/1409939

  简单来说:将变量对齐cpu缓存行,在多核cpu执行的时候每一次尽可能多的将变量加载对齐好的缓存行数据, 避免多核加载数据被修改导致缓存行失效而要重新去加载一次数据, 这种对齐缓存行的方式提高了数据加载和使用的效率。 本次分享到此结束,希望对你有所启发和帮助还请大家多去点个star,开源不易,该项目我写了好几天的,优化重构n多次,也测试了n多次, 请尊重一下原创, 且做了下压测,性能还是非常的好, 请一键三连,么么么哒!

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

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

相关文章

基于C#的串口助手,VS2022最新教程

大家好,给大家分享一个本人集合了CSDN各方的代码做成了一个基于C#的串口助手,学了两三天,还是挺不错的,该有的功能都有,给大家看下界面。 设计的思路也很简单 获取串口号:这边使用定时器来获取,可以达到实时更新串口号的效果,点击选择串口定时器就关闭, 关闭串口就会…

每日OJ题_牛客_NC1大数加法_高精度加法_C++_Java

目录 牛客_NC1大数加法_高精度加法 题目解析 C代码 Java代码 牛客_NC1大数加法_高精度加法 题目解析 模版类型的算法题&#xff0c;模拟加法列竖式运算的过程即可。 假定算法流程&#xff1a; 设定 i&#xff0c;j 两指针分别指向 s&#xff0c;t 尾部&#xff0c;模拟…

Facebook公共主页bug问题解决措施清单

在使用Facebook的过程中&#xff0c;许多用户可能会遇到一些让人困扰的BUG&#xff0c;这些问题往往会让人感到无奈。为了帮助大家更好地应对这些情况&#xff0c;本文将总结一些常见的BUG以及对应的解决方案&#xff0c;主要集中在公共主页的相关问题。如果感兴趣就请读下去吧…

学习记录:js算法(四十七):相同的树

文章目录 相同的树我的思路网上思路队列序列化方法 总结 相同的树 给你两棵二叉树的根节点 p 和 q &#xff0c;编写一个函数来检验这两棵树是否相同。 如果两个树在结构上相同&#xff0c;并且节点具有相同的值&#xff0c;则认为它们是相同的。 图一&#xff1a; 图二&…

软件测试工程师...我是如何正确地申请加薪的!

01 合理加薪&#xff0c;明确价值 突然有一天被人问到&#xff0c;你是怎么跟老板谈薪资和加薪的事情的&#xff1f; 我一愣&#xff0c;思考了一下&#xff0c;回想我这么些年工作&#xff0c;好像除了入职的时候跟老板讲过工资&#xff0c;其他的时候从没有主动跟老板或领导…

从数据到作图,三步教会你风向玫瑰图

很多小伙伴在用0rigin软件作风向玫瑰图时就卡在第一步&#xff0c;精准全面的风向数据去哪获取? 给大家推荐一个平台——羲和能源气象大数据平台&#xff0c;在这你不仅可以获取风向数据还能直接生成“风向玫瑰图”,一步到位! 步骤一&#xff1a;搜索“羲和能源气象大数据平…

CDGA|数据流通新策略:高效利用,解锁数字经济新动能

在数字化浪潮席卷全球的今天&#xff0c;数据已成为驱动经济社会发展的关键生产要素。随着大数据、云计算、人工智能等技术的飞速发展&#xff0c;数据的价值被无限放大&#xff0c;而如何高效地流通与利用这些数据&#xff0c;成为了摆在各行各业面前的重要课题。本文将探讨数…

vue.js——“微商城”后台管理系统

1. 需求背景&#xff1a; 先创建运行环境&#xff0c;“微商城”后台管理系统是一种后台管理系统平台,旨在提供一个便捷、安全和高效的管 理和操作各类数据的平台。系统将涵盖用户登录、商品管理、分类管理、新增分类和个人中 心等功能&#xff0c;以满足用户高效数据管理的各…

Vue2项目中vuex如何简化程序代码,提升代码质量和开发效率

Vuex为Vue中提供了集中式存储 库&#xff0c;其主要分为state、getter、mutation、action四个模块&#xff0c;它们每个担任了不同角色&#xff0c;分工不同&#xff1b;Vuex允许所有的组件共享状态抽取出来&#xff0c;以一个全局单例模式管理&#xff0c;状态集中存储在同一…

安装软件及apt install -f修复均报错

UOS统信安装软件过程及修复依赖过程&#xff0c;可排查deepin-installer和dpkg问题 文章目录 一、问题现象二、问题原因三、解决方案 一、问题现象 执行apt install -f 都会出现该报错&#xff0c;如图所示&#xff1a; 二、问题原因 造成这种情况的原因在于/var/lib/dpkg/…

视频生成模型哪家强?豆包可灵通义海螺全面评测【AI评测】

比较贴切的表述是&#xff0c;豆包的视频模型这次的升级&#xff0c;已然将国内AI视频的美学境界拔高了一个档次&#xff0c;让AI视频也开始变得更加实用了。 作者|斗斗 出品|产业家 国内的文生视频领域&#xff0c;也是吃上“细糠”了。 最近&#xff0c;火山引擎宣布豆…

必收藏,售后客服日常回复必备的话术 (精华版)

在售后客服工作中&#xff0c;使用恰当的话术对客户进行回复至关重要。本文精选了售后客服日常工作中必备的精华话术&#xff0c;旨在帮助客服人员提升回复效率和服务质量。其中包括客户投诉处理、问题解决、礼貌用语等多个方面的话术内容。 前言 在售后客服工作中&#xff0c…

Flux【lora模型】【禅意插画】:画风清新唯美,充满禅意韵味的插画模型:Zenpainting l 禅意插画

大家好我是安琪&#xff01;&#xff01;&#xff01; 今天和大家推荐一款基于Flux训练的禅意插画风格的lora模型:Zenpainting l 禅意插画**。**此Lora模型可生成富禅意韵味的艺术插画&#xff0c;画风清新唯美&#xff0c;充满艺术意境。 提示词&#xff1a;Zen painting il…

URI和URL的区别

1: 将 URI 转换为 URL import java.net.URI; import java.net.URL;public class UriToUrlExample {public static void main(String[] args) {// 创建一个 URI 对象URI uri = new URI("http://example.com/path/to/resource");// 将 URI 转换为 URLtry {URL url = u…

C++:模板(1)

目录 实现泛型的交换函数 函数模板 1.概念 2.格式 3.原理 4.函数模板实例化 5.函数模板参数的匹配原则 类模板 1.定义格式 2.实例化 3.声明与定义问题 实现泛型的交换函数 我们实现一个对所有类型都通用的交换函数&#xff0c;可以用函数重载来实现。 void Swap(in…

Map和Set,TreeMap和TreeSet,HashMap和HashSet

文章目录 TreeSet和TreeMap二叉搜索树模拟TreeMAp定义 基本操作插入查找删除(难点)遍历性能分析应用场景 Map&&Set模型 HashMap常用方法 HashSet常用方法HashMap和HashSet区别数据结构不同元素类型不同方法不同使用场景不同 TreeSet和TreeMap 定义&#xff1a; TreeSe…

个人健康档案管理系统

基于springbootvue实现的个人健康档案管理系统&#xff08;源码L文ppt&#xff09;4-076 4.1 系统功能结构设计 根据对个人健康档案管理系统的具体需求分析&#xff0c;把系统可以划分为几个不同的功能模块&#xff1a;管理员可以对系统首页、用户管理、健康体检管理、疫…

智能密码、指纹锁语音芯片ic方案 可存放40s语音内容 NVD语音芯片

随着科技的飞速发展&#xff0c;智能家居安全领域迎来了前所未有的变革。智能密码与指纹锁作为现代家庭安全防护的重要一环&#xff0c;其背后的语音芯片IC开发更是这一变革中的关键技术突破。 智能密码、指纹锁语音芯片ic方案 选型与简介&#xff1a; NVD语音芯片是一款低成…

基于JAVA+SpringBoot+Vue的疫苗发布和接种预约系统

基于JAVASpringBootVue的疫苗发布和接种预约系统 前言 ✌全网粉丝20W,csdn特邀作者、博客专家、CSDN[新星计划]导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末附源码下载链接&#x1f3…

AlmaLinux 安裝JDK8

在 AlmaLinux 上安装 JDK 8 可以通过包管理器 dnf 来完成。AlmaLinux 是基于 RHEL 的一个开源发行版&#xff0c;因此其包管理系统和 RHEL 类似。以下是详细的步骤来安装 OpenJDK 8 1. 更新系统包列表 sudo dnf update -y 2. 安装 OpenJDK 8 使用 dnf 安装 OpenJDK 8。你可…