当前位置: 首页 > news >正文

深入理解Flink Mailbox线程模型

文章目录

  • 整体设计
  • processMail
    • 1.Checkpoint Tigger
    • 2.ProcessingTime Timer Trigger
  • processInput
  • 兼容SourceStreamTask

整体设计

Mailbox线程模型通过引入阻塞队列配合一个Mailbox线程的方式,可以轻松修改StreamTask内部状态的修改。Checkpoint、ProcessingTime Timer的相关操作(Runnable任务),会以Mail的形式保存到Mailbox内的阻塞队列中。StreamTask在invoke阶段的runMailboxLoop时期,就会轮询Mailbox来处理队列中保存的Mail,Mail处理完毕后才会对DataStream上的数据元素执行处理逻辑。

MailboxProcessor的能力就是负责拉取、处理Mail,以及执行MailboxDefaultAction(默认动作,即processInput()方法中对DataStream上的普通消息的处理逻辑,包括:处理Event、barrier、Watermark等)

/*** 开始轮询Mailbox内的Mail,Checkpoint和ProcessingTime Timer的触发事件会以Runnable的形式(作为Mail)添加到Mailbox的队列中,等待“Mailbox线程”去处理*/
public void runMailboxLoop() throws Exception {// 获取最新的TaskMailbox:主要用于存储提交的Mail,并提供获取接口。// TaskMailbox有2个队列://        1.queue:阻塞队列,通过ReentrantLock控制队列中的读写操作//        2.batch:非阻塞队列,调用createBatch()方法会将queue中的Mail转存到batch中,这样读操作就能通过tryTakeFromBatch()方法从batch队列中批量获取Mail,且只能被Mailbox线程消费final TaskMailbox localMailbox = mailbox;// 检查当前线程是否为Mailbox线程,即StreamTask运行时所在的主线程Preconditions.checkState(localMailbox.isMailboxThread(),"Method must be executed by declared mailbox thread!");// 确认Mailbox的状态:必须为OPENassert localMailbox.getState() == TaskMailbox.State.OPEN : "Mailbox must be opened!";// 创建MailboxController实例:可以控制Mailbox的循环、临时暂停和恢复MailboxDefaultAction(默认动作)final MailboxController defaultActionContext = new MailboxController(this);/*** 核心:事件循环* processMail()方法会检测Mailbox中是否还有Mail需要处理,新Mail会(在ReentrantLock的保护下)被添加到queue队列并转存到batch队列中。* MailboxProcessor处理完batch队列中的全部Mail后(执行作为Mail的Runnable#run()方法),才会进入到while循环内,执行MailboxDefaultAction的默认动作,* 即调用StreamTask#processInput()方法,对读取到的数据(Event、Barrier、Watermark等)进行处理*/while (processMail(localMailbox)) {mailboxDefaultAction.runDefaultAction(defaultActionContext); // lock is acquired inside default action as needed}
}

可以看出,对Mail和MailboxDefaultAction的处理,是由唯一的Mailbox线程负责的。

processMail

在while轮询时,首先会processMail

/*** 处理Mailbox中的Mail:Checkpoint、ProcessingTime Timer的触发事件,会以Runnable的形式作为Mail保存在Mailbox的queue队列中,* 并在ReentrantLock的保护下,将queue队列中的新Mail转移到batch队列中。MailboxProcessor会根据queue队列、batch队列内的Mail情况,* 决定处理Mail or processInput。只有当TaskMailbox内的所有Mail全都处理完毕后,MailboxProcessor才会去processInput()*/
private boolean processMail(TaskMailbox mailbox) throws Exception {/*** 新Mail写入queue队列,TaskMailbox会将queue队列中的新Mail转移到batch队列中,MailboxProcessor会根据queue队列、batch队列内的Mail情况,* 判断执行Mail的run() or processInput()。只有当TaskMailbox内的所有Mail全部处理完成后,MailboxProcessor才会去processInput()。*/if (!mailbox.createBatch()) {return true;}Optional<Mail> maybeMail;/*** 能走到这,说明queue队列中的Mail已被全部转移至batch队列。现在要从batch队列中获取到Mail并执行(它作为Runnable的run()方法),* 直到batch队列中的所有Mail全都处理完毕*/while (isMailboxLoopRunning() && (maybeMail = mailbox.tryTakeFromBatch()).isPresent()) {maybeMail.get().run();}/**如果默认操作处于Unavailable状态,那就先阻塞住,直到它重新回归available状态*/while (isDefaultActionUnavailable() && isMailboxLoopRunning()) {mailbox.take(MIN_PRIORITY).run();}// 返回Mailbox是否还在Loopreturn isMailboxLoopRunning();
}

