状元榜眼探花十大顺序(400005)状元榜眼探花的由来

2022-08-18 20:20:26 基金 xialuotejs

状元榜眼探花十大顺序



本文目录一览:



这几位先后的排名由低到高是这样的:

秀才—举人—贡士—进士—探花—榜眼—状元

古代读书人想求取功名,必经过十年寒窗,参加各级政府举办的考试。分别是:

县试—府试—院试—乡试—会试—殿试。

县府两级属初试,初试合格叫童生。可进入科举考试。

共四级:院试——乡试——会试——殿试四级。

(1)院试考中后称“生员”,也就是秀才。

(2)秀才参加乡试,合格者员叫举人。

(3)会试地点京城,考中后称“贡士”。

(4)贡士可参加殿试,考中了就叫进士。

殿试分三甲:一甲赐进士及第,殿试第一名-状元,二名-叫榜眼,三名-探花。

二甲进士出身,第三甲赐同进士出身。

科举考试,是我国古代国家层层选拔人才及官吏的重要手段。始于隋朝,成熟于唐朝,一直沿用至清朝灭亡。算是中国最早的“高考”。

一、县试、府试

县试与府试算是初选,相当于县、市级的考试,选拔两次。只要是读书人,无论年龄大小,都可以参加。

通过了被称作童生。

基本上相当于高小水平。

有了童生的资格,就可以在求取功名的路上继续前行,进入下一轮的考试。

二、院试

童生可参加由各省学政、提督主持的院试。

院试合格后称为生员,也就是秀才。不合格的仍旧是童生。

很多人都认为秀才水平很一般,影视剧里秀才的形象,往往是贫穷、迂腐的象征。但事实上,能成为秀才的人,已经是那个时代绝对的文化人,所以,有“秀才不出门,便知天下事”的说法。

只是因为秀才与做官无缘,品级不入流,但又放不下文化人的架子,所以给人“穷酸”的感觉。

实际上,秀才在当地也受到一定的尊重。我们可以看到,各地私塾的教书先生,大都是秀才出身。凭自己的能力解决温饱应该不成问题。

三、乡试

乡试,算是科举考试中的大考。一般在秋天(中秋前后)举行,所以又称为“秋闱”,在省城举行。

乡试考试过了,那就是举人了,意味着改变命运的机会来了,做官的几率很大。

一般一个县里,数年才出一个举人。

“范进中举”的故事,大家都知道,范进五十多岁才中举人,结果喜极而疯,可见举人的份量之重,读书人渴求之深。

的确,从“秀才”到“举人”,虽说只差了一个级别,却是人生的大反转。我们看到范进中了秀才以后,虽说吃上了老丈人胡屠户送的肉食,却还是被胡屠夫骂的狗血淋头,上门借钱都被打出来。

后来中了举人,却是吓得老丈人胡屠户唯唯诺诺,人前人后给范进赔笑脸。

所以,“中举”之后,几乎可以一步登天,一夜之间,从草根变土豪。

还有《白鹿原》中的朱举人,即便不能成为高官,也算得上县里的名流,有很高的威望。

四、会试

拥有了举人资格,就可以在乡试的第二年进京赶考了。

会试,在春季举行,每三年一次。由礼部主持,称为“春闱”、“礼闱”。

能够进京赶,进一步深造的,一般都是家境殷实的富家子弟,古代进京赶考路途遥远,耗资巨大,一般家庭难以承担。

举人参加会试,考试通过称为贡士,意思是进贡给天子的士子。

其实这已经是科举考试中最高的级别了,贡士基本就是准进士了。后来皇帝怕主考官徇私舞弊,又加了一道殿试。

五、殿试

殿试又称“御试”、廷试”,算是加试。参加殿试,如果不出大错,基本不会被刷。

殿试最后结果分三等:

第一等,进士及第,共三人,即状元、榜眼、探花。

第二等,赐进士出身,从第四名到第一百名左右都是。

第三等,赐同进士出身,若干名。

中了进士就非常了不得了,多数做到京官。李叔同的父亲李世珍,是同治年间考中的进士,官至吏部主事。

红楼梦里的贾雨村,进京赶考,居然中了进士,转身已是堂堂知府大人。

正可谓:十年寒窗无人问,一举成名天下知”。

有的说,古代进士相当于现在博土后水平,有人说相当于院士一级。有书君认为,比喻虽然通俗、形象,实际上由于古今制度不同,考试科目差异,实际上并没有可比性。

但是有一点是相通的,古往今来,那些莘莘学子,不管是求取功名也好,实现个人抱负也罢,均是一份耕耘 ,一份收获。

科举考试在当时也算是国家选拔人才,最公平的手段了,要不然不会存在几千年。您说呢?




400005

初识 Scheduler

找准定位:分布式任务调度平台

无论是互联网应用或者企业级应用,都充斥着大量的任务。我们常常需要一些任务调度系统帮助我们解决问题。随着微服务化架构的逐步演进,单体架构逐渐演变为分布式、微服务架构。在此的背景下,很多原先的单点式任务调度平台已经不能满足业务系统的需求。于是出现了一些基于分布式的任务调度平台。

Scheduler 是飞书内的分布式任务调度平台。分布式任务调度能力主要包括:

分布式:平台是分布式部署的,各个节点之间可以无状态和无限的水平扩展(保证可扩展);任务调度:涉及到任务状态管理、任务调度请求的发送与接收、具体任务的分配、任务的具体执行;(集群中哪些机器什么时候执行什么任务,所以又需要一个可以感知整个集群运行状态的配置中心);配置中心:可以感知整个集群的状态、任务信息的注册。

