HOW-TO:具有MySQL的JEE应用程序中具有集群功能的Quartz Scheduler

Quartz Scheduler是Java世界中最流行的调度库之一。 过去,我主要在Spring应用程序中使用Quartz。 最近,我一直在研究要在云中部署的JBoss 7.1.1上运行的JEE 6应用程序中的调度。 我考虑的一种选择是Quartz Scheduler,因为它提供了与数据库的集群。 在本文中,我将展示在JEE应用程序中配置Quartz并在JBoss 7.1.1或WildFly 8.0.0上运行它,使用MySQL作为作业存储以及利用CDI在作业中使用依赖注入是多么容易。 所有这些都将在IntelliJ中完成。 让我们开始吧。

创建Maven项目

我使用org.codehaus.mojo.archetypes:webapp-javaee6原型来引导应用程序,然后我稍微修改了pom.xml 。 我还添加了slf4J依赖项,因此生成的pom.xml如下所示:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>pl.codeleak</groupId><artifactId>quartz-jee-demo</artifactId><version>1.0</version><packaging>war</packaging><name>quartz-jee-demo</name><properties><endorsed.dir>${project.build.directory}/endorsed</endorsed.dir><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><dependency><groupId>javax</groupId><artifactId>javaee-api</artifactId><version>6.0</version><scope>provided</scope></dependency><!-- Logging --><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.7.7</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-jdk14</artifactId><version>1.7.7</version></dependency></dependencies><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>2.3.2</version><configuration><source>1.7</source><target>1.7</target><compilerArguments><endorseddirs>${endorsed.dir}</endorseddirs></compilerArguments></configuration></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-war-plugin</artifactId><version>2.1.1</version><configuration><failOnMissingWebXml>false</failOnMissingWebXml></configuration></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-dependency-plugin</artifactId><version>2.1</version><executions><execution><phase>validate</phase><goals><goal>copy</goal></goals><configuration><outputDirectory>${endorsed.dir}</outputDirectory><silent>true</silent><artifactItems><artifactItem><groupId>javax</groupId><artifactId>javaee-endorsed-api</artifactId><version>6.0</version><type>jar</type></artifactItem></artifactItems></configuration></execution></executions></plugin></plugins></build></project>

接下来是将项目导入到IDE。 在我的情况下,这是IntelliJ,并使用JBoss 7.1.1创建运行配置。

值得注意的是,在运行配置中的VM Options中,我添加了两个变量:

-Djboss.server.default.config=standalone-custom.xml
-Djboss.socket.binding.port-offset=100

带有群集的石英计划程序img1

standalone-custom.xml是标准standalone.xml的副本,因为需要修改配置(请参见下文)。

配置JBoss服务器

在我的演示应用程序中,我想将MySQL数据库与Quartz一起使用,因此需要将MySQL数据源添加到我的配置中。 这可以通过两个步骤快速完成。

添加驱动程序模块

我创建了一个文件夹JBOSS_HOME/modules/com/mysql/main 。 在这个文件夹中,我添加了两个文件: module.xmlmysql-connector-java-5.1.23.jar 。 模块文件如下所示:

<?xml version="1.0" encoding="UTF-8"?>  
<module xmlns="urn:jboss:module:1.0" name="com.mysql">  <resources>  <resource-root path="mysql-connector-java-5.1.23.jar"/>  </resources>  <dependencies>  <module name="javax.api"/>  </dependencies>  
</module>

配置数据源

datasources子系统的standalone-custom.xml文件中,我添加了一个新的数据源:

<datasource jta="false" jndi-name="java:jboss/datasources/MySqlDS" pool-name="MySqlDS" enabled="true" use-java-context="true"><connection-url>jdbc:mysql://localhost:3306/javaee</connection-url><driver>com.mysql</driver><security><user-name>jeeuser</user-name><password>pass</password></security>
</datasource>

和驱动程序:

<drivers><driver name="com.mysql" module="com.mysql"/>
</drivers>

注意:就本演示而言,数据源不是由JTA管理的,以简化配置。

使用集群配置Quartz

