整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          學會這10種定時任務,有點飄了...


          最近有幾個讀者私信給我,問我他們的業務場景,要用什么樣的定時任務。確實,在不用的業務場景下要用不同的定時任務,其實我們的選擇還是挺多的。我今天給大家總結10種非常實用的定時任務,總有一種是適合你的。

          一. linux自帶的定時任務

          crontab

          不知道你有沒有遇到過這種場景:有時需要臨時統計線上的數據,然后導出到excel表格中。這種需求有時較為復雜,光靠寫sql語句是無法滿足需求的,這就需要寫java代碼了。然后將該程序打成一個jar包,在線上環境執行,最后將生成的excel文件下載到本地。

          為了減小對線上環境的影響,我們一般會選擇在凌晨1-2點,趁用戶量少的時候,執行統計程序。(其實凌晨4點左右,用戶才是最少的)

          由于時間太晚了,我們完全沒必要守在那里等執行結果,一個定時任務就能可以搞定。

          那么,這種情況用哪種定時任務更合適呢?

          答案是:linux系統的crontab。(不過也不排除有些項目沒部署在linux系統中)

          運行crontab -e,可以編輯定時器,然后加入如下命令:

          0 2 * * * /usr/local/java/jdk1.8/bin/java -jar /data/app/tool.jar > /logs/tool.log &
          

          就可以在每天凌晨2點,定時執行tool.jar程序,并且把日志輸出到tool.log文件中。當然你也可以把后面的執行java程序的命令寫成shell腳本,更方便維護。

          使用這種定時任務支持方便修改定時規則,有界面可以統一管理配置的各種定時腳本。

          crontab命令的基本格式如下:

          crontab [參數] [文件名]
          

          如果沒有指定文件名,則接收鍵盤上輸入的命令,并將它載入到crontab

          參數功能對照表如下:

          參數功能-u指定用戶-e編輯某個用戶的crontab文件內容-l顯示某個用戶的crontab文件內容-r刪除某用戶的crontab文件-i刪除某用戶的crontab文件時需確認

          以上參數,如果沒有使用-u指定用戶,則默認使用的當前用戶。

          通過crontab -e命令編輯文件內容,具體語法如下:

          [分] [小時] [日期] [月] [星期] 具體任務
          

          其中:

          • 分,表示多少分鐘,范圍:0-59
          • 小時,表示多少小時,范圍:0-23
          • 日期,表示具體在哪一天,范圍:1-31
          • 月,表示多少月,范圍:1-12
          • 星期,表示多少周,范圍:0-7,0和7都代表星期日

          還有一些特殊字符,比如:

          • *代表如何時間,比如:*1*** 表示每天凌晨1點執行。
          • /代表每隔多久執行一次,比如:*/5 **** 表示每隔5分鐘執行一次。
          • ,代表支持多個,比如:10 7,9,12 *** 表示在每天的7、9、12點10分各執行一次。
          • -代表支持一個范圍,比如:10 7-9 *** 表示在每天的7、8、9點10分各執行一次。

          此外,順便說一下crontab需要crond服務支持,crondlinux下用來周期地執行某種任務的一個守護進程,在安裝linux操作系統后,默認會安裝crond服務工具,且crond服務默認就是自啟動的。crond進程每分鐘會定期檢查是否有要執行的任務,如果有,則會自動執行該任務。

          可以通過以下命令操作相關服務:

          service crond status // 查看運行狀態
          service crond start //啟動服務
          service crond stop //關閉服務
          service crond restart //重啟服務
          service crond reload //重新載入配置
          

          使用crontab的優缺點:

          • 優點:方便修改定時規則,支持一些較復雜的定時規則,通過文件可以統一管理配好的各種定時腳本。
          • 缺點:如果定時任務非常多,不太好找,而且必須要求操作系統是linux,否則無法執行。

          二. jdk自帶的定時任務

          1.Thread

          各位親愛的朋友,你沒看錯,Thread類真的能做定時任務。如果你看過一些定時任務框架的源碼,你最后會發現,它們的底層也會使用Thread類。

          實現這種定時任務的具體代碼如下:

          public static void init() {
              new Thread(() -> {
                  while (true) {
                      try {
                          System.out.println("doSameThing");
                          Thread.sleep(1000 * 60 * 5);
                      } catch (Exception e) {
                          log.error(e);
                      }
                  }
              }).start();
          }
          

          使用Thread類可以做最簡單的定時任務,在run方法中有個while的死循環(當然還有其他方式),執行我們自己的任務。有個需要特別注意的地方是,需要用try...catch捕獲異常,否則如果出現異常,就直接退出循環,下次將無法繼續執行了。

          這種方式做的定時任務,只能周期性執行,不能支持定時在某個時間點執行。

          此外,該線程可以定義成守護線程,在后臺默默執行就好。

          使用場景:比如項目中有時需要每隔10分鐘去下載某個文件,或者每隔5分鐘去讀取模板文件生成靜態html頁面等等,一些簡單的周期性任務場景。

          使用Thread類的優缺點:

          • 優點:這種定時任務非常簡單,學習成本低,容易入手,對于那些簡單的周期性任務,是個不錯的選擇。
          • 缺點:不支持指定某個時間點執行任務,不支持延遲執行等操作,功能過于單一,無法應對一些較為復雜的場景。

          2.Timer

          Timer類是jdk專門提供的定時器工具,用來在后臺線程計劃執行指定任務,在java.util包下,要跟TimerTask一起配合使用。

          Timer類其實是一個任務調度器,它里面包含了一個TimerThread線程,在這個線程中無限循環從TaskQueue中獲取TimerTask(該類實現了Runnable接口),調用其run方法,就能異步執行定時任務。我們需要繼承TimerTask類,實現它的run方法,在該方法中加上自己的業務邏輯。

          實現這種定時任務的具體代碼如下:

          public class TimerTest {
              public static void main(String[] args) {
                  Timer timer = new Timer();
                  timer.schedule(new TimerTask() {
                      @Override
                      public void run() {
                          System.out.println("doSomething");
                      }
                  },2000,1000);
              }
          }
          

          先實例化一個Timer類,然后調用它的schedule方法,在該方法中實例化TimerTask類,業務邏輯寫在run方法中。schedule方法最后的兩次參數分別表示:延遲時間間隔時間,單位是毫秒。上面例子中,設置的定時任務是每隔1秒執行一次,延遲2秒執行。

          主要包含6個方法:

          • schedule(TimerTask task, Date time), 指定任務task在指定時間time執行
          • schedule(TimerTask task, long delay), 指定任務task在指定延遲delay后執行
          • schedule(TimerTask task, Date firstTime,long period),指定任務task在指定時間firstTime執行后,進行重復固定延遲頻率peroid的執行
          • schedule(TimerTask task, long delay, long period), 指定任務task 在指定延遲delay 后,進行重復固定延遲頻率peroid的執行
          • scheduleAtFixedRate(TimerTask task,Date firstTime,long period), 指定任務task在指定時間firstTime執行后,進行重復固定延遲頻率peroid的執行
          • scheduleAtFixedRate(TimerTask task, long delay, long period), 指定任務task 在指定延遲delay 后,進行重復固定延遲頻率peroid的執行

          不過使用Timer實現定時任務有以下問題:

          1. 由于Timer是單線程執行任務,如果其中一個任務耗時非常長,會影響其他任務的執行。
          2. 如果TimerTask拋出RuntimeException,Timer會停止所有任務的運行。

          使用Timer類的優缺點:

          • 優點:非常方便實現多個周期性的定時任務,并且支持延遲執行,還支持在指定時間之后支持,功能還算強大。
          • 缺點:如果其中一個任務耗時非常長,會影響其他任務的執行。并且如果TimerTask拋出RuntimeExceptionTimer會停止所有任務的運行,所以阿里巴巴開發者規范中不建議使用它。

          3.ScheduledExecutorService

          ScheduledExecutorService是JDK1.5+版本引進的定時任務,該類位于java.util.concurrent并發包下。

          ScheduledExecutorService是基于多線程的,設計的初衷是為了解決Timer單線程執行,多個任務之間會互相影響的問題。

          它主要包含4個方法:

          • schedule(Runnable command,long delay,TimeUnit unit),帶延遲時間的調度,只執行一次,調度之后可通過Future.get()阻塞直至任務執行完畢。
          • schedule(Callable<V> callable,long delay,TimeUnit unit),帶延遲時間的調度,只執行一次,調度之后可通過Future.get()阻塞直至任務執行完畢,并且可以獲取執行結果。
          • scheduleAtFixedRate,表示以固定頻率執行的任務,如果當前任務耗時較多,超過定時周期period,則當前任務結束后會立即執行。
          • scheduleWithFixedDelay,表示以固定延時執行任務,延時是相對當前任務結束為起點計算開始時間。

          實現這種定時任務的具體代碼如下:

          public class ScheduleExecutorTest {
              public static void main(String[] args) {
                  ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
                  scheduledExecutorService.scheduleAtFixedRate(() -> {
                      System.out.println("doSomething");
                  },1000,1000, TimeUnit.MILLISECONDS);
              }
          }
          

          調用ScheduledExecutorService類的scheduleAtFixedRate方法實現周期性任務,每隔1秒鐘執行一次,每次延遲1秒再執行。

          這種定時任務是阿里巴巴開發者規范中用來替代Timer類的方案,對于多線程執行周期性任務,是個不錯的選擇。

          ScheduledExecutorService的優缺點:

          • 優點:基于多線程的定時任務,多個任務之間不會相關影響,支持周期性的執行任務,并且帶延遲功能。
          • 缺點:不支持一些較復雜的定時規則。

          三. spring支持的定時任務

          1.spring task

          spring taskspring3以上版本自帶的定時任務,實現定時任務的功能時,需要引入spring-context包,目前它支持:xml注解 兩種方式。

          1. 項目實戰

          由于xml方式太古老了,我們以springboot項目中注解方式為例。

          第一步,在pom.xml文件中引入spring-context相關依賴。

          <dependency>
              <groupId>org.springframework</groupId>
              <artifactId>spring-context</artifactId>
          </dependency>
          

          第二步,在springboot啟動類上加上@EnableScheduling注解。

          @EnableScheduling
          @SpringBootApplication
          public class Application {
              public static void main(String[] args) {
                  new SpringApplicationBuilder(Application.class).web(WebApplicationType.SERVLET).run(args);
              }
          }
          

          第三步,使用@Scheduled注解定義定時規則。

          @Service
          public class SpringTaskTest {
              @Scheduled(cron = "${sue.spring.task.cron}")
              public void fun() {
                  System.out.println("doSomething");
              }
          }
          

          第四步,在applicationContext.properties文件中配置參數:

          sue.spring.task.cron=*/10 * * * * ?
          

          這樣就能每隔10秒執行一次fun方法了。

          2. cron規則

          spring4以上的版本中,cron表達式包含6個參數:

          [秒] [分] [時] [日期] [月] [星期]
          

          還支持幾個常用的特殊符號:

          • *:表示任何時間觸發任務
          • ,:表示指定的時間觸發任務
          • -:表示一段時間內觸發任務
          • /:表示從哪一個時刻開始,每隔多長時間觸發一次任務。
          • ?:表示用于月中的天和周中的天兩個子表達式,表示不指定值。

          cron表達式參數具體含義:

          1. 秒,取值范圍:0-59,支持*,-/
          2. 分,取值范圍:0-59,支持*,-/
          3. 時,取值范圍:0-23,支持*,-/
          4. 日期,取值范圍:1-31,支持*,-/。比秒多了?,表示如果指定的星期觸發了,則配置的日期變成無效。
          5. 月,取值范圍:1-12,支持*,-/
          6. 星期,取值范圍:1~7,1代表星期天,6代表星期六,其他的以此類推。支持*,-/?。比秒多了?,表示如果指定的日期觸發了,則配置的星期變成無效。

          常見cron表達式使用舉例:

          • 0 0 0 1 * ? 每月1號零點執行
          • 0 0 2 * * ? 每天凌晨2點執行
          • 0 0 2 * * ? 每天凌晨2點執行
          • 0 0/5 11 * * ? 每天11點-11點55分,每隔5分鐘執行一次
          • 0 0 18 ? * WED 每周三下午6點執行

          spring task先通過ScheduledAnnotationBeanPostProcessor類的processScheduled方法,解析和收集Scheduled注解中的參數,包含:cron表達式。

          然后在ScheduledTaskRegistrar類的afterPropertiesSet方法中,默認初始化一個單線程的ThreadPoolExecutor執行任務。

          對spring task感興趣的小伙伴,可以加我微信找我私聊。

          使用spring task的優缺點:

          • 優點:spring框架自帶的定時功能,springboot做了非常好的封裝,開啟和定義定時任務非常容易,支持復雜的cron表達式,可以滿足絕大多數單機版的業務場景。單個任務時,當前次的調度完成后,再執行下一次任務調度。
          • 缺點:默認單線程,如果前面的任務執行時間太長,對后面任務的執行有影響。不支持集群方式部署,不能做數據存儲型定時任務。

          2.spring quartz

          quartzOpenSymphony開源組織在Job scheduling領域的開源項目,是由java開發的一個開源的任務日程管理系統。

          quartz能做什么?

          • 作業調度:調用各種框架的作業腳本,例如shell,hive等。
          • 定時任務:在某一預定的時刻,執行你想要執行的任務。

          架構圖如下:

          quartz包含的主要接口如下:

          • Scheduler 代表調度容器,一個調度容器中可以注冊多個JobDetail和Trigger。
          • Job 代表工作,即要執行的具體內容。
          • JobDetail 代表具體的可執行的調度程序,Job是這個可執行程調度程序所要執行的內容。
          • JobBuilder 用于定義或構建JobDetail實例。
          • Trigger 代表調度觸發器,決定什么時候去調。
          • TriggerBuilder 用于定義或構建觸發器。
          • JobStore 用于存儲作業和任務調度期間的狀態。

          1. 項目實戰

          我們還是以springboot集成quartz為例。

          第一步,在pom.xml文件中引入quartz相關依賴。

          <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-quartz</artifactId>
          </dependency>
          

          第二步,創建真正的定時任務執行類,該類繼承QuartzJobBean

          public class QuartzTestJob extends QuartzJobBean {
              @Override
              protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
                  String userName = (String) context.getJobDetail().getJobDataMap().get("userName");
                  System.out.println("userName:" + userName);
              }
          }
          

          第三步,創建調度程序JobDetail和調度器Trigger

          @Configuration
          public class QuartzConfig {
              @Value("${sue.spring.quartz.cron}")
              private String testCron;
              /**
               * 創建定時任務
               */
              @Bean
              public JobDetail quartzTestDetail() {
                  JobDetail jobDetail = JobBuilder.newJob(QuartzTestJob.class)
                          .withIdentity("quartzTestDetail", "QUARTZ_TEST")
                          .usingJobData("userName", "susan")
                          .storeDurably()
                          .build();
                  return jobDetail;
              }
              /**
               * 創建觸發器
               */
              @Bean
              public Trigger quartzTestJobTrigger() {
                  //每隔5秒執行一次
                  CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(testCron);
                  //創建觸發器
                  Trigger trigger = TriggerBuilder.newTrigger()
                          .forJob(quartzTestDetail())
                          .withIdentity("quartzTestJobTrigger", "QUARTZ_TEST_JOB_TRIGGER")
                          .withSchedule(cronScheduleBuilder)
                          .build();
                  return trigger;
              }
          }
          

          第四步,在applicationContext.properties文件中配置參數:

          sue.spring.quartz.cron=*/5 * * * * ?
          

          這樣就能每隔5秒執行一次QuartzTestJob類的executeInternal方法了。

          CronTrigger配置格式:

          [秒] [分] [小時] [日] [月] [周] [年]
          

          spring quartzspring taskcron表達式規則基本一致,只是spring4以上的版本去掉了后面的,而quartzCronTrigger是非必填的,這里我就不做過多介紹了。

          使用spring quartz的優缺點:

          • 優點:默認是多線程異步執行,單個任務時,在上一個調度未完成時,下一個調度時間到時,會另起一個線程開始新的調度,多個任務之間互不影響。支持復雜的cron表達式,它能被集群實例化,支持分布式部署。
          • 缺點:相對于spring task實現定時任務成本更高,需要手動配置QuartzJobBeanJobDetailTrigger等。需要引入了第三方的quartz包,有一定的學習成本。不支持并行調度,不支持失敗處理策略和動態分片的策略等。

          四. 分布式定時任務

          1.xxl-job

          xxl-job是大眾點評(許雪里)開發的一個分布式任務調度平臺,其核心設計目標是開發迅速、學習簡單、輕量級、易擴展。現已開放源代碼并接入多家公司線上產品線,開箱即用。

          xxl-job框架對quartz進行了擴展,使用mysql數據庫存儲數據,并且內置jetty作為RPC服務調用。

          主要特點如下:

          1. 有界面維護定時任務和觸發規則,非常容易管理。
          2. 能動態啟動或停止任務
          3. 支持彈性擴容縮容
          4. 支持任務失敗報警
          5. 支持動態分片
          6. 支持故障轉移
          7. Rolling實時日志
          8. 支持用戶和權限管理

          管理界面:

          整體架構圖如下:

          使用quartz架構圖如下:

          項目實戰

          xxl-admin管理后臺部署和mysql腳本執行等這些前期準備工作,我就不過多介紹了,有需求的朋友可以找我私聊,這些更偏向于運維的事情。

          假設前期工作已經OK了,接下來我們需要:

          第一步,在pom.xml文件中引入xxl-job相關依賴。

          <dependency>
             <groupId>com.xuxueli</groupId>
             <artifactId>xxl-job-core</artifactId>
          </dependency>
          

          第二步,在applicationContext.properties文件中配置參數:

          xxl.job.admin.address: http://localhost:8088/xxl-job-admin/
          xxl.job.executor.appname: xxl-job-executor-sample
          xxl.job.executor.port: 8888
          xxl.job.executor.logpath: /data/applogs/xxl-job/
          

          第三步,創建HelloJobHandler類繼承IJobHandler類:

          @JobHandler(value = "helloJobHandler")
          @Component
          public class HelloJobHandler extends IJobHandler {
              @Override
              public ReturnT<String> execute(String param) {
                  System.out.println("XXL-JOB, Hello World.");
                  return SUCCESS;
              }
          }
          

          這樣定時任務就配置好了。

          建議把定時任務單獨部署到另外一個服務中,跟api服務分開。根據我以往的經驗,job大部分情況下,會對數據做批量操作,如果操作的數據量太大,可能會對服務的內存和cpu資源造成一定的影響。

          使用xxl-job的優缺點:

          • 優點:有界面管理定時任務,支持彈性擴容縮容、動態分片、故障轉移、失敗報警等功能。它的功能非常強大,很多大廠在用,可以滿足絕大多數業務場景。
          • 缺點:和quartz一樣,通過數據庫分布式鎖,來控制任務不能重復執行。在任務非常多的情況下,有一些性能問題。

          2.elastic-job

          elastic-job是當當網開發的彈性分布式任務調度系統,功能豐富強大,采用zookeeper實現分布式協調,實現任務高可用以及分片。它是專門為高并發和復雜業務場景開發。

          elastic-job目前是apacheshardingsphere項目下的一個子項目,官網地址:http://shardingsphere.apache.org/elasticjob/。

          elastic-job在2.x之后,出了兩個產品線:Elastic-Job-LiteElastic-Job-Cloud,而我們一般使用Elastic-Job-Lite就能夠滿足需求。Elastic-Job-Lite定位為輕量級無中心化解決方案,使用jar包的形式提供分布式任務的協調服務,外部僅依賴于Zookeeper。。

          主要特點如下:

          • 分布式調度協調
          • 彈性擴容縮容
          • 失效轉移
          • 錯過執行作業重觸發
          • 作業分片一致性,保證同一分片在分布式環境中僅一個執行實例
          • 自診斷并修復分布式不穩定造成的問題
          • 支持并行調度

          整體架構圖:

          項目實戰

          第一步,在pom.xml文件中引入elastic-job相關依賴。

          <dependency>
              <groupId>com.dangdang</groupId>
              <artifactId>elastic-job-lite-core</artifactId>
          </dependency>
          <dependency>
              <groupId>com.dangdang</groupId>
              <artifactId>elastic-job-lite-spring</artifactId>
          </dependency>
          

          第二步,增加ZKConfig類,配置zookeeper

          @Configuration
          @ConditionalOnExpression("'${zk.serverList}'.length() > 0")
          public class ZKConfig {
              @Bean
              public ZookeeperRegistryCenter registry(@Value("${zk.serverList}") String serverList,
                                                       @Value("${zk.namespace}") String namespace) {
                  return new ZookeeperRegistryCenter(new ZookeeperConfiguration(serverList, namespace));
              }
          }
          

          第三步,定義一個類實現SimpleJob接口:

          public class TestJob implements SimpleJob {
              @Override
              public void execute(ShardingContext shardingContext){
                  System.out.println("ShardingTotalCount:"+shardingContext.getShardingTotalCount());
                  System.out.println("ShardingItem:"+shardingContext.getShardingItem());
              }
          }
          

          第四步,增加JobConfig配置任務:

          @Configuration
          public class JobConfig {
              @Value("${sue.spring.elatisc.cron}")
              private String testCron;
              @Value("${sue.spring.elatisc.itemParameters}")
              private  String shardingItemParameters;
              @Value("${sue.spring.elatisc.jobParameters}")
              private String jobParameters =;
              @Value("${sue.spring.elatisc.shardingTotalCount}")
              private int shardingTotalCount;
              
              @Autowired
              private ZookeeperRegistryCenter registryCenter;
              @Bean
              public SimpleJob testJob() {
                  return new TestJob();
              }
              @Bean
              public JobScheduler simpleJobScheduler(final SimpleJob simpleJob) {
                  return new SpringJobScheduler(simpleJob, registryCenter, getConfiguration(simpleJob.getClass(),
                          cron, shardingTotalCount, shardingItemParameters, jobParameters));
              }
              private geConfiguration getConfiguration(Class<? extends SimpleJob> jobClass,String cron,int shardingTotalCount,String shardingItemParameters,String jobParameters) {
                  JobCoreConfiguration simpleCoreConfig = JobCoreConfiguration.newBuilder(jobClass.getName(), testCron, shardingTotalCount).
                          shardingItemParameters(shardingItemParameters).jobParameter(jobParameters).build();
                  SimpleJobConfiguration simpleJobConfig = new SimpleJobConfiguration(simpleCoreConfig, jobClass.getCanonicalName());
                  LiteJobConfiguration jobConfig = LiteJobConfiguration.newBuilder(simpleJobConfig).overwrite(true).build();
                  return jobConfig;
              }
          }
          

          其中:

          • cron:cron表達式,定義觸發規則。
          • shardingTotalCount:定義作業分片總數
          • shardingItemParameters:定義分配項參數,一般用分片序列號和參數用等號分隔,多個鍵值對用逗號分隔,分片序列號從0開始,不可大于或等于作業分片總數。
          • jobParameters:作業自定義參數

          第五步,在applicationContext.properties文件中配置參數:

          spring.application.name=elasticjobDemo
          zk.serverList=localhost:2181
          zk.namespace=elasticjobDemo
          sue.spring.elatisc.cron=0/5 * * * * ?
          sue.spring.elatisc.itemParameters=0=A,1=B,2=C,3=D
          sue.spring.elatisc.jobParameters=test
          sue.spring.elatisc.shardingTotalCount=4
          

          這樣定時任務就配置好了,創建定時任務的步驟,相對于xxl-job來說要繁瑣一些。

          使用elastic-job的優缺點:

          • 優點:支持分布式調度協調,支持分片,適合高并發,和一些業務相對來說較復雜的場景。
          • 缺點:需要依賴于zookeeper,實現定時任務相對于xxl-job要復雜一些,要對分片規則非常熟悉。

          3.其他分布式定時任務


          1. Saturn

          Saturn是唯品會開源的一個分布式任務調度平臺。取代傳統的Linux Cron/Spring Batch Job的方式,做到全域統一配置,統一監控,任務高可用以及分片并發處理。


          Saturn是在當當開源的Elastic-Job基礎上,結合各方需求和我們的實踐見解改良而成。使用案例:唯品會、酷狗音樂、新網銀行、海融易、航美在線、量富征信等。


          github地址:https://github.com/vipshop/Saturn/

          2. TBSchedule

          TBSchedule是阿里開發的一款分布式任務調度平臺,旨在將調度作業從業務系統中分離出來,降低或者是消除和業務系統的耦合度,進行高效異步任務處理。


          目前被廣泛應用在阿里巴巴、淘寶、支付寶、京東、聚美、汽車之家、國美等很多互聯網企業的流程調度系統中。


          github地址:https://github.com/taobao/TBSchedule


          老實說優秀的定時任務還是挺多的,不是說哪種定時任務牛逼我們就一定要用哪種,而是要根據實際業務需求選擇。每種定時任務都有優缺點,合理選擇既能滿足業務需求,又能避免資源浪費,才是上上策。當然在實際的業務場景,通常會多種定時任務一起配合使用。

          標簽是默認是自動換行的,因此設置好寬度之后,能夠較好的實現效果,但是最近的項目中發現,使用ajax加載數據之后,p標簽內的內容沒有換行,導致布局錯亂,于是嘗試著使用換行樣式,雖然解決了問題,但是并沒有發現本質原因,本質在于,我當時獲取的數據是一長串的數字,瀏覽器應該是對數字和英文單詞處理方式相近,不會截斷。

          先給出各種方式,再具體介紹每一個屬性。

          強制不換行

          p { white-space:nowrap; }

          自動換行

          p { word-wrap:break-word; }

          強制英文單詞斷行

          p { word-break:break-all; }

          *注意:設置強制將英文單詞斷行,需要將行內元素設置為塊級元素。

          超出顯示省略號

          p{text-overflow:ellipsis;overflow:hidden;}

          white-space: normal|pre|nowrap|pre-wrap|pre-line|inherit;

          white-space 屬性設置如何處理元素內的空白

          normal 默認。空白會被瀏覽器忽略。

          pre 空白會被瀏覽器保留。其行為方式類似 HTML 中的 pre 標簽。

          nowrap 文本不會換行,文本會在在同一行上繼續,直到遇到 br 標簽為止。

          pre-wrap 保留空白符序列,但是正常地進行換行。

          pre-line 合并空白符序列,但是保留換行符。

          inherit 規定應該從父元素繼承 white-space 屬性的值。

          word-wrap: normal|break-word;

          word-wrap 屬性用來標明是否允許瀏覽器在單詞內進行斷句,這是為了防止當一個字符串太長而找不到它的自然斷句點時產生溢出現象。

          normal: 只在允許的斷字點換行(瀏覽器保持默認處理)

          break-word:在長單詞或URL地址內部進行換行

          word-break: normal|break-all|keep-all;

          word-break 屬性用來標明怎么樣進行單詞內的斷句。

          normal:使用瀏覽器默認的換行規則。

          break-all:允許再單詞內換行

          keep-all:只能在半角空格或連字符處換行

          舉例看起區別:

          <!doctype html>

          <html lang="en">

          <head>

          <!--網站編碼格式,UTF-8 國際編碼,GBK或 gb2312 中文編碼-->

          <meta http-equiv="content-type" content="text/html;charset=utf-8" />

          <meta name="Keywords" content="關鍵詞一,關鍵詞二">

          <meta name="Description" content="網站描述內容">

          <meta name="Author" content="Yvette Lau">

          <title>Document</title>

          <!--css js 文件的引入-->

          <style>

          .word{background:#E4FFE9;width:250px;margin:50px auto;padding:20px;font-family:"microsoft yahei";}

          /* 強制不換行 */

          .nowrap{white-space:nowrap;}

          /* 允許單詞內斷句,首先會嘗試挪到下一行,看看下一行的寬度夠不夠,

          不夠的話就進行單詞內的斷句 */

          .breakword{word-wrap: break-word;}

          /* 斷句時,不會把長單詞挪到下一行,而是直接進行單詞內的斷句 */

          .breakAll{word-break:break-all;}

          /* 超出部分顯示省略號 */

          .ellipsis{text-overflow:ellipsis;overflow:hidden;}

          </style>

          </head>

          <body>

          <div class = "word">

          <p class = "nowrap">wordwrap:breakword;absavhsafhuafdfbjhfvsalguvfaihuivfs</p>

          <p class = "breakword">wordwrap:break-word;absavhsafhuafdfbjhfvsalguvfaihui</p>

          <p class = "breakAll">wordwrap:break-word;absavhsafhuafdfbjhfvsalguvfaihuivf</p>

          <p class = "normal">wordwrap:breakword;absavhsafhuafdfbjhfvsalguvfaihuivfsa</p>

          <p class = "ellipsis">wordwrap:breakword;absavhsafhuafdfbjhfvsalguvfaihuivfsab</p>

          </div>

          </body>

          </html>

          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22
          • 23
          • 24
          • 25
          • 26
          • 27
          • 28
          • 29
          • 30
          • 31
          • 32
          • 33

          效果如下:

          開源精選》是我們分享Github、Gitee等開源社區中優質項目的欄目,包括技術、學習、實用與各種有趣的內容。本期推薦的是一個Nginx界面配置工具——nginxWebUI。

          nginxWebUI是一款圖形化管理nginx配置的工具, 可以使用網頁來快速配置nginx的各項功能, 包括http協議轉發、tcp協議轉發、反向代理、負載均衡、靜態html服務器、ssl證書自動申請、續簽、配置等,配置好后可一建生成nginx.conf文件, 同時可控制nginx使用此文件進行啟動與重載,完成對nginx的圖形化控制閉環。


          功能特性

          • 配置文件數據化:將nginx繁瑣的配置文件修改操作,更改為數據的增刪改查,最大限度解放勞動力,不再到處找修改教程。
          • 集群管理:在一臺機器管理所有nginx集群服務器配置文件,進行統一修改與重啟,不再需要頻繁登錄各個服務器操作。
          • 證書一鍵申請與自動續簽:一鍵免費生成ssl證書并進行域名綁定,可在證書過期前進行自動續簽,保證網站的https安全性與持續性。
          • 強大的nginx日志解析:可一鍵配置nginx日志生成格式,并進行圖形化解析,充分了解訪客數據與后臺服務運行狀況。
          • docker容器部署:將nginx與nginxWebUI集成到一個docker鏡像中,完美的打造一個擁有圖形界面的nginx運行包,不與服務器上其他程序沖突。
          • 數據導入導出:可將全站數據導出到一個文件當中,方便程序移植與重新部署。
          • 網頁和證書直接上傳:可在網頁中直接上傳nginx需要的網頁與證書,方便建站一條龍服務,全程舍棄ssh客戶端。
          • 強大的備份管理機制:每一次替換nginx.conf文件,都會產生一個備份,保證操作失誤可以隨時隨地回滾,手賤也沒有任何問題。

          技術說明

          本項目是基于solon的web系統,數據庫使用h2, 因此服務器上不需要安裝任何數據庫。

          本系統通過Let's encrypt申請證書,使用acme.sh腳本進行自動化申請和續簽,開啟續簽的證書將在每天凌晨2點進行續簽,只有超過60天的證書才會進行續簽,只支持在linux下簽發證書。

          添加tcp/ip轉發配置支持時, 一些低版本的nginx可能需要重新編譯,通過添加–with-stream參數指定安裝stream模塊才能使用,但在ubuntu 18.04下,官方軟件庫中的nginx已經帶有stream模塊,不需要重新編譯。本系統如果配置了tcp轉發項的話,會自動引入ngx_stream_module.so的配置項,如果沒有開啟則不引入,最大限度優化ngnix配置文件。

          界面說明

          打開 http://ip:8080 進入主頁。

          登錄頁面,第一次打開會要求初始化管理員賬號。

          進入系統后,可在管理員管理里面添加修改管理員賬號。

          在http參數配置中可以配置nginx的http項目,進行http轉發,默認會給出幾個常用配置,其他需要的配置可自由增刪改查,可以勾選開啟日志跟蹤,生成日志文件。

          在TCP參數配置中可以配置nginx的steam項目參數,進行tcp轉發。

          在反向代理中可配置nginx的反向代理即server項功能,可開啟ssl功能,可以直接從網頁上上傳pem文件和key文件,或者使用系統內申請的證書,可以直接開啟http轉跳https功能,也可開啟http2協議。

          在負載均衡中可配置nginx的負載均衡即upstream項功能,在反向代理管理中可選擇代理目標為配置好的負載均衡。

          在證書管理中可添加證書,并進行簽發和續簽,開啟定時續簽后,系統會自動續簽即將過期的證書。

          備份文件管理,這里可以看到nginx.cnf的備份歷史版本,nginx出現錯誤時可以選擇回滾到某一個歷史版本。

          最終生成conf文件,可在此進行進一步手動修改,確認修改無誤后,可覆蓋本機conf文件,并進行校驗和重啟,可以選擇生成單一nginx.conf文件還是按域名將各個配置文件分開放在conf.d下。

          遠程服務器管理,如果有多臺nginx服務器,可以都部署上nginxWebUI,然后登錄其中一臺,在遠程管理中添加其他服務器的ip和用戶名密碼,就可以在一臺機器上管理所有的nginx服務器了。

          提供一鍵同步功能,可以將某一臺服務器的數據配置和證書文件同步到其他服務器中。

          本系統提供http接口調用,只要開 http://xxx.xxx.xxx.xxx:8080/doc.html 即可查看smat-doc接口頁面。

          接口調用需要在header中添加token,其中token的獲取需要在管理員管理中,打開用戶的接口調用權限,然后通過用戶名密碼調用獲取token接口,才能得到token,然后在knife4j的文檔管理中設置全局token。

          安裝教程

          jar安裝說明:

          注意:本項目需要在root用戶下運行系統命令,極容易被黑客利用,請一定修改密碼為復雜密碼

          以Ubuntu操作系統為例:

          1 安裝java運行環境和nginx

          Ubuntu:

          apt install openjdk-8-jdk
          sudo apt install nginx

          Centos:

          yum install java-1.8.0-openjdk
          yum install nginx

          Windows:

          下載JDK安裝包 https://www.oracle.com/java/technologies/downloads/
          配置JAVA運行環境
          JAVA_HOME : JDK安裝目錄
          Path : JDK安裝目錄\bin
          重啟電腦

          2 下載最新版發行包jar

          有新版本只需要修改路徑中的版本即可。

          Linux: wget -O /home/nginxWebUI/nginxWebUI.jar http://file.nginxwebui.cn/nginxWebUI-3.2.8.jar
          Windows: 直接使用瀏覽器下載 http://file.nginxwebui.cn/nginxWebUI-3.2.8.jar

          3 啟動程序

          Linux: nohup java -jar -Dfile.encoding=UTF-8 /home/nginxWebUI/nginxWebUI.jar --server.port=8080 --project.home=/home/nginxWebUI/ > /dev/null &
          Windows: java -jar -Dfile.encoding=UTF-8 D:/home/nginxWebUI/nginxWebUI.jar --server.port=8080 --project.home=D:/home/nginxWebUI/

          參數說明(都是非必填):

          --server.port 占用端口, 默認以8080端口啟動

          --project.home 項目配置文件目錄,存放數據庫文件,證書文件,日志等, 默認為/home/nginxWebUI/

          --spring.database.type=mysql 使用其他數據庫,不填為使用本地h2,可選mysql

          --spring.datasource.url=jdbc:mysql://ip:port/nginxwebui 數據庫url

          --spring.datasource.username=root 數據庫用戶

          --spring.datasource.password=pass 數據庫密碼

          注意Linux命令最后加一個&號, 表示項目后臺運行


          docker安裝說明

          本項目制作了docker鏡像,同時包含nginx和nginxWebUI在內,一體化管理與運行nginx。

          1 安裝docker容器環境

          ubuntu:

          apt install docker.io

          centos:

          yum install docker

          2 下載鏡像

          docker pull cym1102/nginxwebui:latest

          3 啟動容器

          docker run -itd -v /home/nginxWebUI:/home/nginxWebUI -e BOOT_OPTIONS="--server.port=8080" --privileged=true --net=host cym1102/nginxwebui:latest

          注意:

          啟動容器時請使用--net=host參數, 直接映射本機端口, 因為內部nginx可能使用任意一個端口, 所以必須映射本機所有端口

          容器需要映射路徑/home/nginxWebUI:/home/nginxWebUI, 此路徑下存放項目所有數據文件, 包括數據庫, nginx配置文件, 日志, 證書等, 升級鏡像時, 此目錄可保證項目數據不丟失. 請注意備份

          -e BOOT_OPTIONS 參數可填充java啟動參數, 可以靠此項參數修改端口號, "--server.port 占用端口", 不填默認以8080端口啟動

          日志默認存放在/home/nginxWebUI/log/nginxWebUI.log


          —END—

          開源協議:MulanPSL-1.0

          開源地址:https://github.com/cym1102/nginxWebUI


          主站蜘蛛池模板: 一区二区手机视频| 精品国产日韩亚洲一区在线| 丰满人妻一区二区三区视频53| 亚洲日韩精品无码一区二区三区| 亚洲天堂一区二区| 香蕉视频一区二区| 国产一区二区三区在线2021 | 好爽毛片一区二区三区四无码三飞| 蜜桃视频一区二区| 国产一区高清视频| 狠狠综合久久AV一区二区三区| 久久久久人妻一区二区三区vr| 久久精品国产一区二区三区肥胖| 亚洲成a人一区二区三区| 国产乱码精品一区二区三区麻豆| 日本一区二区不卡视频| 国模无码视频一区| 综合无码一区二区三区| 日韩人妻无码免费视频一区二区三区| 亚洲AV成人一区二区三区观看| 国产成人无码一区二区三区在线| 亚洲国产成人久久一区久久| 精品女同一区二区三区免费站 | 国产一区二区三区不卡在线看 | 韩日午夜在线资源一区二区 | 亚洲熟女www一区二区三区| 中文字幕乱码一区久久麻豆樱花| 国产精品视频免费一区二区| 精品一区二区三区中文| 无码人妻精品一区二区在线视频| 中文字幕乱码一区久久麻豆樱花| 国产一区二区三区高清视频 | 在线一区二区三区| 中文字幕在线一区二区在线| 国产自产V一区二区三区C| 天码av无码一区二区三区四区| 精品国产一区二区三区香蕉| 国产一在线精品一区在线观看| 午夜性色一区二区三区不卡视频 | AV天堂午夜精品一区二区三区| 日本成人一区二区|