摸清脉络:Scheduler 的结构和核心模块

名词解释:

Processor: 编程处理器, 拥有一定的编程规范, 用户自定义实现

Executor: 一个 SDK,运行 Processor 的进行容器,与 Scheduler 通信的载体

Job:用户创建的任务,其中包含任务的调度规则、调度模型、执行器名称等信息

Instance:运行态的 Job,每当 Job 触发后会生成一个 Instance,记录本次执行的调度信息

Task:最小执行单元,不同调度模型的任务产生的 Task 数量不同。

通过架构图可以发现,Scheduler 主要有以下三个部分:

调度器 (Scheduler):任务调度中心,负责管理任务的生命周期。接受任务注册,准时准确找出待触发的任务,进行任务拆分下发。找出与之关联的执行器并下发对应任务。执行器 (Executor):接收调度任务,并将自身状态上报给调度器。控制台 (Web 前端):负责配置执行器的信息以及调度任务的配置、任务状态、信息展示。

因此,我们可以用一句话解释清楚 Scheduler 所做的事情,即:

在「指定时间」「通知执行器」以「指定方式」执行任务

这句话中包含了三个关键点,也分别代表着 Scheduler 的三个核心模块:

指定时间:任务的触发规则,如:每天早上 8 点、每周二、每月 15 号等。触发器模块(Launcher Cron)负责任务触发。指定方式:任务的执行形式,如:单播任务-指定一个机器执行;广播任务-指定所有机器执行;分片任务-任务分阶段分批的执行。分派器模块(Assignment Cron)负责任务的执行方式。通知执行器:将任务发送到指定执行器,执行任务。派遣器模块(Dispatcher Cron)负责任务的发送,采用流式通信,调度器以推送的方式将任务发送给执行器。

在一个 Job 的调度周期中,各个模块各司其职,整个流程

拥有这三个核心模块后,Scheduler 已具备了成熟的任务调度功能。另外,为了增加 Scheduler 的稳定性,有额外两个模块为其保驾护航:

健康管理模块 (Service Health Cron): 负责管理 Job 的生命周期,检测未正常派发执行的 Job、Instance 和 Task,并将结果上报给运维人员。任务进度刷新模块 (Task Cron): 异步更新 Task 状态,流量较高时进行削峰,保证依赖的 mysql 及 redis 不因为流量过高而出现问题。

本篇文章不对 Scheduler 所支持的定时任务能力作赘述,而是从三个方面(易用性、多功能性、稳定性)介绍 Scheduler 对于分布式任务调度的思考和探索:

「易用性」: 决定了用户是否选择使用该框架的意愿,一个好的框架必须是易用且快速接入的;「多功能性」: 接入方需求多种多样,要站在用户角度想问题,不能闭门造车;「稳定性」: 对于分布式任务调度平台来说,不仅仅局限于自身的稳定性,接入方的稳定性也十分重要。

换位思考-快速接入

背景:效率至上,时间是金

以字节跳动内部为例,当前团队想要实现一个定时任务有多种方式:接入字节云的 cronjob 平台、自己实现一套定时任务框架或者接入第三方定时任务框架。

对于第一种接入 cronjob 平台,每一种定时任务都需要注册各自的 psm 和运行时环境(镜像),当任务需要访问依赖资源如 redis/db 等时,需要各自添加授权。任务代码逻辑有变化时也需要各自升级,导致开发、管理起来较为复杂。

对于第二种自己实现一套定时任务框架,不仅整体开发时间较长,且需要大量时间进行测试回归来保证框架的稳定性。如果项目内使用到的定时任务较多,那么自身研发一套框架用途也较广泛;若项目中使用到的定时任务较少,则 ROI 较低,很多时候也只是为了造轮子而造轮子。

因此,大多数项目面对增加定时任务的需求时,都会寻求直接接入第三方成熟的定时任务框架。对于他们来说,是否易于接入、与现有代码联系是否紧密、调试是否方便是很重要的选取指标。

基于这种背景,Scheduler 在设计时就站在了接入方的角度,思考了如何让接入方能够在最短时间内以最低成本接入 Scheduler,实现自己的定时任务。

分析:站在用户角度想问题

站在接入方角度,对定时任务框架进行选型时最关注的几个点无非是定时任务执行准确性、最高支持 qps、定时设置多样性、接入成本这几个。对于前两个指标,Scheduler 目前接入业务方 50+,日均调度任务 20w+ 次,与公司内其他第三方定时任务框架相比也较有竞争性,同时对于后两个关注点,Scheduler 也有自己的风格。

丰富的调度设置

一般的定时任务框架只支持 crontab 表达式,例如 0 1 * * * ,代表每天凌晨一点执行一次。cronTab 功能强大,但是若配置复杂的定时策略,有一定学习成本,且可读性不高。因此,鉴于这种情况,Scheduler 在 crontab 之上设计了更易读更强大的定时策略,做到所见即所得。

{ "startTime":1648029600000, "timeZone":"Asia/Shanghai", "repeatLevel":"month", "repeatInterval":2, "repeatDays":[3,5,23]}startTime: 开始时间戳,在此之前定时任务不会执行,到达该时间后会执行第一次timeZone: 时区设置,根据当前设置的时区准确派发repeatLevel: 重复级别,目前可以设置按「小时」、「天」、「月」、「周」、「年」以及「工作日」进行重复repeatInterval: 重复间隔,代表每隔 $repertInterval $repeatLevel 执行一次repeatDays: 重复天数,重复级别是周或月时生效

