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

pg_cron优化案例--terminate pg_cron launcher可自动拉起

场景

在PostgreSQL中我们可以使用pg_cron来实现数据库定时任务

我有一个select 1的定时任务,每分钟触发一次

testdb=# select * from cron.job ;jobid |  schedule   | command  | nodename  | nodeport | database | username | active |    jobname    
-------+-------------+----------+-----------+----------+----------+----------+--------+---------------2 | */1 * * * * | select 1 | localhost |     1142 | testdb   | admin    | t      | manual active
(1 row)
testdb=#

从执行记录来看从某个时刻开始不执行了

testdb=# select * from cron.job_run_details where jobid='2';jobid | runid | job_pid | database | username | command  |  status   | return_message |          start_time           |           end_time            
-------+-------+---------+----------+----------+----------+-----------+----------------+-------------------------------+-------------------------------2 |     3 |   29616 | testdb   | admin    | select 1 | succeeded | 1 row          | 2023-02-08 22:37:00.014232+08 | 2023-02-08 22:37:00.015855+082 |     4 |   29772 | testdb   | admin    | select 1 | succeeded | 1 row          | 2023-02-08 22:38:00.010803+08 | 2023-02-08 22:38:00.012029+082 |     5 |   29995 | testdb   | admin    | select 1 | succeeded | 1 row          | 2023-02-08 22:39:00.013508+08 | 2023-02-08 22:39:00.015362+08
(3 rows)testdb=# select now();now             
-----------------------------2023-02-13 11:11:10.7302+08
(1 row)testdb=# 

从日志来看是pg_cron launcher进程shutdown了,pg_cron launcher是job的调度进程,当它停止了,任务也就不调度了。

2023-02-08 22:17:04.788 CST,,,25712,,63e3aee0.6470,1,,2023-02-08 22:17:04 CST,2/0,0,LOG,00000,"pg_cron scheduler started",,,,,,,,,"","pg_cron launcher",,0
2023-02-08 22:37:00.008 CST,,,25712,,63e3aee0.6470,2,,2023-02-08 22:17:04 CST,2/0,0,LOG,00000,"cron job 2 starting: select 1",,,,,,,,,"","pg_cron launcher",,0
2023-02-08 22:37:00.017 CST,,,25712,,63e3aee0.6470,3,,2023-02-08 22:17:04 CST,2/0,0,LOG,00000,"cron job 2 completed: 1 row",,,,,,,,,"","pg_cron launcher",,0
2023-02-08 22:38:00.006 CST,,,25712,,63e3aee0.6470,4,,2023-02-08 22:17:04 CST,2/0,0,LOG,00000,"cron job 2 starting: select 1",,,,,,,,,"","pg_cron launcher",,0
2023-02-08 22:38:00.013 CST,,,25712,,63e3aee0.6470,5,,2023-02-08 22:17:04 CST,2/0,0,LOG,00000,"cron job 2 completed: 1 row",,,,,,,,,"","pg_cron launcher",,0
2023-02-08 22:39:00.006 CST,,,25712,,63e3aee0.6470,6,,2023-02-08 22:17:04 CST,2/0,0,LOG,00000,"cron job 2 starting: select 1",,,,,,,,,"","pg_cron launcher",,0
2023-02-08 22:39:00.017 CST,,,25712,,63e3aee0.6470,7,,2023-02-08 22:17:04 CST,2/0,0,LOG,00000,"cron job 2 completed: 1 row",,,,,,,,,"","pg_cron launcher",,0
2023-02-08 22:39:54.618 CST,,,25712,,63e3aee0.6470,8,,2023-02-08 22:17:04 CST,2/0,0,LOG,00000,"pg_cron scheduler shutting down",,,,,,,,,"","pg_cron launcher",,0

总的来说就是实例未停止的情况下,pg_cron launcher shutdown导致job未调度。

分析

从代码来看当pg_cron launcher 收到SIGTERM后退出时会打印"pg_cron scheduler shutting down"这条日志。

/** PgCronLauncherMain is the main entry-point for the background worker* that performs tasks.*/
void
PgCronLauncherMain(Datum arg)
{MemoryContext CronLoopContext = NULL;struct rlimit limit;/* Establish signal handlers before unblocking signals. */pqsignal(SIGHUP, pg_cron_sighup);pqsignal(SIGINT, SIG_IGN);pqsignal(SIGTERM, pg_cron_sigterm);/* We're now ready to receive signals */BackgroundWorkerUnblockSignals();/* 省略部分代码行 *//* 当未接收到SIGTERM时一直在while循环中 */while (!got_sigterm){/* 省略部分代码行 */	}/* 那么当接收到SIGTERM时,打印日志并exit(0)退出 */ereport(LOG, (errmsg("pg_cron scheduler shutting down")));proc_exit(0);
}

