集成DTM实现跨语言分布式事务V1.0

集成DTM实现跨语言分布式事务V1.0

简介

DTM是一款开源的分布式事务管理器,解决跨数据库、跨服务、跨语言栈更新数据的一致性问题。

通俗一点说,DTM提供跨服务事务能力,一组服务要么全部成功,要么全部回滚,避免只更新了一部分数据产生的一致性问题。

https://dtm.pub/

安装运行DTM

以下安装可以使用Dockerfile的方式与dmc服务一起部署,另外官网介绍的docker安装模式我没成功!

安装go语言环境

#下载安装go语言包,请使用1.20以上版本,其他版本安装失败
wget -c https://dl.google.com/go/go1.20.1.linux-amd64.tar.gz -O - | sudo tar -xz -C /usr/local

cd /usr/local/
mkdir gopath 

vi /etc/profile 

#在/etc/profile添加go环境配置

export GOROOT=/usr/local/go
export GOPATH=/usr/local/gopath
export PATH=$PATH:$GOROOT/bin
export GO111MODULE="on" # 开启 Go moudles 特性
export GOPROXY=https://goproxy.cn,direct # 安装 Go 模块时,国内代理服务器设置

#查看版本
go version

安装dtm服务

git clone https://github.com/dtm-labs/dtm && cd dtm
go build

#运行./dtm,看到如下说明成功
[root@paratera128 dtm]# ./dtm
invalid log level: , switching to default: INFO
{"level":"info","ts":"2023-07-25T18:54:17.816-0700","caller":"entry/main.go:46","msg":"dtm version is: v0.0.0-dev"}
{"level":"info","ts":"2023-07-25T18:54:17.818-0700","caller":"config/config.go:121","msg":"config file:  loaded config is: \n{\n  \"Store\": {\n    \"Driver\": \"boltdb\",\n    \"Host\": \"\",\n    \"Port\": 0,\n    \"User\": \"\",\n    \"Password\": \"\",\n    \"Db\": \"dtm\",\n    \"Schema\": \"public\",\n    \"MaxOpenConns\": 500,\n    \"MaxIdleConns\": 500,\n    \"ConnMaxLifeTime\": 5,\n    \"DataExpire\": 604800,\n    \"FinishedDataExpire\": 86400,\n    \"RedisPrefix\": \"{a}\"\n  },\n  \"TransCronInterval\": 3,\n  \"TimeoutToFail\": 35,\n  \"RetryInterval\": 10,\n  \"RequestTimeout\": 3,\n  \"HTTPPort\": 36789,\n  \"GrpcPort\": 36790,\n  \"JSONRPCPort\": 36791,\n  \"MicroService\": {\n    \"Driver\": \"default\",\n    \"Target\": \"\",\n    \"EndPoint\": \"\"\n  },\n  \"HTTPMicroService\": {\n    \"Driver\": \"default\",\n    \"RegistryType\": \"\",\n    \"RegistryAddress\": \"\",\n    \"RegistryOptions\": \"{}\",\n    \"Target\": \"\",\n    \"EndPoint\": \"\"\n  },\n  \"UpdateBranchSync\": 0,\n  \"UpdateBranchAsyncGoroutineNum\": 1,\n  \"LogLevel\": \"info\",\n  \"Log\": {\n    \"Outputs\": \"stderr\",\n    \"RotationEnable\": 0,\n    \"RotationConfigJSON\": \"{}\"\n  },\n  \"TimeZoneOffset\": \"\",\n  \"ConfigUpdateInterval\": 3,\n  \"AlertRetryLimit\": 3,\n  \"AlertWebHook\": \"\",\n  \"AdminBasePath\": \"\"\n}"}
{"level":"info","ts":"2023-07-25T18:54:17.820-0700","caller":"maxprocs/maxprocs.go:47","msg":"maxprocs: Leaving GOMAXPROCS=1: CPU quota undefined"}
{"level":"info","ts":"2023-07-25T18:54:17.827-0700","caller":"dtmsvr/svr.go:32","msg":"start dtmsvr"}
{"level":"info","ts":"2023-07-25T18:54:17.828-0700","caller":"dtmsvr/svr.go:51","msg":"dtmsvr http listen at: 36789"}
{"level":"info","ts":"2023-07-25T18:54:17.830-0700","caller":"dtmsvr/svr.go:65","msg":"grpc listening at [::]:36790"}
{"level":"info","ts":"2023-07-25T18:54:17.931-0700","caller":"dtmsvr/svr.go:80","msg":"RegisterService: default"}
{"level":"info","ts":"2023-07-25T18:54:17.932-0700","caller":"dtm/main.go:96","msg":"admin is proxied to admin.dtm.pub"}
{"level":"info","ts":"2023-07-25T18:54:17.933-0700","caller":"dtm/main.go:98","msg":"admin is running at: http://localhost:36789"}

