您当前的位置:首页 > 电脑百科 > 程序开发 > 框架

Spring Statemachine应用实践

时间:2023-04-12 11:39:18  来源:微信公众号  作者:之家技术

前言 

在日常开发中经常遇到运营审核经销商活动、任务等等类似业务需求,大部分需求中状态稳定且单一无需使用状态机,但是也会出现大量的if...else前置状态代码,也是不够那么的“优雅”。随着业务的发展、需求迭代,每一次的业务代码改动都需要维护使用到状态的代码,更让开发人员头疼的是这些维护状态的代码,像散弹一样遍布在各个Service的方法中,不仅增加发布的风险,同时也增加了回归测试的工作量。

1. 什么是状态机?

通常所说的状态机为有限状态机(英语:finite-state machine,缩写:FSM),简称状态机, 是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。 

应用FSM模型可以帮助对象生命周期的状态的顺序以及导致状态变化的事件进行管理。 将状态和事件控制从不同的业务Service方法的if else中抽离出来。FSM的应用范围很广,状态机 可以描述核心业务规则,核心业务内容. 无限状态机,顾名思义状态无限,类似于“π”,暂不做研究。

状态机可归纳为4个要素,即现态、条件、动作、次态。这样的归纳,主要是出于对状态机的内在因果关系的考虑。“现态”和“条件”是因,“动作”和“次态”是果。详解如下:

现态:是指当前所处的状态。

条件:又称为“事件”,当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移。

动作:条件满足后执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是必需的,当条件满足后,也可以不 执行任何动作,直接迁移到新状态。

次态:条件满足后要迁往的新状态。“次态”是相对于“现态”而言的,“次态”一旦被激活,就转变成新的“现态”了。

动作是在给定时刻要进行的活动的描述。有多种类型的动作:

进入动作(entry action):在进入状态时进行

退出动作(exit action):在退出状态时进行

输入动作:依赖于当前状态和输入条件进行

转移动作:在进行特定转移时进行

其他术语:

Transition: 状态转移节点,是组成状态机引擎的核心。

source/from:现态。

target/to:次态。

event/trigger:触发节点从现态转移到次态的动作,这里也可能是一个timer。

guard/when:状态迁移前的校验,执行于action前。

action:用于实现当前节点对应的业务逻辑处理。

文字描述比较不容易理解,让我们举个栗子:每天上班都需要坐地铁,从刷卡进站到闸机关闭这个过程,将闸机抽象为一个状态机模型,如下图:

 

图片

 

2. 什么场景使用?

以下的场景您可能会需要使用:

您可以将应用程序或其结构的一部分表示为状态。

您希望将复杂的逻辑拆分为更小的可管理任务。

应用程序已经遇到了并发问题,例如异步执行导致了一些异常情况。

当您执行以下操作时,您已经在尝试实现状态机:

使用布尔标志或枚举来建模情况。

具有仅对应用程序生命周期的某些部分有意义的变量。

在if...else结构(或者更糟糕的是,多个这样的结构)中循环,检查是否设置了特定的标志或枚举,然后在标志和枚举的某些组合存在或不存在时,做出进一步的异常处理。

3. 为什么要用?有哪些好处?

最初活动模块功能设计时,并没有想使用状态机,仅仅想把状态的变更和业务剥离开,规范状态转换和程序在不同状态下所能提供的能力,去掉复杂的逻辑判断也就是if...else,想换一种模式实现思路,此前了解过spring“全家桶”有状态机就想到了“它”,场景也符合。

从个人使用的经验,开发阶段和迭代维护期总结了以下几点:

使用状态机来管理状态好处更多体现在代码的可维护性、对于流程复杂易变的业务场景能大大减轻维护和测试的难度。

解耦,业务逻辑与状态流程隔离,避免业务与状态“散弹式”维护,且状态持久化在同一个事务。

状态流转越复杂,越能体现状态流转的逻辑清晰,减少的“胶水”代码也越多。

