《微服务实战》 第二十九章 分布式事务框架seata AT模式

前言

本章节介绍微服务分布式项目中,使用的事务框架seata。
官网:http://seata.io/zh-cn/
springcloud-nacos-seata:https://github.com/seata/seata-samples/tree/master/springcloud-nacos-seata

1、概念

Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。
在这里插入图片描述

  • TC (Transaction Coordinator) - 事务协调者
    维护全局和分支事务的状态,驱动全局事务提交或回滚。
  • TM (Transaction Manager) - 事务管理器
    定义全局事务的范围:开始全局事务、提交或回滚全局事务。
  • RM (Resource Manager) - 资源管理器
    管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。

2、AT模式

2.1、应用前提

  • 基于支持本地 ACID 事务的关系型数据库。
  • Java 应用,通过 JDBC 访问数据库。
  • 文件操作与关系型数据库操作

2.2、两阶段提交概念

  • 一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
  • 二阶段:
    提交异步化,非常快速地完成。
    回滚通过一阶段的回滚日志进行反向补偿。

2.3、写隔离

  • 一阶段本地事务提交前,需要确保先拿到 全局锁 。
  • 拿不到 全局锁 ,不能提交本地事务。
  • 拿 全局锁 的尝试被限制在一定范围内,超出范围将放弃,并回滚本地事务,释放本地锁
    在这里插入图片描述

两个全局事务 tx1 和 tx2,分别对 a 表的 m 字段进行更新操作,m 的初始值 1000。
tx1 先开始,开启本地事务,拿到本地锁,更新操作 m = 1000 - 100 = 900。本地事务提交前,先拿到该记录的 全局锁 ,本地提交释放本地锁。 tx2 后开始,开启本地事务,拿到本地锁,更新操作 m = 900 - 100 = 800。本地事务提交前,尝试拿该记录的 全局锁 ,tx1 全局提交前,该记录的全局锁被 tx1 持有,tx2 需要重试等待 全局锁 。

tx1 二阶段全局提交,释放 全局锁 。tx2 拿到 全局锁 提交本地事务。
在这里插入图片描述
如果 tx1 的二阶段全局回滚,则 tx1 需要重新获取该数据的本地锁,进行反向补偿的更新操作,实现分支的回滚。

此时,如果 tx2 仍在等待该数据的 全局锁,同时持有本地锁,则 tx1 的分支回滚会失败。分支的回滚会一直重试,直到 tx2 的 全局锁 等锁超时,放弃 全局锁 并回滚本地事务释放本地锁,tx1 的分支回滚最终成功。
因为整个过程 全局锁 在 tx1 结束前一直是被 tx1 持有的,所以不会发生 脏写 的问题。

2.4、读隔离

在数据库本地事务隔离级别 读已提交(Read Committed) 或以上的基础上,Seata(AT 模式)的默认全局隔离级别是 读未提交(Read Uncommitted)。

如果应用在特定场景下,必需要求全局的 读已提交 ,目前 Seata 的方式是通过 SELECT FOR UPDATE 语句的代理。
在这里插入图片描述
SELECT FOR UPDATE 语句的执行会申请 全局锁 ,如果 全局锁 被其他事务持有,则释放本地锁(回滚 SELECT FOR UPDATE 语句的本地执行)并重试。这个过程中,查询是被 block 住的,直到 全局锁 拿到,即读取的相关数据是 已提交 的,才返回。

出于总体性能上的考虑,Seata 目前的方案并没有对所有 SELECT 语句都进行代理,仅针对 FOR UPDATE 的 SELECT 语句。

2.5、AT模式工作过程的案例

业务表:product

FieldTypeKey
idbigint(20)PRI
namevarchar(100)
sincevarchar(100)

AT 分支事务的业务逻辑:

update product set name = 'GTS' where name = 'TXC';

2.5.1、一阶段

过程:

解析 SQL:得到 SQL 的类型(UPDATE),表(product),条件(where name = ‘TXC’)等相关的信息。
查询前镜像:根据解析得到的条件信息,生成查询语句,定位数据。

select id, name, since from product where name = 'TXC';

得到前镜像:

idnamesince
1TXC2014

执行业务 SQL:更新这条记录的 name 为 ‘GTS’。
查询后镜像:根据前镜像的结果,通过 主键 定位数据。