由此得知,在实例shutdown或者使用select pg_terminate_backend() 终止pg_cron launcher这两种场景下会打印对应的日志,看起来我这个实例的pg_cron launcher就是被 pg_terminate_backend()函数终止了。

相比其他bgworker比如logical replication launcher,当进程被pg_terminate_backend() 终止后,postmaster会检测到并且再次拉起该进程。为什么pg_cron launcher被SIGTERM终止后,没有被再次拉起呢?

这里其实就在于对进程退出的处理不同。

可以看到PgCronLauncherMain中当接收到SIGTERM时,打印日志后proc_exit(0)退出。

而logical replication launcher这里的实现,SIGTERM注册的处理函数是die,当接收到SIGTERM信号后除了setlatch wakeup进程,还会将Interrupt的全局flag置为ture 进入CHECK_FOR_INTERRUPTS()中执行对应的报错逻辑,最终进程会走FATAL报错退出,可以看到errfinish中对于FATAL错误的处理就是调用proc_exit(1)退出进程。

注册信号处理函数

/** Main loop for the apply launcher process.*/
void
ApplyLauncherMain(Datum main_arg)
{/* 省略部分代码行 *//* Establish signal handlers. */pqsignal(SIGHUP, SignalHandlerForConfigReload);/* 注册SIGTERM处理函数为die */pqsignal(SIGTERM, die);BackgroundWorkerUnblockSignals();/* 省略部分代码行 *//* Enter main loop */for (;;){/* 省略部分代码行 *//* Wait for more work. */rc = WaitLatch(MyLatch,WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,wait_time,WAIT_EVENT_LOGICAL_LAUNCHER_MAIN);if (rc & WL_LATCH_SET){   /* 当进程wakeup,则检测是否发生INTERRUPT */ResetLatch(MyLatch);CHECK_FOR_INTERRUPTS();}if (ConfigReloadPending){ConfigReloadPending = false;ProcessConfigFile(PGC_SIGHUP);}}/* Not reachable */
}

ProcessInterrupts中对bgworker的处理

/** ProcessInterrupts: out-of-line portion of CHECK_FOR_INTERRUPTS() macro** If an interrupt condition is pending, and it's safe to service it,* then clear the flag and accept the interrupt.  Called only when* InterruptPending is true.** Note: if INTERRUPTS_CAN_BE_PROCESSED() is true, then ProcessInterrupts* is guaranteed to clear the InterruptPending flag before returning.* (This is not the same as guaranteeing that it's still clear when we* return; another interrupt could have arrived.  But we promise that* any pre-existing one will have been serviced.)*/
void
ProcessInterrupts(void)
{/* OK to accept any interrupts now? */if (InterruptHoldoffCount != 0 || CritSectionCount != 0)return;InterruptPending = false;if (ProcDiePending){/* 省略部分代码行 */else if (IsBackgroundWorker)ereport(FATAL,(errcode(ERRCODE_ADMIN_SHUTDOWN),errmsg("terminating background worker \"%s\" due to administrator command",MyBgworkerEntry->bgw_type)));/* 省略部分代码行 */}/* 省略部分代码行 */
}

errfinish中对于FATAL错误的处理

/** errfinish --- end an error-reporting cycle** Produce the appropriate error report(s) and pop the error stack.** If elevel, as passed to errstart(), is ERROR or worse, control does not* return to the caller.  See elog.h for the error level definitions.*/
void
errfinish(const char *filename, int lineno, const char *funcname)
{/* 省略部分代码行 *//** Perform error recovery action as specified by elevel.*/if (elevel == FATAL){/** For a FATAL error, we let proc_exit clean up and exit.** If we just reported a startup failure, the client will disconnect* on receiving it, so don't send any more to the client.*/if (PG_exception_stack == NULL && whereToSendOutput == DestRemote)whereToSendOutput = DestNone;/** fflush here is just to improve the odds that we get to see the* error message, in case things are so hosed that proc_exit crashes.* Any other code you might be tempted to add here should probably be* in an on_proc_exit or on_shmem_exit callback instead.*/fflush(stdout);fflush(stderr);/** Let the statistics collector know. Only mark the session as* terminated by fatal error if there is no other known cause.*/if (pgStatSessionEndCause == DISCONNECT_NORMAL)pgStatSessionEndCause = DISCONNECT_FATAL;/** Do normal process-exit cleanup, then return exit code 1 to indicate* FATAL termination.  The postmaster may or may not consider this* worthy of panic, depending on which subprocess returns it.*/proc_exit(1);}/* 省略部分代码行 */
}

