Lottery 分布式抽奖(个人向记录总结)

1.搭建(DDD+RPC)架构

DDD——微服务架构(微服务是对系统拆分的方式)

(Domain-Driven Design 领域驱动设计)
DDD与MVC同属微服务架构
是由Eric Evans最先提出,目的是对软件所涉及到的领域进行建模,以应对系统规模过大时引起的软件复杂性的问题。

DDD分层

在这里插入图片描述

RPC接口:对外提供接口调用整个服务
DDD分层:(箭头是剪头尾对头的引用)

  • 接口层:对内提供接口【接口调用应用层,应用层调用领域层】
  • 基础层:对数据仓储服务【基础层引用领域层,领域层来定义仓储服务的接口,基础层去做仓储服务的实现】
  • 领域层:(核心)封装具体业务功能
  • 通用层:返回对象、枚举
  • 应用层:逻辑包装

MVC与DDD区别

在这里插入图片描述
驱动设计模式:

具体来说,对于这么一个抽奖领域domain
在这里插入图片描述

【上图repository包】这里只定义接口,在基础层infrastructure进行实现。
model,用于提供vo、req、res 和 aggregates 聚合对象
repository,对数据库,其实也就是对Mysql、Redis等数据的统一包装。
service,是具体的业务领域逻辑实现层,在这个包下定义了algorithm抽奖算法实现和具体的抽奖策略包装 draw 层,对外提供抽奖接口 IDrawExec#doDrawExec。

也就是说,这些model里的这些对象都是用于服务自己的领域,不会去服务其他领域

为什么使用斐波那契散列索引(感觉有点怪多余)

为了尽量均匀散列减少碰撞
使用斐波那契散列索引,使用斐波那契计算能起到不错的散列效果。

2.模版模式处理抽奖流程

职责分离,标准定义。
在这里插入图片描述

在这里插入图片描述
配置类:配置抽奖策略
抽象接口方法:【抽奖执行接口】
抽奖数据支撑:支撑类继承配置类里的方法,并提供数据服务
抽象类:提供标准的执行流程***【模板】(上面几个部分这么分就是为了将抽象类瘦身,将标准流程)
实现类:针对情景的具体
业务实现
*(将会在这@Service("drawExec")业务逻辑的实现层)

整个实现过程流式:这样将接口间的职责进行分离,将接口间的功能职责分离;

模板模式应用

本章节最大的目标在于把抽奖流程标准化,需要考虑的一条思路线包括:

  • 1根据入参策略ID获取抽奖策略配置
  • 2校验和处理抽奖策略的数据初始化到内存
  • 3获取那些被排除掉的抽奖列表,这些奖品可能是已经奖品库存为空,或者因为风控策略不能给这个用户薅羊毛的奖品
  • 4执行抽奖算法
  • 5包装中奖结果
    1,2是基础配置,3,4是需要根据自身具体业务实现——所以定义抽象类等具体业务去实现

模板方法定义好标准流程后,其他开发人员格子各自开发自己的业务,不会破坏整体的开发结构。
关于模版模式的核心点在于由抽象类定义抽象方法执行策略,也就是说父类规定了好一系列的执行标准,这些标准的串联成一整套业务流程
遇到适合的场景使用这样的设计模式也是非常方便的,因为他可以控制整套逻辑的执行顺序和统一的输入、输出,而对于实现方只需要关心好自己的业务逻辑即可

3.简单工厂搭建发奖domain

本质:就是为了简化if else判断不同类型使用不同的代码处理, 使用map将不同的类型和对应的代码联系到一起。让代码变得更整洁。

工厂模式:是一种创建型设计模式,在父类中提供一个创建对象的方法,允许子类决定实例化对象的类型。
奖品服务交给工厂,工厂提供对外的发奖服务,由工厂进行统一包装【减少使用ifelse,使用map将奖品类型map过来】。
“发放奖品”工厂作用:外部提供一个奖品类型,工厂提供这个奖品类型需要提供什么样的服务去处理。