select id, name, since from product where id = 1;

得到后镜像:

idnamesince
1GTS2014

插入回滚日志:把前后镜像数据以及业务 SQL 相关的信息组成一条回滚日志记录,插入到
UNDO_LOG 表中。

{
    "branchId": 641789253,
    "undoItems": [{
        "afterImage": {
            "rows": [{
                "fields": [{
                    "name": "id",
                    "type": 4,
                    "value": 1
                }, {
                    "name": "name",
                    "type": 12,
                    "value": "GTS"
                }, {
                    "name": "since",
                    "type": 12,
                    "value": "2014"
                }]
            }],
            "tableName": "product"
        },
        "beforeImage": {
            "rows": [{
                "fields": [{
                    "name": "id",
                    "type": 4,
                    "value": 1
                }, {
                    "name": "name",
                    "type": 12,
                    "value": "TXC"
                }, {
                    "name": "since",
                    "type": 12,
                    "value": "2014"
                }]
                
            }],
            "tableName": "product"
        },
        "sqlType": "UPDATE"
    }],
    "xid": "xid:xxx"
}

提交前,向 TC 注册分支:申请
product 表中,主键值等于 1 的记录的 全局锁 。

本地事务提交:业务数据的更新和前面步骤中生成的 UNDO LOG 一并提交。

将本地事务提交的结果上报给 TC。

2.5.2、 二阶段-回滚

收到 TC 的分支回滚请求,开启一个本地事务,执行如下操作。

通过 XID 和 Branch ID 查找到相应的 UNDO LOG 记录。

数据校验:拿 UNDO LOG 中的后镜与当前数据进行比较,如果有不同,说明数据被当前全局事务之外的动作做了修改。这种情况,需要根据配置策略来做处理,详细的说明在另外的文档中介绍。

根据 UNDO LOG 中的前镜像和业务 SQL 的相关信息生成并执行回滚的语句:

update product set name = 'TXC' where id = 1;

提交本地事务。并把本地事务的执行结果(即分支事务回滚的结果)上报给 TC。

2.5.3、二阶段-提交

收到 TC 的分支提交请求,把请求放入一个异步任务的队列中,马上返回提交成功的结果给 TC。

异步任务阶段的分支提交请求将异步和批量地删除相应 UNDO LOG 记录。

3、spring cloud 与seata整合

在这里插入图片描述

3.1、seata配置

下载地址:https://github.com/seata/seata/releases/download/v1.4.2/seata-server-1.4.2.zip
解压到D盘

3.1.1、修改配置文件

3.1.1.1、conf/file.conf 修改成db模式

## transaction log store, only used in seata-server
store {
  ## store mode: file、db、redis
  mode = "db"
  ## rsa decryption public key
  publicKey = ""
  ## file store property
  file {
    ## store location dir
    dir = "sessionStore"
    # branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions
    maxBranchSessionSize = 16384
    # globe session size , if exceeded throws exceptions
    maxGlobalSessionSize = 512
    # file buffer size , if exceeded allocate new buffer
    fileWriteBufferCacheSize = 16384
    # when recover batch read size
    sessionReloadReadSize = 100
    # async, sync
    flushDiskMode = async
  }

  ## database store property
  db {
    ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari) etc.
    datasource = "druid"
    ## mysql/oracle/postgresql/h2/oceanbase etc.
    dbType = "mysql"
    driverClassName = "com.mysql.jdbc.Driver"
    ## if using mysql to store the data, recommend add rewriteBatchedStatements=true in jdbc connection param
    url = "jdbc:mysql://127.0.0.1:3306/seata?rewriteBatchedStatements=true"
    user = "root"
    password = "root"
    minConn = 5
    maxConn = 100
    globalTable = "global_table"
    branchTable = "branch_table"
    lockTable = "lock_table"
    queryLimit = 100
    maxWait = 5000
  }

  ## redis store property
  redis {
    ## redis mode: single、sentinel
    mode = "single"
    ## single mode property
    single {
      host = "127.0.0.1"
      port = "6379"
    }
    ## sentinel mode property
    sentinel {
      masterName = ""
      ## such as "10.28.235.65:26379,10.28.235.65:26380,10.28.235.65:26381"
      sentinelHosts = ""
    }
    password = ""
    database = "0"
    minConn = 1
    maxConn = 10
    maxTotal = 100
    queryLimit = 100
  }
}

