主頁 > 後端開發 > SpringBoot自定義cron運算式注冊定時任務

SpringBoot自定義cron運算式注冊定時任務

2023-04-22 07:31:08 後端開發

springBoot自定義cron運算式注冊定時任務

一、原理

  • 1、使用Spring自帶的TaskScheduler注冊任務
  • 2、注冊后回傳:ScheduledFuture,用于取消定時任務
  • 3、注冊任務后不會馬上取消任務,所以將任務快取,在需要取消任務的時候呼叫取消介面取消
  • 4、cron運算式可以由前端或者后端生成,實作中會校驗cron運算式
public class TestScheduled {

    /**
     * 1、使用Spring自帶的TaskScheduler注冊任務
     * 2、注冊后回傳:ScheduledFuture,用于取消定時任務
     */
    @Resource
    private TaskScheduler taskScheduler;

    public void registrarTask() {
        //具體的任務Runnable(一般使用類實作Runnable介面)
        Runnable taskRunnable = new Runnable() {
            @Override
            public void run() {

            }
        };
        //cron運算式觸發器
        CronTrigger trigger = new CronTrigger("0/5 * * * * ?");
        //開啟定時任務的真正方法
        ScheduledFuture<?> future = this.taskScheduler.schedule(taskRunnable, trigger);
        //取消定時任務
        future.cancel(true);
    }
}

二、具體實作

1、配置任務調度器

  • 作用:設定:核心執行緒數:可同時執行任務數;設定執行緒名稱前綴
  • 可以不配置,不配置就默認使用spring自帶的
package com.cc.ssd.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

/** TaskScheduler任務調度器配置類
 * @since 2023/4/21 0021
 * @author CC
 **/
@Configuration
public class CronTaskConfig {

    /**
     * 任務調度器自定義配置
     */
    @Bean(name = "taskScheduler")
    public TaskScheduler taskScheduler() {
        // 任務調度執行緒池
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        // 定時任務執行執行緒池核心執行緒數:可同時執行4個任務
        taskScheduler.setPoolSize(4);
        taskScheduler.setRemoveOnCancelPolicy(true);
        // 執行緒名稱前綴
        taskScheduler.setThreadNamePrefix("Cs-ThreadPool-");
        return taskScheduler;
    }

}

2、定時任務注冊類

  • 作用:快取、注冊定時任務;還可以查詢、洗掉定時任務
package com.cc.ssd.registrar;

import com.cc.ssd.task.CronTaskFuture;
import com.cc.ssd.task.CronTaskRunnable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.config.CronTask;
import org.springframework.scheduling.support.CronExpression;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/** 注冊定時任務:快取定時任務、注冊定時任務到調度中心
 * @author CC
 **/
@Component
public class CronTaskRegistrar implements DisposableBean {

    private static final Logger log = LoggerFactory.getLogger(CronTaskRegistrar.class);

    /**
     * 快取任務
     * key:具體的任務
     * value:注冊定時任務后回傳的ScheduledFuture
     */
    private final Map<Runnable, CronTaskFuture> scheduledTasks = new ConcurrentHashMap<>(16);

    /**
     * 使用自定義的任務調度配置
     */
    @Resource(name = "taskScheduler")
    private TaskScheduler taskScheduler;

    /** 獲取任務調度配置
     * @return 任務調度配置
     */
    public TaskScheduler getTaskScheduler() {
        return this.taskScheduler;
    }

    /** 新增定時任務1
     *  存在任務:洗掉此任務,重新新增這個任務
     * @param taskRunnable 執行的具體任務定義:taskRunnable 實作Runnable
     * @param cronExpression cron運算式
     */
    public void addCronTask(Runnable taskRunnable, String cronExpression) {
        //驗證cron運算式是否正確
        boolean validExpression = CronExpression.isValidExpression(cronExpression);
        if (!validExpression) {
            throw new RuntimeException("cron運算式驗證失敗!");
        }
        //獲取下次執行時間
        CronExpression parse = CronExpression.parse(cronExpression);
        LocalDateTime next = parse.next(LocalDateTime.now());
        if (Objects.nonNull(next)) {
            //定時任務下次執行的時間
            String format = next.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            log.info("定時任務下次執行的時間:{}", format);
        }

        //封裝成 CronTask(cron任務)
        CronTask cronTask = new CronTask(taskRunnable, cronExpression);
        this.addCronTask(cronTask);
    }