4.状态模式完成状态流转

状态模式(State Pattern):属于行为型模式,是允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。
在软件开发中,经常需要根据对象的不同状态进行不同的逻辑处理,通常情况下我们会使用if-else/switch-case进行判断处理,但是大量的逻辑判断语句会使程序臃肿,不利用程序的扩展与维护。这时可以将不同状态下的逻辑处理抽象分离出来,使程序更加健壮。状态模式这批篇博客例子挺好
【这里也是用的map,将对应状态】

怎么使用的:
传一个状态实例,实现类里调用对应状态的要进行的操作。

5.策略模式选择生成ID算法

策略模式属于行为模式的一种,一个类的行为或算法可以在运行时进行更改。
使用策略模式把三种生成ID的算法进行统一包装,由调用方根据不同的场景来选择出适合的ID生成策略
这里三种方式生成ID雪花算法、随机算法、日期算法,分别用在订单号、策略ID、活动号的生成上。【使用map】
在这里插入图片描述

优惠策略的选取也是使用策略模式详见 实战策略模式「模拟多种营销类型优惠券,折扣金额计算策略场景」

6.分库分表来缓解缓存压力

基于 HashMap 核心设计原理,使用哈希散列+扰动函数的方式,把数据散列到多个库表中的组件。
由于业务体量较大,数据增长较快,所以需要把用户数据拆分到不同的库表中去,减轻数据库压力。
分库分表操作主要有垂直拆分和水平拆分:

  • 垂直拆分:指按照业务将表进行分类,分布到不同的数据库上,这样也就将数据的压力分担到不同的库上面。最终一个数据库由很多表的构成,每个表对应着不同的业务,也就是专库专用。
  • 水平拆分:如果垂直拆分后遇到单机瓶颈,可以使用水平拆分,区别是:垂直拆分是把不同的表拆到不同的数据库中,而本章节需要实现的水平拆分,是把同一个表拆到不同的数据库中。如:user_001、user_002

这里实现的是水平拆分的路由设计
大致思路:
分库:通过AOP方式,拦截@dbRouter注解,(将用户ID、订单ID)通过一致性Hash计算目标数据源,缓存到Threadlocal里。配置DynamicDataSource,从Threadlocal中读取目标数据源key执行切换。
分表:利用MyBatis拦截器,在@DBRouterStrategy(true)标记的类里,从Threadlocal中读取目标ID,补全SQL语句。
计算落到那个库中然后动态切换数据源
计算落到那个表中然后mybatis拦截器,拦截到对应的sql语句然后添加上具体的表单号。
在这里插入图片描述

只分库部分表只加@DBRouter路由,就到对应的
既分库又分表需要再@Mapper下加@DBRouterStrategy(splitTable = true)

(1)数据库路由设计要包括哪些技术知识点

  • AOP 切面拦截的使用:这是因为需要给使用数据库路由的方法做上标记,便于处理分库分表逻辑。
  • 数据源的切换操作:既然有分库那么就会涉及在多个数据源间进行链接切换,以便把数据分配给不同的数据库
  • 数据库表寻址操作:一条数据分配到哪个数据库,哪张表,都需要进行索引计算。在方法调用的过程中最终通过 ThreadLocal 记录
  • 数据散列的操作:让数据均匀的分配到不同的库表中去

(2)为什么分库分表?

分库分表基本是单表200万,才分,你们为什么分库分表?

  1. 我们分库分表用的非常熟。但不能为了等到系统到了200万数据,才拆。那么工作量会非常大
  2. 我们的做法是,因为有成熟方案,所以前期就分库分表了。但,为了解释服务器空间。所以把分库分表的库,用服务器虚拟出来机器安装。这样即不过多的占用服务器资源,也方便后续数据量真的上来了,好拆分。
  3. 同时,抽奖系统,是瞬时峰值较高的系统,历史数据不一定多。所以我们希望,用户可以**快速的检索到个人数据,做最优响应。**因为大家都知道,抽奖这东西,push发完,基本就1~3分钟结束,10分钟人都没了。所以我们这也是做了分库分表的理由。