3.1.1.2、conf/registry.conf

registry {
  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  type = "nacos"

  nacos {
    application = "seata-server"
    serverAddr = "127.0.0.1:8848"
    group = "SEATA_GROUP"
    namespace = "1ff3782d-b62d-402f-8bc4-ebcf40254d0a"
    cluster = "default"
    username = "nacos"
    password = "nacos"
  }
  eureka {
    serviceUrl = "http://localhost:8761/eureka"
    application = "default"
    weight = "1"
  }
  redis {
    serverAddr = "localhost:6379"
    db = 0
    password = ""
    cluster = "default"
    timeout = 0
  }
  zk {
    cluster = "default"
    serverAddr = "127.0.0.1:2181"
    sessionTimeout = 6000
    connectTimeout = 2000
    username = ""
    password = ""
  }
  consul {
    cluster = "default"
    serverAddr = "127.0.0.1:8500"
    aclToken = ""
  }
  etcd3 {
    cluster = "default"
    serverAddr = "http://localhost:2379"
  }
  sofa {
    serverAddr = "127.0.0.1:9603"
    application = "default"
    region = "DEFAULT_ZONE"
    datacenter = "DefaultDataCenter"
    cluster = "default"
    group = "SEATA_GROUP"
    addressWaitTime = "3000"
  }
  file {
    name = "file.conf"
  }
}

config {
  # file、nacos 、apollo、zk、consul、etcd3
  type = "file"

  nacos {
    serverAddr = "127.0.0.1:8848"
    namespace = ""
    group = "SEATA_GROUP"
    username = "nacos"
    password = "nacos"
    dataId = "seataServer.properties"
  }
  consul {
    serverAddr = "127.0.0.1:8500"
    aclToken = ""
  }
  apollo {
    appId = "seata-server"
    ## apolloConfigService will cover apolloMeta
    apolloMeta = "http://192.168.1.204:8801"
    apolloConfigService = "http://192.168.1.204:8080"
    namespace = "application"
    apolloAccesskeySecret = ""
    cluster = "seata"
  }
  zk {
    serverAddr = "127.0.0.1:2181"
    sessionTimeout = 6000
    connectTimeout = 2000
    username = ""
    password = ""
    nodePath = "/seata/seata.properties"
  }
  etcd3 {
    serverAddr = "http://localhost:2379"
  }
  file {
    name = "file.conf"
  }
}

注意:nacos命名空间namespace定义成自己的。

3.1.1.3、下载config.tex

https://github.com/seata/seata/tree/develop/script/config-center/config.text 保存到seata根目录,文件名config.txt