    /** 新增定時任務2
     * @param cronTask :<p>CronTask用于在指定時間間隔內執行定時任務,</p>
     *                   <p>它是通過CronTrigger來實作的,CronTrigger是一個基于cron運算式的觸發器,</p>
     *                   <p>可以在指定的時間間隔內觸發任務執行,</p>
     * @since 2023/4/21 0021
     * @author CC
     **/
    private void addCronTask(CronTask cronTask) {
        if (Objects.nonNull(cronTask)) {
            //1有這個任務,先洗掉這個任務,再新增
            Runnable task = cronTask.getRunnable();
            String taskId = null;
            if (task instanceof CronTaskRunnable) {
                taskId = ((CronTaskRunnable) task).getTaskId();
            }
            //通過任務id獲取快取的任務,如果包含則洗掉,然后新增任務
            Runnable taskCache = this.getTaskByTaskId(taskId);
            if (Objects.nonNull(taskCache) && this.scheduledTasks.containsKey(taskCache)) {
                this.removeCronTaskByTaskId(taskId);
            }
            //2注冊定時任務到調度中心
            CronTaskFuture scheduledFutureTask = this.scheduleCronTask(cronTask);

            //3快取定時任務
            this.scheduledTasks.put(task, scheduledFutureTask);

            //todo cc 4可以將任務保存到資料庫中……重新啟動程式然后加載資料庫中的任務到快取中……

        }
    }

    /** 注冊 ScheduledTask 定時任務
     * @param cronTask cronTask
     * @return 注冊定時任務后回傳的 ScheduledFutureTask
     */
    private CronTaskFuture scheduleCronTask(CronTask cronTask) {
        //注冊定時任務后記錄的Future
        CronTaskFuture scheduledTask = new CronTaskFuture();
        //開啟定時任務的真正方法
        scheduledTask.future = this.taskScheduler.schedule(cronTask.getRunnable(), cronTask.getTrigger());
//        scheduledTask.setThreadLocal(this.taskScheduler.schedule(cronTask.getRunnable(), cronTask.getTrigger()));
        return scheduledTask;
    }

    /** 獲取任務串列
     * @return
     */
    public List<CronTaskRunnable> getScheduledTasks() {
        List<CronTaskRunnable> tasks = new ArrayList<>();

        Set<Runnable> keySet = scheduledTasks.keySet();
        keySet.forEach(key -> {
            CronTaskRunnable task = new CronTaskRunnable();
            if (key instanceof CronTaskRunnable) {
                CronTaskRunnable taskParent = (CronTaskRunnable) key;
                BeanUtils.copyProperties(taskParent, task);
            }
            tasks.add(task);
        });

        return tasks.stream()
                .sorted(Comparator.comparing(CronTaskRunnable::getTaskId))
                .collect(Collectors.toList());
    }

    /** 根據任務id洗掉單個定時任務
     * @param taskId 任務id
     */
    public void removeCronTaskByTaskId(String taskId) {
        //通過任務id獲取任務
        Runnable task = this.getTaskByTaskId(taskId);
        //需要通過任務id獲取任務,然后再移除
        CronTaskFuture cronTaskFuture = this.scheduledTasks.remove(task);
        if (Objects.nonNull(cronTaskFuture)) {
            cronTaskFuture.cancel();
        }
    }

    /** 通過任務id獲取任務,未查詢到回傳null
     * @param taskId 任務id
     * @return java.lang.Runnable
     * @since 2023/4/21 0021
     * @author CC
     **/
    private Runnable getTaskByTaskId(String taskId) {
        Assert.notNull(taskId, "任務id不能為空!");
        Set<Map.Entry<Runnable, CronTaskFuture>> entries = scheduledTasks.entrySet();
        //根據任務id獲取該任務快取
        Map.Entry<Runnable, CronTaskFuture> rcf = entries.stream().filter(rf -> {
            Runnable key = rf.getKey();
            String taskId1 = null;
            if (key instanceof CronTaskRunnable) {
                taskId1 = ((CronTaskRunnable) key).getTaskId();
            }
            return taskId.equals(taskId1);
        }).findAny().orElse(null);

        if (Objects.nonNull(rcf)) {
            return rcf.getKey();
        }
        return null;
    }