在C语言中exit(0)表示的是程序正常退出,exit(1)则为异常退出。

当子进程退出时,会向父进程Postmaster发送SIGCHLD信号,postmaster注册了这个信号的信号处理函数reaper,通过waitpid去回收子进程,并做一些处理。
那么在Postmaster进程的serverLoop主循环里会检测子进程状态判断是否需要拉起子进程,以bgworker这种为例,在maybe_start_bgworkers里获取BackgroundWorkerList读取对应bgworker信息,
如果对应的bgworker是正常退出的,那么则不在这个列表中,因此不会拉起。
当bgworker是异常退出,对应信息会保留在BackgroundWorkerList里,但是当前的pid为0,因此就会将其拉起。

maybe_start_bgworkers的处理逻辑

/** If the time is right, start background worker(s).** As a side effect, the bgworker control variables are set or reset* depending on whether more workers may need to be started.** We limit the number of workers started per call, to avoid consuming the* postmaster's attention for too long when many such requests are pending.* As long as StartWorkerNeeded is true, ServerLoop will not block and will* call this function again after dealing with any other issues.*/
static void
maybe_start_bgworkers(void)
{
#define MAX_BGWORKERS_TO_LAUNCH 100int			num_launched = 0;TimestampTz now = 0;slist_mutable_iter iter;/** During crash recovery, we have no need to be called until the state* transition out of recovery.*/if (FatalError){StartWorkerNeeded = false;HaveCrashedWorker = false;return;}/* Don't need to be called again unless we find a reason for it below */StartWorkerNeeded = false;HaveCrashedWorker = false;/* 这里对BackgroundWorkerList进行遍历,看是否有bgworker需要start */slist_foreach_modify(iter, &BackgroundWorkerList){RegisteredBgWorker *rw;rw = slist_container(RegisteredBgWorker, rw_lnode, iter.cur);/* 跳过pid非0的进程,这些bgworker已启动。*//* ignore if already running */if (rw->rw_pid != 0)continue;/* 省略部分代码行 *//* 这里拉起这些pid为0的bgworker */if (!do_start_bgworker(rw)){StartWorkerNeeded = true;return;}/* 省略部分代码行 */
}

按照这里的逻辑在PgCronLauncherMain中当接收到SIGTERM使用proc_exit(1)退出那就可以再次拉起了。

方案

给插件加入一个参数,打开参数后,当pg_terminate_backend() pg_cron launcher后就会自动被拉起

DefineCustomBoolVariable("cron.enable_autostart_launcher",gettext_noop("Allow postmaster to pull pg_cron launcher process when it is terminated by SIGTERM."),NULL,&EnableAutostartLauncher,false,PGC_POSTMASTER,GUC_SUPERUSER_ONLY,NULL, NULL, NULL);

对于进程退出的处理,打开参数时exit(1)

void
PgCronLauncherMain(Datum arg)
{MemoryContext CronLoopContext = NULL;struct rlimit limit;/* Establish signal handlers before unblocking signals. */pqsignal(SIGHUP, pg_cron_sighup);pqsignal(SIGINT, SIG_IGN);pqsignal(SIGTERM, pg_cron_sigterm);/* We're now ready to receive signals */BackgroundWorkerUnblockSignals();/* 省略部分代码行 *//* 当未接收到SIGTERM时一直在while循环中 */while (!got_sigterm){/* 省略部分代码行 */	}ereport(LOG, (errmsg("pg_cron scheduler shutting down")));/* Modify by Nickyong at 2023-02-13 PM *//* 如果cron.enable_autostart_launcher ='on' ,则proc_exit(1),否则 proc_exit(0) */if (EnableAutostartLauncher)proc_exit(1);elseproc_exit(0);/* End at 2023-02-13 PM */
}

验证

参数展示

testdb=# select * from pg_settings where name='cron.enable_autostart_launcher';
-[ RECORD 1 ]---+------------------------------------------------------------------------------------
name            | cron.enable_autostart_launcher
setting         | off
unit            | 
category        | Customized Options
short_desc      | Allow postmaster to pull pg_cron launcher process when it is terminated by SIGTERM.
extra_desc      | 
context         | postmaster
vartype         | bool
source          | configuration file
min_val         | 
max_val         | 
enumvals        | 
boot_val        | off
reset_val       | off
sourcefile      | /data/pg14-2debug/master/postgresql.auto.conf
sourceline      | 3
pending_restart | f