访问:http://localhost:36789,可以看到所有的事务状态和过程数据
在这里插入图片描述

数据存储

DTM部署后会默认将数据进行文件的形式存储,文件名:dtm.bolt,当然,你也可以通过修改conf.yml文件以关系型数据库,redis等方式存储

如下提供MySQL的数据库脚本,可以根据需要定制化监控处理功能,并且DTM提供了相应的API供服务方进行RM表的数据保存

RM表(资源管理器)

CREATE TABLE dtm.barrier (
  `id` bigint(22) NOT NULL AUTO_INCREMENT,
  `trans_type` varchar(45) DEFAULT '' COMMENT '事务模式',
  `gid` varchar(128) DEFAULT '' COMMENT '全局事务id',
  `branch_id` varchar(128) DEFAULT '' COMMENT '分支事务id',
  `op` varchar(45) DEFAULT '' COMMENT '事务操作', 
  `barrier_id` varchar(45) DEFAULT '' COMMENT '分支事务层级,如果分支事务包含事务会递增',
  `reason` varchar(45) DEFAULT '' COMMENT '插入此记录的分支类型,如果成功与op相同,如果失败与op相反',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `gid` (`gid`,`branch_id`,`op`,`barrier_id`),
  KEY `create_time` (`create_time`),
  KEY `update_time` (`update_time`)
) ENGINE=InnoDB AUTO_INCREMENT=44 DEFAULT CHARSET=utf8;

TM表(事务管理器)