4. 实践

JAVA语言状态机框架有很多,目前Github star 数比较多的有 spring-statemachine(star 1.3K) 、squirrel-foundation(star1.9K)即“松鼠”状态机,stateless4j相较前两个名气较小,未深入研究。spring-statemachine是spring官方提供的状态机实现,功能强大,但是相对来说很“重”,加载实例的时间也长于squirrel-foundation,不过好在一直都是有更新(目前官方已更新3.2.0),相信会越来越成熟。

实际生产中使用的是spring statemachine ,版本是2.2.0.RELEASE。线下对比使用的是squirrel-foundation,版本是0.3.10。这里仅供使用对比。

从创建活动到活动下线状态流转作为示例,如下图:

 

图片

 

pom

<?xml versinotallow="1.0" encoding="utf-8" ?>
<!-- spring statemachine -->
<dependency>
    <groupId>org.springframework.statemachine</groupId>
    <artifactId>spring-statemachine-starter</artifactId>
    <version>2.2.0.RELEASE</version>
</dependency>
        <!-- spring statemachine context 序列化 -->
<dependency>
<groupId>org.springframework.statemachine</groupId>
<artifactId>spring-statemachine-kryo</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>
        <!-- squirrel-foundation -->
<dependency>
<groupId>org.squirrelframework</groupId>
<artifactId>squirrel-foundation</artifactId>
<version>0.3.10</version>
</dependency>

状态&事件定义

 
public enum State {
    INIT("初始化"),
    DRAFT("草稿"),
    WAIT_VERIFY("待审核"),
    PASSED("审核通过"),
    REJECTED("已驳回"),
    //已发起上线操作,未到上线时间的状态
    WAIT_ONLIE("待上线"),
    ONLINED("已上线"),
    //过渡状态无实际意义,无需事件触发
    OFFLINING("下线中"),
    OFFLINED("已下线"),
    FINISHED("已结束");
    private final String desc;
}

public enum Event {
    SAVE("保存草稿"),
    SUBMIT("提交审核"),
    PASS("审核通过"),
    REJECT("提交驳回"),
    ONLINE("上线"),
    OFFLINE("下线"),
    FINISH("结束");
    private final String desc;
}

状态流转定义

@Configuration
@EnableStateMachineFactory
public class ActivitySpringStateMachineAutoConfiguration extends StateMachineConfigurerAdapter<State, Event> {

    @Autowired
    private ApplicationContext applicationContext;

    @Autowired
    private StateMachineRuntimePersister<State, Event, String> activityStateMachinePersister;

    @Bean
    public StateMachineService<State, Event> activityStateMachineService(StateMachineFactory<State, Event> stateMachineFactory) {

        return new DefaultStateMachineService<>(stateMachineFactory, activityStateMachinePersister);
    }

    @Override
    public void configure(StateMachineConfigurationConfigurer<State, Event> config) throws Exception {
        // @formatter:off
        config
                .withPersistence()
                .runtimePersister(activityStateMachinePersister)
                .and().withConfiguration()
                .stateDoActionPolicy(StateDoActionPolicy.TIMEOUT_CANCEL)
                .stateDoActionPolicyTimeout(300, TimeUnit.SECONDS)
                .autoStartup(false);
        // @formatter:on
    }

    @Override
    public void configure(StateMachineStateConfigurer<State, Event> states) throws Exception {
        states.withStates()
                .initial(State.INIT)
                .choice(State.OFFLINING)
                .states(EnumSet.allOf(State.class));
    }