默认关闭

testdb=# show cron.enable_autostart_launcher;
-[ RECORD 1 ]------------------+----
cron.enable_autostart_launcher | off

terminate pg_cron launcher后没自动拉起

testdb=# select * from pg_stat_activity where backend_type like '%pg_cron launcher%' ;
-[ RECORD 1 ]----+-------------------------------------------------------------------------------------------------
datid            | 24589
datname          | testdb
pid              | 23893
leader_pid       | 
usesysid         | 10
usename          | postgres
application_name | pg_cron scheduler
client_addr      | 
client_hostname  | 
client_port      | 
backend_start    | 2023-02-13 19:22:16.062689+08
xact_start       | 
query_start      | 2023-02-13 19:23:10.023643+08
state_change     | 2023-02-13 19:23:10.025066+08
wait_event_type  | Extension
wait_event       | Extension
state            | idle
backend_xid      | 
backend_xmin     | 
query_id         | 
query            | update cron.job_run_details set status = $1, return_message = $2, end_time = $3 where runid = $4
backend_type     | pg_cron launchertestdb=# select pg_terminate_backend(23893);
-[ RECORD 1 ]--------+--
pg_terminate_backend | ttestdb=# select * from pg_stat_activity where backend_type like '%pg_cron launcher%' ;
(0 rows)testdb=#

打开参数

testdb=# show cron.enable_autostart_launcher;
-[ RECORD 1 ]------------------+---
cron.enable_autostart_launcher | on

terminate pg_cron launcher后自动拉起

testdb=# select * from pg_stat_activity where backend_type like '%pg_cron launcher%' ;
-[ RECORD 1 ]----+-------------------------------------------------------------------------------------------------
datid            | 24589
datname          | testdb
pid              | 24125
leader_pid       | 
usesysid         | 10
usename          | postgres
application_name | pg_cron scheduler
client_addr      | 
client_hostname  | 
client_port      | 
backend_start    | 2023-02-13 19:23:59.601739+08
xact_start       | 
query_start      | 2023-02-13 19:24:10.019018+08
state_change     | 2023-02-13 19:24:10.020397+08
wait_event_type  | Extension
wait_event       | Extension
state            | idle
backend_xid      | 
backend_xmin     | 
query_id         | 
query            | update cron.job_run_details set status = $1, return_message = $2, end_time = $3 where runid = $4
backend_type     | pg_cron launchertestdb=# select pg_terminate_backend(24125);
-[ RECORD 1 ]--------+--
pg_terminate_backend | ttestdb=# select * from pg_stat_activity where backend_type like '%pg_cron launcher%' ;
-[ RECORD 1 ]----+------------------------------------------------------------------------------------------------------------------------------
datid            | 24589
datname          | testdb
pid              | 24329
leader_pid       | 
usesysid         | 10
usename          | postgres
application_name | pg_cron scheduler
client_addr      | 
client_hostname  | 
client_port      | 
backend_start    | 2023-02-13 19:24:54.976542+08
xact_start       | 
query_start      | 2023-02-13 19:24:54.978153+08
state_change     | 2023-02-13 19:24:54.981451+08
wait_event_type  | Extension
wait_event       | Extension
state            | idle
backend_xid      | 
backend_xmin     | 
query_id         | 
query            | update cron.job_run_details set status = 'failed', return_message = 'server restarted' where status in ('starting','running')
backend_type     | pg_cron launchertestdb=#

小结

pg_cron的作者并没有说明这样设计的原因,我猜测是预留了一个可以强制停止所有job的入口。
如果job对于业务来说比较重要,希望被终止后可以自动拉起,以免job不调度造成一些损失,个人感觉可以做成参数来控制的方式。默认关闭,打开参数当pg_terminate_backend() 后可以自动拉起,虽然重启实例也能再次拉起pg_cron launcher,但并不是任何时候都可以重启实例的。

相关文章:

pg_cron优化案例--terminate pg_cron launcher可自动拉起

场景 在PostgreSQL中我们可以使用pg_cron来实现数据库定时任务 我有一个select 1的定时任务,每分钟触发一次 testdb# select * from cron.job ;jobid | schedule | command | nodename | nodeport | database | username | active | jobname -------…...