因此,该设置所代表的定时间隔为每两月的 3、5、23 号触发一次

触发时间(北京时间)

2022-03-23 18:00:00

2022-05-03 18:00:00

2022-05-05 18:00:00

2022-05-23 18:00:00

为什么要做工作日调度

可能有同学注意到,Scheduler 对于重复级别的支持十分丰富,不仅可以按照普通的年、月、日等级别进行设置,还可以按照工作日进行重复调度(例如每两个工作日执行一次),这归因在 Scheduler 孵化于字节跳动内部企业服务系统,为诸如人事系统、权限系统等 ToB 服务提供定时任务能力。往往 ToB 客户的需求复杂多变,因此,需要提前具备更多能力,才能更好地服务好 ToB 客户。

Scheduler 在调研接入方需求时,得到了有些客户对于定时提醒这类任务的需求是尽量不要在「非工作日」打扰。于是,Scheduler 决定增加工作日调度选项来适配客户潜在需求,也侧面说明了 Scheduler 为了让接入方更快更小成本接入做出的努力。

轻松的使用方式

相信「开箱即用」对于人们在采买诸如家电、数码产品时,是十分重要的一个考核指标。而对于对外提供的服务 or 框架,亦是如此。Scheduler 的目标就是让接入方能够在短时间熟悉 Scheduler、编写测试代码以及上线定时任务。

专注业务

如果想实现一个定时任务,接入方只需要三步:引入 Scheduler sdk,绑定相应 processor,在 process 接口中实现具体业务逻辑。同时,由于定时任务的实现位于原代码中,启动配置无需更改,本地测试也较为便捷。同时,在字节跳动环境下,无需新增 psm、授权配置等,尽可能做到了「开箱即用」。