    @Override
    public void configure(StateMach.NETransitionConfigurer<State, Event> transitions) throws Exception {
        // 待提交审核 --提交审核--> 待审核
        // @formatter:off
        // 现态-->事件-->次态
        transitions.withExternal()
                .source(State.INIT).target(State.DRAFT).event(Event.SAVE)
                .and().withExternal()
                .source(State.DRAFT).target(State.WAIT_VERIFY).event(Event.SUBMIT)
                .guard(applicationContext.getBean(SubmitCondition.class));
        transitions.withExternal().source(State.WAIT_VERIFY).target(State.PASSED).event(Event.PASS)
                .action(applicationContext.getBean(PassAction.class));
        transitions.withExternal().source(State.WAIT_VERIFY).target(State.REJECTED).event(Event.REJECT)
                .guard(applicationContext.getBean(RejectCondition.class));
        transitions.withExternal()
                .source(State.REJECTED)
                .target(State.WAIT_VERIFY)
                .event(Event.SUBMIT)
                .guard(applicationContext.getBean(SubmitCondition.class));

        // 审核通过-->上线-->待上线
        transitions.withExternal().source(State.PASSED).target(State.WAIT_ONLIE).event(Event.ONLINE);
        // 待上线-->上线-->已上线
        transitions.withExternal().source(State.WAIT_ONLIE).target(State.ONLINED).event(Event.ONLINE);
        // 已上线-->下线-->已下线
        transitions.withExternal()
                .source(State.ONLINED).target(State.OFFLINING).event(Event.OFFLINE);
        // 待上线-->下线-->下线中
        transitions.withExternal()
                .source(State.WAIT_ONLIE).target(State.OFFLINING).event(Event.OFFLINE)
                .and()
                // 已下线-->结束-->已结束
                .withChoice()
                .source(State.OFFLINING)
                .first(State.FINISHED, new Guard<State, Event>() {
                    @Override
                    public boolean evaluate(StateContext<State, Event> context) {
                        return true;
                    }
                })
                .last(State.OFFLINED);
        // @formatter:on
    }
}

说明:

  • 多个状态节点配置可用.and()串联。
  • withExternal是当现态和次态不相同时使用。
  • withChoice是当执行一个动作,当前状态(瞬时状态)可能迁移不同的的状态,此时可以使用Choice和Guard组合使用,且无需事件触发。相当于if...else的分支状态功能。
  • StateMachineService 这个类是spring statemachine自带的接口,用于获取和释放一个状态机的辅助service,依赖状态机工厂和持久化 实例, 但由于默认实现 StateMachinePersist< S, E, String> 规定了StateMachineContext的泛型为String 类型,故而持久层的参数contextObj 为string 类型,实际是状态机的id。
  • 持久化 spring-statemachine官方支持MongoDB和redis持久化存储,开发无需关心状态持久化,但是存在业务数据存储和状态存储事务的问题, 这里需要自己实现(StateMachineRuntimePersister)持久化以存储状态。
  • 上下文传递时都使用的StateMachineContext,其内部包含StateMachine实例,可以通过增加StateMachine实例扩展参数传递参数。

Guard与Action

@Component
public class SaveGuard implements Guard<State, Event> {
    @Override
    public boolean evaluate(StateContext<State, Event> context) {
        log.info("[execute save guard]");
        return true;
    }
}

@Component
public class SaveAction implements Action<State, Event> {

    @Override
    public void execute(StateContext<State, Event> context) {
        try {
            log.info("[execute saveAction]");
        } catch (Exception e) {
            context.getExtendedState().getVariables().put("ERROR", e.getMessage());
        }
    }
}

说明:

  1. Guard 门卫,条件判断返回true时再执行状态转移,可以做业务前置校验。

持久化配置

 
@Component
public class ActivityStateMachinePersister extends AbstractStateMachineRuntimePersister<State, Event, String> {

    @Autowired
    private ActivityStateService activityStateService;

    @Override
    public void write(StateMachineContext<State, Event> context, String id) {
        Activity state = new Activity();
        state.setMachineId(id);
        state.setState(context.getState());
        activityStateService.save(state);
    }

    @Override
    public StateMachineContext<State, Event> read(String id) {
        return deserialize(activityStateService.getContextById(id));
    }
}