我使用官方教程通过集群配置Quarts: http : //quartz-scheduler.org/documentation/quartz-2.2.x/configuration/ConfigJDBCJobStoreClustering

将Quartz依赖项添加到pom.xml

<dependency><groupId>org.quartz-scheduler</groupId><artifactId>quartz</artifactId><version>2.2.1</version>
</dependency>
<dependency><groupId>org.quartz-scheduler</groupId><artifactId>quartz-jobs</artifactId><version>2.2.1</version>
</dependency>

quartz.properties添加到src/main/resources

#============================================================================
# Configure Main Scheduler Properties  
#============================================================================org.quartz.scheduler.instanceName = MyScheduler
org.quartz.scheduler.instanceId = AUTO#============================================================================
# Configure ThreadPool  
#============================================================================org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 1#============================================================================
# Configure JobStore  
#============================================================================org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.useProperties = false
org.quartz.jobStore.dataSource=MySqlDSorg.quartz.jobStore.isClustered = true
org.quartz.jobStore.clusterCheckinInterval = 5000org.quartz.dataSource.MySqlDS.jndiURL=java:jboss/datasources/MySqlDS

创建供Quartz使用MySQL表

可以在Quartz发行版中找到该模式文件: quartz-2.2.1\docs\dbTables

演示代码

完成配置后,我想检查Quartz是否正常工作,因此我创建了一个没有作业和触发器的调度程序。

package pl.codeleak.quartzdemo;import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.TriggerKey;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.matchers.GroupMatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.Singleton;
import javax.ejb.Startup;@Startup
@Singleton
public class SchedulerBean {private Logger LOG = LoggerFactory.getLogger(SchedulerBean.class);private Scheduler scheduler;@PostConstructpublic void scheduleJobs() {try {scheduler = new StdSchedulerFactory().getScheduler();            scheduler.start();printJobsAndTriggers(scheduler);} catch (SchedulerException e) {LOG.error("Error while creating scheduler", e);}}private void printJobsAndTriggers(Scheduler scheduler) throws SchedulerException {LOG.info("Quartz Scheduler: {}", scheduler.getSchedulerName());for(String group: scheduler.getJobGroupNames()) {for(JobKey jobKey : scheduler.getJobKeys(GroupMatcher.<JobKey>groupEquals(group))) {LOG.info("Found job identified by {}", jobKey);}}for(String group: scheduler.getTriggerGroupNames()) {for(TriggerKey triggerKey : scheduler.getTriggerKeys(GroupMatcher.<TriggerKey>groupEquals(group))) {LOG.info("Found trigger identified by {}", triggerKey);}}}@PreDestroypublic void stopJobs() {if (scheduler != null) {try {scheduler.shutdown(false);} catch (SchedulerException e) {LOG.error("Error while closing scheduler", e);}}}
}

运行应用程序时,您应该能够从Quartz中看到一些调试信息:

Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.NOT STARTED.Currently in standby mode.Number of jobs executed: 0Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 1 threads.Using job-store 'org.quartz.impl.jdbcjobstore.JobStoreTX' - which supports persistence. and is clustered.

让Quartz利用CDI

在Quartz中,作业必须实现org.quartz.Job接口。