(3)库表数量设置为什么是2的n次幂

算法基于HashMap,分表数量也要基于HashMap从而更好的散列,避免id字段都分配到一个库表上去不均匀。

在这里插入图片描述

7. 编程式事务领取活动开发

由于多次切换数据源导致使用注解式的事务失效,所以这里使用编程式事务,在开始之前将路由切换好。
好处:不用等抛出异常再事务回滚
扩展路由组件,拆解路由策略满足编程式路由配合编程式事务一起使用。
使用模板模式开发领取活动领域,因为在领取活动中需要进行活动的日期、库存、状态等校验,并处理扣减库存、添加用户领取信息、封装结果等一系列流程操作,因此使用抽象类定义模板模式更为妥当。
通过编程式事务将参与次数表的活动次数扣减写入用户领取活动表连在一起合并为一个事务。

每次写入参与活动时,会生成uuid用来防重,由uid(用户ID)+活动id+参与次数组成

8.应用层编排抽奖过程

领域层:写入一些功能
应用层:流程编排
在这里插入图片描述

(1)抽奖整个活动过程的流程编排,主要包括:对活动的领取、对抽奖的操作、对中奖结果的存放,以及如何处理发奖,MQ触发发奖流程。
对于每一个流程节点编排的内容,都是在领域层开发完成的,而应用层只是做最为简单的且很薄的一层。其实这块也很符合目前很多低代码的使用场景,通过界面可视化控制流程编排,生成代码。
(2)给user_take_activity表增加state【活动单使用状态 0未使用、1已使用】用于记录当前领取的活动有没有执行抽奖。目的是当抽奖过程中发生失败(系统,网络等原因),还触发到数据库中,这时用于保留未使用抽奖的状态。
同时,state还可以将两张表做一个幂等性的事务处理。
(幂等性问题就是同一个接口,多次发出同一个请求,必须保证操作只执行一次。)

(3)将user_take_activity表和 user_strategy_export_00(0-3) 表做一个幂等性的事务
用户领取活动表user_take_activity使用takeID生成user_strategy_export_表的UUID来防重,达到一次参加活动只生成一个抽奖单
UUID设置了unique唯一约束

抽奖单的UUID由领取活动表的takeid而来。

9.规则引擎,量化人群参与活动

使用组合模式搭建用于量化人群的规则引擎,用于用户参与活动之前,通过规则引擎过滤性别、年龄、首单消费、消费金额、忠实用户等各类身份来量化出具体可参与的抽奖活动。通过这样的方式控制运营成本和精细化运营
(1)增加规则引擎开发需要的相关的配置类表:rule_tree规则树(包括名字,描述等)'、rule_tree_node(节点类型,值,规则)、rule_tree_node_line(节点连接情况from,to)
(2)运用组合模式搭建规则引擎领域服务,包括:logic 逻辑过滤器、engine 引擎执行器
(3)修改 lottery-infrastructure 基础层中仓储实现类更为合适的的注解为 @Repository 包括: ActivityRepository、RuleRepository、StrategyRepository、UserTakeActivityRepository
(1)为什么使用组合模式?
组合模式是一种结构型模式,它将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。
如果使用if-else语句去判断是哪种数据会比较麻烦且代码量大大增加,对以后的维护增加了难度,使用组合模式解决了这个问题,让代码更加干净整洁,为后续添加更多决策信息的时候更加轻便,且维护起来更加简单。
(2)这个规则树为啥要放到数据库里啊,直接写在代码里判断不行么?
放在数据库可以动态化配置,要的是这个。否则后面调整,只能改代码了。
决策树:
在这里插入图片描述
树结构原子模块的组织关系:开发每个节点(过滤器)的逻辑这每一个节点是用来作比对的
节点开发完后,执行引擎根据规则来串联节点关系得到决策树,最后遍历决策树得到过滤后的活动。

10.门面接口封装和对象转换