说明:

  • AbstractStateMachineRuntimePersister 继承AbstractPersistingStateMachineInterceptor 并实现了StateMachineRuntimePersister接口, AbstractPersistingStateMachineInterceptor主要拦截状态变更时的状态监听。不同于StateMachineListener被动监听,interceptor拥有可以改变状态变化链的能力。
  • 序列化存储实现参考了spring-statemachine-data-redis的实现。

状态服务调用

 
@Service
public class StateTransitService {

    @Autowired
    private StateMachineService<State, Event> stateMachineService;

    @Transactional
    public void transimit(String machineId, Message<Event> message) {
        StateMachine<State, Event> stateMachine = stateMachineService.acquireStateMachine(machineId);
        stateMachine.addStateListener(new DefaultStateMachineListener<>(stateMachine));
        stateMachine.sendEvent(message);
        if (stateMachine.hasStateMachineError()) {
            String errorMessage = stateMachine.getExtendedState().get("message", String.class);
            stateMachineService.releaseStateMachine(machineId);
            throw new ResponseException(errorMessage);
        }
    }
}

@AllArgsConstructor
public class DefaultStateMachineListener<S, E> extends StateMachineListenerAdapter<S, E> {

    private final StateMachine<S, E> stateMachine;

    @Override
    public void eventNotAccepted(Message<E> event) {
        stateMachine.getExtendedState().getVariables().put("message", "当前状态不满足执行条件");
        stateMachine.setStateMachineError(new ResponseException(500, "Event not accepted"));
    }

    @Override
    public void transitionEnded(Transition<S, E> transition) {
        log.info("source {} to {}", transition.getSource().getId(), transition.getTarget().getId());
    }
}

说明:

  • Message为发送事件的载体,其内部封装了消息体、事件等上下文扩展参数。
  • StateMachineListenerAdapter为默认监听接口的空实现,依据业务需要重写监听的方法。
  • eventNotAccepted此为事件未正确执行时的监听器。

集成单元测试

 
@SpringBootTest
@RunWith(SpringRunner.class)
public class StateMachineITest {

    @Autowired
    private StateTransitService transmitService;

    @Autowired
    private ActivityStateService activityStateService;

    @Test
    public void test() {
        String machineId = "test";//业务主键ID
        transmitService.transimit(machineId, MessageBuilder.withPayload(Event.SAVE).build());
        transmitService.transimit(machineId, MessageBuilder.withPayload(Event.SUBMIT).build());
        transmitService.transimit(machineId, MessageBuilder.withPayload(Event.PASS).build());
        transmitService.transimit(machineId, MessageBuilder.withPayload(Event.ONLINE).build());
        transmitService.transimit(machineId, MessageBuilder.withPayload(Event.ONLINE).build());
        transmitService.transimit(machineId, MessageBuilder.withPayload(Event.OFFLINE).build());
        assert activityStateService.getStateById(machineId).equals(State.FINISHED);
    }

}

注意事项

  • 由于框架中每次都是加载一个状态机内存实例,所以在执行状态转移相关代码时一定要加分布式锁!!!建议状态维护提供统一调用service, 开启事务、处理异常。
  • spring-statemachine异常包装比较另类,如guard、action以及listener中发生异常,状态机会捕获并把异常信息捕获为警告,状态也能够成功转移到次态,这显然不符合 我们的需求,所以调用后需要手动判断是否发生异常stateMachine.hasStateMachineError(),但statemachine并没有给提供获取异常信息的接口,所以在guard 和action中将异常信息用变量的方式解决此问题,stateMachine.getExtendedState().getVariables().put("message", "当前状态不满足执行条件");
  • @EnableStateMachineFactory开启工厂模式,然后通过StateMachineService从持久化层加载一个状态机实例。
  • 当一个project中有多个业务状态机时,@EnableStateMachineFactory(name = "xxx")为工厂配置名称以区别不同的业务状态机。
  • 当使用withChoice()时,一定要在配置StateMachineStateConfigurer.choice()配置分支状态,否则将不生效。

扩展-与squirrel-foundation异同