package pl.codeleak.quartzdemo;import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;public class SimpleJob implements Job {@Overridepublic void execute(JobExecutionContext context) throws JobExecutionException {// do something}
}

然后使用JobBuilder创建一个Job:

JobKey job1Key = JobKey.jobKey("job1", "my-jobs");
JobDetail job1 = JobBuilder.newJob(SimpleJob.class).withIdentity(job1Key).build();

在我的示例中,我需要将EJB注入到我的作业中,以便重新使用现有的应用程序逻辑。 因此,实际上,我需要注入EJB参考。 Quartz如何做到这一点? 简单。 Quartz Scheduler有一种提供JobFactory的方法,该方法将负责创建Job实例。

package pl.codeleak.quartzdemo;import org.quartz.Job;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.spi.JobFactory;
import org.quartz.spi.TriggerFiredBundle;import javax.enterprise.inject.Any;
import javax.enterprise.inject.Instance;
import javax.inject.Inject;
import javax.inject.Named;public class CdiJobFactory implements JobFactory {@Inject@Anyprivate Instance<Job> jobs;@Overridepublic Job newJob(TriggerFiredBundle triggerFiredBundle, Scheduler scheduler) throws SchedulerException {final JobDetail jobDetail = triggerFiredBundle.getJobDetail();final Class<? extends Job> jobClass = jobDetail.getJobClass();for (Job job : jobs) {if (job.getClass().isAssignableFrom(jobClass)) {return job;}}throw new RuntimeException("Cannot create a Job of type " + jobClass);}
}

到目前为止,所有作业都可以使用依赖项注入和注入其他依赖项,包括EJB。

package pl.codeleak.quartzdemo.ejb;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import javax.ejb.Stateless;@Stateless
public class SimpleEjb {private static final Logger LOG = LoggerFactory.getLogger(SimpleEjb.class);public void doSomething() {LOG.info("Inside an EJB");}
}package pl.codeleak.quartzdemo;import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import pl.codeleak.quartzdemo.ejb.SimpleEjb;import javax.ejb.EJB;
import javax.inject.Named;public class SimpleJob implements Job {@EJB // @Inject will work tooprivate SimpleEjb simpleEjb;@Overridepublic void execute(JobExecutionContext context) throws JobExecutionException {simpleEjb.doSomething();}
}

最后一步是修改SchedulerBean:

package pl.codeleak.quartzdemo;import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.matchers.GroupMatcher;
import org.quartz.spi.JobFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.inject.Inject;@Startup
@Singleton
public class SchedulerBean {private Logger LOG = LoggerFactory.getLogger(SchedulerBean.class);private Scheduler scheduler;@Injectprivate JobFactory cdiJobFactory;@PostConstructpublic void scheduleJobs() {try {scheduler = new StdSchedulerFactory().getScheduler();scheduler.setJobFactory(cdiJobFactory);JobKey job1Key = JobKey.jobKey("job1", "my-jobs");JobDetail job1 = JobBuilder.newJob(SimpleJob.class).withIdentity(job1Key).build();TriggerKey tk1 = TriggerKey.triggerKey("trigger1", "my-jobs");Trigger trigger1 = TriggerBuilder.newTrigger().withIdentity(tk1).startNow().withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(10)).build();scheduler.scheduleJob(job1, trigger1);scheduler.start();printJobsAndTriggers(scheduler);} catch (SchedulerException e) {LOG.error("Error while creating scheduler", e);}}private void printJobsAndTriggers(Scheduler scheduler) throws SchedulerException {// not changed}@PreDestroypublic void stopJobs() {// not changed}
}

注意:在运行应用程序之前,将bean.xml文件添加到WEB-INF目录。

<?xml version="1.0" encoding="UTF-8"?>
<beansxmlns="http://xmlns.jcp.org/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"bean-discovery-mode="all"></beans>

现在,您可以启动服务器并观察结果。 首先,创建作业和触发器:

12:08:19,592 INFO   (MSC service thread 1-3) Quartz Scheduler: MyScheduler
12:08:19,612 INFO   (MSC service thread 1-3) Found job identified by my-jobs.job1
12:08:19,616 INFO   (MSC service thread 1-3) Found trigger identified by m

我们的工作正在运行(大约每10秒运行一次):

12:08:29,148 INFO   (MyScheduler_Worker-1) Inside an EJB
12:08:39,165 INFO   (MyScheduler_Worker-1) Inside an EJB

还要查看Quartz表内部,您将看到其中已填充数据。

测试应用

我要检查的最后一件事是在多个实例中如何触发作业。 为了进行测试,我只是在IntelliJ中克隆了两次服务器配置,并为每个新副本分配了不同的端口偏移。

带有群集的石英计划程序img2
我需要做的其他更改是修改作业和触发器的创建。 由于所有Quartz对象都存储在数据库中,因此创建相同的作业和触发器(使用相同的键)将引发异常:

Error while creating scheduler: org.quartz.ObjectAlreadyExistsException: Unable to store Job : 'my-jobs.job1', because one already exists with this identification.

我需要更改代码,以确保如果作业/触发器存在,请对其进行更新。 此测试的scheduleJobs方法的最终代码为同一作业注册了三个触发器。

@PostConstruct
public void scheduleJobs() {try {scheduler = new StdSchedulerFactory().getScheduler();scheduler.setJobFactory(cdiJobFactory);JobKey job1Key = JobKey.jobKey("job1", "my-jobs");JobDetail job1 = JobBuilder.newJob(SimpleJob.class).withIdentity(job1Key).build();TriggerKey tk1 = TriggerKey.triggerKey("trigger1", "my-jobs");Trigger trigger1 = TriggerBuilder.newTrigger().withIdentity(tk1).startNow().withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(10)).build();TriggerKey tk2 = TriggerKey.triggerKey("trigger2", "my-jobs");Trigger trigger2 = TriggerBuilder.newTrigger().withIdentity(tk2).startNow().withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(10)).build();TriggerKey tk3 = TriggerKey.triggerKey("trigger3", "my-jobs");Trigger trigger3 = TriggerBuilder.newTrigger().withIdentity(tk3).startNow().withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(10)).build();scheduler.scheduleJob(job1, newHashSet(trigger1, trigger2, trigger3), true);scheduler.start();printJobsAndTriggers(scheduler);} catch (SchedulerException e) {LOG.error("Error while creating scheduler", e);}
}