在领域层编写各种活动参与过程,抽奖过程,规则过程
在应用层进行逻辑包装
在接口层对外部提供应用
描述:在 lottery-interfaces 接口层创建 facade 门面模式 包装抽奖接口,并在 assembler 包 使用 MapStruct 做对象转换操作处理。

  • 补充 lottery-application 应用层对规则引擎的调用,添加接口方法 IActivityProcess#doRuleQuantificationCrowd
  • 删掉 lottery-rpc 测试内容,新增加抽奖活动展台接口 ILotteryActivityBooth,并添加两个抽奖的接口方法,普通抽奖和量化人群抽奖。
  • 开发 lottery-interfaces 接口层,对抽奖活动的封装,并对外提供抽奖服务。

对象转换
背景:以 DDD 设计的结构框架,在接口层和应用层需要做防污处理,也就是说不能直接把应用层、领域层的对象直接暴露处理,因为暴露出去可能会随着业务发展的过程中不断的添加各类字段,从而破坏领域结构。那么就需要增加一层对象转换,也就有了 vo2dto、dto2vo 的操作。但这些转换的字段又基本都是重复的,在保证性能的情况下,一些高并发场景就只会选择手动编写 get、set,但其实也有很多其他的方式,转换性能也不差,这里我们列举一下。

在这里插入图片描述
上图总结:BeanUtils.copyProperties 是大家代码里最常出现的工具类,但只要你不把它用错成 Apache 包下的,而是使用 Spring 提供的,就基本还不会对性能造成多大影响。
但如果说性能更好,**可替代手动get、set的,还是 MapStruct 更好用,**因为它本身就是在编译期生成get、set代码,和我们写get、set一样。

(1)MapStruct 对象转换操作

MapStruct使用:只需要定义一个 Mapper 接口,MapStruct就会自动实现这个映射接口,避免了复杂繁琐的映射实现。

//MapStruct 对象转换操作
@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE, unmappedSourcePolicy = ReportingPolicy.IGNORE)
public interface AwardMapping extends IMapping<DrawAwardVO, AwardDTO> {

    @Mapping(target = "userId", source = "uId")
    @Override
    AwardDTO sourceToTarget(DrawAwardVO var1);

    @Override
    DrawAwardVO targetToSource(AwardDTO var1);

}
  1. 定义接口 AwardMapping 继承 IMapping<DrawAwardVO, AwardDTO> 做对象转换操作
  2. 如果一些接口字段在两个对象间不是同名的,则需要进行配置,就像 uId -> userId

11.MQ解耦抽奖发货流程

描述:使用MQ消息的特性,把用户抽奖到发货到流程进行解耦。这个过程中包括了消息的发送、库表中状态的更新、消息的接收消费、发奖状态的处理等。

  • Kafka:是一个高性能、可扩展的分布式发布订阅消息系统,主要用于处理大规模的实时数据流。它可以处理大量的数据,并使您能够将消息从一个端点传递到另一个端点。 Kafka适合离线和在线消息消费。 Kafka消息保留在磁盘上,并在群集内复制以防止数据丢失。

  • Kafka的Topic是一个消息的逻辑分类。一套消息系统中为多个模块(比如:订单模块和商品模块)提供服务。那就要对不同类型的消息进行逻辑分类,具体分类的方式就是用Topic进行区分,不同类别的消息具有不同的Topic。

  • Zookeeper:则是一个分布式协调服务,负责管理和协调分布式系统中的各种资源。
    Kafka构建在ZooKeeper同步服务之上。 它与Apache Storm和Spark非常好地集成,用于实时流式数据分析。

  • MQ消息队列:消息队列(Message Queue,简称MQ)指保存消息的一个容器,其实本质就是一个保存数据的队列。
    消息中间件是指利用高效可靠的消息传递机制进行与平台无关的数据交流,并基于数据通信来进行分布式系统的构建
    消息中间件是分布式系统中重要的组件,主要解决应用解耦,异步消息,流量削峰等问题,实现高性能,高可用,可伸缩和最终一致性的系统架构。目前使用较多的消息队列有ActiveMQ,RabbitMQ,ZeroMQ,Kafka,MetaMQ,RocketMQ等。
    主要用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。以下介绍消息队列在实际应用中常用的使用场景:异步处理,应用解耦,流量削峰和消息通讯四个场景
    这里对MQ总结的挺好 全面了解消息队列MQ

  • 应用解耦:一般一个流程到另一个流程需要函数方法调用,而使用了MQ就可以不用直接调用而是通过订阅状态获取通知消息。