    /** 洗掉所有的定時任務
     *     DisposableBean是Spring框架中的一個介面,它定義了一個destroy()方法,
     *     用于在Bean銷毀時執行清理作業,
     *     當一個Bean實作了DisposableBean介面時,
     *     Spring容器會在該Bean銷毀時自動呼叫destroy()方法,
     *     以便進行一些清理作業,例如釋放資源等,
     *     如果您的Bean需要在銷毀時執行一些清理作業,
     *     那么實作DisposableBean介面是一個很好的選擇,
     */
    @Override
    public void destroy() {
        //關閉所有定時任務
        for (CronTaskFuture task : this.scheduledTasks.values()) {
            task.cancel();
        }
        //清空快取
        this.scheduledTasks.clear();

        log.info("取消所有定時任務!");
        //todo cc 修改或洗掉資料庫的任務
    }

}

3、定時任務的執行結果ScheduledFuture

  • 作用:CronTaskFuture類中使用的是ScheduledFuture物件來表示定時任務的執行結果,
package com.cc.ssd.task;

import java.util.Objects;
import java.util.concurrent.ScheduledFuture;

/** CronTaskFuture類中使用的是ScheduledFuture物件來表示定時任務的執行結果,
 *  ——最后ps:也可以不要這個記錄類,直接快取ScheduledFuture物件,
 *  用來記錄單獨的Future、定時任務注冊任務后產生的
 * @author CC
 **/
public final class CronTaskFuture {

    /** 每個執行緒一個副本
     * 經過測驗這里不能使用ThreadLocal
     */
//    private static final ThreadLocal<ScheduledFuture<?>> THREAD_LOCAL = new ThreadLocal<>();

    /** 最后ps:由于ScheduledFuture是執行緒安全的,這里不用 volatile 或者 ThreadLocal
     *      注冊任務后回傳的:ScheduledFuture 用于記錄并取消任務
     *      這兩個都可以不使用,直接給future賦值
     *          volatile:執行緒之間可見:volatile用于實作多執行緒之間的可見性和一致性,保證資料的正確性,
     *          ThreadLocal:用于實作執行緒封閉,保證執行緒安全
     * 使用建議:
     *      CronTaskFuture類中使用的是ScheduledFuture物件來表示定時任務的執行結果,
     *      ScheduledFuture物件是執行緒安全的,因此不需要使用volatile關鍵字來保證多執行緒同步,
     *      如果需要在多執行緒中使用執行緒本地變數,可以使用ThreadLocal,
     *      因此,建議在CronTaskFuture類中使用ScheduledFuture物件,而不是使用volatile或ThreadLocal,
     *      另外,如果需要在Spring容器銷毀時執行一些清理操作,可以實作DisposableBean介面,并在destroy()方法中進行清理操作,
     */
    public ScheduledFuture<?> future;
//    public volatile ScheduledFuture<?> future;
//    public void setThreadLocal(ScheduledFuture<?> future){
//        THREAD_LOCAL.set(future);
//    }

    /**
     * 取消當前定時任務
     */
    public void cancel() {
        try {
//            ScheduledFuture<?> future = THREAD_LOCAL.get();
            ScheduledFuture<?> future = this.future;
            if (Objects.nonNull(future)) {
                future.cancel(true);
            }
        } catch (Exception e) {
            throw new RuntimeException("銷毀定時任務失敗!");
        } finally {
//            THREAD_LOCAL.remove();
        }

    }

}

4、具體的任務,

  • 實作Runable介面
  • 任務處理的方式按照自己的需求去實作即可
package com.cc.ssd.task;

import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

/** 具體任務實作
 * @author CC
 * @since 2023/4/21 0021
 */
@Data
public class CronTaskRunnable implements Runnable {

    private static final Logger log = LoggerFactory.getLogger(CronTaskRunnable.class);

