CompletableFuture 异步编排

目录

  • CompletableFuture 的详解
  • 代码测试
  • 配置类的引入
  • Demo1
  • Demo2
  • CompletableFuture的async后缀函数与不带async的函数的区别
  • ThreadPoolTaskExecutor 和 ThreadPoolExecutor 的区别
  • Spring 线程池的使用
  • 业务使用多线程的原因
  • 场景一:
  • 场景二:
  • FutureTask介绍
  • 线程池为什么要使用阻塞队列
  • Spring 常用的线程池的使用
  • 序列
  • 常规使用
  • 异步使用

CompletableFuture 的详解

 

🍫 它就是创建一个异步任务,然后在干什么,可以使用多任务组合

 
  • 创建任务的方法
static CompletableFuture<Void> runAsync(Runnable runnable)
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
  • 然后继续上一段的任务(里面包含了串行,AND,OR)

🚩 串行:

public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)public CompletionStage<Void> thenAccept(Consumer<? super T> action);
public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action);
public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action,Executor executor);public CompletionStage<Void> thenRun(Runnable action);
public CompletionStage<Void> thenRunAsync(Runnable action);
public CompletionStage<Void> thenRunAsync(Runnable action,Executor executor);
Function<? super T,? extends U>
T:上一个任务返回结果的类型
U:当前任务的返回值类型
 

参数解析:

  • thenApply 方法:当一个线程依赖另一个线程时,获取上一个任务返回的结果,并返回当前任务的返回值。(接收上一阶段任务结果,返回结果)
  • thenAccept方法:消费处理结果。接收任务的处理结果,并消费处理,无返回结果。 (接收上一阶段任务结果,不返回结果)
  • thenRun方法:不接收上一阶段任务结果,并且无返回值 带有Async默认是异步执行的。这里所谓的异步指的是不在当前线程内执行。

🚡 AND

public <U,V> CompletionStage<V> thenCombineAsync (CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn,Executor executor);
- 上一阶段任务与other任务均执行结束,接收两个任务的结果,并可获取返回值public <U> CompletionStage<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn,Executor executor);
- 使用上一阶段任务的结果,返回一个新的CompletableFuture实例
 

更多的参数详解: 博客链接

 
  • 多任务组合
 
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs);public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs);

代码测试

 
配置类的引入

yml

thread:pool:corePoolSize: 4maxPoolSize: 8workQueue: 25keepAliveTime: 30
 

config

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;@Configuration
@ConfigurationProperties(prefix = "thread.pool")
public class AsyncConfig{//核心线程数量大小private   int corePoolSize = 4;//线程池最大容纳线程数private   int maxPoolSize =8;//阻塞队列private   int workQueue = 25;//线程空闲后的存活时长private   int keepAliveTime = 30;@Bean("asyncTaskExecutor")public Executor getAsyncExecutor() {ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();//核心线程数threadPoolTaskExecutor.setCorePoolSize(corePoolSize);//最大线程数threadPoolTaskExecutor.setMaxPoolSize(maxPoolSize);//等待队列threadPoolTaskExecutor.setQueueCapacity(workQueue);//线程前缀threadPoolTaskExecutor.setThreadNamePrefix("taskExecutor-");//线程池维护线程所允许的空闲时间,单位为秒threadPoolTaskExecutor.setKeepAliveSeconds(keepAliveTime);// 线程池对拒绝任务(无线程可用)的处理策略threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());threadPoolTaskExecutor.initialize();return threadPoolTaskExecutor;}
}