(1)用kafka+MQ处理发奖流程

MQ处理时需要考虑:(1)MQ发送失败需要Worker进行补偿发送(2)MQ消费(接收)失败,进行重试
这两次发送失败而发送多次,但对数据库的操作只能是一次,所以要进行幂等性操作。

  • 在数据库表 user_strategy_export 添加字段 mq_state 这个字段用于发送 MQ 成功更新库表状态,如果 MQ 消息发送失败则需要通过定时任务补偿 MQ 消息。
  • 启动 kafka 新增 topic:lottery_invoice 用于发货单消息,当抽奖完成后则发送一个发货单,再异步处理发货流程,这个部分就是MQ的解耦流程使用
    在这里插入图片描述

(1)生产消息:
lottery.application.mq.producer

@Component
public class KafkaProducer {

    private Logger logger = LoggerFactory.getLogger(KafkaProducer.class);

    @Resource
    private KafkaTemplate<String, Object> kafkaTemplate;

    /**
     * MQ主题:中奖发货单
     */
    public static final String TOPIC_INVOICE = "lottery_invoice";

    /**
     * 发送中奖物品发货单消息
     *
     * @param invoice 发货单
     */
    public ListenableFuture<SendResult<String, Object>> sendLotteryInvoice(InvoiceVO invoice) {
        String objJson = JSON.toJSONString(invoice);
        logger.info("发送MQ消息 topic:{} bizId:{} message:{}", TOPIC_INVOICE, invoice.getuId(), objJson);
        return kafkaTemplate.send(TOPIC_INVOICE, objJson);
    }
}

我们会把所有的生产消息都放到 KafkaProducer 中,并对外提供一个可以发送 MQ 消息的方法。
因为我们配置的类型转换为 StringDeserializer 所以发送消息的方式是 JSON 字符串,当然这个编解码器是可以重写的,满足你发送其他类型的数据。
(2)消费消息:
lottery.application.mq.consumer

@Component
public class LotteryInvoiceListener {

    private Logger logger = LoggerFactory.getLogger(LotteryInvoiceListener.class);

    @Resource
    private DistributionGoodsFactory distributionGoodsFactory;

    @KafkaListener(topics = "lottery_invoice", groupId = "lottery")
    public void onMessage(ConsumerRecord<?, ?> record, Acknowledgment ack, @Header(KafkaHeaders.RECEIVED_TOPIC) String topic) {
        Optional<?> message = Optional.ofNullable(record.value());

        // 1. 判断消息是否存在
        if (!message.isPresent()) {
            return;
        }

        // 2. 处理 MQ 消息
        try {
            // 1. 转化对象(或者你也可以重写Serializer<T>)
            InvoiceVO invoiceVO = JSON.parseObject((String) message.get(), InvoiceVO.class);

            // 2. 获取发送奖品工厂,执行发奖
            IDistributionGoods distributionGoodsService = distributionGoodsFactory.getDistributionGoodsService(invoiceVO.getAwardType());
            DistributionRes distributionRes = distributionGoodsService.doDistribution(new GoodsReq(invoiceVO.getuId(), invoiceVO.getOrderId(), invoiceVO.getAwardId(), invoiceVO.getAwardName(), invoiceVO.getAwardContent()));

            Assert.isTrue(Constants.AwardState.SUCCESS.getCode().equals(distributionRes.getCode()), distributionRes.getInfo());

            // 3. 打印日志
            logger.info("消费MQ消息,完成 topic:{} bizId:{} 发奖结果:{}", topic, invoiceVO.getuId(), JSON.toJSONString(distributionRes));

            // 4. 消息消费完成
            ack.acknowledge();
        } catch (Exception e) {
            // 发奖环节失败,消息重试。所有到环节,发货、更新库,都需要保证幂等。
            logger.error("消费MQ消息,失败 topic:{} message:{}", topic, message.get());
            throw e;
        }
    }
}

