【Spring】Spring状态机

1.什么是状态机

(1). 什么是状态

先来解释什么是“状态”( State )。现实事物是有不同状态的,例如一个自动门,就有 open 和 closed 两种状态。我们通常所说的状态机是有限状态机,也就是被描述的事物的状态的数量是有限个,例如自动门的状态就是两个 open 和 closed 。
在这里插入图片描述
状态机,也就是 State Machine ,不是指一台实际机器,而是指一个数学模型。说白了,一般就是指一张状态转换图。例如,根据自动门的运行规则,我们可以抽象出下面这么一个图。

自动门有两个状态,open 和 closed ,closed 状态下,如果读取开门信号,那么状态就会切换为 open 。open 状态下如果读取关门信号,状态就会切换为 closed 。

状态机的全称是有限状态自动机,自动两个字也是包含重要含义的。给定一个状态机,同时给定它的当前状态以及输入,那么输出状态时可以明确的运算出来的。例如对于自动门,给定初始状态 closed ,给定输入“开门”,那么下一个状态时可以运算出来的。

这样状态机的基本定义我们就介绍完毕了。重复一下:状态机是有限状态自动机的简称,是现实事物运行规则抽象而成的一个数学模型。

(2).四大概念

下面来给出状态机的四大概念。

第一个是 State ,状态。一个状态机至少要包含两个状态。例如上面自动门的例子,有 open 和 closed 两个状态。
第二个是 Event ,事件。事件就是执行某个操作的触发条件或者口令。对于自动门,“按下开门按钮”就是一个事件。
第三个是 Action ,动作。事件发生以后要执行动作。例如事件是“按开门按钮”,动作是“开门”。编程的时候,一个 Action一般就对应一个函数。
第四个是 Transition ,变换。也就是从一个状态变化为另一个状态。例如“开门过程”就是一个变换。

(3).状态机

有限状态机(Finite-state machine,FSM),又称有限状态自动机,简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。

FSM是一种算法思想,简单而言,有限状态机由一组状态、一个初始状态、输入和根据输入及现有状态转换为下一个状态的转换函数组成。

其作用主要是描述对象在它的生命周期内所经历的状态序列,以及如何响应来自外界的各种事件。

2.状态机图

做需求时,需要了解以下六种元素:起始、终止、现态、次态(目标状态)、动作、条件,我们就可以完成一个状态机图了:

以订单为例:以从待支付状态转换为待发货状态为例
在这里插入图片描述
①现态:是指当前所处的状态。待支付
②条件:又称为“事件”,当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移。支付事件
③动作:条件满足后执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。状态转换为待发货
④次态:条件满足后要迁往的新状态。“次态”是相对于“现态”而言的,“次态”一旦被激活,就转变成新的“现态”了。

3.spring statemachine

(1).状态机spring statemachine 概述

Spring Statemachine是应用程序开发人员在Spring应用程序中使用状态机概念的框架

Spring Statemachine旨在提供以下功能:

1.易于使用的扁平单级状态机,用于简单的使用案例。
2.分层状态机结构,以简化复杂的状态配置。
3.状态机区域提供更复杂的状态配置。
4.使用触发器,转换,警卫和操作。
5.键入安全配置适配器。
6.生成器模式,用于在Spring Application上下文之外使用的简单实例化通常用例的食谱
7.基于Zookeeper的分布式状态机
8.状态机事件监听器。
9.UML Eclipse Papyrus建模。
10.将计算机配置存储在永久存储中。
11.Spring IOC集成将bean与状态机关联起来。

状态机功能强大,因为行为始终保证一致,使调试相对容易。这是因为操作规则是在机器启动时写成的。这个想法是你的应用程序可能存在于有限数量的状态中,某些预定义的触发器可以将你的应用程序从一个状态转移到另一个状态。此类触发器可以基于事件或计时器。

在应用程序之外定义高级逻辑然后依靠状态机来管理状态要容易得多。您可以通过发送事件,侦听更改或仅请求当前状态来与状态机进行交互。

(2).快速开始

以订单状态扭转的例子为例:

表结构设计如下:

CREATE TABLE `tb_order` (`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',`order_code` varchar(128) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '订单编码',`status` smallint(3) DEFAULT NULL COMMENT '订单状态',`name` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '订单名称',`price` decimal(12,2) DEFAULT NULL COMMENT '价格',`delete_flag` tinyint(2) NOT NULL DEFAULT '0' COMMENT '删除标记,0未删除  1已删除',`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间',`update_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '更新时间',`create_user_code` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '创建人',`update_user_code` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '更新人',`version` int(11) NOT NULL DEFAULT '0' COMMENT '版本号',`remark` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '备注',PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='订单表';/*Data for the table `tb_order` */insert  into `tb_order`(`id`,`order_code`,`status`,`name`,`price`,`delete_flag`,`create_time`,`update_time`,`create_user_code`,`update_user_code`,`version`,`remark`) values (2,'A111',1,'A','22.00',0,'2022-10-15 16:14:11','2022-10-02 21:29:14','zhangsan','zhangsan',0,NULL),(3,'A111',1,'订单A','22.00',0,'2022-10-02 21:53:13','2022-10-02 21:29:14','zhangsan','zhangsan',0,NULL),(4,'A111',1,'订单A','22.00',0,'2022-10-02 21:53:13','2022-10-02 21:29:14','zhangsan','zhangsan',0,NULL),(5,'A111',1,'订单A','22.00',0,'2022-10-03 09:08:30','2022-10-02 21:29:14','zhangsan','zhangsan',0,NULL);
1)引入依赖
 <!-- redis持久化状态机 --><dependency><groupId>org.springframework.statemachine</groupId><artifactId>spring-statemachine-redis</artifactId><version>1.2.9.RELEASE</version></dependency><!--状态机--><dependency><groupId>org.springframework.statemachine</groupId><artifactId>spring-statemachine-starter</artifactId><version>2.0.1.RELEASE</version></dependency>
2)定义状态机状态和事件

状态枚举:

/**
* author:芋道源码
*/
public enum OrderStatus {// 待支付,待发货,待收货,已完成WAIT_PAYMENT(1, "待支付"),WAIT_DELIVER(2, "待发货"),WAIT_RECEIVE(3, "待收货"),FINISH(4, "已完成");private Integer key;private String desc;OrderStatus(Integer key, String desc) {this.key = key;this.desc = desc;}public Integer getKey() {return key;}public String getDesc() {return desc;}public static OrderStatus getByKey(Integer key) {for (OrderStatus e : values()) {if (e.getKey().equals(key)) {return e;}}throw new RuntimeException("enum not exists.");}}

事件:

/**
* author:芋道源码
*/
public enum OrderStatusChangeEvent {// 支付,发货,确认收货PAYED, DELIVERY, RECEIVED;
}
3)定义状态机规则和配置状态机
 @Configuration@EnableStateMachine(name = "orderStateMachine")public class OrderStateMachineConfig extends StateMachineConfigurerAdapter<OrderStatus, OrderStatusChangeEvent> {/*** 配置状态** @param states* @throws Exception*/public void configure(StateMachineStateConfigurer<OrderStatus, OrderStatusChangeEvent> states) throws Exception {states.withStates().initial(OrderStatus.WAIT_PAYMENT).states(EnumSet.allOf(OrderStatus.class));}/*** 配置状态转换事件关系** @param transitions* @throws Exception*/public void configure(StateMachineTransitionConfigurer<OrderStatus, OrderStatusChangeEvent> transitions) throws Exception {transitions//支付事件:待支付-》待发货.withExternal().source(OrderStatus.WAIT_PAYMENT).target(OrderStatus.WAIT_DELIVER).event(OrderStatusChangeEvent.PAYED).and()//发货事件:待发货-》待收货.withExternal().source(OrderStatus.WAIT_DELIVER).target(OrderStatus.WAIT_RECEIVE).event(OrderStatusChangeEvent.DELIVERY).and()//收货事件:待收货-》已完成.withExternal().source(OrderStatus.WAIT_RECEIVE).target(OrderStatus.FINISH).event(OrderStatusChangeEvent.RECEIVED);}}

配置持久化:

 /*** author:芋道源码*/ @Configuration@Slf4jpublic class Persist<E, S> {/*** 持久化到内存map中** @return*/@Bean(name = "stateMachineMemPersister")public static StateMachinePersister getPersister() {return new DefaultStateMachinePersister(new StateMachinePersist() {@Overridepublic void write(StateMachineContext context, Object contextObj) throws Exception {log.info("持久化状态机,context:{},contextObj:{}", JSON.toJSONString(context), JSON.toJSONString(contextObj));map.put(contextObj, context);}@Overridepublic StateMachineContext read(Object contextObj) throws Exception {log.info("获取状态机,contextObj:{}", JSON.toJSONString(contextObj));StateMachineContext stateMachineContext = (StateMachineContext) map.get(contextObj);log.info("获取状态机结果,stateMachineContext:{}", JSON.toJSONString(stateMachineContext));return stateMachineContext;}private Map map = new HashMap();});}@Resourceprivate RedisConnectionFactory redisConnectionFactory;/*** 持久化到redis中,在分布式系统中使用** @return*/@Bean(name = "stateMachineRedisPersister")public RedisStateMachinePersister<E, S> getRedisPersister() {RedisStateMachineContextRepository<E, S> repository = new RedisStateMachineContextRepository<>(redisConnectionFactory);RepositoryStateMachinePersist p = new RepositoryStateMachinePersist<>(repository);return new RedisStateMachinePersister<>(p);}}
4)业务系统

controller:

 /*** author:芋道源码*/ @RestController@RequestMapping("/order")public class OrderController {@Resourceprivate OrderService orderService;/*** 根据id查询订单** @return*/@RequestMapping("/getById")public Order getById(@RequestParam("id") Long id) {//根据id查询订单Order order = orderService.getById(id);return order;}/*** 创建订单** @return*/@RequestMapping("/create")public String create(@RequestBody Order order) {//创建订单orderService.create(order);return "sucess";}/*** 对订单进行支付** @param id* @return*/@RequestMapping("/pay")public String pay(@RequestParam("id") Long id) {//对订单进行支付orderService.pay(id);return "success";}/*** 对订单进行发货** @param id* @return*/@RequestMapping("/deliver")public String deliver(@RequestParam("id") Long id) {//对订单进行确认收货orderService.deliver(id);return "success";}/*** 对订单进行确认收货** @param id* @return*/@RequestMapping("/receive")public String receive(@RequestParam("id") Long id) {//对订单进行确认收货orderService.receive(id);return "success";}}

servie:

 /*** author:芋道源码*/ @Service("orderService")@Slf4jpublic class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {@Resourceprivate StateMachine<OrderStatus, OrderStatusChangeEvent> orderStateMachine;@Resourceprivate StateMachinePersister<OrderStatus, OrderStatusChangeEvent, String> stateMachineMemPersister;@Resourceprivate OrderMapper orderMapper;/*** 创建订单** @param order* @return*/public Order create(Order order) {order.setStatus(OrderStatus.WAIT_PAYMENT.getKey());orderMapper.insert(order);return order;}/*** 对订单进行支付** @param id* @return*/public Order pay(Long id) {Order order = orderMapper.selectById(id);log.info("线程名称:{},尝试支付,订单号:{}" ,Thread.currentThread().getName() , id);if (!sendEvent(OrderStatusChangeEvent.PAYED, order)) {log.error("线程名称:{},支付失败, 状态异常,订单信息:{}", Thread.currentThread().getName(), order);throw new RuntimeException("支付失败, 订单状态异常");}return order;}/*** 对订单进行发货** @param id* @return*/public Order deliver(Long id) {Order order = orderMapper.selectById(id);log.info("线程名称:{},尝试发货,订单号:{}" ,Thread.currentThread().getName() , id);if (!sendEvent(OrderStatusChangeEvent.DELIVERY, order)) {log.error("线程名称:{},发货失败, 状态异常,订单信息:{}", Thread.currentThread().getName(), order);throw new RuntimeException("发货失败, 订单状态异常");}return order;}/*** 对订单进行确认收货** @param id* @return*/public Order receive(Long id) {Order order = orderMapper.selectById(id);log.info("线程名称:{},尝试收货,订单号:{}" ,Thread.currentThread().getName() , id);if (!sendEvent(OrderStatusChangeEvent.RECEIVED, order)) {log.error("线程名称:{},收货失败, 状态异常,订单信息:{}", Thread.currentThread().getName(), order);throw new RuntimeException("收货失败, 订单状态异常");}return order;}/*** 发送订单状态转换事件* synchronized修饰保证这个方法是线程安全的** @param changeEvent* @param order* @return*/private synchronized boolean sendEvent(OrderStatusChangeEvent changeEvent, Order order) {boolean result = false;try {//启动状态机orderStateMachine.start();//尝试恢复状态机状态stateMachineMemPersister.restore(orderStateMachine, String.valueOf(order.getId()));Message message = MessageBuilder.withPayload(changeEvent).setHeader("order", order).build();result = orderStateMachine.sendEvent(message);//持久化状态机状态stateMachineMemPersister.persist(orderStateMachine, String.valueOf(order.getId()));} catch (Exception e) {log.error("订单操作失败:{}", e);} finally {orderStateMachine.stop();}return result;}}

监听状态的变化:

 /*** author:芋道源码*/     @Component("orderStateListener")@WithStateMachine(name = "orderStateMachine")@Slf4jpublic class OrderStateListenerImpl {@Resourceprivate OrderMapper orderMapper;@OnTransition(source = "WAIT_PAYMENT", target = "WAIT_DELIVER")public void payTransition(Message<OrderStatusChangeEvent> message) {Order order = (Order) message.getHeaders().get("order");log.info("支付,状态机反馈信息:{}",  message.getHeaders().toString());//更新订单order.setStatus(OrderStatus.WAIT_DELIVER.getKey());orderMapper.updateById(order);//TODO 其他业务}@OnTransition(source = "WAIT_DELIVER", target = "WAIT_RECEIVE")public void deliverTransition(Message<OrderStatusChangeEvent> message) {Order order = (Order) message.getHeaders().get("order");log.info("发货,状态机反馈信息:{}",  message.getHeaders().toString());//更新订单order.setStatus(OrderStatus.WAIT_RECEIVE.getKey());orderMapper.updateById(order);//TODO 其他业务}@OnTransition(source = "WAIT_RECEIVE", target = "FINISH")public void receiveTransition(Message<OrderStatusChangeEvent> message) {Order order = (Order) message.getHeaders().get("order");log.info("确认收货,状态机反馈信息:{}",  message.getHeaders().toString());//更新订单order.setStatus(OrderStatus.FINISH.getKey());orderMapper.updateById(order);//TODO 其他业务}}
(3).测试验证

1)验证业务

  • 新增一个订单
    http://localhost:8084/order/create

  • 对订单进行支付
    http://localhost:8084/order/pay?id=2

  • 对订单进行发货
    http://localhost:8084/order/deliver?id=2

  • 对订单进行确认收货
    http://localhost:8084/order/receive?id=2

  • 正常流程结束。如果对一个订单进行支付了,再次进行支付,则会报错:http://localhost:8084/order/pay?id=2

报错如下:

在这里插入图片描述
2)验证持久化
内存

使用内存持久化类持久化:

/*** author:芋道源码*/ @Resourceprivate StateMachinePersister<OrderStatus, OrderStatusChangeEvent, String> stateMachineMemPersister;/*** 发送订单状态转换事件* synchronized修饰保证这个方法是线程安全的** @param changeEvent* @param order* @return*/private synchronized boolean sendEvent(OrderStatusChangeEvent changeEvent, Order order) {boolean result = false;try {//启动状态机orderStateMachine.start();//尝试恢复状态机状态stateMachineMemPersister.restore(orderStateMachine, String.valueOf(order.getId()));Message message = MessageBuilder.withPayload(changeEvent).setHeader("order", order).build();result = orderStateMachine.sendEvent(message);//持久化状态机状态stateMachineMemPersister.persist(orderStateMachine, String.valueOf(order.getId()));} catch (Exception e) {log.error("订单操作失败:{}", e);} finally {orderStateMachine.stop();}return result;}

redis持久化

引入依赖:

<!-- redis持久化状态机 -->
<dependency><groupId>org.springframework.statemachine</groupId><artifactId>spring-statemachine-redis</artifactId><version>1.2.9.RELEASE</version>
</dependency>

配置yaml:

spring:redis:database: 0host: localhostjedis:pool:max-active: 8max-idle: 8max-wait: ''min-idle: 0password: ''port: 6379timeout: 0

使用redis持久化类持久化:

 /*** author:芋道源码*/ @Resourceprivate StateMachinePersister<OrderStatus, OrderStatusChangeEvent, String> stateMachineRedisPersister;/*** 发送订单状态转换事件* synchronized修饰保证这个方法是线程安全的** @param changeEvent* @param order* @return*/private synchronized boolean sendEvent(OrderStatusChangeEvent changeEvent, Order order) {boolean result = false;try {//启动状态机orderStateMachine.start();//尝试恢复状态机状态stateMachineRedisPersister.restore(orderStateMachine, String.valueOf(order.getId()));Message message = MessageBuilder.withPayload(changeEvent).setHeader("order", order).build();result = orderStateMachine.sendEvent(message);//持久化状态机状态stateMachineRedisPersister.persist(orderStateMachine, String.valueOf(order.getId()));} catch (Exception e) {log.error("订单操作失败:{}", e);} finally {orderStateMachine.stop();}return result;}

https://mp.weixin.qq.com/s/flRS7bYcRrKi3wjulaqCmQ

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

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

相关文章

Python 一步一步教你用pyglet制作汉诺塔游戏

目录 汉诺塔游戏 1. 抓取颜色 2. 绘制圆盘 3. 九层汉塔 4. 绘制塔架 5. 叠加圆盘 6. 游戏框架 汉诺塔游戏 汉诺塔&#xff08;Tower of Hanoi&#xff09;&#xff0c;是一个源于印度古老传说的益智玩具。这个传说讲述了大梵天创造世界的时候&#xff0c;他做了三根金刚…

【Leetcode每日一刷】滑动窗口:209.长度最小的子数组

一、209.长度最小的子数组 1.1&#xff1a;题目 题目链接 1.2&#xff1a;解题思路 题型&#xff1a;滑动窗口&#xff1b;时间复杂度&#xff1a;O(n) &#x1faa7; 滑动窗口本质也是双指针的一种技巧&#xff0c;特别适用于字串问题 ❗❗核心思想/ 关键&#xff1a;左右…

【笔记】原油阳谋论

文章目录 石油的属性能源属性各国石油替代 金融属性黄金石油美元 油价历史油价传导路径 石油供需格局与发展供需格局各国状况美国俄罗斯沙特 产油国困境运输 分析格局分析供需平衡分析价差分析价差概念基本面的跨区模型跨区模型下的价差逻辑 长中短三期分析长期视角——供应看投…

【笔记】全国大学生GIS应用技能大赛练习总结

该总结笔记为小组成员在练习完毕了历届题目后自我总结的结果&#xff0c;如有不足之处可以在评论区提出&#xff0c;排版较乱往谅解 绘制带空洞的面要素&#xff1a; 法一&#xff1a; 1、矢量化整个区域。2、矢量化空洞区域。3、将矢量化空洞区域进行合并&#xff08;编辑器…

Spring MVC 全局异常处理器

如果不加以异常处理&#xff0c;错误信息肯定会抛在浏览器页面上&#xff0c;这样很不友好&#xff0c;所以必须进行异常处理。 1.异常处理思路 系统的dao、service、controller出现都通过throws Exception向上抛出&#xff0c;最后由springmvc前端控制器交由异常处理器进行异…

Linux C/C++下使用Lex/Yacc构建实现DBMS(Minisql)

DBMS&#xff08;数据库管理系统&#xff09;是一种用于管理和组织数据库的软件系统。它的重要性在于提供了一种有效地存储、管理和访问大量数据的方式。本文将深入探讨如何使用C语言、Lex&#xff08;词法分析器生成器&#xff09;和Yacc&#xff08;语法分析器生成器&#xf…

Linux安装MeterSphere并结合内网穿透实现公网远程访问本地服务

文章目录 前言1. 安装MeterSphere2. 本地访问MeterSphere3. 安装 cpolar内网穿透软件4. 配置MeterSphere公网访问地址5. 公网远程访问MeterSphere6. 固定MeterSphere公网地址 前言 MeterSphere 是一站式开源持续测试平台, 涵盖测试跟踪、接口测试、UI 测试和性能测试等功能&am…

企业官网朝这个方向建设,保准你和客户双丰收!

“企业官网是一个没落的行业”&#xff0c;不少人都是这个论调&#xff0c;那是因为你没有经历过大起大落&#xff0c;大风大浪&#xff0c;躺在安乐窝舒服久了&#xff0c;就放弃了进步了。 提升企业官网的颜值和体验&#xff0c;是企业官网建设的未来之路。 随着互联网的发展…

如何在RTMP推送端和RTMP播放端支持Enhanced RTMP H.265(HEVC)

技术背景 时隔多年&#xff0c;在Enhancing RTMP, FLV With Additional Video Codecs And HDR Support&#xff08;2023年7月31号正式发布&#xff09;官方规范出来之前&#xff0c;如果RTMP要支持H.265&#xff0c;大家约定俗成的做法是扩展flv协议&#xff0c;CDN厂商携手给…

0103n阶行列式-行列式-线性代数

文章目录 一 n阶行列式二 三阶行列式三 特殊行列式结语 一 n阶行列式 ∣ a 11 a 12 ⋯ a 1 n a 21 a 22 ⋯ a 2 n ⋯ ⋯ ⋯ ⋯ a n 1 a n 2 ⋯ a n n ∣ \begin{vmatrix}a_{11}&a_{12}&\cdots&a_{1n}\\a_{21}&a_{22}&\cdots&a_{2n}\\\cdots&\cdots…

知名比特币质押协议项目Babylon确认参加Hack.Summit()2024区块链开发者大会

Babylon项目已确认将派遣其项目代表出席2024年在香港数码港举办的Hack.Summit()2024区块链开发者大会。作为比特币生态的领军项目&#xff0c;Babylon积极参与全球区块链领域的交流与合作&#xff0c;此次出席大会将为其提供一个展示项目进展、交流技术与创新思路的重要平台。B…

深入了解二叉搜索树:原理、实现与应用

目录 一、介绍二叉搜索树 二、二叉搜索树的基本性质 三、二叉搜索树的实现 四、总结 在计算机科学中&#xff0c;数据结构是构建算法和程序的基础。其中&#xff0c;二叉搜索树&#xff08;Binary Search Tree&#xff0c;简称 BST&#xff09;作为一种常见的数据结构&#…

Java开发从入门到精通(一):Java的进阶语法知识

Java大数据开发和安全开发 Java的方法1.1 方法是什么1.1.1 方法的定义1.1.2 方法如何执行?1.1.3 方法定义时注意点1.1.4 使用方法的好处是? 1.2 方法的多种形式1.2.1 无参数 无返回值1.2.2 有参数 无返回值 1.3 方法使用时的常见问题1.4 方法的设计案例1.4.1 计算1-n的和1.4.…

uniapp隐藏状态栏并强制横屏

uniapp隐藏状态栏并强制横屏 1.manifest.json中&#xff1a; "screenOrientation": ["landscape-primary", //可选&#xff0c;字符串类型&#xff0c;支持横屏"landscape-secondary" //可选&#xff0c;字符串类型&#xff0c;支持反向横屏]…

表单进阶(5)-文本域

默认文本框可以用自由拖动控制大小 通过设置在css中设置控制&#xff1a; resize:重新设置文本框大小 horizontal&#xff08;水平&#xff09;&#xff0c;vertical&#xff08;垂直&#xff09;&#xff0c;none&#xff08;不能控制大小&#xff09;&#xff0c;默认both&…

部署LVS+Keepalived高可用群集(抢占模式,非抢占模式,延迟模式)

目录 一、LVSKeepalived高可用群集 1、实验环境 2、 主和备keepalived的配置 2.1 yum安装ipvsadm和keepalived工具 2.2 添加ip_vs模块并开启ipvsadm 2.3 修改keepalived的配置文件 2.4 调整proc响应参数&#xff0c;关闭linux内核的重定向参数响应 2.5 将主服务器的kee…

23 经典卷积神经网络 LeNet【李沐动手学深度学习v2课程笔记】 (备注:提到如何把代码从CPU改到在GPU上使用)

目录 1. LeNet 2. 实现代码 3. 模型训练 4. 小结 本节将介绍LeNet&#xff0c;它是最早发布的卷积神经网络之一&#xff0c;因其在计算机视觉任务中的高效性能而受到广泛关注。 这个模型是由AT&T贝尔实验室的研究员Yann LeCun在1989年提出的&#xff08;并以其命名&…

VMware下载与安装

准备一个Linux的系统&#xff0c;成本最低的方式就是在本地安装一台虚拟机&#xff0c;VMware是业界最好用的虚拟机软件之一 官网&#xff1a;https://www.vmware.com/ 下载页面&#xff1a;https://www.vmware.com/products/workstation-pro/workstation-pro-evaluation.html …

小文件问题及GlusterFS的瓶颈

01海量小文件存储的挑战 为了解决海量小文件的存储问题&#xff0c;必须采用分布式存储&#xff0c;目前分布式存储主要采用两种架构&#xff1a;集中式元数据管理架构和去中心化架构。 (1)集中式元数据架构&#xff1a; 典型的集中式元数据架构的分布式存储有GFS&#xff0…

【深度学习笔记】7_1 优化与深度学习

注&#xff1a;本文为《动手学深度学习》开源内容&#xff0c;部分标注了个人理解&#xff0c;仅为个人学习记录&#xff0c;无抄袭搬运意图 7.1 优化与深度学习 本节将讨论优化与深度学习的关系&#xff0c;以及优化在深度学习中的挑战。在一个深度学习问题中&#xff0c;我们…