CREATE TABLE if not EXISTS dtm.trans_global (
  `id` bigint(22) NOT NULL AUTO_INCREMENT,
  `gid` varchar(128) NOT NULL COMMENT 'global transaction id',
  `trans_type` varchar(45) not null COMMENT 'transaction type: saga | xa | tcc | msg',
  `status` varchar(12) NOT NULL COMMENT 'transaction status: prepared | submitted | aborting | succeed | failed',
  `query_prepared` varchar(1024) NOT NULL COMMENT 'url to check for msg|workflow',
  `protocol` varchar(45) not null comment 'protocol: http | grpc | json-rpc',
  `create_time` datetime DEFAULT NULL,
  `update_time` datetime DEFAULT NULL,
  `finish_time` datetime DEFAULT NULL,
  `rollback_time` datetime DEFAULT NULL,
  `options` varchar(1024) DEFAULT '' COMMENT 'options for transaction like: TimeoutToFail, RequestTimeout',
  `custom_data` varchar(1024) DEFAULT '' COMMENT 'custom data for transaction',
  `next_cron_interval` int(11) default null comment 'next cron interval. for use of cron job',
  `next_cron_time` datetime default null comment 'next time to process this trans. for use of cron job',
  `owner` varchar(128) not null default '' comment 'who is locking this trans',
  `ext_data` TEXT comment 'extra data for this trans. currently used in workflow pattern',
  `result` varchar(1024) DEFAULT '' COMMENT 'result for transaction',
  `rollback_reason` varchar(1024) DEFAULT '' COMMENT 'rollback reason for transaction',
  PRIMARY KEY (`id`),
  UNIQUE KEY `gid` (`gid`),
  key `owner`(`owner`),
  key `status_next_cron_time` (`status`, `next_cron_time`) comment 'cron job will use this index to query trans'
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;

CREATE TABLE IF NOT EXISTS dtm.trans_branch_op (
  `id` bigint(22) NOT NULL AUTO_INCREMENT,
  `gid` varchar(128) NOT NULL COMMENT 'global transaction id',
  `url` varchar(1024) NOT NULL COMMENT 'the url of this op',
  `data` TEXT COMMENT 'request body, depreceated',
  `bin_data` BLOB COMMENT 'request body',
  `branch_id` VARCHAR(128) NOT NULL COMMENT 'transaction branch ID',
  `op` varchar(45) NOT NULL COMMENT 'transaction operation type like: action | compensate | try | confirm | cancel',
  `status` varchar(45) NOT NULL COMMENT 'transaction op status: prepared | succeed | failed',
  `finish_time` datetime DEFAULT NULL,
  `rollback_time` datetime DEFAULT NULL,
  `create_time` datetime DEFAULT NULL,
  `update_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `gid_uniq` (`gid`, `branch_id`, `op`)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;

CREATE TABLE IF NOT EXISTS dtm.kv (
  `id` bigint(22) NOT NULL AUTO_INCREMENT,
  `cat` varchar(45) NOT NULL COMMENT 'the category of this data',
  `k` varchar(128) NOT NULL,
  `v` TEXT,
  `version` bigint(22) default 1 COMMENT 'version of the value',
  create_time datetime default NULL,
  update_time datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE key `uniq_k`(`cat`, `k`)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;

DTM入门示例

maven依赖

<dependency>
    <groupId>io.github.dtm-labs</groupId>
    <artifactId>dtmcli-java</artifactId>
    <version>2.1.4</version>
</dependency>

application.yml

server:
  port: 8081
  
#DTM服务地址
dtm:
  ipport: '192.168.137.128:36789'

spring:
  datasource:
    url: jdbc:mysql://182.92.67.160:3316/dtm?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
    username: root
    password: paratera
    driver-class-name: com.mysql.cj.jdbc.Driver

Saga模式

将长事务拆分为多个分支事务,由Saga事务协调器协调,如果每个分支事务都成功提交完成,那么全局事务就正常完成,如果某个步骤失败,则根据相反顺序一次调用补偿操作。

时序图

成功完成

在这里插入图片描述

失败回滚

模拟正向操作及反向补偿

@RequestMapping("TransOut")
public Object TransOut() {
    try {
        logger.info("TransOut");
        return DtmResponse.buildDtmResponse(Constants.SUCCESS_RESULT);
    }catch (Exception e){
        return DtmResponse.buildDtmResponse(Constants.FAILURE_RESULT);
    }
}

@RequestMapping("TransOutCompensate")
public Object TransOutCompensate() {
    try {
        logger.info("TransOutCompensate");
        return DtmResponse.buildDtmResponse(Constants.SUCCESS_RESULT);
    }catch (Exception e){
        return DtmResponse.buildDtmResponse(Constants.FAILURE_RESULT);
    }
}

@RequestMapping("TransIn")
public Object TransIn() {
    try {
        logger.info("TransIn");
        //int i = 1/0 ;模拟异常时回滚
        return DtmResponse.buildDtmResponse(Constants.SUCCESS_RESULT);
    }catch (Exception e){
        return DtmResponse.buildDtmResponse(Constants.FAILURE_RESULT);
    }
}

@RequestMapping("TransInCompensate")
public Object TransInCompensate() {
    try {
        logger.info("TransInCompensate");
        return DtmResponse.buildDtmResponse(Constants.SUCCESS_RESULT);
    }catch (Exception e){
        return DtmResponse.buildDtmResponse(Constants.FAILURE_RESULT);
    }
}

执行全局事务

//这个地方如果DTM服务跟项目不是同一台机器,请填写IP地址,并保障可以ping通
private static final String svc = "http://192.168.110.174:8081/api";

@Value("${dtm.ipport}")
private String ipPort;

@RequestMapping("testSaga")
public String testSage() {

    DtmClient dtmClient = new DtmClient(ipPort);

    try {
        // 全局事务id,保证唯一即可
        String customGid = UUID.randomUUID().toString();
        Saga saga = dtmClient
                .newSaga(customGid)
            	//按业务顺序将正向操作和反向补偿操作注入到Saga流中,通过事务协调器执行,第三个参数为分支接口传参(正向操作和反向补偿传参都是一样的)
                .add(svc + "/TransOut", svc + "/TransOutCompensate", null)
                .add(svc + "/TransIn", svc + "/TransInCompensate", null)
                .enableWaitResult();

        saga.submit();
    } catch (Exception e) {
        log.error("saga submit error", e);
        return "fail";
    }
    return "success";
}

成功完成:

在这里插入图片描述

失败回滚:

在这里插入图片描述

TCC模式

TCC是Try、Confirm、Cancel三个词语的缩写

  • Try 阶段:尝试执行,完成所有业务检查(一致性), 预留必须业务资源(准隔离性)
  • Confirm 阶段:如果所有分支的Try都成功了,则走到Confirm阶段。Confirm真正执行业务,不作任何业务检查,只使用 Try 阶段预留的业务资源
  • Cancel 阶段:如果所有分支的Try有一个失败了,则走到Cancel阶段。Cancel释放 Try 阶段预留的业务资源。

时序图

成功完成

在这里插入图片描述

失败回滚

在这里插入图片描述

模拟三阶段事务

@RequestMapping("TransOutTry")
public Object TransOutTry() {
    try {
        logger.info("TransOutTry");
        return DtmResponse.buildDtmResponse(Constants.SUCCESS_RESULT);
    }catch (Exception e){
        return DtmResponse.buildDtmResponse(Constants.FAILURE_RESULT);
    }
}

@RequestMapping("TransOutConfirm")
public Object TransOutConfirm() {
    try {
        logger.info("TransOutConfirm");
        return DtmResponse.buildDtmResponse(Constants.SUCCESS_RESULT);
    }catch (Exception e){
        return DtmResponse.buildDtmResponse(Constants.FAILURE_RESULT);
    }
}

@RequestMapping("TransOutCancel")
public Object TransOutCancel() {
    try {
        logger.info("TransOutCancel");
        return DtmResponse.buildDtmResponse(Constants.SUCCESS_RESULT);
    }catch (Exception e){
        return DtmResponse.buildDtmResponse(Constants.FAILURE_RESULT);
    }
}

@RequestMapping("TransInTry")
public Object TransInTry() {
    try {
        logger.info("TransInTry");
        return DtmResponse.buildDtmResponse(Constants.SUCCESS_RESULT);
    }catch (Exception e){
        return DtmResponse.buildDtmResponse(Constants.FAILURE_RESULT);
    }
}

@RequestMapping("TransInConfirm")
public Object TransInConfirm() {
    try {
        logger.info("TransInConfirm");
        return DtmResponse.buildDtmResponse(Constants.SUCCESS_RESULT);
    }catch (Exception e){
        return DtmResponse.buildDtmResponse(Constants.FAILURE_RESULT);
    }
}

@RequestMapping("TransInCancel")
public Object TransInCancel() {
    try {
        logger.info("TransInCancel");
        return DtmResponse.buildDtmResponse(Constants.SUCCESS_RESULT);
    }catch (Exception e){
        return DtmResponse.buildDtmResponse(Constants.FAILURE_RESULT);
    }
}

注册执行事务分支

private static final String svc = "http://192.168.110.174:8081/api";
    
@Value("${dtm.ipport}")
private String ipport;

@RequestMapping("testTcc")
public String testTcc() {
    //创建dtm clinet
    DtmClient dtmClient = new DtmClient(ipport);
    //创建tcc事务
    String customGid = UUID.randomUUID().toString();
    try {
        dtmClient.tccGlobalTransaction(customGid, tcc -> {
            //第一个参数为接口传参,三阶段对应的都是相同的参数
            tcc.callBranch(null, svc + "/TransOutTry", svc + "/TransOutConfirm", svc + "/TransOutCancel");
            tcc.callBranch(null, svc + "/TransInTry", svc + "/TransInConfirm", svc + "/TransInCancel");
        });
    } catch (Exception e) {
        log.error("tccGlobalTransaction error", e);
        return "fail";
    }
    return "success";
}

成功完成:

在这里插入图片描述

失败回滚:

在这里插入图片描述

操作异常思考

假如Compensate/Confirm/Cancel操作遇见失败会怎么样?按照业务的思路,Compensate/Confirm/Cancel操作是要求最终成功的,遇见失败的情况,都是由于临时故障或者程序bug。dtm在Compensate/Confirm/Cancel操作遇见失败时,会不断进行重试(时间间隔为10 * 2的幂次方),直到成功,当然,如果是因为业务条件不满足等非技术环境问题,不应该进入重试,这一点应该在程序编写时充分考虑。

为了避免程序bug导致补偿操作一直无法成功,可以通过DTM管理控制台进行监控,由运维人员手动后台处理,强制停止!,不过这个目前做得比较粗糙,凑合着用!

在这里插入图片描述

另外如果对业务没有特别的要求,可以暴力设置超时回滚:

saga.setTimeoutToFail(1800);

Saga与TCC模式对比

其实还有两阶段、XA模式等,因为并发锁的原因,性能比较低,特别是对于死锁的处理需要大量的人工介入

SagaTCC
难度简单,只需要正反两个分支事务复杂,需要Try、Confirm、Cancel三个分支事务
一致性低,缺少资源预留,并发度高时可能出现脏读高,一开始就会对所有资源进行预留(try)
并发执行能力支持并发和按序执行不支持并发
事务嵌套不支持支持

先易后难,我们后续的功能开发先基于Saga模式进行

DMC平台集成分布式事务V1.0

数据库设计

DROP TABLE IF EXISTS trans_setting;
CREATE TABLE IF NOT EXISTS trans_setting (
  id bigint(22) NOT NULL AUTO_INCREMENT,
  call_url varchar(45) NOT NULL COMMENT '全局事务执行标志,原则上只需要保证唯一性即可,建议使用项目名+模块名+方法名,如console.user.add',
  create_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at datetime ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `call_url` (`call_url`)
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '全局事务执行表' ROW_FORMAT = Compact;

DROP TABLE IF EXISTS trans_setting_detail;
CREATE TABLE IF NOT EXISTS trans_setting_detail (
  id bigint(22) NOT NULL AUTO_INCREMENT,
  fid bigint(22) NOT NULL COMMENT '全局事务执行表主键',
  branch_url varchar(100) NOT NULL COMMENT '分支url,完整地址:ip、port、restful接口',
  branch_rb_url varchar(100) NOT NULL COMMENT '分支回滚url,完整地址:ip、port、restful接口',
  create_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at datetime ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `fid_branch_url` (`fid`,`branch_url`)
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '分支事务URL表' ROW_FORMAT = Compact;

增删改查

略…

DTM服务地址配置

dtm:
  url: 192.168.137.128:36789

全局事务执行接口

通过restful接口的方式,兼容跨语言、服务、数据库的全局事务处理

/**
     * 分布式事务统一调度接口
     *
     * @param callUrl  配置的全局事务执行标志
     * @param postData 分支事务接口参数
     * @return
     */
@PostMapping("/s-api/globalTransaction")
public String execGlobalTransaction(String callUrl, Object postData) {
    return transactionService.execGlobalTransaction(callUrl, postData);
}
@Value("${dtm.url}")
private String dtmUrl;

public String execGlobalTransaction(String callUrl, Object postData) {
    QueryWrapper wrapper = new QueryWrapper();
    wrapper.eq("call_url", callUrl);
    TransSetting transSetting = settingDao.selectOne(wrapper);
    wrapper.clear();
    wrapper.eq("fid", transSetting.getId());
    List<TransSettingDetail> list = settingDetailDao.selectList(wrapper);

    DtmClient dtmClient = new DtmClient(dtmUrl);

    try {
        //使用callUrl + 时间戳 + 四位随机数作为gid,比较好定位问题
        String time = DateUtil.date2String(Calendar.getInstance().getTime(), DateUtil.DATE_TIME);
        int number = new Random().nextInt(9000) + 1000;
        String customGid = callUrl + ":" + time + ":" + number;
        Saga saga = dtmClient.newSaga(customGid);
        //循环遍历添加分支url
        list.forEach(detail -> {
            saga.add(detail.getBranchUrl(), detail.getBranchRbUrl(), postData);
        });
        saga.enableWaitResult();
        saga.submit();
    } catch (Exception e) {
        log.error("saga submit error", e);
        return "fail";
    }
    return "success";
}

调度测试

在这里插入图片描述

从DTM控制台可以看到,调度成功并且没有触发回滚:

在这里插入图片描述

我们人为在转入分支制造异常,再次测试:

在这里插入图片描述

调度失败,发生回滚:

在这里插入图片描述

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

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

相关文章

《Go 语言第一课》课程学习笔记(一)

配好环境&#xff1a;选择一种最适合你的 Go 安装方法 选择 Go 版本 一般情况下&#xff0c;建议采用最新版本。因为 Go 团队发布的 Go 语言稳定版本的平均质量一直是很高的&#xff0c;少有影响使用的重大 bug。可以根据不同实际项目需要或开源社区的情况使用不同的版本。 有…

Redis-分布式锁!

分布式锁&#xff0c;顾名思义&#xff0c;分布式锁就是分布式场景下的锁&#xff0c;比如多台不同机器上的进程&#xff0c;去竞争同一项资源&#xff0c;就是分布式锁。 分布式锁特性 互斥性:锁的目的是获取资源的使用权&#xff0c;所以只让一个竞争者持有锁&#xff0c;这…

【学习心得】安装cuda/cudann和pytorch

一、查看驱动信息 # 进入CMD输入命令 nvidia-smi 也可以右下角图标打开NVIDIA 设置进行查看 二、下载安装CUDA 1、下载 下载地址 https://developer.nvidia.com/ 2、安装 推荐自定义安装。建议只勾选Cuda&#xff0c;只安装这一个就好&#xff0c;以免报错安装失败。 3、验证…

Python学习笔记_基础篇(八)_正则表达式

1. 正则表达式基础 1.1. 简单介绍 正则表达式并不是Python的一部分。正则表达式是用于处理字符串的强大工具&#xff0c;拥有自己独特的语法以及一个独立的处理引擎&#xff0c;效率上可能不如str自带的方法&#xff0c;但功能十分强大。得益于这一点&#xff0c;在提供了正则…

ppt中线材相交接的地方,如何绘画

ppt中线材相交接的地方&#xff1a; 在ppt中绘画线材相互交接的地方&#xff1a; 1.1绘图工具中的“弧形” 1.2小技巧 “弧形”工具点一下&#xff0c;在ppt中如下 1.3拖动活动点进行调整图形 1.4绘画圆弧 1.5调整“圆弧”的大小&#xff0c;鼠标放在“黄色点”位置&#xf…

W5500-EVB-PICO做UDP Client进行数据回环测试(八)

前言 上一章我们用开发板作为UDP Server进行数据回环测试&#xff0c;本章我们让我们的开发板作为UDP Client进行数据回环测试。 连接方式 使开发板和我们的电脑处于同一网段&#xff1a; 开发板通过交叉线直连主机开发板和主机都接在路由器LAN口 测试工具 网路调试工具&a…

【C语言】动态通讯录 -- 详解

⚪前言 前面详细介绍了静态版通讯录【C语言】静态通讯录 -- 详解_炫酷的伊莉娜的博客-CSDN博客&#xff0c;但是静态版通讯录的空间是无法被改变的&#xff0c;而且空间利用率也不高。为了解决静态通讯录这一缺点&#xff0c;这时就要有一个能够随着存入联系人数量的增加而增大…

案例15 Spring Boot入门案例

1. 选择Spring Initializr快速构建项目 ​ 2. 设置项目信息 ​ 3. 选择依赖 ​ 4. 设置项目名称 ​ 5. 项目结构 ​ 6. 项目依赖 自动配置了Spring MVC、内置了Tomcat、配置了Logback(日志)、配置了JSON。 ​ 7. 创建HelloController类 com.wfit.boot.hello目录下创建HelloCo…

【云原生】Docker 详解(二):Docker 架构及工作原理

Docker 详解&#xff08;二&#xff09;&#xff1a;Docker 架构及工作原理 Docker 在运行时分为 Docker 引擎&#xff08;服务端守护进程&#xff09; 和 客户端工具&#xff0c;我们日常使用各种 docker 命令&#xff0c;其实就是在使用 客户端工具 与 Docker 引擎 进行交互。…

哈夫曼树(赫夫曼树、最优树)详解

目录 哈夫曼树&#xff08;赫夫曼树、最优树&#xff09;详解 哈夫曼树相关的几个名词 什么是哈夫曼树 构建哈夫曼树的过程 哈弗曼树中结点结构 构建哈弗曼树的算法实现 哈夫曼树&#xff08;赫夫曼树、最优树&#xff09;详解 哈夫曼树相关的几个名词 路径&#xff1a;…

深入理解 Flutter 图片加载原理

作者&#xff1a;京东零售 徐宏伟 来源&#xff1a;京东云开发者社区 前言 随着Flutter稳定版本逐步迭代更新&#xff0c;京东APP内部的Flutter业务也日益增多&#xff0c;Flutter开发为我们提供了高效的开发环境、优秀的跨平台适配、丰富的功能组件及动画、接近原生的交互体验…

Nginx详解

1、高并发时代 单台tomcat在理想情况下可支持的最大并发数量在200~500之间&#xff0c;如果大于这个数量可能会造成响应缓慢甚至宕机。 解决方案是通过多台服务器分摊并发压力&#xff0c;这不仅需要有多台tomcat服务器&#xff0c;还需要一台服务器专门用来分配请求。这既是…

Socks5代理在多线程爬虫中的应用

在进行爬虫开发过程中&#xff0c;我们常常需要处理大量的数据&#xff0c;并执行多任务并发操作。然而&#xff0c;频繁的请求可能会引起目标网站的反爬机制&#xff0c;导致IP封禁或限制访问。为了规避这些限制&#xff0c;我们可以借助Socks5代理的强大功能&#xff0c;通过…

产品经理如何突破职业瓶颈,杀出重围?

随着社会的进步和科技的发展&#xff0c;互联网行业从未停止过发展的脚步。而在这个充满机遇和挑战的赛道上&#xff0c;互联网产品经理的角色显得尤为重要。然而&#xff0c;随着互联网产品经理的数量逐年增加&#xff0c;内卷化现象也日益严重。那么&#xff0c;产品经理应该…

一篇文章教会你搭建私人kindle图书馆,并内网穿透实现公网访问

搭建私人kindle图书馆&#xff0c;并内网穿透实现公网访问 在电子书风靡的时期&#xff0c;大部分人都购买了一本电子书&#xff0c;虽然这本电子书更多的时候是被搁置在储物架上吃灰&#xff0c;或者成为盖泡面的神器&#xff0c;但当亚马逊发布消息将放弃电子书在中国的服务…

excel填数据转json格式

定制化比较严重&#xff0c;按需更改 excel文件如下 代码 # -*- coding: utf-8 -*- import oss2 import shutil import sys import xlwt import xlrd import json from datetime import datetime, timedeltafile1 "C:\\Users\\cxy\\Desktop\\generate.xls" #打开表…

操作系统搭建相关知识

文章目录 系统篇netstat命令systemctl命令Systemd系统资源分类&#xff08;12类&#xff09; 网络篇ifconfig命令操作系统配置动态IP脚本dhcp服务的安装与配置防火墙相关知识 操作系统常用配置文件 系统篇 netstat命令 netstat指路 systemctl命令 常用于重启系统的每个服务…

机器学习算法之-逻辑回归(2)

为什么需要逻辑回归 拟合效果太好 特征与标签之间的线性关系极强的数据&#xff0c;比如金融领域中的 信用卡欺诈&#xff0c;评分卡制作&#xff0c;电商中的营销预测等等相关的数据&#xff0c;都是逻辑回归的强项。虽然现在有了梯度提升树GDBT&#xff0c;比逻辑回归效果更…

TCP/IP协议追层分析物理层(第三十九课)

TCP/IP协议追层分析物理层(第三十九课) 1 物理层:建立、维护、断开物理连接,定义了接口及介质,实现了比特流的传输。 1、传输介质分类 有线介质:网线(双绞线)、光纤 无线介质:无线电 微波 激光 红外线 2、双绞线分类: 五类cat5: 适用于100Mbps 超五类cat5e:适用于…

多维时序 | MATLAB实现CNN-BiGRU-Attention多变量时间序列预测

多维时序 | MATLAB实现CNN-BiGRU-Attention多变量时间序列预测 目录 多维时序 | MATLAB实现CNN-BiGRU-Attention多变量时间序列预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 MATLAB实现CNN-BiGRU-Attention多变量时间序列预测&#xff0c;CNN-BiGRU-Attent…