import ( "context" "code.byted.org/apaas/scheduler_sdk/executor" // 引入 sdk)func main() { executorSvc, err := executor.NewExecutor(executor.NewDefaultExecutorConfig(), &HelloWorld{}) // 绑定 processor if err != nil { panic(err) } if err = executorSvc.Run(); err != nil { panic(err) }}type HelloWorld struct { ProcessorApiName string}func (h *HelloWorld) GetApiName() string { return h.ProcessorApiName}// process 中实现具体业务逻辑func (h *HelloWorld) SimpleProcess(ctx context.Context, tc *executor.TaskContext) (err error) { tc.LogInfo(ctx, "hello world") return}

快速运维

没有程序员想主动写出 bug,但问题总是会突然出现。如何在出现问题时快速运维、快速止损,是所有工程师都追求的目标。Scheduler 在这方面做了几种尝试:

报警更直观

用户可以在创建 job 时,可以选择配置报警机器人,并把 Scheduler 机器人拉入对应报警群组。当检测到对应 job 出现问题时,Scheduler 机器人会把相应报警推送到对应群组,做到实时响应。

状态更清晰

目前 task 的相关状态如下,当一个 task 长期没有到终态时,根据状态码即可知 task 目前处于什么状态,从而推断是哪一步骤出了问题。

状态码

状态

100

等待触发

101

Ready 就绪态,等待推送

201

推送到 Executor,还未实际执行(任务太多排队)

202

执行中

203

执行超时,逻辑复杂导致

301

执行成功

302

执行失败

401

Ready 超时,没有 Executor 拉取

402

推送到 Executor 后长期未执行

403

执行超时,Executor 宕机导致

并且一些 Scheduler 常见的报错也做了封装,帮助快速定位问题,例如

错误码

错误原因

k_sc_ec_000004

找不到任务{{.jobApiName}}

k_sc_ec_100004

找不到任务实例{{.instanceID}}

k_sc_ec_300001

Processor Name 未注册{{.content}}

k_sc_ec_300006

processor({{.content}}) 找不到对应 executor

k_sc_ec_400002

找不到Executor {{.content}}

k_sc_ec_400005

无权限操作

并肩作战-分片任务

背景:任务越多,挑战越大

一个成熟的项目中避免不了大型批量任务,比如通过 Excel、csv 或其他数据源批量创建或更新数据,批量任务一般数据量很大,如果按照单实例串行执行,那么不能充分利用计算机资源且一次运行会消费大量时间,用户体验不友好。

以 Kunlun 举例, 旧阶段的批量任务依赖于消息队列、Redis 实现,总体分为三大部分:

解析并校验 Excel,将数据解析成一条条数据,将每条消息封装成一条消息发送至消息队列。消费消息队列,进行创建、更新等操作,并在 Redis 记录总体进度并推送给用户,如果任务失败,会将行数和错误原因同时记录进 redis 。待所有数据处理结束后,如果 redis 中没有错误数据,则提示用户成功,否则根据错误信息生成 Excel 返回给用户。

使用消息队列、redis 的定时任务可以提速和优化用户体验,但有以下不足:

维护起来不方便,例如当有新服务需要此类功能时,需要自己再实现一套差不多的框架,所以需要将分片功能托管到第三方服务,而业务方只用专注于具体业务。依赖于消息队列和 Redis 两个外部组件,对两个组件的稳定性要求极高,当其中一个出现问题,都会带来不小的麻烦。

基于这种背景,Scheduler 丰富了原本的任务调度能力,补充了分片能力,以满足复杂繁琐的任务分片处理的需求。

分析:旧问题,新解法

若打算做出一套贴合业务需求的分片任务框架,需要先了解现阶段的分片任务的实现步骤。

现阶段的分片任务大致可以抽象成3个步骤:

总。获取数据,可以从上传的文件解析数据、从 DB 查询出大批数据或其他数据源。分。处理数据,聚焦于具体业务,如:创建、查询、更新。总。处理结果,将此次任务运行结果处理成结果报告返回给用户,报告可以为 Excel、一条消息、一封邮件等。

Scheduler 要做的事情则是替换其中分片、消息队列、Redis 的功能,做出以下抽象:

type ShardingProcessor interface { PreProcess(ctx context.Context, tc *TaskContext) error ShardingProcess(ctx context.Context, tc *TaskContext) error Notify(ctx context.Context, tc *TaskContext)error PostProcess(ctx context.Context, tc *TaskContext) error}PreProcess(总):数据准备阶段。可在此方法中对数据进行额外处理,如计算拓扑关系,定义数据优先级。单机运行。ShardingProcess(分):分片处理阶段。实际处理函数,多机运行。分片处理函数,执行批量导入、更新等处理。(入参:Scheduler 对 PreProcess 返回结果的分片子参数)Notify(阶段式-总):进度更新处理,每当进度变更的大小大于设定阈值,则生成一次 Notify 的Task。Executor 向 Scheduler 汇报子任务进度,Scheduler 计算出总体进度,当总进度发生变更后生成 NotifyTask 通知 Executor 进行处理。PostProcess(总):结果处理阶段。任务执行完毕后可在此函数进行后续处理,单机运行。当所有分片子任务都执行完后,Scheduler 会将子任务的执行结果发送到此函数处理。(入参:每一个子任务 ShardingProcess 后的结果数组)

执行器需要实现 ShardingProcessor 接口以供调度器进行调度。调度过程

Scheduler 支持分片任务重点在于丰富调度模型,提升调度器调度能力,完善执行器执行能力来达到支持分片任务的目的。

调度侧能力

分批调度的能力

调度侧需要根据任务进度依次生成 PreTask、ProcessTask、NotifyTask、PostTask 来调度执行器 ShardingProcessor 中的 PreProcess - Process - Notify - PostProcess 四个方法。单机调度 PreProcess,PostProcess,Notify,并行调度 PostProcess,总体调度呈现总-分-总的形式。调度过程

数据拆分的能力

数据拆分即任务分片,指的是将单一任务按照特定的逻辑切分为多个独立的子任务,将其分派到不同的节点执行,以提高任务的执行效率。

而 Scheduler 要处理的任务内部可能存在依赖关系(比如 kunlun 业务中 metadata 批量创建的需求,由于存在 lookup 和 reference 字段等,记录创建之间存在拓扑关系),所以在执行时需要优先级的概念,而不能被简单拆分为独立的子任务。

为了支持带优先级的任务分片,Scheduler 接收的分片任务的数据特点

是业务方自定义任务的二维数组;第一个维度是任务执行的优先级,位于同一优先级下的任务并发执行,位于不同优先级的任务按优先级串行执行;第二个维度是同一优先级下的自定义任务列表。关于自定义任务的结构,Scheduler 不感知。业务方可以选择存储任务详情或是主键信息,并自定义处理逻辑,而 Scheduler 只做分片和调度工作。

二维数组可以是下面这样:

[ [ // 第一优先级的任务1,可以是主键 101, // 第一优先级的任务2,可以是SQL语句 "Insert into tablename xxx", // 第一优先级的任务3,可以是结构体等等... { "ID": 999, "Name": "zhangsan" } ], [ // 第二优先级的任务1、2、... 102, 103, ... ]]

了解了待分片任务的结构,我们来讨论如何对任务进行分片。比如,分片的数量由什么决定,单个分片上的信息是如何分配的,不同分片又是不同分派到不同的处理器上的...

分片数的确定

分片数的确定基于以下参数的值:数据量、任务创建时用户指定的单片最大数量、单片最小数量,以及实际可用的执行器数量。

分片算法

分片特征值(sharding key)的选择要遵循的原则应该是基于最常用的访问方式。

由于 Scheduler 分片时并不关心业务数据的结构,所以选用数据数组的下标来作为分片特征值。

由于分片数量确定后,不涉及到由于分片的增加或减少对数据进行 Rehash 的情况,所以无需考虑虚拟节点、一致性哈希等方式进行分片。

这里选用哈希分片的分片算法,原因是既可以均匀分布数据,实现起来也很简单。

分片的存储和派发

分片完成后,需要给每个分片创建一个 Task,并把分片的数据存储下来。

关于 Task 的派发,根据上面关于分片数的讨论,可以得到分片数和 Executor 数的关系:

在数据量合适的情况下(单片最大数量和单片最小数量设置合理时,是最普遍的情况),分片数和 Executor 的数目是一致的;当数据量很小时,会出现分片数小于 Executor 数;当数据量很大时,会出现分片数大于 Executor 数,甚至可能是后者的几倍。

为了让各个 Task / 各个分片 能够均匀派发给各个 Executor,也为了避免某个 executor 挂掉时,其他 Executor 不能均匀分摊挂掉的节点原先承担的分片,需要采用合理的分片策略。

在分片时,我们保证了各分片的数据是尽量均匀分布的,所以从分片到 Executor 的分派方式可以尽可能地简单,采用平均分配的策略即可。对于挂掉的节点所承担的分片,也采用同样的策略派发到存活的 Executors 上即可。

例如:

有 3 个 Executor,分成 9 片,则每个 Executor 分到的分片是: 1=[0,1,2], 2=[3,4,5], 3=[6,7,8]

如果 Executor 1 挂掉,则将 1 的分片平均分配到 Executor 2、3: 2=[0,1,3,4,5], 3=[2,6,7,8]

平均分配*:对于不能均分的情况,为了避免靠前的 Executor 总是承担更多的压力,可以根据待分配分片数量的奇偶来决定是升序分派还是降序分派。

进度通知的能力

Scheduler 支持通知 Executor 任务执行的整体进度

Executor 上报子任务进度至 SchedulerScheduler 计算总任务进度总任务进度发生变化,则生成 NotifyTask 发送至 Executor

执行侧分批执行的能力

执行侧需要实现并注册 SDK 提供的 ShardingProcessor 接口,来处理由调度侧发来的多种类型的 Task。

PreProcess

预处理方法,可以进行但不限于以下的操作:

启动参数不符合 Scheduler 规定格式,可以通过 PreProcess 方法进行一次转换。数据存在导入优先级,可以在 PreProcess 中编写计算拓扑关系的方法。

如果不需要预处理,可直接在方法内 return,分片时数据使用启动时的 Data

func (s *ShardingProcessor) PreProcess(ctx context.Context, tc taskContext) error{ oldData := tc.GetData() // 用户业务, 数据处理 newData := Transform(oldData) // 返回带拓扑排序的数据 tc.SetResult(newData) return nil}

ShardingProcess

分片处理函数,主要是进行数据更新、创建操作。ShardingProcess 的入参是切分后的数组([]interface{})。Executor 需要对参数进行两部分额外处理:

将 []interface{} 中的 interface{} 断言成具体 struct{}。处理后。计算当前子任务执行进度,进行上报;如果省略上报,服务端以分片粒度生成 NotifyTask 通知执行器。

func (s *ShardingProcessor) ShardingProcess(ctx context.Context, tc taskContext) error{ taskData := tc.GetData() for _, data := range taskData{ // 用户业务, 数据处理 } tc.SetResult(...) return}

PostProcess

接受所有分片处理结果,进行后续处理,如生成错误文件。

func (s *ShardingProcessor) PostProcess(ctx context.Context, tc taskContext) error{ taskData := tc.GetData() for _, result := range taskData{ // do something } // 用户业务 tc.SetResult(...)}

Notify

提供给子任务上报的能力,Scheduler 会根据所有子任务上报结果计算进度,通知 Executor,通知粒度为数据条数。如果接入方不主动上报子任务进度,Scheduler 会根据子任务完成度进行通知,通知粒度为分片粒度。

分片任务流程

削峰填谷-流量控制

背景:提供能力,而非施加压力

在 Scheduler 设计初期时,更多的是把注意力放在了如何能够快速、准确、低延迟的触发任务,为此还多次优化了触发器、分派器、派遣器三大模块的轮询逻辑,但是忽略了任务量过大时下游能否抗住流量的问题。

如果 Scheduler 在调度时无法准确感知下游压力,那么很容易将下游打挂,如:在定时任务首次上线时,因为 kunlun 的装包机制导致数千个应用下配置了同样的定时任务,虽然一个包内的数十个定时任务触发时间分散,但是应用包之间的同一个任务触发时间相同,导致下游需要在同一时刻处理数千个任务,再加上任务的处理流程还会通过消息中间件进行扩散,导致数据库在任务执行阶段一直处理低 IDLE 阶段。

分析:流量追踪,剥茧抽丝

目前大部分后端服务,通过分析任务的流量走向,可以大致确认每一条任务在执行过程中不论扩散还是非扩散流量都会走向 DB,流量图大致如图。

任务的流量最终打到了 DB,所以流量控制的目标就更加清晰:对 DB 的流量控制。

需要对 DB 进行流量控制,那么就要设定合理的指标,理论上,只要指标采纳的足够合理,就能严格、准确的控制流量,指标则需要具备以下条件:

实时性。能够准时反应数据库健康状况。权威性。能够准确反应数据库健康状况。

优先级: 实时性 > 权威性。当一个指标的实时性不够高,那么它的权威性就不再有价值。

只需要实时监听着 DB 的指标,来判断任务是立刻执行,还是延迟执行就能有效的保护 DB。

指标选择

消费 metrics 的监控点,关于数据库的打点信息非常全面,能够非常轻易的获取到数据库宿主机的 CPU、内存或数据库本身的连接数、查询数等指标,这些指标的权威性毋庸置疑,但是 metrics 通过将指标收集到本地代理,代理每 30s 做一次聚合发送至服务端,其时效性太差。数据库不可用因素为:大量任务触发 -> DB 访问流量增高 -> CPU idle 降低 -> 数据库不可用。造成 CPU idle 降低的因素为 DB 流量增高,可以将 DB 的流量作为指标进行流量控制,缺点是需要自己采集指标。

指标收集

指标范围

反映 DB 压力较为直接的指标是 cpu idle,但考虑到服务部署往往多实例以及 cpu idle 采集难度大的情况,以近似指标来代替。另一方面,通过历史数据分析,DB 流量与 cpu idle 有一定的关联,因此以 DB 流量作为 DB 压力指标。

数据存储

参考限流的实现方案,采用单独的 Redis 存储流量数据,以 1s 为时间窗口作为 Redis key,每个时间窗口的流量作为 Redis value,每次发生 DB 操作时更新流量数据。系统中存在多个 DB,每个 DB 单独统计,在 Redis key 中加入 db 信息。Redis key 设置 10s 过期时间,查询时根据过去 3 个窗口的加权平均(80%/15%/5%)作为当前流量,以处理窗口交界处的突发流量。

收集方式

目前 DB 流量已有 metrics 监控数据,但由于 metrics 会在本地聚合 30s 数据后上报,至少会有 30s 的延迟。而造成 DB 压力大的定时任务多为短期集中触发,使用 metrics 数据会有感知不及时的问题,因此需要额外收集数据。参考 DB metrics 数据采集的方式,通过 Gorm 的 callback 机制插入具体的采集逻辑,减少对业务代码的侵入。

func SetMonitorCallBack(db *gorm.DB) { db.Callback().Create().Before("gorm:before_create").Register("metric:before_create", beforeCreateCallback) db.Callback().Delete().Before("gorm:before_delete").Register("metric:before_delete", beforeDeleteCallback) ...}func beforeCreateCallback(scope *gorm.Scope) { beforeCallback(scope, "create")}func beforeCallback(scope *gorm.Scope, method string) { ...}

在采集逻辑上,需要考虑以下几个问题:

性能:callback 中需要尽量减少延迟,优先使用异步的方式上报数据。使用 channel 充当队列,callback 中将数据写入 channel,当 channel 容量满时丢弃数据,防止阻塞。另有异步协程从 channel 中取数据上报。兼顾时效性和网络开销,在上报前预先在本地以 100ms 窗口做聚合。

type MetricType int8const ( QueryCount MetricType = 1)type DBMetric struct { DBName string DBMethod string Type MetricType Timestamp int64 Value interface{}}// callback中将metric数据写入channelfunc beforeCallback(scope *gorm.Scope, method string) { dbName := getStringValueFromCtx(scope.DB().Ctx, CtxVariableDBName) curMs := time.Now().UnixNano()/int64(time.Millisecond) metric := DBMetric{dbName, method, QueryCount, curMs, 1} select { case ch <- metric: default: // channel is full, ignore this metric }}// 异步上报,在resource_sdk的Init()中根据配置判断是否启动此协程func metricAgent() { windowSize := 100 // window time -> (metric key(dbName|dbMethod|type) -> metric) aggrMetrics := map[int64]map[string]DBMetric timer := time.NewTicker(windowSize) defer func() { timer.Stop() }() for { select { case msg := <-ch: curWindow := curMs/windowSize 更新aggrMetrics中curWindow对应的metric(对queryCount来说是加1) case <-timer.C: 将aggrMetrics中key+windowSize<=curTime的数据上报并清除 } }}运维成本:采集逻辑会运行在各个服务上,考虑到后续会收集更多的指标,直接上报 Redis 需要给各个服务开通读写权限,运维管理成本较高。基于此,使用额外的服务来管理指标数据,接收上报的指标数据存入 Redis,并通过接口的方式提供查询服务。指标存放在更加聚焦在 DB 资源的 resource 服务中,在 resource 服务中通过增加接口的方式实现指标数据的管理功能,同时,为了不影响 resource 原有业务的稳定性,使用单独的集群提供服务。

Scheduler 调度反馈

流量阈值限制

Scheduler 调度速率与 DB 负载之间的关系较为复杂,本期采用简单的阈值反馈机制,设置 DB 流量阈值,当流量超出阈值时,停止 Scheduler 当前周期调度。根据历史数据,设置阈值为 5K。

当流量未超出阈值时,不能预估任务对 DB 流量的影响,采用简单策略对任务数进行限制:

任务数 = max((DB流量阈值 - DB当前流量)* 100 / DB 流量阈值, 0)

DB路由

目前 Kunlun 的 DB 资源根据租户进行分配,不同租户的数据和流量会落在不同的 DB 上。Scheduler 会记录 Job 所处租户,所以在调度时,需要根据租户查找真实的 DB 资源,通过 DB 指标的健康状况来决定是否派遣任务:

查询 Job 所在租户分配的 pg 资源标识根据 pg 资源标识去 Redis 查询对应的流量数据

调度控制流程

- END -




状元榜眼探花的由来


以进士及第第一人为状元始于唐,第二个人称榜眼、第三个人称探花始于南宋,元、明、清科场上沿用之,此说原已由清人赵翼在《陔余丛考》中考明:“世称进士廷试第一甲三人为状元、榜眼、探花。按状元之名,唐已有之。自武后初试贡士于殿前,列其等第,门有例有奏状,其居首者因曰状头,亦曰状元。……榜眼之名起于北宋无疑。……北宋时第三人亦呼为榜眼,盖眼必有二,故第二、第三人皆谓之榜眼,其后以第三人为探花,遂专以第二人为榜眼耳。……宋南渡后固以第三人为探花矣。”问题的关键是,这第三人称探花,始于何时?探花,原名为探花郎,始见于唐。唐进士及第后,照例筹钱举行期集,于杏园赏花会时,挑选进士中年龄最少者二人为探花郎,使赋诗。宋初沿用,“自唐以来榜榜有之。”



称呼之起源于宋,并非说已成为宋代科举中一项定制。进士第一人为状元,第二人为榜眼、第三人为探花,定制于明太祖时:“举人试之京师曰会试,中式者,天子亲策于廷曰廷试,亦曰殿试,分一、二、三甲以为名第三次。一甲止三人,曰状元、榜眼、探花。……状元、榜眼、探花之名,制所定也。”






状元榜眼探花十大顺序图片

以下顺序:

年号 科别 状元 榜眼 探花 传胪

1646年 丙戌科清顺治3年 傅以渐

吕攒祖 李爽棠 梁清宽

1647 年 丁亥科顺治4年吕宫 程芳朝 蒋超 于明宝

1649年 已丑科顺治6年 刘子状 熊伯龙 张天植 范光文

1652年 壬辰科顺治9年 邹忠倚

张永祺 沈荃李愫

顺治9年 麻勒吉 折库纳 巴海杨官

1655 年 乙未科顺治 12 年 史大成

戴王纶 秦試王益朋

顺治 12 年 图尔宸 贾勤索 泰董色

1658年 戊戌科顺治 15 年 孙承恩

孙一致 吴国对 王遵训

1659年 已亥科顺治 16 年 徐元文

华亦祥 叶芳蔼 王勋

1661年 辛丑科顺治 18 年 马世俊

李佩根 吴光孙錄

1664 年 甲辰科康熙3年 严我斯

李元振 秦 弘 沈珩

1667年 丁未科康熙6年 缪彤 张玉载 董 讷 夏沅

1670年 庚戌科康熙9年 蔡启尊

孙在丰 徐乾学 何金兰

1673年 癸丑科康熙 12 年 韩炎

王度心 徐秉义 顾 汗

1676年 丙辰科康熙 15 年 彭定求

胡会恩 翁叔元 魏希征

1679年 已未科康熙 18 年 归允肃

孙 卓 茆荐馨 吴震方

1682 年 壬戌科康熙 21 年 蔡升元

吴涵 彭宁求史夔

1685年 乙丑科康熙 24 年 陆肯堂

陈元龙 黄梦麟 张希良

1688年 戊辰科康熙 27年 沈廷文

查嗣韩张豫章范光阳

1691年 辛未科康熙 30 年 戴有祺

吴晒 黄叔琳 杨中纳

1694年 甲戌科康熙33年 胡任與

顾图河 顾悦履 汪俊

1697年 丁丑科康熙36年 李 蟠

严虞悖 姜宸英 汪士站

1700年 庚辰科康熙39年 汪绎 季愈

王露 张成遇

1703年 癸未科康熙42年 王式丹

赵晋 钱名世 汪灏

1706年 丙戌科康熙45年 王云锦

吕葆中 贾国维 愈兆晟

1709年 已丑科康熙48年 赵能诏

载名世 缪沅 朱元英

1712年 壬辰科康熙51年 王世琛

沈树本 徐葆光 卜俊民

1713年 癸已恩科康熙52 年 王敬铭 任兰枝 魏廷珍 杨绳武

1715年 乙未科康熙54年 徐陶璋

缪日藻 傅王露 李文锐

1718年 戊戌科康熙57年 汪应铨

张廷璐 沈锡絡 金以成

1721年 辛丑科康熙60年 邓钟岳

吴文焕 程元章 王兰生

1723年 癸丑恩科雍正1年 于振 戴

瀚 杨炳 张廷珩

1724年 甲辰科雍正2年 陈德华

王安国 汪德容 汪由敦

1727年 丁未科雍正5年 彭启丰

邓启元 马宏琦 邹一桂

1730年 庚戌科雍正8年 周澍 沈昌宇 梁诗正 蒋 溥

1733年 癸丑科雍正11年 陈倓

田志勤 沈文镐 张若霭

1736年 丙辰科乾隆1年 金德英

黄孙懋 秦蕙田 蔡新

1737年 已恩科乾隆 2 年 于敏中

林枝春 任端书 孙中溥

1739年 已未科乾隆4 年 庄有恭

涂逢震 秦勇均 陆秩

1742年 壬戌科乾隆7年 金甡 杨述曾 汤大绅 张进

1745年 乙丑科乾隆10 年 钱维城

庄存与 王际华 章恺

1748年 戊辰乾隆 13 年 梁国治 陈楠 汪廷屿 刘星炜

1751年 辛未科乾隆 16 年 吴鸿

饶学曙 周 沣 沈試

1752年 壬申恩科乾隆17年 秦大士 范械士 卢文弼 钱载

1754年 甲戌科乾隆19年 庄培因

王鸣盛 倪承宽 汪永锡

1757年 丁丑科乾隆22年 蔡以台

梅立本 邹亦孝 李汪度

1760年 庚辰科乾隆25年 毕沅

诸重光 王文治 曹文填

1761年 辛已恩科乾隆 26年 王杰

胡高望 赵翼 蒋永植

1763年 癸未科乾隆28年 秦大成

沈初 韦谦恒 董诰

1766年 丙戌科乾隆31年 张书勋

姚颐 刘跃云 陆费樨

1769年 已丑科乾隆 34 年 陈初哲

徐天柱 陈嗣龙 任大椿

1771年 辛卯恩科乾隆36年 黄轩

王增 范衷 王尔烈

1772年 壬辰科乾隆37年 金 榜

孙辰东 俞大猷 平恕

1775年 已未科乾隆40年 吴锡龄

汪铺 沈清藻 王春煦

1778年 戊戌科乾隆 43 年 戴衢亨

蔡廷衡 孙希旦 邵自昌

1780年 庚子恩科乾隆 45 年 汪如洋 江德量 程昌期 关槐

1781年 辛丑科乾隆46 年 钱棨

陈万青 汪学金 秦承业

1784年 甲辰科乾隆 49 年 茹菜 邵瑛 邵玉清 李长森

1783 年 丁未科乾隆 52 年 史致光

孙星衍 董教增 朱理

1790年 已西科乾隆 54 年 胡长龄

汪廷珍 刘凤诰 钱楷

1793年 庚戌恩科乾隆 55 年 石韫玉 洪亮吉 王宗诚 辛从益

1795 年 癸丑科乾隆58 年 潘世恩

陈 云 陈希曾 陈秋水

1796年 乙卯恩科乾隆 60 年 王以衔 莫 晋 潘世璜 陈廷桂

1796年 丙辰科嘉庆1年 赵文楷

汪守和 帅承瀛 戴殿泗

1799年 乙未科嘉庆 4 年 姚文田

苏兆登 王引之 程国仁

1801年 辛酉恩科嘉庆6年 顾皋

刘彬士 邹家燮 席煜

1802年 壬戌科嘉庆7年 吴廷琛

李宗昉 朱士彦 李仲昭

1805年 乙丑科嘉庆10 年 彭浚 徐颐 何凌汉 徐松

1808年 戊辰科嘉庆13年 吴信中

谢阶树 石承藻 朱榮

1809年 己已恩科嘉庆14年 洪莹

廖金城 张岳崧 黄安涛

1811年 辛未科嘉庆16年 蒋立铺

吴毓英 吴廷珍 毛鼎亨

1814年 甲戌科嘉庆19年 龙汝言

祝庆蕃 伍长华 裘元善

1817年 丁丑科嘉庆22年 吴其溶

凌泰封 吴清鹏 孙如金

1819年 已卯恩科嘉庆24 年 陈沆

杨九碗 胡达源 孙起端

1820年 庚辰科嘉庆25年 陈继昌

许乃普 陈銮 龚文辉

1822 年 壬午恩科道光2年 戴兰芬

郑秉恬 罗文俊 陈嘉树

1823年 癸未科道光3年 林召棠

王广荫 周开麒 杜受田

1826年 丙戌科道光6 年 朱昌颐 贾桢 帅方蔚 麟魁

1829 年 已丑科道光9年 李振钧

钱富昌 朱兰 朱淳

1832年 壬辰恩科道光12 年 吴钟骏 朱风标 季芝昌 赵德涝

1833 年 癸已科道光13 年 汪鸣相

曹履泰 蒋元溥 司徒煦

1835年 乙未科道光15 年 刘绎

曹联桂 乔晋芳 张 芾

1836年 丙申恩科道光 16 年 林鸿年 何冠英 苏敬衡 张锡庚

1838年 戊戌科道光18 年 钮福保

金国均 江国霖 灵桂

1840 年 庚子科道光 20 年 李承霖

冯桂芬 张百揆 殷寿彭

1841年 辛丑恩科道光21 年 龙启瑞 龚宝莲 胡家玉 何若瑶

1844年 甲辰科道光24 年 孙毓湘

周学睿 冯培元 王景淳

1845年 乙已恩科道光 25 年 萧锦忠 金鹤清 吴福年 钟启峋

1847 年 丁未科道光27年 张之万

袁绩懋 庞钟璐 许彭寿

1850年 庚戌科道光30年 陆增祥

许其光谢增 黄统

1852年 壬子恩科咸丰2年 章鋆

杨泗孙 潘祖荫彭瑞毓

1853 年 癸丑科咸丰3年 孙如仅

吴凤藻 吕朝瑞 黄钰

1856 年 丙辰科咸丰6年 翁同稣

孙毓汶 洪昌燕 钟宝华

1859年 已未科咸丰9年 孙念鼐

孙念祖 李文田 朱学笃

1860年 庚申恩科咸丰10 年 钟骏声 林彭年 区阳保极 黎培敬

1862年 壬戌科同治1年 徐郁 何金寿 温忠翰 陈彝

1863 年 癸亥恩科同治 2 年 翁曾源 龚承钧 张之洞 周兰

1865年 已丑科同治 4 年 崇绮

于建章 杨霁 牛 暄

1868年 戌辰科同治7年 洪钧 黄自元 王文在 许有麟

1871年 辛未科同治 10 年 梁耀枢

高岳崧 郁昆 恽彦彬

1874 年 甲戌科同治13 年 陆润祥

谭宗浚 黄贻楫 华金寿

1876年 丙子恩科光绪 2 年 曹鸿勋 王康荣 冯文蔚 吴树梅

1877 年 丁丑科光绪 3 年 王仁堪

余联元 朱虞扬 孙宗锡

1880年 庚辰科光绪6年 黄思永

曹治孙 谭鑫振 戴彬元

1883 年 癸未科光绪 9 年 陈冕 寿者 管廷献 朱祖谋

1886年 丙戌科光绪 12 年 赵以炯

邹福保 冯煦 彭述

1889年 已丑科光绪15年 张建勋

李盛铎 刘世安 杜本荣

1890年 庚寅恩科光绪 16 年 吴 鲁

文廷式 吴荫培 萧大猷

1892 年 壬辰科光绪 18 年 刘福姚

吴士鉴 陈伯陶 恽毓嘉

1894 年 甲午恩科光绪 20 年 张 賽 尹铭绶 郑沅 吴筠孙

1895 年 乙未科光绪 21 年 骆成骧

喻长霖 王龙文 萧荣爵

1898 年 戊戌科光绪 24 年 夏同稣

夏寿田 俞陛云 李稷勋

1903 年 癸卯科光绪 29 年 王寿彭

左 霈 杨兆麟 黎湛枝

1904年 甲辰科光绪 30 年 刘春霖

朱汝珍 商衍鎏 张启后

(节选自《清代甘肃贡院》)


今天的内容先分享到这里了,读完本文《状元榜眼探花十大顺序》之后,是否是您想找的答案呢?想要了解更多状元榜眼探花十大顺序、400005相关的财经新闻请继续关注本站,是给小编最大的鼓励。