    /**
     * 任務id(必須唯一)
     */
    private String taskId;
    /**
     * 任務型別:自定義
     */
    private Integer taskType;
    /**
     * 任務名字
     */
    private String taskName;
    /**
     * 任務引數
     */
    private Object[] params;

    public CronTaskRunnable() {
    }

    public CronTaskRunnable(String taskId, Integer taskType, String taskName, Object... params) {
        this.taskId = taskId;
        this.taskType = taskType;
        this.taskName = taskName;
        this.params = params;
    }

    /** 執行任務
     * @since 2023/4/21 0021
     * @author CC
     **/
    @Override
    public void run() {
        long start = System.currentTimeMillis();

        //具體的任務,
        log.info("\n\t {}號.定時任務開始執行 - taskId:{},taskName:{},taskType:{},params:{}",
                taskType, taskId, taskName, taskType, params);

        //任務處理的方式:
        //todo cc 1就在這里執行:模擬任務
        //todo cc 2開啟策略模式,根據任務型別 調度不同的任務
        //todo cc 3使用反射:傳來bean名字,方法名字,呼叫不同的任務
        //todo cc 4開啟佇列,把要執行的任務放到佇列中,然后執行 —— 使用場景:每個任務執行很耗時的情況下使用
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        log.info("\n\t {}號.任務執行完成 - 耗時:{},taskId:{},taskType:{}",
                taskType, System.currentTimeMillis() - start, taskId, taskType);

    }


}

5、測驗Controller

package com.cc.ssd.web.controller;

import com.cc.ssd.registrar.CronTaskRegistrar;
import com.cc.ssd.task.CronTaskRunnable;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;

/**
 * @author CC
 * @since 2023/4/21 0021
 */
@RestController
@RequestMapping("/scheduled")
public class TestScheduledController {

    @Resource
    private CronTaskRegistrar cronTaskRegistrar;

    /** 獲取任務串列
     * @return java.util.List<com.cc.ssd.task.SchedulingRunnableTask>
     * @since 2023/4/21 0021
     * @author CC
     **/
    @GetMapping
    public List<CronTaskRunnable> getScheduledTasks() {
        return cronTaskRegistrar.getScheduledTasks();
    }

    /** 添加任務
     * @param param param
     * @return java.lang.String
     * @since 2023/4/21 0021
     * @author CC
     **/
    @PostMapping
    public String addCronTask(@RequestBody Map<String, Object> param) {
        //自己拿任務引數的邏輯:可以把每個任務保存到資料庫,重新啟動任務的同時,加載這些任務到任務調度中心
        String taskId = (String) param.get("taskId");
        Integer taskType = (Integer) param.get("taskType");
        String taskName = (String) param.get("taskName");
        Object params = param.get("params");
        //添加任務引數
        CronTaskRunnable task = new CronTaskRunnable(taskId, taskType, taskName, params);
        //注冊任務:cron運算式,可以從傳入不一樣的
        cronTaskRegistrar.addCronTask(task, "0/5 * * * * ?");
        return "ok";
    }

    /** 根據任務id洗掉定時任務
     * @param taskId 任務id
     * @return java.lang.String
     * @since 2023/4/21 0021
     * @author CC
     **/
    @DeleteMapping
    public String removeCronTaskByTaskId(@RequestParam String taskId) {
        cronTaskRegistrar.removeCronTaskByTaskId(taskId);
        return "ok";
    }

    /** 洗掉全部任務
     * @return java.lang.String
     * @since 2023/4/21 0021
     * @author CC
     **/
    @DeleteMapping("/removeAll")
    public String removeCronTask() {
        cronTaskRegistrar.destroy();
        return "ok";
    }

}

6、最后效果

  • 自己用controller去測驗一波吧

image

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/550768.html

標籤:其他

上一篇:從原理聊JVM(一):染色標記和垃圾回收演算法

下一篇:返回列表

