分布式之任务调度Elastic-Job学习二

4 Spring 集成与分片详解

ejob-springboot 工程

4.1 pom 依赖

<properties>
	<elastic-job.version>2.1.5</elastic-job.version>
</properties>
<dependency>
	<groupId>com.dangdang</groupId>
	<artifactId>elastic-job-lite-core</artifactId>
	<version>${elastic-job.version}</version>
</dependency>
<!-- elastic-job-lite-spring -->
<dependency>
	<groupId>com.dangdang</groupId>
	<artifactId>elastic-job-lite-spring</artifactId>
	<version>${elastic-job.version}</version>
</dependency>

4.2 application.properties

定义配置类和任务类中要用到的参数
server.port=${random.int[10000,19999]}
regCenter.serverList = localhost:2181
regCenter.namespace = gupao-ejob-springboot
gupaoJob.cron = 0/3 * * * * ?
gupaoJob.shardingTotalCount = 2
gupaoJob.shardingItemParameters = 0=0,1=

4.3 创建任务

创建任务类,加上@Component 注解

@Component
public class SimpleJobDemo implements SimpleJob {
	public void execute(ShardingContext shardingContext) {
		System.out.println(String.format("------Thread ID: %s, %s,任务总片数: %s, " +
		"当前分片项: %s.当前参数: %s," +
		"当前任务名称: %s.当前任务参数 %s", 
		Thread.currentThread().getId()
		new SimpleDateFormat("HH:mm:ss").format(new Date()), shardingContext.getShardingTotalCount(), shardingContext.getShardingItem(), shardingContext.getShardingParameter(), shardingContext.getJobName(), shardingContext.getJobParameter()
		));
	}
}

4.4 注册中心配置

Bean 的 initMethod 属性用来指定 Bean 初始化完成之后要执行的方法,用来替代继承 InitializingBean 接口,以便在容器启动的时候创建注册中心。
@Configuration
public class ElasticRegCenterConfig {
	@Bean(initMethod = "init")
	public ZookeeperRegistryCenter regCenter(
	@Value("${regCenter.serverList}") final String serverList, @Value("${regCenter.namespace}") final String namespace) {
		return new ZookeeperRegistryCenter(new ZookeeperConfiguration(serverList, namespace));
	}
}

4.5 作业三级配置