@Component
public class ActivityMachine extends SquirrelStateMachine<ActivityMachine, State, Event, TransmitCmd> {

    private final ActivityStateService activityStateService;

    public ActivityMachine(ApplicationContext applicationContext) {
        super(applicationContext);
        activityStateService = applicationContext.getBean(ActivityStateService.class);
    }

    @Override
    public void buildStateMachine(StateMachineBuilder<ActivityMachine, State, Event, TransmitCmd> stateMachineBuilder) {
        stateMachineBuilder.externalTransition().from(State.INIT).to(State.DRAFT).on(Event.SAVE).when(applicationContext.getBean(SubmitCondition.class));
        //以下省略,大致与spring-statemachine相同
    }

    @Override
    public ActivityMachine createStateMachine(State stateId) {
        ActivityMachine activityMachine = super.createStateMachine(stateId);
        activityMachine.addStartListener(new StartListener<ActivityMachine, State, Event, TransmitCmd>() {

        });
        return activityMachine;
    }

    @Override
    protected void afterTransitionDeclined(S fromState, E event, C context) {
        //转移状态未执行
    }

    @Override
    protected void afterTransitionCausedException(S fromState, S toState, E event, C context) {
        // 转移状态时发生异常
    }

    @Override
    protected void afterTransitionCompleted(State fromState, State toState, Event event, TransmitCmd context) {
        log.info("from {} to {} on {}, {}", fromState.getDesc(), toState.getDesc(), event.getDesc(), context);
    }

}

说明:

  • squirrel-foundation直接可继承AbstractStateMachine实例化状态机,配置上大体相同只是使用的是from、to、on、when词不同,框架builder的约束太强。
  • 不支持choice分支状态。
  • 状态机异常处理afterTransitionCausedException相比spring-statemachine更加方便、易用。
  • 状态的持久化通过重写afterTransitionCompleted方法即可。

5.使用后的效果如何?

以下是在开发和迭代维护期间,真切体会到状态机带来好处的两个小场景。

  • 由于新项目中涉及到跨部门卡券业务,在开发初期审核活动通过时同步创建卡券批次,却忽略了异步生成券码的时间,随着开发的深入才意识到此问题。此时只需要在状态审核通过时加一个过渡状态并启动一个任务去轮询券码是否创建完成即可,丝毫不影响已开发的代码。
  • 最初的需求设计时,活动下线后是不能再次上线的,在需求迭代期内又增加了再次上线的功能,状态机流转逻辑清晰,只需要再增加个状态配置流转事件就行,就为状态机赋予了再次上线的能力。

6.总结 

在实践的过程中,在spring-statemachine官方文档结合google摸索使用的过程中,遇到持久化存储StateMachineContext、异常处理,以及状态分支等问题。目前回头看来也不复杂,如今写出来总结一下,希望对小伙伴们有所帮助。

最后建议在状态流程不是很复杂的情况,如果您也厌烦了if...else,那么不妨尝试一下squirrel-foundation,相信也是不错的选择。

参考文献

  1. ​https://baike.baidu.com/item/%E7%8A%B6%E6%80%81%E6%9C%BA/6548513?fr=aladdin​
  2. ​https://zh.wikipedia.org/wiki/%E6%9C%89%E9%99%90%E7%8A%B6%E6%80%81%E6%9C%BA​
  3. ​https://spring.io/projects/spring-statemachine#learn​
  4. ​http://hekailiang.github.io/squirrel/​

作者简介

 

图片

 

姜强强

■ 经销商技术部-商业资源团队。

■ 2016年加入汽车之家,目前主要负责经销商事业部内创新商业项目的研发工作,热衷于业内新技术的探索与实践。