標籤雲
其他(157781) Python(38089) JavaScript(25379) Java(17985) C(15215) 區塊鏈(8256) C#(7972) AI(7469) 爪哇(7425) MySQL(7135) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4555) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1959) Web開發(1951) python-3.x(1918) HtmlCss(1917) 弹簧靴(1913) C++(1910) xml(1889) PostgreSQL(1872) .NETCore(1854) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • SpringBoot自定義cron運算式注冊定時任務

    springBoot自定義cron運算式注冊定時任務 一、原理 1、使用Spring自帶的TaskScheduler注冊任務 2、注冊后回傳:ScheduledFuture,用于取消定時任務 3、注冊任務后不會馬上取消任務,所以將任務快取。在需要取消任務的時候呼叫取消介面取消 4、cron運算式可以 ......

    uj5u.com 2023-04-22 07:31:08 more
  • 從原理聊JVM(一):染色標記和垃圾回收演算法

    本篇介紹了JVM中垃圾回收器相關的基礎知識,后續會深入介紹CMS、G1、ZGC等不同垃圾收集器的運作流程和原理,歡迎關注。 ......

    uj5u.com 2023-04-22 07:30:35 more
  • 記一次jedis連接池頑固問題排查與修改

    這輩子不想再看到jedisBrokenPipe!! 測驗環境運行16天后報錯資訊: 05:42:32.629 [http-nio-8093-exec-2] ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - [log,175] - Servlet.service( ......

    uj5u.com 2023-04-22 07:30:16 more
  • 我的OpenAI庫發布了!!!

    chatGPT正式發布已經有段時間了,這段時間我也深度體驗了chatGPT的魅力。 OpenAI除了提供網頁版的chatGPT,還通過api的形式提供了很多其它服務,包括文字糾錯、圖片生成、音頻轉換等等。 作為程式員,即使有現成的openai庫,但還是免不了想自己造輪子,所以就有這個openai庫。 ......

    uj5u.com 2023-04-22 07:30:10 more
  • MDC輕量化日志鏈路跟蹤的若干種應用場景

    "If debugging is the process of removing software bugs, then programming must be the process of putting them in." - Edsger Dijkstra “如果除錯是消除軟體Bug的程序,那 ......

    uj5u.com 2023-04-22 07:30:06 more
  • WCF教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows通訊開發平臺(Windows Communication Foundation,簡稱WCF)是由微軟開發的一系列支持資料通信的應用程式框架,可以翻譯為Windows通訊開發平臺。 整合了原有的windows通訊的 .net Remoting,WebService,Socket ......

    uj5u.com 2023-04-22 07:30:01 more
  • Django筆記二十七之資料庫函式之文本函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十七之資料庫函式之文本函式 這篇筆記將介紹如何使用資料庫函式里的文本函式。 顧名思義,文本函式,就是針對文本欄位進行操作的函式,如下是目錄匯總: Concat() —— 合并 Left() —— 從左邊開始截取 Length() —— ......

    uj5u.com 2023-04-22 07:29:19 more
  • 圖片的腐蝕,膨脹,開丶閉運算,梯度計算,禮帽與黑帽

    1 腐蝕操作 用于圖片的去毛刺,內容削減 1 #腐蝕操作 2 #cv2.erode(src,kernel,iterations) 3 #src是圖片數字化陣列 4 #kernel則是一個盒,對該盒內的像素進行復試操作,值越小腐蝕能力越狠 5 #iterations是一個迭代次數,就是說你對這個圖片進 ......

    uj5u.com 2023-04-22 07:29:14 more
  • Kaggle上使用Tensorboard

    Kaggle上使用Tensorboard 1. 前言 想在Kaggle上使用Tensorboard,找了一圈。 參考了Kaggle上的一個Code:Tensorboard on Kaggle 但發現有些變化,Code中用到的內網穿透工具Ngrok需要加一個Token,所以需要注冊一個Ngrok賬號, ......

    uj5u.com 2023-04-22 07:29:01 more
  • odoo 開發入門教程系列-QWeb簡史

    QWeb簡史 到目前為止,我們的房地產模塊的界面設計相當有限。構建串列視圖很簡單,因為只需要欄位串列。表單視圖也是如此:盡管使用了一些標記,如<group>或<page>,但在設計方面幾乎沒有什么可做的。 然而,如果我們想給我們的應用程式一個獨特的外觀,就必須更進一步,能夠設計新的視圖。此外,PDF ......

    uj5u.com 2023-04-22 07:28:43 more