Core——Type——Lite
return LiteJobConfiguration.newBuilder(new SimpleJobConfiguration(JobCoreConfiguration.newBuilder()
@Configuration
public class ElasticJobConfig {
	@Autowired
	private ZookeeperRegistryCenter regCenter;
	@Bean(initMethod = "init")
	public JobScheduler simpleJobScheduler(final SimpleJobDemo simpleJob, @Value("${gupaoJob.cron}") final String cron, @Value("${gupaoJob.shardingTotalCount}") final int shardingTotalCount,@Value("${gupaoJob.shardingItemParameters}") final String
	shardingItemParameters) {
		return new SpringJobScheduler(simpleJob, regCenter, getLiteJobConfiguration(simpleJob.getClass(), cron, shardingTotalCount, shardingItemParameters));
	}
	private LiteJobConfiguration getLiteJobConfiguration(final Class<? extends SimpleJob> jobClass, final String cron, final int shardingTotalCount, final String shardingItemParameters) {
		return LiteJobConfiguration.newBuilder(new SimpleJobConfiguration(
		JobCoreConfiguration.newBuilder(jobClass.getName(), cron, shardingTotalCount)
		.shardingItemParameters(shardingItemParameters).build()
		, jobClass.getCanonicalName())
		).overwrite(true).build();
	}
}

4.6 作业运行

先把 application.properties 中的分片数全部改成 1
启动 com.gupaoedu.EjobApp 的 main 方法

4.7 分片策略

4.7.1 分片项与分片参数
任务分片,是为了实现把一个任务拆分成多个子任务,在不同的 ejob 示例上执行。
例如 100W 条数据,在配置文件中指定分成 10 个子任务(分片项),这 10 个子任务再按照一定的规则分配到 5 个实际运行的服务器上执行。除了直接用分片项 ShardingItem获取分片任务之外,还可以用 item 对应的 parameter 获取任务。
standalone 工程:simple.SimpleJobTest
JobCoreConfiguration coreConfig = JobCoreConfiguration.newBuilder("MySimpleJob", "0/2 * * * * ?", 4).shardingItemParameters("0=RDP, 1=CORE, 2=SIMS, 3=ECIF").build();

springboot 工程,在 application.properties 中定义。
定义几个分片项,一个任务就会有几个线程去运行它。
注意:分片个数和分片参数要一一对应。通常把分片项设置得比 E-Job 服务器个数大一些,比如 3 台服务器,分成 9 片,这样如果有服务器宕机,分片还可以相对均匀。
4.7.2 分片验证
为避免运行的任务太多看不清楚运行结果,可以注释在 ElasticJobConfig 中注释DataFlowJob 和 ScriptJob。SimpleJob 的分片项改成 2。
直接运行 com.gupaoedu.EjobApp。
或者打成 jar 包:mvn package -Dmaven.test.skip=true
Jar 包路径:ejob-springboot\target\ejob-springboot-0.0.1-SNAPSHOT.jar
修改名称为 ejob.jar 放到 D 盘下
多实例运行(单机):
java –jar ejob.jar
1、 多运行一个点,任务不会重跑(两个节点各获得一个分片项)
2、 关闭一个节点,任务不会漏

在这里插入图片描述

4.7.3 分片策略
http://elasticjob.io/docs/elastic-job-lite/02-guide/job-sharding-strategy/  分片项如何分配到服务器?这个跟分片策略有关。
策略类描述具体规则
AverageAllocationJobShardingStrategy基于平均分配算法的分片策略,也是默认的分片策略。如果分片不能整除,则不能整除的多余分片将依次追加到序号小的服务器。如: 如果有 3 台服务器,分成 9 片,则每台服务器 分 到 的 分 片 是 : 1=[0,1,2], 2=[3,4,5], 3=[6,7,8] 如果有 3 台服务器,分成 8 片,则每台服务器分到的分片是:1=[0,1,6], 2=[2,3,7], 3=[4,5] 如果有 3 台服务器,分成 10 片,则每台服务器 分 到 的 分 片 是 : 1=[0,1,2,9], 2=[3,4,5], 3=[6,7,8]
OdevitySortByNameJobShardingStrategy根据作业名的哈希值奇偶数决定 IP 升降序算法的分片策略。根据作业名的哈希值奇偶数决定 IP 升降序算法的分片策略。 作业名的哈希值为奇数则 IP 升序。 作业名的哈希值为偶数则 IP 降序。用于不同的作业平均分配负载至不同的服务器。
RotateServerByNameJobShardingStrategy根据作业名的哈希值对服务器列表进行轮转的分片策略。
自定义分片策略实现 JobShardingStrategy 接口并实现 sharding 方法,接口方法参数为作业服务器 IP 列表和分片策略选项,分片策略选项包括作业名称,分片总数以及分片序列号和个性化参数对照表,可以根据需求定制化自己的分片策略。
AverageAllocationJobShardingStrategy 的缺点是,一旦分片数小于作业服务器数,作业将永远分配至 IP 地址靠前的服务

器,导致 IP 地址靠后的服务器空闲。而 OdevitySortByNameJobShardingStrategy 则可以根据作业名称重新分配服务器负
载。如:
如果有 3 台服务器,分成 2 片,作业名称的哈希值为奇数,则每台服务器分到的分片是:1=[0], 2=[1], 3=[]
如果有 3 台服务器,分成 2 片,作业名称的哈希值为偶数,则每台服务器分到的分片是:3=[0],
在 Lite 配置中指定分片策略:

String jobShardingStrategyClass = AverageAllocationJobShardingStrategy.class.getCanonicalName();
LiteJobConfiguration simpleJobRootConfig = LiteJobConfiguration.newBuilder(simpleJobConfig).jobShardingStrategyClass(jobShardingStrategyClass).build();

4.7.4 分片方案
获取到分片项 shardingItem 之后,怎么对数据进行分片嗯?
1、对业务主键进行取模,获取余数等于分片项的数据
举例:获取到的 sharding item 是 0,1
在 SQL 中加入过滤条件:where mod(id, 4) in (1, 2)。
这种方式的缺点:会导致索引失效,查询数据时会全表扫描。
解决方案:在查询条件中在增加一个索引条件进行过滤。
2、在表中增加一个字段,根据分片数生成一个 mod 值。取模的基数要大于机器数。否则在增加机器后,会导致机器空闲。例如取模基数是 2,而服务器有 5 台,那么有三台服务器永远空闲。而取模基数是 10,生成 10 个 shardingItem,可以分配到 5 台服务器。当然,取模基数也可以调整。
3、如果从业务层面,可以用 ShardingParamter 进行分片。例如 0=RDP, 1=CORE, 2=SIMS, 3=ECIF List<users> = SELECT * FROM user WHERE status = 0 AND SYSTEM_ID = 'RDP' limit 0, 10
在 Spring Boot 中要 Elastic-Job 要配置的内容太多了,有没有更简单的添加任务的方法呢?比如在类上添加一个注解?这个时候我们就要用到 starter 了。

4.8 e-job starter

Git 上有一个现成的实现
https://github.com/TFdream/elasticjob-spring-boot-starter
工程:elasticjob-spring-boot-starter
需求(一个 starter 应该有什么样子):
需求实现作用
可以在启动类上使用@Enable 功能开启 E-Job 任务调度注解@EnableElasticJob在自动配置类上用@ConditionalOnBean决定是否自动配置
可以在 properties 或 yml 中识别配置内容配置类 RegCenterProperties.java支 持 在 properties 文 件 中 使 用elasticjob.regCenter 前缀,配置注册中心参数
在类上加上注解,直接创建任务注解 @JobScheduled配置任务参数,包括定分片项、分片参数等等
不用创建 ZK 注册中心自动配置类 RegCentreAutoConfiguration.java注入从 RegCenterProperties.java 读取到的参数,自动创 ZookeeperConfiguration
不用创建三级(Core、Type、Lite)配置自动配置类 JobAutoConfiguration.java读 取 注 解 的 参 数 , 创 建JobCoreConfiguration 、
JobTypeConfiguration 、LiteJobConfiguration 在注册中心创建之后再创建
Spring Boot 启动时自动配置创 建Resource/META-INF/spring.factories指定两个自动配置类
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
io.dreamstudio.elasticjob.autoconfigure.RegCentreAutoConfiguration,\
io.dreamstudio.elasticjob.autoconfigure.JobAutoConfiguration
打包 starter 的工程,引入 starter 的依赖,即可在项目中使用注解开启任务调度功能。

5 E-Job 原理

5.1 启动

standalone 工程
new JobScheduler(regCenter, simpleJobRootConfig).init();

init 方法

public void init() {
	LiteJobConfiguration liteJobConfigFromRegCenter = schedulerFacade.updateJobConfiguration(liteJobConfig);
	// 设置分片数
	JobRegistry.getInstance().setCurrentShardingTotalCount(liteJobConfigFromRegCenter.getJobName(), liteJobConfigFromRegCenter.getTypeConfig().getCoreConfig().getShardingTotalCount());
	// 构建任务,创建调度器
	JobScheduleController jobScheduleController = new JobScheduleController(
	createScheduler(),createJobDetail(liteJobConfigFromRegCenter.getTypeConfig().getJobClass()), liteJobConfigFromRegCenter.getJobName());
	// 在 ZK 上注册任务
	JobRegistry.getInstance().registerJob(liteJobConfigFromRegCenter.getJobName(), jobScheduleController, regCenter);
	// 添加任务信息并进行节点选举
	schedulerFacade.registerStartUpInfo(!liteJobConfigFromRegCenter.isDisabled());
	// 启动调度器
	jobScheduleController.scheduleJob(liteJobConfigFromRegCenter.getTypeConfig().getCoreConfig().getCron());
}

registerStartUpInfo 方法

public void registerStartUpInfo(final boolean enabled) {
	// 启动所有的监听器
	listenerManager.startAllListeners();
	// 节点选举
	leaderService.electLeader();
	// 服务信息持久化(写到 ZK)
	serverService.persistOnline(enabled);
	// 实例信息持久化(写到 ZK)
	instanceService.persistOnline();
	// 重新分片
	shardingService.setReshardingFlag();
	// 监控信息监听器
	monitorService.listen();
	// 自诊断修复,使本地节点与 ZK 数据一致
	if (!reconcileService.isRunning()) {
	reconcileService.startAsync();
	}
}

监听器用于监听 ZK 节点信息的变化。
启动的时候进行主节点选举
/**
* 选举主节点. */
public void electLeader() {
	log.debug("Elect a new leader now.");
	jobNodeStorage.executeInLeader(LeaderNode.LATCH, new LeaderElectionExecutionCallback());
	log.debug("Leader election completed.");
}

Latch 是一个分布式锁,选举成功后在 instance 写入服务器信息
在这里插入图片描述
// 服务信息持久化(写到 ZK servers 节点)

serverService.persistOnline(enabled);

以下是单机运行多个实例:
在这里插入图片描述
// 实例信息持久化(写到 ZK instances 节点)

instanceService.persistOnline();

运行了两个实例:
在这里插入图片描述

5.2 任务执行与分片原理

关注两个问题:
1、LiteJob 是怎么被执行的?
2、分片项是怎么分配给不同的服务实例的?
在创建 Job 的时候(createJobDetail),创建的是实现了 Quartz 的 Job 接口的LiteJob 类,LiteJob 类实现了 Quartz 的 Job 接口。
在 LiteJob 的 execute 方法中获取对应类型的执行器,调用 execute()方法。
public static AbstractElasticJobExecutor getJobExecutor(final ElasticJob elasticJob, final JobFacade jobFacade) {
	if (null == elasticJob) {
	return new ScriptJobExecutor(jobFacade);
	}
	if (elasticJob instanceof SimpleJob) {
		return new SimpleJobExecutor((SimpleJob) elasticJob, jobFacade);
	}
	if (elasticJob instanceof DataflowJob) {
		return new DataflowJobExecutor((DataflowJob) elasticJob, jobFacade);
	}
	throw new JobConfigurationException("Cannot support job type '%s'", elasticJob.getClass().getCanonicalName());
}
EJOB 提供管理任务执行器的抽象类 AbstractElasticJobExecutor,核心动作在execute()方法中执行。
public final void execute() {}

调用了另一个 execute()方法,122 行:

execute(shardingContexts, JobExecutionEvent.ExecutionSource.NORMAL_TRIGGER);
private void execute(final ShardingContexts shardingContexts, final JobExecutionEvent.ExecutionSource
executionSource) {}

在这个 execute 方法中又调用了 process()方法,150 行

private void process(final ShardingContexts shardingContexts, final JobExecutionEvent.ExecutionSource
executionSource) {
	Collection<Integer> items = shardingContexts.getShardingItemParameters().keySet();
	// 只有一个分片项时,直接执行
	if (1 == items.size()) {
		int item = shardingContexts.getShardingItemParameters().keySet().iterator().next();
		JobExecutionEvent jobExecutionEvent = new JobExecutionEvent(shardingContexts.getTaskId(), jobName, executionSource, item);
		process(shardingContexts, item, jobExecutionEvent);
		return;
	}
	final CountDownLatch latch = new CountDownLatch(items.size());
	// 本节点遍历执行相应的分片信息
	for (final int each : items) {
	final JobExecutionEvent jobExecutionEvent = new JobExecutionEvent(shardingContexts.getTaskId(), jobName, executionSource, each);
		if (executorService.isShutdown()) {
			return;
		}
		executorService.submit(new Runnable() {
			@Override
			public void run() {
				try {
					process(shardingContexts, each, jobExecutionEvent);
				} finally {
					latch.countDown();
				}
			}
		});
	}
	try {
		// 等待所有的分片项任务执行完毕
		latch.await();
	} catch (final InterruptedException ex) {
		Thread.currentThread().interrupt();
	}
}

又调用了另一个 process()方法,206 行

protected abstract void process(ShardingContext shardingContext);
交给具体的实现类(SimpleJobExecutor、DataflowJobExecutor、ScriptJobExecutor)去处理。

在这里插入图片描述
最终调用到任务类

@Override
protected void process(final ShardingContext shardingContext) {
	simpleJob.execute(shardingContext);
}

5.3 失效转移

所谓失效转移,就是在执行任务的过程中发生异常时,这个分片任务可以在其他节点再次执行。
simple.SimpleJobTest,failover 方法:
// 设置失效转移
JobCoreConfiguration coreConfig = JobCoreConfiguration.newBuilder("MySimpleJob", "0/2 * * * * ?", 4).shardingItemParameters("0=RDP, 1=CORE, 2=SIMS, 3=ECIF").failover(true).build()
FailoverListenerManager 监听的是 zk 的 instance 节点删除事件。如果任务配置了 failover 等于 true,其中某个 instance 与 zk 失去联系或被删除,并且失效的节点又不是本身,就会触发失效转移逻辑。
Job 的失效转移监听来源于 FailoverListenerManager 中内部类JobCrashedJobListener 的 dataChanged 方法。
当节点任务失效时会调用 JobCrashedJobListener 监听器,此监听器会根据实例 id获取所有的分片,然后调用 FailoverService 的 setCrashedFailoverFlag 方法,将每个分片 id 写到/jobName/leader/failover/items 下,例如原来的实例负责 1、2 分片项,那么 items 节点就会写入 1、2,代表这两个分片项需要失效转移。
protected void dataChanged(final String path, final Type eventType, final String data) {
	if (isFailoverEnabled() && Type.NODE_REMOVED == eventType && instanceNode.isInstancePath(path)) {
	String jobInstanceId = path.substring(instanceNode.getInstanceFullPath().length() + 1);
		if (jobInstanceId.equals(JobRegistry.getInstance().getJobInstance(jobName).getJobInstanceId())) {
			return;
		}
		List<Integer> failoverItems = failoverService.getFailoverItems(jobInstanceId);
		if (!failoverItems.isEmpty()) {
			for (int each : failoverItems) {
				// 设置失效的分片项标记
				failoverService.setCrashedFailoverFlag(each);
				failoverService.failoverIfNecessary();
			}
		} else {
			for (int each : shardingService.getShardingItems(jobInstanceId)) {
				failoverService.setCrashedFailoverFlag(each);
				failoverService.failoverIfNecessary();
			}
		}
	}
}
然后接下来调用 FailoverService 的 failoverIfNessary 方法,首先判断是否需要失败转移,如果可以需要则只需作业失败转移。
public void failoverIfNecessary() {
	if (needFailover()) {
		jobNodeStorage.executeInLeader(FailoverNode.LATCH, new FailoverLeaderExecutionCallback());
	}
}
条件一:${JOB_NAME}/leader/failover/items/${ITEM_ID} 有失效转移的作业分片项。
条件二:当前作业不在运行中。
private boolean needFailover() {
	return jobNodeStorage.isJobNodeExisted(FailoverNode.ITEMS_ROOT)
	&& !jobNodeStorage.getJobNodeChildrenKeys(FailoverNode.ITEMS_ROOT).isEmpty()
	&& !JobRegistry.getInstance().isJobRunning(jobName);
}
在主节点执行操作
public void executeInLeader(final String latchNode, final LeaderExecutionCallback callback) {
	try (LeaderLatch latch = new LeaderLatch(getClient(), jobNodePath.getFullPath(latchNode))) {
		latch.start();
		latch.await();
		callback.execute();
		//CHECKSTYLE:OFF
	} catch (final Exception ex) {
		//CHECKSTYLE:ON
		handleException(ex);
	}
}

1、再次判断是否需要失效转移;
2、从注册中心获得一个 `${JOB_NAME}/leader/failover/items/${ITEM_ID}` 作业分片项;
3、在注册中心节点`${JOB_NAME}/sharding/${ITEM_ID}/failover` 注册作业分片项为当前作业节点;
4、然后移除任务转移分片项;
5、最后调用执行,提交任务。
class FailoverLeaderExecutionCallback implements LeaderExecutionCallback {
@Override
public void execute() {
	// 判断是否需要失效转移
	if (JobRegistry.getInstance().isShutdown(jobName) || !needFailover()) {
		return;
	}
	// 从${JOB_NAME}/leader/failover/items/${ITEM_ID}获得一个分片项
	int crashedItem =
	Integer.parseInt(jobNodeStorage.getJobNodeChildrenKeys(FailoverNode.ITEMS_ROOT).get(0));
	log.debug("Failover job '{}' begin, crashed item '{}'", jobName, crashedItem);
	// 在注册中心节点`${JOB_NAME}/sharding/${ITEM_ID}/failover`注册作业分片项为当前作业节点
	jobNodeStorage.fillEphemeralJobNode(FailoverNode.getExecutionFailoverNode(crashedItem), JobRegistry.getInstance().getJobInstance(jobName).getJobInstanceId());
	// 移除任务转移分片项
	jobNodeStorage.removeJobNodeIfExisted(FailoverNode.getItemsNode(crashedItem));
	JobScheduleController jobScheduleController = JobRegistry.getInstance().getJobScheduleController(jobName);
	if (null != jobScheduleController) {
		// 提交任务
		jobScheduleController.triggerJob();
		}
	}
}

这里仅仅是触发作业,而不是立即执行。

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

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

相关文章

每日一练:LeeCode-101. 对称二叉树【二叉树】

本文是力扣LeeCode-101. 对称二叉树 学习与理解过程&#xff0c;本文仅做学习之用&#xff0c;对本题感兴趣的小伙伴可以出门左拐LeeCode。 给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 提示&#xff1a; 树中节点数目在范围 [1, 1000] 内 -100 < Node.v…

通过盲对抗性扰动实时击败基于DNN的流量分析系统

文章信息 论文题目&#xff1a;Defeating DNN-Based Traffic Analysis Systems in Real-Time With Blind Adversarial Perturbations 期刊&#xff08;会议&#xff09;&#xff1a;30th USENIX Security Symposium 时间&#xff1a;2021 级别&#xff1a;CCF A 文章链接&…

理论U3 决策树

文章目录 一、决策树算法1、基本思想2、构成1&#xff09;节点3&#xff09;有向边/分支 3、分类步骤1&#xff09;第1步-决策树生成/学习、训练2&#xff09;第2步-分类/测试 4、算法关键 二、信息论基础1、概念2、信息量3、信息熵&#xff1a; 二、ID3 (Iterative Dichotomis…

Python+requests搭建接口自动化测试框架

一、接口自动化的意义&#xff08;为什么做这个框架&#xff09; 新版本上线时之前版本的功能需要进行回归测试&#xff0c;导致大量的重复性手工测试。引入自动化测试可以使用自动化技术代替部分手工的回归性测试&#xff0c;解放更多人力做其它更有必要的事情。但目前项目UI变…

抖去推账号矩阵+无人直播+文案引流系统开发搭建--开源

核心技术 1. AI自动直播&#xff1a; 智能系统通过丰富可定制的文案库&#xff0c; 拥有有料有趣的灵魂。不仅能自动语音讲解内容&#xff0c;还可以在直播中和用户灵活互动。直播中可将团购商品同话术自动上下架。 2. AI剪辑 可一键智能批量成片&#xff0c;也可跟着模板剪…

书生·浦语大模型全链路开源体系 学习笔记 第二课

基础作业&#xff1a; 使用 InternLM-Chat-7B 模型生成 300 字的小故事&#xff08;需截图&#xff09;。熟悉 hugging face 下载功能&#xff0c;使用 huggingface_hub python 包&#xff0c;下载 InternLM-20B 的 config.json 文件到本地&#xff08;需截图下载过程&#xf…

基于STM32和MPU6050的自平衡小车设计与实现

基于STM32和MPU6050的自平衡小车设计和实现是一个有趣而具有挑战性的项目。在本文中&#xff0c;我们将介绍如何利用STM32微控制器和MPU6050传感器实现自平衡小车&#xff0c;并提供相应的代码示例。 1. 硬件设计 自平衡小车的核心硬件包括STM32微控制器、MPU6050传感器以及电…

基于springboot+html的汽车销售管理系统设计与实现

基于springboothtml的汽车销售管理系统 &#x1f345; 作者主页 央顺技术团队 &#x1f345; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; &#x1f345; 文末获取源码联系方式 &#x1f4dd; 前言 随着汽车市场的快速发展&#xff0c;汽车销售企业面临着越来越大的管理…

计算机基础面试题 |18.精选计算机基础面试题

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

【代码复现系列】paper:CycleGAN and pix2pix in PyTorch

或许有冗余步骤、之后再优化。 1.桌面右键-git bash-输入命令如下【git clone https://github.com/junyanz/pytorch-CycleGAN-and-pix2pix】 2.打开anaconda的prompt&#xff0c;cd到pytorch-CycleGAN-and-pix2pix路径 3.在prompt里输入【conda env create -f environment.y…

基于SSM的校园二手交易管理系统的设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…

多项式回归

定义&#xff1a;使用多项式函数来拟合数据点&#xff0c;以预测因变量和自变量之间的关系。 基本形式如下&#xff1a; 理解&#xff1a; 在了解了线性回归模型之后&#xff0c;我们会意识到数据集上的点有时使用曲线拟合效果会更好。我们可以选择使用多项式曲线进行拟合。 …

Gromacs轨迹相关

1. 如何用VMD保存和查看gromacs的模拟轨迹 sob老师&#xff0c;请问一下&#xff0c;vmd载入两个文件后&#xff0c;是在TK中输入animate delete 什么命令可以删除第0帧么&#xff1f; 老师&#xff0c;在载入轨迹的时候出现out of memory&#xff0c;md.xtc文件过大导致vmd闪…

图解JVM (及一些垃圾回收\GC相关面试题 持续更新)

垃圾回收&#xff0c;顾名思义就是释放垃圾占用的空间&#xff0c;从而提升程序性能&#xff0c;防止内存泄露。当一个对象不再被需要时&#xff0c;该对象就需要被回收并释放空间。 Java 内存运行时数据区域包括程序计数器、虚拟机栈、本地方法栈、堆等区域。其中&#xff0c;…

Unity组件开发--相机跟随角色和旋转

1.相机跟随组件&#xff0c;节点&#xff1a; 2.相机跟随组件脚本&#xff1a; using System; using System.Collections; using System.Collections.Generic; using Unity.Burst.Intrinsics; using UnityEngine; using UnityEngine.UI;public class CameraFollow : Singleton&…

软件测试|深入理解SQL CROSS JOIN:交叉连接

简介 在SQL查询中&#xff0c;CROSS JOIN是一种用于从两个或多个表中获取所有可能组合的连接方式。它不依赖于任何关联条件&#xff0c;而是返回两个表中的每一行与另一个表中的每一行的所有组合。CROSS JOIN可以用于生成笛卡尔积&#xff0c;它在某些情况下非常有用&#xff…

服务器监控软件夜莺使用(二)

文章目录 一、采集器安装1. Categraf简介2. Categraf部署3. 测试服务器部署4. 系统监控插件5. 显卡监控插件6. 服务监控插件 二、监控仪表盘1. 机器列表2. 系统监控3. 服务监控 三、告警配置1. 邮件通知2. 告警规则3. 告警自愈 一、采集器安装 1. Categraf简介 Categraf 需要…

一种DevOpts的实现方式:基于gitlab的CICD(二)

写在之前 前文已经搭建了基于gitlab的cicd环境&#xff0c;现在我们来更近一步&#xff0c;结合官网给出的案例来详细介绍如何一步一步实现CI的过程。 基于gitlab搭建一个前端静态页面 环境依赖&#xff1a; gitlabgitlab runner&#xff08;docker版本&#xff09; 环境达吉…

【华为】IPsec VPN 实验配置(动态地址接入)

【华为】IPsec VPN 实验配置&#xff08;动态地址接入&#xff09; 注意实验需求配置思路配置命令拓扑R1基础配置配置第一阶段 IKE SA配置第二阶段 IPsec SA ISP_R2基础配置 R3基础配置配置第一阶段 IKE SA配置第二阶段 IPsec SA PCPC1PC2 检查建立成功查看命令清除IKE / IPsec…

VBA中类的解读及应用第八讲:实现定时器功能的自定义类事件

《VBA中类的解读及应用》教程【10165646】是我推出的第五套教程&#xff0c;目前已经是第一版修订了。这套教程定位于最高级&#xff0c;是学完初级&#xff0c;中级后的教程。 类&#xff0c;是非常抽象的&#xff0c;更具研究的价值。随着我们学习、应用VBA的深入&#xff0…