每一个 MQ 消息的消费都会有一个对应的 XxxListener 来处理消息体,如果你使用一些其他的 MQ 可能还会看到一些抽象类来处理 MQ 消息集合。
在这个 LotteryInvoiceListener 消息监听类中,主要就是通过消息中的发奖类型获取到对应的奖品发货工厂,处理奖品的发送操作。
在奖品发送操作中,已经补全了 DistributionBase# updateUserAwardState 更新奖品发送状态的操作。

(3)抽奖流程解耦:

 // 5. 发送MQ,触发发奖流程
        InvoiceVO invoiceVO = buildInvoiceVO(drawOrderVO);
        ListenableFuture<SendResult<String, Object>> future = kafkaProducer.sendLotteryInvoice(invoiceVO);//发送一个中奖结果的发货单
        future.addCallback(new ListenableFutureCallback<SendResult<String, Object>>() {//消息发送完毕后进行回调处理,更新数据库中 MQ 发送的状态

            @Override
            public void onSuccess(SendResult<String, Object> stringObjectSendResult) {
                // 4.1 MQ 消息发送完成,更新数据库表 user_strategy_export.mq_state = 1
                activityPartake.updateInvoiceMqState(invoiceVO.getuId(), invoiceVO.getOrderId(), Constants.MQState.COMPLETE.getCode());
            }

            @Override
            public void onFailure(Throwable throwable) {
                // 4.2 MQ 消息发送失败,更新数据库表 user_strategy_export.mq_state = 2 【等待定时任务扫码补偿MQ消息】
                activityPartake.updateInvoiceMqState(invoiceVO.getuId(), invoiceVO.getOrderId(), Constants.MQState.FAIL.getCode());
            }

        });

        // 6. 返回结果
        return new DrawProcessResult(Constants.ResponseCode.SUCCESS.getCode(), Constants.ResponseCode.SUCCESS.getInfo(), drawAwardVO);

消息发送完毕后进行回调处理,更新数据库中 MQ 发送的状态,如果:场景-1:mq_state = 2 发送消息失败,定时任务扫描后直接触发发送,并更新发送状态。场景-2:mq_state = 1 且更新时间与现在时间对比超过15分钟或者10分钟,那么定时任务扫描触发发送MQ,并更新发送状态
现在从用户领取活动、执行抽奖、结果落库,到 发送MQ处理后续发奖的流程就解耦了,因为用户只需要知道自己中奖了,但发奖到货是可以等待的,毕竟发送虚拟商品的等待时间并不会很长,而实物商品走物流就更可以接收了。所以对于这样的流程进行解耦是非常有必要的,否则你的程序逻辑会让用户在界面等待更久的时间。

12.xxl job处理活动状态扫描

XXL-JOB 系统简介:
XXL-JOB是一个分布式任务调度平台,处理需要使用定时任务解决的场景。其核心设计目标是开发迅速、学习简单、轻量级、易扩展。现已开放源代码并接入多家公司线上产品线,开箱即用。

任务需要在多台机器上跑

名词总结:

在代码发那个面这一整 个Project:叫做工程
业务:比如抽奖、添加购物车、删除购物车
事务:Spring的事务是逻辑上的一组操作,要么都执行,要么都不执行。
系统(应用):整个外卖平台、营销平台。有的时候也分开把每个微服务叫系统,比如前台系统、交易系统、支付系统等。
微服务:一套商城内,商品、下单、支付、发货、结算、营销(抽奖、优惠券)每一个是一套微服务,来构成整个商城。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