Tags:Spring   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
Spring Security:保障应用安全的利器
SpringSecurity作为一个功能强大的安全框架,为Java应用程序提供了全面的安全保障,包括认证、授权、防护和集成等方面。本文将介绍SpringSecurity在这些方面的特性和优势,以及它...【详细内容】
2024-02-27  Search: Spring  点击:(54)  评论:(0)  加入收藏
Spring Security权限控制框架使用指南
在常用的后台管理系统中,通常都会有访问权限控制的需求,用于限制不同人员对于接口的访问能力,如果用户不具备指定的权限,则不能访问某些接口。本文将用 waynboot-mall 项目举例...【详细内容】
2024-02-19  Search: Spring  点击:(39)  评论:(0)  加入收藏
详解基于SpringBoot的WebSocket应用开发
在现代Web应用中,实时交互和数据推送的需求日益增长。WebSocket协议作为一种全双工通信协议,允许服务端与客户端之间建立持久性的连接,实现实时、双向的数据传输,极大地提升了用...【详细内容】
2024-01-30  Search: Spring  点击:(16)  评论:(0)  加入收藏
Spring实现Kafka重试Topic,真的太香了
概述Kafka的强大功能之一是每个分区都有一个Consumer的偏移值。该偏移值是消费者将读取的下一条消息的值。可以自动或手动增加该值。如果我们由于错误而无法处理消息并想重...【详细内容】
2024-01-26  Search: Spring  点击:(86)  评论:(0)  加入收藏
SpringBoot如何实现缓存预热?
缓存预热是指在 Spring Boot 项目启动时,预先将数据加载到缓存系统(如 Redis)中的一种机制。那么问题来了,在 Spring Boot 项目启动之后,在什么时候?在哪里可以将数据加载到缓存系...【详细内容】
2024-01-19  Search: Spring  点击:(86)  评论:(0)  加入收藏
Spring Boot2.0深度实践 核心原理拆解+源码分析
Spring Boot2.0深度实践:核心原理拆解与源码分析一、引言Spring Boot是一个基于Java的轻量级框架,它简化了Spring应用程序的创建过程,使得开发者能够快速搭建一个可运行的应用...【详细内容】
2024-01-15  Search: Spring  点击:(95)  评论:(0)  加入收藏
SpringBoot3+Vue3 开发高并发秒杀抢购系统
开发高并发秒杀抢购系统:使用SpringBoot3+Vue3的实践之旅随着互联网技术的发展,电商行业对秒杀抢购系统的需求越来越高。为了满足这种高并发、高流量的场景,我们决定使用Spring...【详细内容】
2024-01-14  Search: Spring  点击:(91)  评论:(0)  加入收藏
Spring Boot 3.0是什么?
Spring Boot 3.0是一款基于Java的开源框架,用于简化Spring应用程序的构建和开发过程。与之前的版本相比,Spring Boot 3.0在多个方面进行了改进和增强,使其更加易用、高效和灵活...【详细内容】
2024-01-11  Search: Spring  点击:(133)  评论:(0)  加入收藏
GraalVM与Spring Boot 3.0:加速应用性能的完美融合
在2023年,SpringBoot3.0的发布标志着Spring框架对GraalVM的全面支持,这一支持是对Spring技术栈的重要补充。GraalVM是一个高性能的多语言虚拟机,它提供了Ahead-of-Time(AOT)编...【详细内容】
2024-01-11  Search: Spring  点击:(124)  评论:(0)  加入收藏
Spring Boot虚拟线程的性能还不如Webflux?
早上看到一篇关于Spring Boot虚拟线程和Webflux性能对比的文章,觉得还不错。内容较长,抓重点给大家介绍一下这篇文章的核心内容,方便大家快速阅读。测试场景作者采用了一个尽可...【详细内容】
2024-01-10  Search: Spring  点击:(115)  评论:(0)  加入收藏
▌简易百科推荐
Web Components实践:如何搭建一个框架无关的AI组件库
一、让人又爱又恨的Web ComponentsWeb Components是一种用于构建可重用的Web元素的技术。它允许开发者创建自定义的HTML元素,这些元素可以在不同的Web应用程序中重复使用,并且...【详细内容】
2024-04-03  京东云开发者    Tags:Web Components   点击:(8)  评论:(0)  加入收藏
Kubernetes 集群 CPU 使用率只有 13% :这下大家该知道如何省钱了
作者 | THE STACK译者 | 刘雅梦策划 | Tina根据 CAST AI 对 4000 个 Kubernetes 集群的分析,Kubernetes 集群通常只使用 13% 的 CPU 和平均 20% 的内存,这表明存在严重的过度...【详细内容】
2024-03-08  InfoQ    Tags:Kubernetes   点击:(12)  评论:(0)  加入收藏
Spring Security:保障应用安全的利器
SpringSecurity作为一个功能强大的安全框架,为Java应用程序提供了全面的安全保障,包括认证、授权、防护和集成等方面。本文将介绍SpringSecurity在这些方面的特性和优势,以及它...【详细内容】
2024-02-27  风舞凋零叶    Tags:Spring Security   点击:(54)  评论:(0)  加入收藏
五大跨平台桌面应用开发框架:Electron、Tauri、Flutter等
一、什么是跨平台桌面应用开发框架跨平台桌面应用开发框架是一种工具或框架,它允许开发者使用一种统一的代码库或语言来创建能够在多个操作系统上运行的桌面应用程序。传统上...【详细内容】
2024-02-26  贝格前端工场    Tags:框架   点击:(47)  评论:(0)  加入收藏
Spring Security权限控制框架使用指南
在常用的后台管理系统中,通常都会有访问权限控制的需求,用于限制不同人员对于接口的访问能力,如果用户不具备指定的权限,则不能访问某些接口。本文将用 waynboot-mall 项目举例...【详细内容】
2024-02-19  程序员wayn  微信公众号  Tags:Spring   点击:(39)  评论:(0)  加入收藏
开发者的Kubernetes懒人指南
你可以将本文作为开发者快速了解 Kubernetes 的指南。从基础知识到更高级的主题,如 Helm Chart,以及所有这些如何影响你作为开发者。译自Kubernetes for Lazy Developers。作...【详细内容】
2024-02-01  云云众生s  微信公众号  Tags:Kubernetes   点击:(50)  评论:(0)  加入收藏
链世界:一种简单而有效的人类行为Agent模型强化学习框架
强化学习是一种机器学习的方法,它通过让智能体(Agent)与环境交互,从而学习如何选择最优的行动来最大化累积的奖励。强化学习在许多领域都有广泛的应用,例如游戏、机器人、自动驾...【详细内容】
2024-01-30  大噬元兽  微信公众号  Tags:框架   点击:(68)  评论:(0)  加入收藏
Spring实现Kafka重试Topic,真的太香了
概述Kafka的强大功能之一是每个分区都有一个Consumer的偏移值。该偏移值是消费者将读取的下一条消息的值。可以自动或手动增加该值。如果我们由于错误而无法处理消息并想重...【详细内容】
2024-01-26  HELLO程序员  微信公众号  Tags:Spring   点击:(86)  评论:(0)  加入收藏
SpringBoot如何实现缓存预热?
缓存预热是指在 Spring Boot 项目启动时,预先将数据加载到缓存系统(如 Redis)中的一种机制。那么问题来了,在 Spring Boot 项目启动之后,在什么时候?在哪里可以将数据加载到缓存系...【详细内容】
2024-01-19   Java中文社群  微信公众号  Tags:SpringBoot   点击:(86)  评论:(0)  加入收藏
花 15 分钟把 Express.js 搞明白,全栈没有那么难
Express 是老牌的 Node.js 框架,以简单和轻量著称,几行代码就可以启动一个 HTTP 服务器。市面上主流的 Node.js 框架,如 Egg.js、Nest.js 等都与 Express 息息相关。Express 框...【详细内容】
2024-01-16  程序员成功  微信公众号  Tags:Express.js   点击:(88)  评论:(0)  加入收藏
站内最新
站内热门
站内头条