Python 之 NumPy 随机函数和常用函数

文章目录一、随机函数1. numpy.random.rand(d0,d1,…,dn)2. numpy.random.randn(d0,d1,…,dn)3. numpy.random.normal()4. numpy.random.randint()5. numpy.random.sample6. 随机种子np.random.seed()7. 正态分布 numpy.random.normal二、数组的其他函数1. numpy.resize()2. nu…...

【目标检测】K-means和K-means++计算anchors结果比较(附完整代码,全网最详细的手把手教程)

写在前面: 首先感谢兄弟们的订阅,让我有创作的动力,在创作过程我会尽最大努力,保证作品的质量,如果有问题,可以私信我,让我们携手共进,共创辉煌。 一、介绍 YOLO系列目标检测算法中基于anchor的模型还是比较多的,例如YOLOv3、YOLOv4、YOLOv5等,我们可以随机初始化a…...

Java高手速成 | 图说重定向与转发

我们先回顾一下Servlet的工作原理,Servlet的工作原理跟小猪同学食堂就餐的过程很类似。小猪同学点了烤鸡腿(要奥尔良风味的),食堂窗口的服务员记下了菜单,想了想后厨的所有厨师,然后将菜单和餐盘交给专门制…...

Git:不小心在主分支master上进行修改,怎么才能将修改的数据保存到正确的分支中

1.如果还没有push commit 代码第一步:将所修改的代码提交到暂存区git stash第二步:切换到正确的分支git checkout 分支名第三步:从暂存区中取出保存到正确的分支中git stash pop第四步:重新提交git push origin 分支名2.如果已经p…...

都2023年了,如果不会Stream流、函数式编程?你确定能看懂公司代码?

👳我亲爱的各位大佬们好😘😘😘 ♨️本篇文章记录的为 Stream流、函数式编程 相关内容,适合在学Java的小白,帮助新手快速上手,也适合复习中,面试中的大佬🙉🙉🙉。 ♨️如果…...

亚马逊云科技汽车行业解决方案

当今,随着万物智联、云计算等领域的高速发展,创新智能网联汽车和车路协同技术正在成为车企加速发展的关键途径,推动着汽车产品从出行代步工具向着“超级智能移动终端”快速转变。 挑战无处不在,如何抢先预判? 随着近…...

为什么学了模数电还是看不懂较复杂的电路图

看懂电路并不难。 (1) 首先要摆正心态,不要看到错综复杂的电路图就一脸懵逼,不知所错。你要明白,再复杂的电路也是由一个个的基本电路拼装出来的。 (2) 基础知识当然是少不了的,常用的基本电路结构搞搞清楚。 (3) 分析电路之前先要…...

帮公司面试了一个30岁培训班出来的程序员,没啥工作经验...

首先,我说一句:培训出来的,优秀学员大有人在,我不希望因为带着培训的标签而无法达到用人单位和候选人的双向匹配,是非常遗憾的事情。 最近,在网上看到这样一个留言,引发了程序员这个圈子不少的…...

勒索软件、网络钓鱼、零信任和网络安全的新常态

当疫情来袭时,网络罪犯看到了他们的机会。随着公司办公、政府机构、学校和大学从以往的工作模式转向远程线上办公模式,甚至许多医疗保健设施都转向线上,这种快速的过渡性质导致了不可避免的网络安全漏洞。消费者宽带和个人设备破坏了企业安全…...

python3 字符串拼接与抽取

我们经常会有对字符串进行拼接和抽取的需求,下面有几个例子可以作为参考。 需求1:取出ip地址的网络地址与网络掩码进行拼接,分别使用shell脚本和python3实现 # echo "192.168.0.1"|awk -F. {print $1"."$2"."…...

Linux就该这么学:存储结构与管理硬盘

Linux系统中常见的目录名称以及相应内容 目录名称应放置文件的内容/boot开机所需文件—内核、开机菜单以及所需配置文件等/dev以文件形式存放任何设备与接口/etc配置文件/home用户主目录/bin存放单用户模式下还可以操作的命令/lib开机时用到的函数库,以及/bin与/sbin下面的命令…...

JSP四大作用域,九大内置对象

面试题:JSP和Servlet的区别?JSP的本质就是servleJSP更加侧重于视图的展示,servlet更注重逻辑的处理。面试题:include指令和jsp:include标签的区别?从效果上来说,没区别。include指令是把两个页面合成一个js…...

机器学习笔记之生成模型综述(五)重参数化技巧(随机反向传播)