除了上述内容之外,我还添加了在SimpleJob中记录JobExecutionContext的信息,因此可以更好地分析结果。

@Override
public void execute(JobExecutionContext context) throws JobExecutionException {try {LOG.info("Instance: {}, Trigger: {}, Fired at: {}",context.getScheduler().getSchedulerInstanceId(),context.getTrigger().getKey(),sdf.format(context.getFireTime()));} catch (SchedulerException e) {}simpleEjb.doSomething();
}

运行所有三个服务器实例后,我观察了结果。

带有群集的石英计划程序-img3

工作执行

我观察到在所有三个节点上都执行trigger2,并且在三个节点上执行了trigger2,如下所示:

Instance: kolorobot1399805959393 (instance1), Trigger: my-jobs.trigger2, Fired at: 13:00:09
Instance: kolorobot1399805989333 (instance3), Trigger: my-jobs.trigger2, Fired at: 13:00:19
Instance: kolorobot1399805963359 (instance2), Trigger: my-jobs.trigger2, Fired at: 13:00:29
Instance: kolorobot1399805959393 (instance1), Trigger: my-jobs.trigger2, Fired at: 13:00:39
Instance: kolorobot1399805959393 (instance1), Trigger: my-jobs.trigger2, Fired at: 13:00:59

对于其他触发器类似。

复苏

断开kolorobot1399805989333(instance3)的连接后,一段时间后,我在日志中看到以下内容:

ClusterManager: detected 1 failed or restarted instances.
ClusterManager: Scanning for instance "kolorobot1399805989333"'s failed in-progress jobs.

然后我断开了kolorobot1399805963359(instance2)的连接,这也是我在日志中看到的内容:

ClusterManager: detected 1 failed or restarted instances.
ClusterManager: Scanning for instance "kolorobot1399805963359"'s failed in-progress jobs.
ClusterManager: ......Freed 1 acquired trigger(s).

到目前为止,由kolorobot1399805959393(instance1)执行的所有触发器

在Wildfly 8上运行