#For details about configuration items, see https://seata.io/zh-cn/docs/user/configurations.html
#Transport configuration, for client and server
transport.type=TCP
transport.server=NIO
transport.heartbeat=true
transport.enableTmClientBatchSendRequest=false
transport.enableRmClientBatchSendRequest=true
transport.enableTcServerBatchSendResponse=false
transport.rpcRmRequestTimeout=30000
transport.rpcTmRequestTimeout=30000
transport.rpcTcRequestTimeout=30000
transport.threadFactory.bossThreadPrefix=NettyBoss
transport.threadFactory.workerThreadPrefix=NettyServerNIOWorker
transport.threadFactory.serverExecutorThreadPrefix=NettyServerBizHandler
transport.threadFactory.shareBossWorker=false
transport.threadFactory.clientSelectorThreadPrefix=NettyClientSelector
transport.threadFactory.clientSelectorThreadSize=1
transport.threadFactory.clientWorkerThreadPrefix=NettyClientWorkerThread
transport.threadFactory.bossThreadSize=1
transport.threadFactory.workerThreadSize=default
transport.shutdown.wait=3
transport.serialization=seata
transport.compressor=none
#Transaction routing rules configuration, only for the client
service.vgroupMapping.default_tx_group=default
#If you use a registry, you can ignore it
service.default.grouplist=127.0.0.1:8091
service.enableDegrade=false
service.disableGlobalTransaction=false
#Transaction rule configuration, only for the client
client.rm.asyncCommitBufferLimit=10000
client.rm.lock.retryInterval=10
client.rm.lock.retryTimes=30
client.rm.lock.retryPolicyBranchRollbackOnConflict=true
client.rm.reportRetryCount=5
client.rm.tableMetaCheckEnable=true
client.rm.tableMetaCheckerInterval=60000
client.rm.sqlParserType=druid
client.rm.reportSuccessEnable=false
client.rm.sagaBranchRegisterEnable=false
client.rm.sagaJsonParser=fastjson
client.rm.tccActionInterceptorOrder=-2147482648
client.tm.commitRetryCount=5
client.tm.rollbackRetryCount=5
client.tm.defaultGlobalTransactionTimeout=60000
client.tm.degradeCheck=false
client.tm.degradeCheckAllowTimes=10
client.tm.degradeCheckPeriod=2000
client.tm.interceptorOrder=-2147482648
client.undo.dataValidation=true
client.undo.logSerialization=jackson
client.undo.onlyCareUpdateColumns=true
server.undo.logSaveDays=7
server.undo.logDeletePeriod=86400000
client.undo.logTable=undo_log
client.undo.compress.enable=true
client.undo.compress.type=zip
client.undo.compress.threshold=64k
#For TCC transaction mode
tcc.fence.logTableName=tcc_fence_log
tcc.fence.cleanPeriod=1h
#Log rule configuration, for client and server
log.exceptionRate=100
#Transaction storage configuration, only for the server. The file, db, and redis configuration values are optional.
store.mode=db
store.lock.mode=db
store.session.mode=db
#Used for password encryption
store.publicKey=
#If store.mode,store.lock.mode,store.session.mode are not equal to file, you can remove the configuration block.
store.file.dir=file_store/data
store.file.maxBranchSessionSize=16384
store.file.maxGlobalSessionSize=512
store.file.fileWriteBufferCacheSize=16384
store.file.flushDiskMode=async
store.file.sessionReloadReadSize=100
#These configurations are required if the store mode is db. If store.mode,store.lock.mode,store.session.mode are not equal to db, you can remove the configuration block.
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.jdbc.Driver
store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&rewriteBatchedStatements=true
store.db.user=root
store.db.password=root
store.db.minConn=5
store.db.maxConn=30
store.db.globalTable=global_table
store.db.branchTable=branch_table
store.db.distributedLockTable=distributed_lock
store.db.queryLimit=100
store.db.lockTable=lock_table
store.db.maxWait=5000
#These configurations are required if the store mode is redis. If store.mode,store.lock.mode,store.session.mode are not equal to redis, you can remove the configuration block.
store.redis.mode=single
store.redis.single.host=127.0.0.1
store.redis.single.port=6379
store.redis.sentinel.masterName=
store.redis.sentinel.sentinelHosts=
store.redis.maxConn=10
store.redis.minConn=1
store.redis.maxTotal=100
store.redis.database=0
store.redis.password=
store.redis.queryLimit=100
#Transaction rule configuration, only for the server
server.recovery.committingRetryPeriod=1000
server.recovery.asynCommittingRetryPeriod=1000
server.recovery.rollbackingRetryPeriod=1000
server.recovery.timeoutRetryPeriod=1000
server.maxCommitRetryTimeout=-1
server.maxRollbackRetryTimeout=-1
server.rollbackRetryTimeoutUnlockEnable=false
server.distributedLockExpireTime=10000
server.xaerNotaRetryTimeout=60000
server.session.branchAsyncQueueSize=5000
server.session.enableBranchAsyncRemove=false
server.enableParallelRequestHandle=false
#Metrics configuration, only for the server
metrics.enabled=false
metrics.registryType=compact
metrics.exporterList=prometheus
metrics.exporterPrometheusPort=9898

3.1.1.4、下载nacos-config.sh到conf

然后执行命令:sh nacos-config.sh -h localhost -p 8848 -g SEATA_GROUP -t 1ff3782d-b62d-402f-8bc4-ebcf40254d0a -u nacos -w nacos
把seata配置同步到nacos
在这里插入图片描述

3.1.2、导入数据库

创建数据库:create database seata;
然后执行以下的脚本。
https://github.com/seata/seata/blob/2.x/script/server/db/mysql.sql
global_table: 全局事务表,每当有一个全局事务发起后,就会在该表中记录全局事务的ID
branch_table: 分支事务表,记录每一个分支事务的 ID,分支事务操作的哪个数据库等信息
lock_table: 全局锁

3.1.3、启动seata server