机器学习笔记之生成模型综述——重参数化技巧[随机反向传播]引言回顾神经网络的执行过程变分推断——重参数化技巧重参数化技巧(随机反向传播)介绍示例描述——联合概率分布示例描述——条件概率分布总结引言 本节将系统介绍重参数化技巧。 回顾 神经网络的执行过程 上一节…...

1、创建第一个Android项目

1.1、创建Android工程项目:双击打开Android Studio。在菜单栏File中new-->new project3、在界面中选择Empty Activity,然后选择next4、在下面界面中修改工程名称,工程保存路径选择java语言,然后点击finishAndroid studio自动为…...

【python百炼成魔】手把手带你学会python数据类型

文章目录前言一. python的基本数据类型1.1 如何查看数据类型1.2 数值数据类型1.2.1 整数类型1.2.2 浮点数类型1.2.3 bool 布尔数值类型1.2.4 字符串类型二. 数据类型强制转换2.1 强制转换为字符串类型2.2 强制转换为int类型2.3 强制转换函数之float() 函数三. 拓展几个运算函数…...

数据储存以及大小端判断

目录 数据存储 1,二进制存储方式(补码,反码,源码) 2,指针类型 3,大端,小段判断 1,二进制存储方式(补码,反码,源码) 我…...

GRASP设计原则

GRASP设计原则介绍9种基本原则创建者 Creator问题解决方法何时不使用?好处信息专家 Information Expert问题解决方法信息怎么做优点低耦合 Low Coupling耦合问题解决方法原则何时不使用?控制器 Controller问题解决方法外观控制器会话控制器优点臃肿控制器的解决方法高内聚 Hi…...

再遇周杰伦隐私协议

本隐私信息保护政策版本:2021 V1 一、重要提示 请您(以下亦称“用户”)在使用本平台App时仔细阅读本协议之全部条款,并确认您已完全理解本协议之规定,尤其是涉及您的重大权益及义务的加粗或划线条款。如您对协议有任…...

关于项目上的一些小操作记录

一 如何在项目的readme.md文件中插入图片说明 1 准备一张图片命名为test.png 2 在maven项目的resources目录下新建文件夹picture,将图片放入该目录下 3 在readme.md文件中期望插入图片的地方编辑如下: ![Alt](src/main/resources/picture/test.png) 此时&#…...

练习(含atoi的模拟实现,自定义类型等练习)

一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...

相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)

【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...

【HTML-16】深入理解HTML中的块元素与行内元素

HTML元素根据其显示特性可以分为两大类:块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...

【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)

1.获取 authorizationCode: 2.利用 authorizationCode 获取 accessToken:文档中心 3.获取手机:文档中心 4.获取昵称头像:文档中心 首先创建 request 若要获取手机号,scope必填 phone,permissions 必填 …...

Element Plus 表单(el-form)中关于正整数输入的校验规则

目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入&#xff08;联动&#xff09;2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...

Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)

在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马&#xff08;服务器方面的&#xff09;的原理&#xff0c;连接&#xff0c;以及各种木马及连接工具的分享 文件木马&#xff1a;https://w…...

以光量子为例,详解量子获取方式

光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学&#xff08;silicon photonics&#xff09;的光波导&#xff08;optical waveguide&#xff09;芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中&#xff0c;光既是波又是粒子。光子本…...

tomcat入门

1 tomcat 是什么 apache开发的web服务器可以为java web程序提供运行环境tomcat是一款高效&#xff0c;稳定&#xff0c;易于使用的web服务器tomcathttp服务器Servlet服务器 2 tomcat 目录介绍 -bin #存放tomcat的脚本 -conf #存放tomcat的配置文件 ---catalina.policy #to…...

消息队列系统设计与实践全解析

文章目录 &#x1f680; 消息队列系统设计与实践全解析&#x1f50d; 一、消息队列选型1.1 业务场景匹配矩阵1.2 吞吐量/延迟/可靠性权衡&#x1f4a1; 权衡决策框架 1.3 运维复杂度评估&#x1f527; 运维成本降低策略 &#x1f3d7;️ 二、典型架构设计2.1 分布式事务最终一致…...

Android写一个捕获全局异常的工具类

项目开发和实际运行过程中难免会遇到异常发生&#xff0c;系统提供了一个可以捕获全局异常的工具Uncaughtexceptionhandler&#xff0c;它是Thread的子类&#xff08;就是package java.lang;里线程的Thread&#xff09;。本文将利用它将设备信息、报错信息以及错误的发生时间都…...