html(抽奖设计)

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>抽奖</title><style type"text/css">* {margin: 0;padding: 0;}.container {width: 800px;height: 800px;border: 1px dashed red;position: absolut…

【学术会议征稿】第三届智能电网与能源系统国际学术会议

第三届智能电网与能源系统国际学术会议 2024 3rd International Conference on Smart Grid and Energy Systems 第三届智能电网与能源系统国际学术会议&#xff08;SGES 2024&#xff09;将于2024年10月25日-27日在郑州召开。 智能电网可以优化能源布局&#xff0c;让现有能源…

C++之多态使用小结

1、多态定义 1.1 多态概念 C多态性&#xff08;Polymorphism&#xff09;是面向对象编程(OOP)的一个重要特性之一&#xff0c;它允许我们使用统一的接口来处理不同类型的对象。多态性使得程序更加灵活、可扩展并且易于维护。 通俗来说&#xff0c;就是多种形态&#xff0…

Java小白入门到实战应用教程-开发环境搭建-IDEA2024安装激huo详细教程

writer:eleven 安装IDEA2024 一、下载IDEA 推荐大家去官网下载 我这里也给大家直接准备了安装包&#xff0c;和激huo教程&#xff0c;大家可以自行下载使用。 注意&#xff1a;激huo教程只用于学习交流&#xff0c;不可商用。 IDEA2024安装包及激huo教程 说明&#xff1a…

stm32入门-----初识stm32

目录 前言 ARM stm32 1.stm32家族 2.stm32的外设资源 3.命名规则 4.系统结构 5.引脚定义 6.启动配置 7.STM32F103C8T6芯片 8.STM32F103C8T6芯片原理图与最小系统电路 前言 已经很久没跟新了&#xff0c;上次发文的时候是好几个月之前了&#xff0c;现在我是想去学习st…

35 解决单条链路故障问题-华三链路聚合

InLoopBack接口是一种虚拟接口。InLoopBack接口由系统自动创建&#xff0c;用户不能进行配置和删除&#xff0c;但是可以显示&#xff0c;其物理层和链路层协议永远处于up状态。InLoopBack接口主要用于配合实现报文的路由和转发&#xff0c;任何送到InLoopBack接口的IP报文都会…

zigbee开发工具:3、驱动安装与程序下载(更新中...)

zigbee开发工具前两篇讲解了IAR开发工具的安装与注册&#xff0c;还介绍了新建一个cc2530开发工程的建立与配置。在进行zigbee开发&#xff0c;代码编写编译好后还需要下载到zigbee节点设备上进行调试与验证&#xff0c;那么就需要安装SmartRF Flash Programmer软件 和仿真器等…

Vim使用教程

目录 引言1. Vim的基本概念1.1 模式1.2 启动和退出 2. 基础操作2.1 导航2.2 插入文本2.3 删除和复制2.4 查找和替换 3. 高级功能3.1 多文件编辑3.2 宏录制和执行3.3 使用插件3.4 自定义快捷键 4. Vim脚本和自定义配置4.1 基本配置4.2 编写Vim脚本 5. 实用技巧5.1 快速移动5.2 批…

基于复旦微JFMQL100TAI的全国产化FPGA+AI人工智能异构计算平台,兼容XC7Z045-2FFG900I

基于上海复旦微电子FMQL45T900的全国产化ARM核心板。该核心板将复旦微的FMQL45T900&#xff08;与XILINX的XC7Z045-2FFG900I兼容&#xff09;的最小系统集成在了一个87*117mm的核心板上&#xff0c;可以作为一个核心模块&#xff0c;进行功能性扩展&#xff0c;能够快速的搭建起…

Golang | Leetcode Golang题解之第233题数字1的个数

题目&#xff1a; 题解&#xff1a; func countDigitOne(n int) (ans int) {// mulk 表示 10^k// 在下面的代码中&#xff0c;可以发现 k 并没有被直接使用到&#xff08;都是使用 10^k&#xff09;// 但为了让代码看起来更加直观&#xff0c;这里保留了 kfor k, mulk : 0, 1;…

Dify中的weaviate向量数据库操作

一.安装weaviate客户端 1.Dify 0.6.9中weaviate信息 在Dify 0.6.9版本中weaviate容器信息如下: # The Weaviate vector store. weaviate:image: semitechnologies/weaviate:1.19.0restart: alwaysvolumes:# Mount the Weaviate data directory to the container.- ./volume…

数据结构(空间复杂度介绍)超详细!!!

1. 数据结构前言 1.1 数据结构 数据结构是计算机存储、组织数据的形式&#xff0c;指相互之间存在一种或多种特定关系的数据元素的集合 1.2 算法 算法&#xff1a;良好的计算过程&#xff0c;它取一个或一组的值为输入&#xff0c;并产生出一个或一组的值作为输出。即算法经…

初学SpringMVC之 Ajax 篇

Ajax&#xff08;Asynchronous JavaScript and XML&#xff09;是一种在无需重新加载整个网页&#xff0c;能够更新部分网页的技术 Ajax 不是编程语言&#xff0c;而是一种用于创建更好更快以及交互性更强的 Web 应用程序技术 使用 Ajax 技术的网页&#xff0c;通过在后台服务…

课程设计——Python+OpenCV数字图像处理[车牌识别]

Python opencv 车牌识别 数字图像处理课程设计作业Python3OpenCV使用tkinter搭建界面tmp/文件夹是数字图像处理过程chepai/文件夹是车牌图片pic/文件夹是程序界面图PPT文件是验收时要讲的程序是从网上学习的并自己弄的&#xff0c;不完善&#xff0c;识别率不高 开发环境配置…

jenkins系列-09.jpom构建java docker harbor

本地先启动jpom server agent: /Users/jelex/Documents/work/jpom-2.10.40/server-2.10.40-release/bin jelexjelexxudeMacBook-Pro bin % sh Server.sh start/Users/jelex/Documents/work/jpom-2.10.40/agent-2.10.40-release/bin jelexjelexxudeMacBook-Pro bin % ./Agent.…

OpenCV图像处理——判断轮廓是否在圆环内

要判断一个轮廓是否在圆环内&#xff0c;可以将问题分解为两个步骤&#xff1a; 确保轮廓的所有点都在外圆内。确保轮廓的所有点都在内圆外。 下面是一个完整的示例代码&#xff0c;展示如何实现这一点&#xff1a; #include <opencv2/opencv.hpp> #include <iostr…

pytorch-LSTM

目录 1. RNN存在的问题2. LSTM的由来3. LSTM门3.1 遗忘门3.2 输入门3.3 输出门 4. LSTM是如何减轻梯度弥散问题 1. RNN存在的问题 如下图&#xff1a;RNN能满足预测下一个单词&#xff0c;但是对于获取更多的上下文信息就做不到了。 2. LSTM的由来 RNN能做到短时记忆即shor…

开发业务(2)——wordpress使用基础教程

外贸领域里面wordpress是比较通用的框架。由于多年的发展&#xff0c;性能和插件非常强大&#xff0c;包括支持各种企业站&#xff08;很多人已经设计了各种风格&#xff0c;只需要你将对应主题风格安装即可&#xff0c;当然也有付费的&#xff09;。这导致其内部生态非常强大&…

2024年上半年信息系统项目管理师——综合知识真题题目及答案(第1批次)(4)

2024年上半年信息系统项目管理师 ——综合知识真题题目及答案&#xff08;第1批次&#xff09;&#xff08;4&#xff09; 第61题&#xff1a;The project manager should use &#xff08;tool for the purpose to report on the work remaining for projects. A. cumulativ…

<数据集>夜间车辆识别数据集<目标检测>

数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;5000张 标注数量(xml文件个数)&#xff1a;5000 标注数量(txt文件个数)&#xff1a;5000 标注类别数&#xff1a;8 标注类别名称&#xff1a;[car, pedestrian, traffic light, traffic sign, bicycle, bus, truck…