🍫 参数详解: 建议看看下面的线程池对拒绝任务(无线程可用)的处理策略

  1、corePoolSize:核心线程数* 核心线程会一直存活,及时没有任务需要执行* 当线程数小于核心线程数时,即使有线程空闲,线程池也会优先创建新线程处理* 设置allowCoreThreadTimeout=true(默认false)时,核心线程会超时关闭2、queueCapacity:任务队列容量(阻塞队列)* 当核心线程数达到最大时,新任务会放在队列中排队等待执行3、maxPoolSize:最大线程数* 当线程数>=corePoolSize,且任务队列已满时。线程池会创建新线程来处理任务* 当线程数=maxPoolSize,且任务队列已满时,线程池会拒绝处理任务而抛出异常4、 keepAliveTime:线程空闲时间* 当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数量=corePoolSize* 如果allowCoreThreadTimeout=true,则会直到线程数量=05、allowCoreThreadTimeout:允许核心线程超时6、rejectedExecutionHandler:任务拒绝处理器* 两种情况会拒绝处理任务:- 当线程数已经达到maxPoolSize,切队列已满,会拒绝新任务- 当线程池被调用shutdown()后,会等待线程池里的任务执行完毕,再shutdown。如果在调用shutdown()和线程池真正shutdown之间提交任务,会拒绝新任务* 线程池会调用rejectedExecutionHandler来处理这个任务。如果没有设置默认是AbortPolicy,会抛出异常* ThreadPoolExecutor类有几个内部实现类来处理这类情况:- AbortPolicy 丢弃任务,抛运行时异常(默认)- CallerRunsPolicy 执行任务- DiscardPolicy 忽视,什么都不会发生- DiscardOldestPolicy 从队列中踢出最先进入队列(最后一个执行)的任务* 实现RejectedExecutionHandler接口,可自定义处理器


 

Demo1
  

image-20221115170031342

 
        CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> {System.out.println("洗水壶");try {Thread.sleep(1000);} catch (InterruptedException ex) {ex.printStackTrace();}return "水壶";}).thenApply(e->{System.out.println("烧水");try {Thread.sleep(5000);} catch (InterruptedException ex) {ex.printStackTrace();}return "热水";});//洗水壶->洗水杯->拿茶叶CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> {System.out.println("洗茶壶");try {Thread.sleep(1000);} catch (InterruptedException ex) {ex.printStackTrace();}return "茶壶";}).thenApply(e->{try {Thread.sleep(2000);} catch (InterruptedException ex) {ex.printStackTrace();}System.out.println("洗水杯");return "水杯";}).thenApply(e->{System.out.println("拿茶叶");return "茶叶";});//泡茶CompletableFuture<String> task3 = task1.thenCombine(task2, (a, b) -> {System.out.println("泡茶");return "茶";});String tea = task3.join();System.out.println(tea);
 

🍫 参数解析:

更多的参数详解: 博客链接

Demo2
 
  • 这个测试我就没有写了,自己可以看看

问题:当查询接口较复杂时候,数据的获取都需要远程调用,必然需要花费更多的时间。

假如查询文章详情页面,需要如下标注的时间才能完成:

 


// 1. 查询文章详情 0.5s

// 2. 查询文章博主个人信息 0.5s

// 3. 查询文章评论 1s

// 4. 查询博主相关文章分类 1s

// 5. 相关推荐文章 1s

// ......
上面的描述只是举个例子不要在意这里的查询描述,看实际情况使用,有些相关的查询我们可以拆分接口实现,上面的描述只是为了举例子。
 