无需任何更改,我就可以在WildFly 8.0.0上部署相同的应用程序。 与JBoss 7.1.1相似,我添加了MySQL模块(WildFly 8上modules文件夹的位置不同– modules/system/layers/base/com/mysql/main 。数据源和驱动程序的定义与上图完全相同。我为WildFly 8创建了运行配置:

带有群集的石英计划程序-img4
然后我运行该应用程序,结果与JBoss 7相同。

我发现WildFly似乎为持久EJB计时器提供了基于数据库的存储 ,但是我尚未对其进行调查。 也许是另一篇博客文章的内容。

源代码

  • 请在GitHub上找到此博客文章的源代码: https : //github.com/kolorobot/quartz-jee-demo

翻译自: https://www.javacodegeeks.com/2014/05/how-to-quartz-scheduler-with-clustering-in-jee-application-with-mysql.html

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

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

相关文章

办公网络对工业交换机的功能要求

如今&#xff0c;随着社会的发展&#xff0c;很多公司对网络的要求越来越高&#xff0c;系统越来越复杂&#xff0c;很多老线路需要改造升级&#xff0c;对工业交换机的要求也越来越高。但是&#xff0c;很多企业并不懂如何改造升级。今天飞畅科技的小编就来为大家详细讲解下公…

工业交换机和工控交换机有什么区别?

众所周知&#xff0c;以太网交换机一般分为&#xff1a;商用(以太网)交换机、工业(以太网)交换机、家用(以太网)交换机&#xff0c;因为我们是专业的工业交换机厂家&#xff0c;在这里着重介绍下工业交换机。 工业交换机一般用在工业生产场合&#xff0c;通常外观和安装形式多…

工业型交换机相比普通交换机有哪些要求?

随着自动化技术逐渐成熟&#xff0c;并伴随工业以太网的应用和大中型工业控制的网络的创建&#xff0c;工业交换机的应用越来越广泛。工业型交换机与一般交换机对比有必须的差异&#xff0c;工业型交换机在整体规划上及其在电子器件的采用上&#xff0c;其抗压强度和可接受性层…

使用eclipse调试ns3配置说明

Tips&#xff1a;安装eclipse时注意选择C开发组件&#xff1b; &#xff08;环境配置参考&#xff1a;https://www.cnblogs.com/zlcxbb/p/3852810.html&#xff09; &#xff08;官方配置介绍&#xff1a;https://www.nsnam.org/wiki/HOWTO_configure_Eclipse_with_ns-3&…

工业以太网交换机特点分析及使用注意事项

在网络发达的今天&#xff0c;交换机被许多需要使用网络的朋友使用&#xff0c;可以实现一个网络多台电脑公用。但是什么是工业以太网交换机&#xff0c;很惯性的思维就是工业用的交换机&#xff0c;但是具体工业以太网交换机性能特点有什么呢&#xff1f;接下来就由杭州飞畅科…

java整数的因式分解_如何在Java中找到整数的质数-因式分解

java整数的因式分解编程课程中的常见家庭作业/任务之一是关于Prime Factorization。 要求您编写一个程序以找到给定整数的素因子 。 一个数字的素数因子是将精确地除以给定数字的所有素数。 例如&#xff0c;素数因子35是7和5&#xff0c;它们本身都是素数&#xff0c;并且精确…

串口服务器的通讯模式

串口服务器&#xff0c;一个为RS-232/485/422到PC/IP之间完成数据转换的具有强大功能的方便快捷的通讯接口转换器。串口服务器通过作为服务器端&#xff0c;提供RS-232/485/422终端串口与TCP/IP网络的数据双向透明传输&#xff0c;提供串口转网络功能&#xff0c;RS-232/485/42…

ns3gym与ns3ai的安装方法

编译运行的常用命令 1&#xff0c;针对ns3主项目的编译命令 分两步&#xff1a;第一&#xff0c;./waf configure&#xff1b;第二&#xff0c;./waf&#xff08;或者./waf build&#xff09; 详见《开源网络模拟器ns3》P13 2&#xff0c;针对多脚本同时运行的编译命令 ns3…

apache hadoop_通过Apache Hadoop大规模扩展Apache Solr实时实时索引

apache hadoop播客的第22集是与Patrick Hunt的谈话 我们讨论了Apache Solr&#xff08;上游&#xff09;中的新工作&#xff0c;使它可以在Apache Hadoop上工作。 Solr支持将其索引和事务日志文件写入和读取到HDFS分布式文件系统。 这不使用Hadoop Map-Reduce处理Solr数据&…

显示/隐藏我的电脑与回收站

Windows徽标键i”——打开——“Windows设置”——点击——“个性化”——进入——“主题”——下滑——“相关设置”——中的——“桌面图标设置”。把勾勾全去掉即可。

在硒中查找具有链接文本和部分链接文本的元素

Selenium中CSS定位器是一个基本概念&#xff0c;每个旨在使用Selenium执行自动化测试的测试人员都应该意识到这一点。 在Selenium中充分使用CSS定位器可以帮助您以更高效&#xff0c;更彻底的方式执行测试。 我从7年以来一直从事自动化测试行业&#xff0c;并且我经常观察到测试…

java8 函数式编程_您必须学习Java 8的函数式编程吗?

java8 函数式编程我最近一直在研究Java 8&#xff0c;并掌握了Manning出版的“ Java 8 In Action” 。 让我印象深刻的第一件事是Java 8独特的销售主张是函数式编程。 函数现在是一流的变量&#xff0c;您可以像int或String一样在代码中传递它们。 这是一个很大的变化。 近年来…

Dell笔记本双系统安装(Ubuntu 16.04)

一 参考博客 重点 以上博客在分盘问题中描写不详细&#xff0c;参考右边博客&#xff1a; 有关分盘问题 注意&#xff1a;其中&#xff0c;由于Bios Dell与其余电脑存在区别&#xff0c; 1. 开机按F12&#xff0c;进入BIOS 2.在 BIOS 的Boot菜单下&#xff0c;将Secure Bo…

pat 乙级 1006 换个格式输出整数(C++)

题目 让我们用字母 B 来表示“百”、字母 S 表示“十”&#xff0c;用 12…n 来表示不为零的个位数字 n&#xff08;<10&#xff09;&#xff0c;换个格式来输出任一个不超过 3位的正整数。 例如 234 应该被输出为 BBSSS1234&#xff0c;因为它有 2 个“百”、3 个“十”、以…

Selenium Java教程– Selenium中的类名定位器

Selenium中CSS Locator是编写脚本的最重要方面之一。 如果您无法通过使用Selenium中的任何CSS定位器来定位元素&#xff0c;那么精通Selenium自动化将是一项艰巨的任务。 硒提供多种定位元素的方法。 您可以查看有关Selenium中不同CSS定位器的其他文章&#xff0c;这些文章可通…

C++ floor、ceil、round函数的区别

函数解释 floor函数&#xff0c;向下取整。即向负无穷方向取离传入参数最近的整数ceil函数&#xff0c;向上取整。即向正无穷方向取离传入参数最近的整数round函数&#xff0c;四舍五入。 头文件 C语言&#xff1a;math.hC: cmath 函数原型 double floor(double)double ce…

C++ 函数参数3种传值方式(以交换两个数为例)

简介 在C中&#xff0c;函数参数的传值方式有传值调用、指针调用和引用调用。接下来&#xff0c;通过一个实例来演示三者区别。 代码 #include <iostream>using namespace std; //传值调用 void Swap(int a,int b) {int tempa;ab;btemp; } //指针调用 void Swap2(int…

Ubuntu系统中利用Sublime分别运行Python与Python3

背景 两台Ubuntu系统的电脑均使用Sublime&#xff0c;运行相同的代码&#xff0c;出现不同的效果。 经观察&#xff0c;一个Sublime python F5 编译采用的为python3&#xff0c;另一个采用的为python2&#xff08;通过两个版本的print函数不同观察&#xff0c;一个print&…

AWS Lambda事件源映射:使您的触发器混乱无序

最近&#xff0c;我们为Sigma Cloud IDE上的无服务器项目引入了两个新的AWS Lambda事件源&#xff08;触发类型&#xff09;&#xff1a; SQS队列和DynamoDB流 。 &#xff08;是的&#xff0c;AWS在几个月前就向他们介绍了&#xff1b;但是我们仍然是一个很小的团队&#xff0…

java 并发计数器_Java 8 LongAdders:管理并发计数器的正确方法

java 并发计数器我只是买了新玩具&#xff0c;而Java 8有很多 。 这次我想谈谈我的最爱之一-并发加法器。 这是一组新的类&#xff0c;用于管理由多个线程编写和读取的计数器。 新的API有望显着提高性能&#xff0c;同时仍使事情简单明了。 自从多核体系结构问世以来人们一直在…