很核心的一个点就是Mailbox要去createBatch,TaskMailboxImpl提供了具体的实现逻辑。Mailbox引入了2个队列,新Mail被add到Mailbox内的queue队列中(此过程受ReentrantLock保护)。同时为了减少读取queue队列时的同步开销,Mailbox还构建了一个batch队列专门用来后续消费(避免加锁操作)。

/*** 对Deque<Mail>队列的读写,通过ReentrantLock加以保护*/
private final ReentrantLock lock = new ReentrantLock();/*** Internal queue of mails.* 使用Deque(内部队列)保存所有的Mail*/
@GuardedBy("lock")
private final Deque<Mail> queue = new ArrayDeque<>();
/*** 为了减少读取queue队列所造成的同步开销,TaskMailbox会创建一个batch队列,queue队列中的Mail会被转移到batch队列中,* 有效避免了后续消费时的加锁操作*/
private final Deque<Mail> batch = new ArrayDeque<>();@Override
public boolean createBatch() {checkIsMailboxThread();/*** 如果queue队列中没有新Mail,那就要看batch队列是否为空。* 1.如果batch也是空的(Mailbox里已经没有任何Mail了,需要去processInput()了),那processMail()也会return true,* MailboxProcessor就会进入到while循环内部,执行processInput()来处理DataStream上的数据;* 2.如果batch不空,说明MailboxProcessor还需要继续processMail(),即取出Mail执行它(作为Runnable)的run()方法;* 由此可见,Mailbox中的batch队列中的Mail最终一定会被Mailbox线程消耗殆尽(轮询、处理),然后才会去processInput()*/if (!hasNewMail) { // 只要queue队列里还有Mail,hasNewMail就为truereturn !batch.isEmpty();}/**能走到这说明queue队列中仍有新Mail,接下来需要将它的新Mail向batch队列转移,该过程受ReentrantLock保护*/final ReentrantLock lock = this.lock;// 获取锁lock.lock();try {Mail mail;/**每次循环都将queue队列中的First Mail,转移到batch队列中,直至queue队列被消耗殆尽。此时一定return true*/while ((mail = queue.pollFirst()) != null) {batch.addLast(mail);}// 此时queue队列内的所有Mail都被转移到batch队列中了,queue中没有新Mail了hasNewMail = false;// 此时根据batch队列是否为空,MailboxProcessor会判断执行Mail的run() or processInput()return !batch.isEmpty();} finally {// 最终释放锁lock.unlock();}
}

如果Mailbox内的queue队列中仍有新Mail,那就在ReentrantLock的加持下将queue内的Mail全都转移到batch队列中;如果Mailbox内的queue队列中没有新Mail,那就看batch队列的情况了。决断权交给外层的MailboxProcessor,总的来看:

  • 如果batch队列中有Mail,MailboxProcessor会从Mailbox内的batch队列中逐个pollFirst,然后执行(它作为Runnable#run()方法),直到batch队列中的所有Mail全都被“消耗殆尽”为止
  • 如果batch队列中没有Mail,MailboxProcessor此时就没有Mail可处理了,那就直接processInput

1.Checkpoint Tigger

对Checkpoint的触发,是通过MailboxExecutor向Mailbox提交Mail的

/*** 触发执行StreamTask中的Checkpoint操作:异步的通过MailboxExecutor,将“执行Checkpoint”的请求封装成Mail后,* 提交到TaskMailbox中,最终由MailboxProcessor来处理*/
@Override
public Future<Boolean> triggerCheckpointAsync(CheckpointMetaData checkpointMetaData,CheckpointOptions checkpointOptions,boolean advanceToEndOfEventTime) {// 通过MailboxExecutor,将“触发执行Checkpoint”的具体逻辑封装成Mail,提交到Mailbox中,后期会被MailboxProcessor执行return mailboxProcessor.getMainMailboxExecutor().submit(// 触发Checkpoint的具体逻辑() -> triggerCheckpoint(checkpointMetaData, checkpointOptions, advanceToEndOfEventTime),"checkpoint %s with %s",checkpointMetaData,checkpointOptions);
}

triggerCheckpoint操作会被封装成Mail,添加到Mailbox中等待被处理。

@Override
public void execute(@Nonnull final RunnableWithException command,final String descriptionFormat,final Object... descriptionArgs) {try {mailbox.put(new Mail(command, priority, actionExecutor, descriptionFormat, descriptionArgs));} catch (IllegalStateException mbex) {throw new RejectedExecutionException(mbex);}
}

当然,Checkpoint的完成操作,也是同样的套路。

2.ProcessingTime Timer Trigger

/*** 借助Mailbox线程模型,由MailboxExecutor负责将"ProcessingTime Timer触发的消息"封装成Mail提交到TaskMailbox中,后续由MailboxProcessor处理*/
public ProcessingTimeService getProcessingTimeService(int operatorIndex) {Preconditions.checkState(timerService != null, "The timer service has not been initialized.");MailboxExecutor mailboxExecutor = mailboxProcessor.getMailboxExecutor(operatorIndex);// 通过MailboxExecutor将Mail提交到Mailbox中等待处理return new ProcessingTimeServiceImpl(timerService, callback -> deferCallbackToMailbox(mailboxExecutor, callback));
}private ProcessingTimeCallback deferCallbackToMailbox(MailboxExecutor mailboxExecutor, ProcessingTimeCallback callback) {return timestamp -> {mailboxExecutor.execute(() -> invokeProcessingTimeCallback(callback, timestamp),"Timer callback for %s @ %d",callback,timestamp);};
}

processInput

StreamInputProcessor会对输入的数据进行处理、输出,包含:StreamTaskInput + OperatorChain + DataOutput。每次processInput都相当于是在处理一个有界流(外层MailboxProcessor在不断地的轮询),处理完DataStream上的StreamRecord后,会返回InputStatus的枚举值,根据InputStatus值来决定下一步该“何去何从”。

/*** StreamTask的执行逻辑:处理输入的数据,返回InputStatus状态,并根据InputStatus决定是否需要结束当前Task。* 该方法会通过MailboxProcessor调度、执行(作为MailboxProcessor的默认动作),底层调用StreamInputProcessor#processInput()方法*/
protected void processInput(MailboxDefaultAction.Controller controller) throws Exception {/*** 核心:借助StreamInputProcessor完成数据的读取,并交给算子处理,处理完毕后会返回InputStatus。* 每次触发,相当于处理一个有界流,在外层Mailbox拉取Mail才是while循环无限拉取*/InputStatus status = inputProcessor.processInput();/*** case 1:上游如果还有数据 && RecordWriter是可用的,立即返回。意为:继续处理!*/if (status == InputStatus.MORE_AVAILABLE && recordWriter.isAvailable()) {return;}/*** case 2:当状态为END_OF_INPUT,说明本批次的有界流数据已经处理完毕,* 通过MailboxCollector来告诉Mailbox*/if (status == InputStatus.END_OF_INPUT) {controller.allActionsCompleted();return;}/*** case 3:当前有界流中没有数据,但未来可能会有。此时处理线程会被挂起:直到有新的可用数据到来 && RecordWriter可用* 此时会先临时暂停对MailboxDefaultAction的处理,等啥时候又有新数据了,再重新恢复MailboxDefaultAction的处理。*/CompletableFuture<?> jointFuture = getInputOutputJointFuture(status);// 通过MailboxCollector让Mailbox线程暂时停止对MailboxDefaultAction的处理MailboxDefaultAction.Suspension suspendedDefaultAction = controller.suspendDefaultAction();// 等啥时候又有了input、output,RecordWriter也变得可用了以后,再重新继续执行默认操作jointFuture.thenRun(suspendedDefaultAction::resume);
}

MailboxController是MailboxDefaultAction和Mailbox之间交互的桥梁,在StreamTask处理DataStream元素的过程中,会利用MailboxController将处理状态及时通知给Mailbox。如果这批有界流处理完毕,就会通过MailboxController通知Mailbox(本质就是向Mailbox发送一个Mail),进行下一轮的处理。

private void sendControlMail(RunnableWithException mail, String descriptionFormat, Object... descriptionArgs) {mailbox.putFirst(new Mail(mail,Integer.MAX_VALUE /*not used with putFirst*/,descriptionFormat,descriptionArgs));
}

兼容SourceStreamTask

作为DataStream Source是专门用来生产无界流数据的,并不能穿插兼顾Mailbox内Mail的检测。如果仅有一个线程生产无界流数据的话,那将永远无法检测Mailbox内的Mail。作为StreamTask的子类,SourceStreamTask会额外启动另一个独立的LegacySourceFunctionThread线程来执行SourceFunction中的循环(生产无界流),Mailbox线程(主线程)依然负责处理Mail和默认操作。

/*** 专门为Source源生产数据的线程*/
private class LegacySourceFunctionThread extends Thread {private final CompletableFuture<Void> completionFuture;LegacySourceFunctionThread() {this.completionFuture = new CompletableFuture<>();}@Overridepublic void run() {try {// CheckpointLock保证线程安全headOperator.run(getCheckpointLock(), getStreamStatusMaintainer(), operatorChain);completionFuture.complete(null);} catch (Throwable t) {// Note, t can be also an InterruptedExceptioncompletionFuture.completeExceptionally(t);}}public void setTaskDescription(final String taskDescription) {setName("Legacy Source Thread - " + taskDescription);}CompletableFuture<Void> getCompletionFuture() {return isFailing() && !isAlive() ? CompletableFuture.completedFuture(null) : completionFuture;}
}

负责为Source生产无界流数据的LegacySourceFunctionThread线程启动后,不管是启动成功 or 出现异常,都会封装对应的Mail并发送给Mailbox,而Mailbox线程的processMail一直在等待处理Mail。

/*** SourceStreamTask中,一个Thread负责专门生产无界流,另一个MailBox Thread处理Checkpoint、ProcessingTime Timer等事件Mail*/
@Override
protected void processInput(MailboxDefaultAction.Controller controller) throws Exception {/*** 通过MailboxDefaultAction.Controller告诉Mailbox:让MailboxThread先暂停处理MailboxDefaultAction。* TaskMailbox收到该消息后,就会在processMail()中一直等待并处理Mail(在MailboxThread中会一直处理Mail)*/controller.suspendDefaultAction();/**启动LegacySourceFunctionThread线程:专门生产Source无界流数据的,和MailboxThread线程一起运行*/sourceThread.setTaskDescription(getName());sourceThread.start();/**LegacySourceFunctionThread线程启动后,会通知Mailbox,Mailbox会在processMail()中一直等待并处理mail(不会返回,即Mailbox线程会一直处理mail)*/sourceThread.getCompletionFuture().whenComplete((Void ignore, Throwable sourceThreadThrowable) -> {/**LegacySourceFunctionThread线程启动过程中发生的任何异常、以及启动成功,都会以Mail的形式发送给Mailbox*/if (isCanceled() && ExceptionUtils.findThrowable(sourceThreadThrowable, InterruptedException.class).isPresent()) {mailboxProcessor.reportThrowable(new CancelTaskException(sourceThreadThrowable));} else if (!isFinished && sourceThreadThrowable != null) {mailboxProcessor.reportThrowable(sourceThreadThrowable);} else {mailboxProcessor.allActionsCompleted();}});
}

Mailbox主线程和LegacySourceFunctionThread线程线程都在运行,通过CheckpointLock锁来保证线程安全。

相关文章:

深入理解Flink Mailbox线程模型

文章目录 整体设计processMail1.Checkpoint Tigger2.ProcessingTime Timer Trigger processInput兼容SourceStreamTask 整体设计 Mailbox线程模型通过引入阻塞队列配合一个Mailbox线程的方式&#xff0c;可以轻松修改StreamTask内部状态的修改。Checkpoint、ProcessingTime Ti…...

Docker搭建LNMP运行Wordpress平台

一、项目1.1 项目环境1.2 服务器环境1.3 任务需求 二、Linux 系统基础镜像三、Nginx1、建立工作目录2、编写 Dockerfile 脚本3、准备 nginx.conf 配置文件4、生成镜像5、创建自定义网络6、启动镜像容器7、验证 nginx 四、Mysql1、建立工作目录2、编写 Dockerfile3、准备 my.cnf…...

10个常见渐变交互效果

1、透明度渐变背景交互 <div class"fade-background"></div> Copy .fade-background {width: 200px;height: 200px;background: linear-gradient(to bottom, rgba(255, 0, 0, 0), rgba(255, 0, 0, 1));transition: background 0.5s ease; }.fade-backgro…...

[线程/C]基础

文章目录 1. 线程介绍2. 创建线程2.1 线程函数2.2 创建线程 3. 线程退出4. 线程回收4.1 线程函数4.2 回收子线程数据4.2.1 使用子线程栈4.2.2 使用全局变量4.2.3 使用主线程栈 5. 线程分离6. 其他线程函数6.1 线程取消6.2 线程ID的比较 1. 线程介绍 线程是轻量级的进程&#x…...

Spring Clould 负载均衡 - Ribbon

视频地址&#xff1a;微服务&#xff08;SpringCloudRabbitMQDockerRedis搜索分布式&#xff09; Ribbon-负载均衡原理&#xff08;P14&#xff09; 具体实现时通过LoaBalanced注解实现&#xff0c;表示RestTemplate要被Ribbon拦截处理 orderservice调用user时候&#xff0c…...

活用DNS技术实现相同IP的不同端口映射不同域名

WindowsDNS基本配置 在内网的 Windows 服务器环境中&#xff0c;你可以通过配置 DNS 服务和 Web 服务器来实现所需的域名解析和端口转发。如下是一些基本的步骤来实现配置&#xff1a; 1&#xff0c;配置 Windows DNS 服务 在你的 Windows 服务器上配置 DNS 服务&#xff0c…...

AutoHotkey:定时删除目录下指定分钟以前的文件,带UI界面

删除指定目录下&#xff0c;所有在某个指定分钟以前的文件&#xff0c;可以用来清理经常生成很多文件的目录&#xff0c;但又需要保留最新的一部分文件 支持拖放目录到界面 能够记忆设置&#xff0c;下次启动后不用重新设置&#xff0c;可以直接开始 应用场景比如&#xff1a…...

一文学会sklearn中的交叉验证的方法

前言 在机器学习中&#xff0c;我们经常需要评估模型的性能。而为了准确评估模型的性能&#xff0c;我们需要使用一种有效的评估方法。五折交叉验证&#xff08;5-fold cross-validation&#xff09;就是其中一种常用的模型评估方法&#xff0c;用于评估机器学习模型的性能和泛…...

【MySQL面试题(66道)】

文章目录 MySQL面试题(66道)基础1.什么是内连接、外连接、交叉连接、笛卡尔积呢&#xff1f;2.那 MySQL 的内连接、左连接、右连接有有什么区别&#xff1f;3.说一下数据库的三大范式&#xff1f;4.varchar 与 char 的区别&#xff1f;5.blob 和 text 有什么区别&#xff1f;6.…...

CSSCI、北核期刊投稿指南(2023年更新)

该数据为经管类的期刊投稿指南&#xff0c;包含发表难度&#xff0c;文章数量&#xff0c;影响因子&#xff0c;用户评价等指标。共5份文件&#xff0c;分别为国内所有期刊信息库、投稿指南&#xff08;CSSCI版本、CSSCI扩展版本、北大核刊版本、建议期刊版本&#xff09; 一、…...

构建 NodeJS 影院微服务并使用 docker 部署它(02/4)

一、说明 构建一个微服务的电影网站&#xff0c;需要Docker、NodeJS、MongoDB&#xff0c;这样的案例您见过吗&#xff1f;如果对此有兴趣&#xff0c;您就继续往下看吧。 图片取自网络 — 封面由我制作 这是✌️“构建 NodeJS 影院微服务”系列的第二篇文章。 二、对第一部分的…...

HTML <style> 标签

实例 <html> <head> <style type="text/css"> h1 {color:red} p {color:blue} </style> </head><body> <h1>Header 1</h1> <p>A paragraph.</p> </body> </html>定义和用法 <style>…...

设计模式——迪米特法则

文章目录 基本介绍应用实例应用实例改进迪米特法则注意事项和细节 基本介绍 一个对象应该对其他对象保持最少的了解类与类关系越密切&#xff0c;耦合度越大迪米特法则(Demeter Principle)又叫最少知道原则&#xff0c;即一个类对自己依赖的类知道的越少越好。也就是说&#x…...

区块链基本概念与当前生态简介

区块链是一种去中心化的分布式账本技术&#xff0c;它通过将数据按照时间顺序链接成区块&#xff0c;并使用密码学算法确保数据的安全性和完整性。每个区块包含一定数量的交易记录&#xff0c;而且每个区块都包含了前一个区块的哈希值&#xff0c;这样形成了一个不可篡改的链式…...

mac安装lrzsz出错Command failed with exit 128: git

终端检查电脑是否安装了rz和sz which sz若报错&#xff0c;则需要下载。由于网络和代理的原因&#xff0c;以下命令会报错&#xff1a; brew install lrzsz是因为brew和git配置的代理存在冲突&#xff0c;对于无外网链接功能&#xff0c;无特殊配置的git而言&#xff0c;需要…...

“深入探索JVM内部机制:揭秘Java虚拟机“

标题&#xff1a;深入探索JVM内部机制&#xff1a;揭秘Java虚拟机 摘要&#xff1a;本文将深入探索Java虚拟机&#xff08;JVM&#xff09;的内部机制&#xff0c;从内存管理、垃圾回收、即时编译等方面进行详细剖析。通过了解JVM的工作原理&#xff0c;我们可以更好地理解Jav…...

lvs-DR

lvs-DR数据包流向分析 client向目标VIP发出请求。 DIR根据负载均衡算法一台active的RS&#xff08;RIR1&#xff09;&#xff0c;将RIP1所在的网卡的mac地址作为目标的mac地址&#xff0c;发送到局域网里。 RIRI在局域网中的收到这个帧&#xff0c;拆开后发现目标&#xff08…...

Vue 项目运行 npm install 时,卡在 sill idealTree buildDeps 没有反应

解决方法&#xff1a;切换到淘宝镜像。 以下是之前安装的 xmzs 包&#xff0c;用于控制切换淘宝镜像。 该截图是之前其他项目切换淘宝镜像的截图。 切换镜像后&#xff0c;顺利执行 npm install 。...

ShardingSphere介绍

ShardingSphere从4.X到5.X的内容发生了很多的改变&#xff0c;感兴趣的伙伴可以到ShardingSphere的博客查看各个版本的新特性。https://blog.csdn.net/ShardingSphere?typeblog 此次使用最新版本 shardingShpere5.4.0&#xff0c;实现数据库读写分离、数据分片、分布式事务等…...

【SA8295P 源码分析】44 - 如何替换 NON-HLOS.bin 中的 Wifi Firmware 固件

【SA8295P 源码分析】44 - 如何替换 NON-HLOS.bin 中的 Wifi Firmware 固件 1、提取 NON-HLOS.bin 中的 Wifi Firmware 出来2、把提取出来的 wifi 固件放到代码中3、重新打包生成 NON-HLOS.bin4、将生成的 NON-HLOS.bin 与 老的 NON-HLOS.bin 对比5、使用fastboot 下载测试wifi…...

市面上那里有稳定L2股票行情数据接口?

随着市场的发展和技术的进步&#xff0c;level2股票行情数据接口已经成为股票交易软件的标准配置之一。虽然这些券商软件的功能在很大程度上相似&#xff0c;但它们仍然有自己的特点和优势。 例如&#xff1a;通过股票交易所以其专业的研究报告和丰富的信息服务而受到广泛关注&…...

个人信息保护影响评估(PIA)怎么做?解发条件、实施步骤、操作指南

个人信息保护一直是人们关注的热点话题&#xff0c;互联网、人工智能、大数据等新兴技术的快速发展极大地增强了入侵个人信息的能力&#xff0c;对个人信息的随意收集、违法获取、过度使用、非法买卖、泄露等问题引起了全球各国的普遍关注。同时随着用户的个人信息保护意识的逐…...

HTML <sub> 标签

例子 这段文本包含 <sub>下标</sub> 定义和用法 <sub> 标签可定义下标文本。 包含在 <sub> 标签和其结束标签 </sub> 中的内容将会以当前文本流中字符高度的一半来显示&#xff0c;但是与当前文本流中文字的字体和字号都是一样的。 提示&am…...

C# 设置、获取程序,产品版本号

右键&#xff0c;程序属性。打开“程序集信息” 选择需要设置的版本信息。下面的代码&#xff0c;获取不同的设置内容。 string 其他 Assembly.GetExecutingAssembly().FullName; string 程序集版本 Assembly.GetExecutingAssembly().G…...

LeetCode 面试题 01.04. 回文排列

文章目录 一、题目二、C# 题解 一、题目 给定一个字符串&#xff0c;编写一个函数判定其是否为某个回文串的排列之一。 回文串是指正反两个方向都一样的单词或短语。排列是指字母的重新排列。 回文串不一定是字典当中的单词。 点击此处跳转题目。 示例1&#xff1a; 输入&…...

CentOS 7 安装MySQL8.0.33

一、查看 CentOS 版本 要查看当前 CentOS 版本&#xff0c;你可以执行以下命令&#xff1a; cat /etc/centos-release 该命令将显示当前 CentOS 的版本信息&#xff0c;例如&#xff1a; CentOS Linux release 7.9.2009 (Core) 在这个示例中&#xff0c;CentOS 版本为 7.…...

OpenCV(二)——图像基本处理(四)

目录 4.图像形态学操作 4.1 图像腐蚀 4.2 图像膨胀 4.3 开运算 4.4 闭运算...

11.小程序的配置项

window导航配置 全局配置通过 app.json进行 “window”: { “backgroundTextStyle”: “light”, “navigationBarBackgroundColor”: “#fff”, “navigationBarTitleText”: “Weixin”, “navigationBarTextStyle”: “black” }, 局部配置通过页面的xx.json配置 { “navig…...

一文科普,配资门户网是什么?

配资门户网是一个为投资者提供配资服务的平台。配资是指通过借用他人资金进行投资交易的一种金融操作方式。配资门户网作为一个线上平台&#xff0c;为投资者提供了方便、快捷的配资服务。 配资门户网提供了多种不同的配资方案&#xff0c;以满足不同投资者的需求。投资者可以…...

编写一个俄罗斯方块

编写俄罗斯方块 思路。 1、创建容器数组&#xff0c;方块&#xff0c; 2、下落&#xff0c;左右移动&#xff0c;旋转&#xff0c;判断结束&#xff0c;消除。 定义一个20行10列的数组表示游戏区。初始这个数组里用0填充&#xff0c;1表示有一个方块&#xff0c;2表示该方块固…...

dw软件网站建设教程视频/seo短视频保密路线

1、先在数据库中新建一个数据库2、配置ODBC连接点击Database --> 配置连接(Configure Connection) --> 选择系统DSN选项卡 --> 按添加按钮 --> 选择SQL Server数据库 --> 设置名称并选择当前服务器--> 使用Windows验证 --> 更改默认数据库为第一步建立的数…...

贵州贵阳网站开发/如何推广网站运营

今天给大家介绍一下经典的开源机器学习软件&#xff1a; 编程语言&#xff1a;搞实验个人认为当然matlab最灵活了&#xff08;但是正版很贵&#xff09;&#xff0c;但是更为前途的是Python&#xff08;numpyscipymatplotlib)和C/C&#xff0c;这样组合既可搞研究&#xff0c;也…...

建设网站是什么意思/百度服务中心人工24小时电话

首先登陆mysql的交互shell&#xff0c;输入下面的命令查看当前数据库的编码方式 show variables like %character%; 从上面可以看出&#xff0c;mysql数据库装上之后初始并不均是utf8 退出刚才的交互shell&#xff0c;去修改下面的配置文件 输入下面的命令&#xff0c;打开第一…...

学院的网站建设的er图怎么画/百度网站

公众号关注 “GitHubDaily”设为 “星标”&#xff0c;每天带你逛 GitHub&#xff01;转自机器之心LaTex 是很多人在写论文时使用的方便工具&#xff0c;但是如何将书本上的公式直接转换为 LaTex 格式呢&#xff1f;近日&#xff0c;一位中国开发者开源了这样一款工具。用户可以…...

阅读网站怎样做/网络销售平台有哪些软件

前言 一名DBA的经历&#xff0c;做自己想做的&#xff0c;永不放弃&#xff0c;感谢生命中的贵人&#xff0c;我的师傅带我入行&#xff0c;感谢CSDN平台&#xff0c;让我分享更多DBA的干货 文章目录前言一、为什么要转行&#xff1f;1.DBA行业有哪些优势2.我转行的原因3.DBA我…...

保定网络公司建设网站/电脑优化大师有用吗

首先写好一个列表写好的样式是这样滴操作来了在computed里面定义了一个search函数 使用filter过滤接下来在method 里面写一个sousuo1函数 进行一个判断 如果搜索这个输入框框里是空 就是展示原数据 如果这个不为空 就会展示搜索到的数据最后 很重要把list改为sousuo1()这个函数…...