@Service
public class ArticleService {@Autowiredprivate ArticleClient articleClient;@Autowiredprivate UserClient userClient;@Autowiredprivate ThreadPoolExecutor threadPoolExecutor;public ItemVo load(Long id) {// 1. 查询文章详情 0.5s// 下面的查询需要用到文章对应的发布用户,所以这里需要使用CompletableFuture.supplyAsyncCompletableFuture<ArticleEntity> articleCompletableFuture = CompletableFuture.supplyAsync(() -> {ResponseVo<ArticleEntity> skuEntityResponseVo = this.articleClient.getArticleById(id);ArticleEntity articleEntity = skuEntityResponseVo.getData();if (articleEntity == null) {return null;}itemVo.setId(id);itemVo.setTitle(articleEntity.getTitle());itemVo.setDefaltImage(articleEntity.getDefaultImage());return articleEntity;}, threadPoolExecutor);// 2. 查询文章博主个人信息 0.5s// 这里查询需要依赖文章关联的用户id,所以需要使用articleCompletableFuture.thenAcceptAsync()CompletableFuture<Void> userCompletableFuture = articleCompletableFuture.thenAcceptAsync(articleEntity -> {ResponseVo<UserEntity> categoryResponseVo = this.userClient.queryUserInfoById(articleEntity.getUserId());UserEntity userEntity = categoryResponseVo.getData();itemVo.setUserInfo(userEntity);}, threadPoolExecutor);    // 3. 查询博主相关文章分类 1s// 这里查询需要依赖文章关联的用户id,所以需要使用articleCompletableFuture.thenAcceptAsync()CompletableFuture<Void> userOtherArticleCompletableFuture = articleCompletableFuture.thenAcceptAsync(articleEntity -> {ResponseVo<List<UserAuserOtherArticleEntity>> categoryResponseVo = this.articleClient.queryUserAuserOtherArticleById(articleEntity.getUserId());UserAuserOtherArticleEntity userAuserOtherArticleEntity = categoryResponseVo.getData();itemVo.setUserAuserOtherArticleList(userAuserOtherArticleEntity);}, threadPoolExecutor);// 4. 查询文章评论 1s// 不需要依赖其他请求返回值,可以使用新的异步对象 CompletableFuture.runAsync()CompletableFuture<Void> commentsCompletableFuture =  CompletableFuture.runAsync(() -> {ResponseVo<List<UserArticleCategoryEntity>> userArticleCategoryVo = this.userClient.queryCommentsByArticleId(id);UserArticleCategoryEntity userArticleCategoryEntity = userArticleCategoryVo.getData();itemVo.setUserArticleCategoryList(userArticleCategoryEntity);}, threadPoolExecutor);// 5. 相关推荐文章 1s// 不需要依赖其他请求返回值,可以使用新的异步对象 CompletableFuture.runAsync()CompletableFuture<Void> relatedArticlesCompletableFuture =  CompletableFuture.runAsync(() -> {ResponseVo<List<RelatedArticlesEntity>> userArticleCategoryVo = this.articleClient.queryRelatedArticles(id);UserArticleCategoryEntity userArticleCategoryEntity = userArticleCategoryVo.getData();itemVo.setUserArticleCategoryList(userArticleCategoryEntity);}, threadPoolExecutor);}// 多任务执行组合 CompletableFuture.allOf()CompletableFuture.allOf(articleCompletableFuture, userCompletableFuture, userOtherArticleCompletableFuture,commentsCompletableFuture, relatedArticlesCompletableFuture).join();return itemVo;
}


 

CompletableFuture的async后缀函数与不带async的函数的区别

 

参考链接: 博客链接

 

🍫 结论:

不带async的函数的动作比较复杂f的whenComplete的内容由哪个线程来执行,取决于哪个线程X执行了f.complete()。但是当X线程执行了f.complete()的时候,whenComplete还没有被执行到的时候(就是事件还没有注册的时候),那么X线程就不会去同步执行whenComplete的回调了。这个时候哪个线程执行到了whenComplete的事件注册的时候,就由哪个线程自己来同步执行whenComplete的事件内容。而whenCompleteAsync的场合,就简单很多。一句话就是线程池里面拿一个空的线程或者新启一个线程来执行回调。和执行f.complete的线程以及执行whenCompleteAsync的线程无关。



 

ThreadPoolTaskExecutor 和 ThreadPoolExecutor 的区别

 

参考链接: 博客链接

🍫 结论:

其实最主要的原因很直观:ThreadPoolExecutor是一个不受Spring管理生命周期、参数装配的Java类,而有了ThreadPoolTaskExecutor的封装,线程池才有Spring“内味”。

 

Spring 线程池的使用


 

业务使用多线程的原因

 
  • 目的是面对高并发的时候,提高运行速度

场景一:

一个业务逻辑有很多次的循环,每次循环之间没有影响,比如验证1万条url路径是否存在,正常情况要循环1万次,逐个去验证每一条URL,这样效率会很低,假设验证一条需要1分钟,总共就需要1万分钟,有点恐怖。这时可以用多线程,将1万条URL分成50等份,开50个线程,没个线程只需验证200条,这样所有的线程执行完是远小于1万分钟的。


 

场景二:

需要知道一个任务的执行进度,比如我们常看到的进度条,实现方式可以是在任务中加入一个整型属性变量(这样不同方法可以共享),任务执行一定程度就给变量值加1,另外开一个线程按时间间隔不断去访问这个变量,并反馈给用户。总之使用多线程就是为了充分利用cpu的资源,提高程序执行效率,当你发现一个业务逻辑执行效率特别低,耗时特别长,就可以考虑使用多线程。

问题:不过CPU执行哪个线程的时间和顺序是不确定的,即使设置了线程的优先级,因此使用多线程的风险也是比较大的,会出现很多预料不到的问题,一定要多熟悉概念,多构造不同的场景去测试才能够掌握!

项目中可以通过:

@Order()
设置运行的优先级,数字越小,级别越高


 

FutureTask介绍

 

🍫 参考: 博客链接

线程池为什么要使用阻塞队列

 

阻塞队列可以保证任务队列中没有任务时阻塞获取任务的线程,使得线程进入wait 状态,释放 cpu 资源,当队列中有任务时才唤醒对应线程从队列中取出消息进行执行。

使得在线程不至于一直占用cpu资源。(线程执行完任务后通过循环再次从任务队列中取出任务进行执行,代码片段如:while (task != null || (task = getTask()) != null) {})。

不用阻塞队列也是可以的,不过实现起来比较麻烦而已,有好用的为啥不用呢
 

Spring 常用的线程池的使用

 

序列

Spring 通过任务执行器(TaskExecutor)来实现多线程和并发编程,使用 ThreadPoolTaskExecutor 实现一个基于线程池的TaskExecutor,
还得需要使用 @EnableAsync 开启异步,并通过在需要的异步方法那里使用注解@Async声明是一个异步任务

Spring 已经实现的异常线程池:

  •  
  • - SimpleAsyncTaskExecutor:不是真的线程池,这个类不重用线程,每次调用都会创建一个新的线程。
  • - SyncTaskExecutor:这个类没有实现异步调用,只是一个同步操作。只适用于不需要多线程的地方
  • - ConcurrentTaskExecutor:Executor的适配类,不推荐使用。如果ThreadPoolTaskExecutor不满足要求时,才用考虑使用这个类
  • - SimpleThreadPoolTaskExecutor:是Quartz的 SimpleThreadPool 的类。线程池同时被quartz和非quartz使用,才需要使用此类
  • - ThreadPoolTaskExecutor :最常使用,推荐。 其实质是对 java.util.concurrent.ThreadPoolExecutor 的包装
     

🍫 扩展:相信大家在 Java 里面也学过 JUC ,里面有 Java 里面的线程池,可以直接去看看 ThreadPoolExecutor

至于为什么有线程池,Spring 为什么还有在自己搞一个,可以自己去探索,Spring 的底层还是 ThreadPoolExecutor ,只是它的生命周期不受它控制。


 

常规使用

 
//    线程池(config里面的Bean)@Autowiredprivate Executor taskExecutor;Callable<ScyTeacher> scy =()-> scyTeacherMapper.selectOne(new LambdaQueryWrapper<ScyTeacher>().eq(ScyTeacher::getUsername,test));
FutureTask<ScyTeacher> commentCallable = new FutureTask<>(scy);
Future<Map> submit = executor.submit(commentCallable);
Map map = submit.get();

异步使用

 

🚩 记得开启异步(配置类添加)

@EnableAsync //开启异步执行
 
package com.melodyjerry.thread;import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;/*** @classname AsyncTaskService* @description 异步任务的执行类*/
@Service
public class AsyncTaskService {@Async //异步方法public void executeAsyncTask(Integer i) {System.out.javaprintln("执行异步任务: "+i);}@Async //异步方法public void executeAsyncTaskPlus(Integer i) {System.out.println("执行异步任务+1: " + (i+1));}
}

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

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

相关文章

数据结构 1.1 初学数据结构

数据结构的基本概念 数据结构在学什么&#xff1f; 如何用程序代码把现实世界的问题信息化 如何用计算机高效处理信息从而创造价值 数据&#xff1a; 数据元素、数据项&#xff1a; 数据元素——描述一个个体 数据对象——数据元素之间具有同样的性质 同一个数据对象里的数…

Springboot+Vue+Mysql实现模拟汽车保养系统(附源码)

前言 本项目基于springbootvue搭建的汽车保养的系统&#xff0c;页面较为粗糙&#xff0c;前端好的小伙伴可自行优化。 项目环境 -环境框架后端JDK1.8SpringBootmybatisPlus前端NodeJS16.0Vue2.0ElementPlus数据库MySQL8.0- 数据库设计 数据表备注banner轮播图表car用户汽…

linux命令行配置音频设备

linux命令行配置音频设备 TLTR在linux命令行播放音乐cmus需要开始声音条件功能才能调节播放的音量&#xff0c;看这个链接&#xff0c;继续折腾&#xff0c;have fun! TLTR 以archLinux为例&#xff0c;把下面软件都装一遍。 sudo pacman -S alsa-utils sudo pacman -S alsa-…

算法-数学-斜率-直线上最多的点数

算法-数学-斜率-直线上最多的点数 1 题目概述 1.1 题目出处 https://leetcode.cn/problems/max-points-on-a-line/ 1.2 题目描述 给你一个数组 points &#xff0c;其中 points[i] [xi, yi] 表示 X-Y 平面上的一个点。求最多有多少个点在同一条直线上。 2 暴力搜索斜率…

十二、【漏洞复现】Rails任意文件读取(CVE-2019-5418)

十二、【漏洞复现】Rails任意文件读取(CVE-2019-5418&#xff09; 12.1、漏洞原理 Ruby on Rails是一个使用 Ruby 语言写的开源 Web 应用框架&#xff0c;它是严格按照 MVC 结构开发的。它努力使自身保持简单&#xff0c;来使实际的应用开发时的代码更少&#xff0c;使用最少…

BGP高级特性——4字节AS号

目录 4字节AS号 相关概念 两种过渡属性 4字节AS号的格式 4字节AS号建立邻居 4字节AS号路由传递 配置命令 4字节AS号 相比于2字节AS号&#xff0c;范围更大。由1~65535扩展到1~4294967295 支持4字节AS号的BGP设备兼容仅支持2字节AS号的BGP设备 相关概念 Speaker&#…

多线程(如何理解pthread库)

上一节&#xff0c;我们主要介绍了pthread库中一些常见函数的用法&#xff0c;这节我们主要分析一下pthread库到底是什么&#xff1f; 什么是库 我们之前提过&#xff0c;在每一个linux平台下&#xff0c;必定会存在对应的pthread库 它存在于/lib64这个路径底下 换句话说&am…

【从入门到起飞】IO高级流(1)(缓冲流,转换流,序列化流,反序列化流)

&#x1f38a;专栏【JavaSE】 &#x1f354;喜欢的诗句&#xff1a;天行健&#xff0c;君子以自强不息。 &#x1f386;音乐分享【如愿】 &#x1f384;欢迎并且感谢大家指出小吉的问题&#x1f970; 文章目录 &#x1f384;缓冲流&#x1f354;字节缓冲流&#x1f6f8;一次读取…

简单的考试系统

开发一个简单的考试系统&#xff0c;在HTML页面中建立一个表单&#xff0c;通过post方法传递参数。题目类型包括单选题、多选题和填空题&#xff0c;要求程序给出考试成绩。 <!DOCTYPE html> <html> <head><title>question.html</title><met…

【LeetCode热题100】--108.将有序数组转换为二叉搜索树

108.将有序数组转换为二叉搜索树 给你一个整数数组 nums &#xff0c;其中元素已经按 升序 排列&#xff0c;请你将其转换为一棵 高度平衡 二叉搜索树。 高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树。 二叉搜索树的中序遍历是升序…

13链表-简单思路练习

目录 LeetCode之路——876. 链表的中间结点 分析&#xff1a; 解法一&#xff1a;常规思路 解法二&#xff1a;快慢指针 LeetCode之路——876. 链表的中间结点 给你单链表的头结点 head &#xff0c;请你找出并返回链表的中间结点。 如果有两个中间结点&#xff0c;则返回…

竞赛 机器视觉opencv答题卡识别系统

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 答题卡识别系统 - opencv python 图像识别 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f947;学长这里给一个题目综合评分(每项满分5分…

互联网Java工程师面试题·ZooKeeper 篇·第一弹

目录 1. ZooKeeper 面试题&#xff1f; 2. ZooKeeper 提供了什么&#xff1f; 3. Zookeeper 文件系统 4. ZAB 协议&#xff1f; 5. 四种类型的数据节点 Znode 6. Zookeeper Watcher 机制 -- 数据变更通知 7. 客户端注册 Watcher 实现 8. 服务端处理 Watcher 实现 9. 客…

ArcGIS Engine:实现Shp/Mxd数据的加载、图层的简单查询

本博客参考&#xff1a;BiliBili UP主 <羊羊旸> &#xff1a; Arcgis Engine学习 目录 01 加载控件以及控件的基本信息等调整 02 编写 <菜单-地图控件> 中各个子工具的代码 2.1 加载Shapefile数据-代码 2.2 加载地图文档数据-代码 2.3 获取图层数量-代码 2.…

vue3 +elementplus | vue2+elementui 动态地通过验证规则子新增或删除单个表单字段

效果图 点击 ‘’ 新增一行&#xff0c;点击‘-’ 删除一行 vue3elementplus写法 template <el-dialog v-model"dialogFormVisible" :title"title"><el-form ref"ruleFormRef" :model"form" :inline"true" lab…

【算法|动态规划No.8】leetcode面试题 17.16. 按摩师

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【手撕算法系列专栏】【LeetCode】 &#x1f354;本专栏旨在提高自己算法能力的同时&#xff0c;记录一下自己的学习过程&#xff0c;希望…

对图像中边、线、点的检测(支持平面/鱼眼/球面相机)附源码

前言 图像的线段检测是计算机视觉和遥感技术中的一个基本问题,可广泛应用于三维重建和 SLAM 。虽然许多先进方法在线段检测方面表现出了良好的性能,但对未去畸变原始图像的线 段检测仍然是一个具有挑战性的问题。此外,对于畸变和无畸变的图像都缺乏统一的…

1.2.C++项目:仿muduo库实现并发服务器之时间轮的设计

文章目录 一、为什么要设计时间轮&#xff1f;&#xff08;一&#xff09;简单的秒级定时任务实现&#xff1a;&#xff08;二&#xff09;Linux提供给我们的定时器&#xff1a;1.原型2.例子 二、时间轮&#xff08;一&#xff09;思想&#xff08;一&#xff09;代码 一、为什…

云原生数据库TDSQL-C

数据库系统核心模块 云原生数据库要解决什么问题&#xff1f; HTAP 云数据库VS云原生数据库

mysql双主互从通过KeepAlived虚拟IP实现高可用

mysql双主互从通过KeepAlived虚拟IP实现高可用 在mysql 双主互从的基础上&#xff0c; 架构图&#xff1a; Keepalived有两个主要的功能&#xff1a; 提供虚拟IP&#xff0c;实现双机热备通过LVS&#xff0c;实现负载均衡 安装 # 安装 yum -y install keepalived # 卸载 …