D:\seata\seata-server-1.4.2\bin\seata-server.bat

3.2、用例

用户购买商品的业务逻辑。整个业务逻辑由3个微服务提供支持:

  • 仓储服务:对给定的商品扣除仓储数量。
  • 订单服务:根据采购需求创建订单。
  • 帐户服务:从用户帐户中扣除余额。
    在这里插入图片描述
    解决方案:
    在这里插入图片描述

3.2.1、项目配置

3.2.1.1、创建四个服务

order-service(对应order数据库)
account-service(对应account数据库)
storage-service(对应storage数据库)
business-service

3.2.1.2、每个业务数据库增加回滚表

CREATE TABLE `undo_log` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `branch_id` bigint(20) NOT NULL,
  `xid` varchar(100) NOT NULL,
  `context` varchar(128) NOT NULL,
  `rollback_info` longblob NOT NULL,
  `log_status` int(11) NOT NULL,
  `log_created` datetime NOT NULL,
  `log_modified` datetime NOT NULL,
  `ext` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

3.2.1.3、执行业务表脚本

USE storage;
DROP TABLE IF EXISTS `storage_tbl`;
CREATE TABLE `storage_tbl` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `commodity_code` varchar(255) DEFAULT NULL,
  `count` int(11) DEFAULT 0,
  PRIMARY KEY (`id`),
  UNIQUE KEY (`commodity_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

USE order;
DROP TABLE IF EXISTS `order_tbl`;
CREATE TABLE `order_tbl` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` varchar(255) DEFAULT NULL,
  `commodity_code` varchar(255) DEFAULT NULL,
  `count` int(11) DEFAULT 0,
  `money` int(11) DEFAULT 0,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

USE account;
DROP TABLE IF EXISTS `account_tbl`;
CREATE TABLE `account_tbl` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` varchar(255) DEFAULT NULL,
  `money` int(11) DEFAULT 0,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

3.2.2、common-service中添加依赖

<!--seata 分布式事务-->
<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-spring-boot-starter</artifactId>
    <version>1.4.2</version>
</dependency>
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
    <version>2021.0.4.0</version>
</dependency>

3.2.3、加配置

seata:
  enabled: true
  enable-auto-data-source-proxy: false
  application-id: vforumc-user
  tx-service-group: default_tx_group
  service:
    vgroup-mapping:
      default_tx_group: default
    disable-global-transaction: false
  registry:
    type: nacos
    nacos:
      application: seata-server
      server-addr: 127.0.0.1:8848
      namespace: 1ff3782d-b62d-402f-8bc4-ebcf40254d0a
      group: SEATA_GROUP
      username: nacos
      password: nacos
  config:
    nacos:
      server-addr: 127.0.0.1:8848
      namespace: 1ff3782d-b62d-402f-8bc4-ebcf40254d0a
      group: SEATA_GROUP
      username: nacos
      password: nacos

3.2.4、定义数据库代理

package com.xxxx.store.account.config;

import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

@Data
@Configuration
public class DataSourceConfig {
    @Value("${spring.datasource.url}")
    private String url;

    @Value("${spring.datasource.username}")
    private String username;

    @Value("${spring.datasource.password}")
    private String password;

    @Value("${spring.datasource.driver-class-name}")
    private String driveClassName;
}
package com.xxxx.store.account.config;

import com.zaxxer.hikari.HikariDataSource;
import io.seata.rm.datasource.DataSourceProxy;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import javax.annotation.Resource;
import javax.sql.DataSource;

/**
 * 数据源代理
 */
@Configuration
public class DataSourceProxyConfig {
    @Resource
    private DataSourceConfig dataSourceConfig;

    @Bean("dataSource")
    public DataSource druidDataSource() {
        HikariDataSource hikariDataSource = new HikariDataSource();
        hikariDataSource.setUsername(dataSourceConfig.getUsername());
        hikariDataSource.setPassword(dataSourceConfig.getPassword());
        hikariDataSource.setJdbcUrl(dataSourceConfig.getUrl());
        hikariDataSource.setDriverClassName(dataSourceConfig.getDriveClassName());
        return hikariDataSource;
    }

    @Bean
    @Primary
    public DataSourceProxy dataSourceProxy(DataSource dataSource) {
        return new DataSourceProxy(dataSource);
    }
}

每个业务服务启动类加上注解,把自动配置的数据源剔除掉,让以上代理的数据源生效

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)

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

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

相关文章

使用docker快速搭建redis哨兵模式

说明 本文主要参考&#xff1a; https://www.cnblogs.com/coderaniu/p/15352323.html https://developer.aliyun.com/article/892805 但是这两篇博客均缺失部分关键性细节&#xff0c;所以重新撰文。读者可以结合本文和上述文章一起阅读。 安装步骤 安装docker和docker-co…

小作文--流程图(练习1)

【【雅思写作】带你打破小作文‘流程图’的传说】 https://www.bilibili.com/video/BV1QP411Q7Gh/?share_sourcecopy_web&vd_source78768d4ae65c35ff26534bbaa8afc267 雅思小作文-流程图, 看这一篇就够了! - 冯凯文的文章 - 知乎 https://zhuanlan.zhihu.com/p/35868880 …

算法模板(3):搜索(3):图论提高

图论提高 最小生成树 &#xff08;1&#xff09;朴素版prim算法&#xff08; O ( n 2 ) O(n ^ 2) O(n2)&#xff09; 适用范围&#xff1a;稠密图易错&#xff1a;注意有向图还是无向图&#xff1b;注意有没有重边和负权边。从一个集合向外一个一个扩展&#xff0c;最开始只…

记录基于Vue.js的移动端Tree树形组件

目录 一、Liquor Tree 入门 &#xff1a; Development Component Options 组件选项 Structure 结构 二、vue-treeselect Introduction 介绍 Getting Started 入门 Vue 树形选择器&#xff08; Vue tree select &#xff09;组件在搭建 Vue 的 app 中特别常用&#xff0…

【Java|golang】2611. 老鼠和奶酪

有两只老鼠和 n 块不同类型的奶酪&#xff0c;每块奶酪都只能被其中一只老鼠吃掉。 下标为 i 处的奶酪被吃掉的得分为&#xff1a; 如果第一只老鼠吃掉&#xff0c;则得分为 reward1[i] 。 如果第二只老鼠吃掉&#xff0c;则得分为 reward2[i] 。 给你一个正整数数组 reward1…

【TypeScript】枚举类型和泛型的详细介绍

目录 TypeScript枚举类型 TypeScript泛型介绍 &#x1f3b2;泛型的基本使用 &#x1f3b2;泛型接口的使用 &#x1f3b2;泛型类的使用 &#x1f3b2;泛型的类型约束 枚举类型 枚举类型是为数不多的TypeScript特有的特性之一, JavaScript是没有的&#xff1a; 枚举其实就…

【FPGA零基础学习之旅#8】阻塞赋值与非阻塞赋值讲解

&#x1f389;欢迎来到FPGA专栏~阻塞赋值与非阻塞赋值 ☆* o(≧▽≦)o *☆嗨~我是小夏与酒&#x1f379; ✨博客主页&#xff1a;小夏与酒的博客 &#x1f388;该系列文章专栏&#xff1a;FPGA学习之旅 文章作者技术和水平有限&#xff0c;如果文中出现错误&#xff0c;希望大家…

解决elementUI弹出框关闭后再打开el-select下拉框无法选中的问题

文章目录 一、问题描述&#xff1a;二、问题解决 一、问题描述&#xff1a; 使用的前端UI框架为elementUI。 el-select组件在一个弹框中&#xff0c;打开该弹框&#xff0c;el-select可以正常选中&#xff0c;但是保存弹框中的表单信息关闭弹框后&#xff0c;再打开弹框&…

Spark大数据处理讲课笔记4.1 Spark SQL概述、数据帧与数据集

零、本讲学习目标 了解Spark SQL的基本概念掌握DataFrame的基本概念掌握Dataset的基本概念会基于DataFrame执行SQL查询 在很多情况下&#xff0c;开发工程师并不了解Scala语言&#xff0c;也不了解Spark常用API&#xff0c;但又非常想要使用Spark框架提供的强大的数据分析能力…

物联网Lora模块从入门到精通(二) LED灯泡闪烁与呼吸灯

目录 一、前言 二、实践与代码 1.电亮LED1 2.熄灭LED1 3.翻转LED电平 4.LED1与LED2交替闪烁 5.LED1呼吸灯 三、程序代码 一、前言 本篇内容属于新大陆物联网Lora模块开发&#xff0c;使用给定的Lora基础例程&#xff0c;并在其基础上开发完成&#xff0c;并可为其他版本的Lo…

亚马逊云科技Serverless构建的实时数仓解决方案,助力猎豹降低30%成本

也许你也听过这样一句话&#xff1a;“21世纪什么最贵&#xff1f;人才&#xff01;”当数字经济全面席卷而来&#xff0c;这个问题的答案不可置否地变为了“数据”。通过数据分析获取近乎实时的洞察&#xff0c;以驱动业务的全流程&#xff0c;是企业数字化转型的必经之路。借…

Linux操作系统学习——启动

概要 Linux操作系统内核是服务端学习的根基&#xff0c;也是提高编程能力、源码阅读能力和进阶知识学习能力的重要部分&#xff0c;本文开始将记录Linux操作系统中的各个部分源码学习历程。 1. 理解代码的组织结构 以Linux源码举例&#xff0c;首先你得知道操作系统分为哪几个部…

【SpringCloud入门】-- 初识Eureka注册中心

目录 1.SpringCloudEureka简介 2.什么是CAP原则&#xff1f; 3.注册中心的概念&#xff1f; 4.SpringCloud其他注册中心 5.搭建Eureka注册中心 6.eureka的配置(主要是server&#xff0c;instance&#xff0c;client) 7.eureka集群概念 8.eureka集群搭建 1.SpringCloudE…

基于R语言的物种气候生态位动态量化与分布特征模拟

目录 专题一 引言 专题二 数据获取与处理方法 专题三 组合物种分布模型&#xff08;Ensemble Species Distribution Model&#xff09;的原理与使用 专题四 拓展研究 专题五 结果分析与论文写作 专题六 案例分析 专题七 总结和展望 利用R语言进行物种气候生态位动态量化…

【蓝桥杯算法题】用java遍写税收计算

【蓝桥杯算法题】用java遍写税收计算 题目&#xff1a;劳务报酬税收计算&#xff1a;输入 m &#xff0c;输出税后收入。如果 m <800&#xff0c;不扣税&#xff0c; 如果800< m <4000&#xff0e;则 m 减去800后的金额扣除20&#xff05;所得税。如果 m >4000&…

【计算机网络详解】——运输层(学习笔记)

&#x1f4d6; 前言&#xff1a;两台主机的通信&#xff0c;实际上两台主机中的应用进程进行通信&#xff0c;而在一台计算机中&#xff0c;用不同的端口号标识不同的应用进程。本节将介绍传输层的相关内容&#xff0c;包括端口号的分配方法、端口号的复用与分用、以及传输层的…

编译tolua——3、以pbc为例子,添加第三方库

目录 1、编译工具和环境说明 2、基础编译tolua 3、以pbc为例子&#xff0c;添加第三方库 4、更新luaJit 大家好&#xff0c;我是阿赵。 之前分享过怎样正常编译基础版本的tolua。这次用添加pbc为例&#xff0c;看看怎样往tolua里面添加其他的第三方库。知道了方法之后&#xf…

本地项目托管到 Gitee

本地项目托管到 Gitee 1、创建远程仓库2、Git Bash Here第一步&#xff1a;初始化本地仓库 git init第二步&#xff1a;建立链接git remote add origin xxx第三步&#xff1a;将远程仓库中的文件推送至本地仓库中git pull --rebase origin master第四步&#xff1a;将本地项目放…

【利用AI让知识体系化】简要了解网络七层协议

文章目录 一、前言引言目的和意义 二、网络七层协议简介OSI参考模型七层协议分层结构和各层协议简介 三、物理层 - Layer 1物理层概述物理层的作用物理层标准和协议 四、数据链路层 - Layer 2数据链路层概述常见的协议 五、网络层 - Layer 3网络层概述网络层的作用IP地址的作用…

cool-admin框架后端使用-node版本,线上宝塔部署

版本6.x 宝塔新建一个文件夹和创建好数据库&#xff0c;记录账号和密码&#xff0c;自行创建&#xff0c;不做说明 特别注意&#xff0c;如果用宝塔node管理那里运行&#xff0c;如果按照到有pm2的&#xff0c;要先卸载&#xff0c;不可以共存&#xff0c;会有冲突 cool-vue前端…