SegmentFault 最新的文章 |
- 【高并发】两种异步模型与深度解析Future接口
- xxl-job Vs ElasticJob,谁牛?
- 【躲过裁员,成功上岸】发现小公司有不好的苗头,赶紧学习!
- 2022招聘季|从招聘方的角度理解求职
- MySQL 表分区?涨知识了!
- React Router v6 探索
- HMS Core视频编辑服务:AI着色, 忆往昔看今朝
Posted: 25 Mar 2022 01:27 AM PDT 大家好,我是冰河~~ 本文有点长,但是满满的干货,以实际案例的形式分析了两种异步模型,并从源码角度深度解析Future接口和FutureTask类,希望大家踏下心来,打开你的IDE,跟着文章看源码,相信你一定收获不小! 一、两种异步模型在Java的并发编程中,大体上会分为两种异步编程模型,一类是直接以异步的形式来并行运行其他的任务,不需要返回任务的结果数据。一类是以异步的形式运行其他任务,需要返回结果。 1.无返回结果的异步模型 无返回结果的异步任务,可以直接将任务丢进线程或线程池中运行,此时,无法直接获得任务的执行结果数据,一种方式是可以使用回调方法来获取任务的运行结果。 具体的方案是:定义一个回调接口,并在接口中定义接收任务结果数据的方法,具体逻辑在回调接口的实现类中完成。将回调接口与任务参数一同放进线程或线程池中运行,任务运行后调用接口方法,执行回调接口实现类中的逻辑来处理结果数据。这里,给出一个简单的示例供参考。
便于接口的通用型,这里为回调接口定义了泛型。
回调接口的实现类主要用来对任务的返回结果进行相应的业务处理,这里,为了方便演示,只是将结果数据返回。大家需要根据具体的业务场景来做相应的分析和处理。
任务的执行类是具体执行任务的类,实现Runnable接口,在此类中定义一个回调接口类型的成员变量和一个String类型的任务参数(模拟任务的参数),并在构造方法中注入回调接口和任务参数。在run方法中执行任务,任务完成后将任务的结果数据封装成TaskResult对象,调用回调接口的方法将TaskResult对象传递到回调方法中。
到这里,整个大的框架算是完成了,接下来,就是测试看能否获取到异步任务的结果了。
在测试类中,使用Thread类创建一个新的线程,并启动线程运行任务。运行程序最终的接口数据如下所示。
大家可以细细品味下这种获取异步结果的方式。这里,只是简单的使用了Thread类来创建并启动线程,也可以使用线程池的方式实现。大家可自行实现以线程池的方式通过回调接口获取异步结果。 2.有返回结果的异步模型 尽管使用回调接口能够获取异步任务的结果,但是这种方式使用起来略显复杂。在JDK中提供了可以直接返回异步结果的处理方案。最常用的就是使用Future接口或者其实现类FutureTask来接收任务的返回结果。
使用Future接口往往配合线程池来获取异步执行结果,如下所示。
运行结果如下所示。
FutureTask类既可以结合Thread类使用也可以结合线程池使用,接下来,就看下这两种使用方式。 结合Thread类的使用示例如下所示。
运行结果如下所示。
结合线程池的使用示例如下。
运行结果如下所示。
可以看到使用Future接口或者FutureTask类来获取异步结果比使用回调接口获取异步结果简单多了。注意:实现异步的方式很多,这里只是用多线程举例。 接下来,就深入分析下Future接口。 二、深度解析Future接口1.Future接口 Future是JDK1.5新增的异步编程接口,其源代码如下所示。
可以看到,在Future接口中,总共定义了5个抽象方法。接下来,就分别介绍下这5个方法的含义。
取消任务的执行,接收一个boolean类型的参数,成功取消任务,则返回true,否则返回false。当任务已经完成,已经结束或者因其他原因不能取消时,方法会返回false,表示任务取消失败。当任务未启动调用了此方法,并且结果返回true(取消成功),则当前任务不再运行。如果任务已经启动,会根据当前传递的boolean类型的参数来决定是否中断当前运行的线程来取消当前运行的任务。
判断任务在完成之前是否被取消,如果在任务完成之前被取消,则返回true;否则,返回false。 这里需要注意一个细节:只有任务未启动,或者在完成之前被取消,才会返回true,表示任务已经被成功取消。其他情况都会返回false。
判断任务是否已经完成,如果任务正常结束、抛出异常退出、被取消,都会返回true,表示任务已经完成。
当任务完成时,直接返回任务的结果数据;当任务未完成时,等待任务完成并返回任务的结果数据。
当任务完成时,直接返回任务的结果数据;当任务未完成时,等待任务完成,并设置了超时等待时间。在超时时间内任务完成,则返回结果;否则,抛出TimeoutException异常。 2.RunnableFuture接口 Future接口有一个重要的子接口,那就是RunnableFuture接口,RunnableFuture接口不但继承了Future接口,而且继承了java.lang.Runnable接口,其源代码如下所示。
这里,问一下,RunnableFuture接口中有几个抽象方法?想好了再说!哈哈哈。。。 这个接口比较简单run()方法就是运行任务时调用的方法。 3.FutureTask类 FutureTask类是RunnableFuture接口的一个非常重要的实现类,它实现了RunnableFuture接口、Future接口和Runnable接口的所有方法。FutureTask类的源代码比较多,这个就不粘贴了,大家自行到java.util.concurrent下查看。 (1)FutureTask类中的变量与常量 在FutureTask类中首先定义了一个状态变量state,这个变量使用了volatile关键字修饰,这里,大家只需要知道volatile关键字通过内存屏障和禁止重排序优化来实现线程安全,后续会单独深度分析volatile关键字是如何保证线程安全的。紧接着,定义了几个任务运行时的状态常量,如下所示。
其中,代码注释中给出了几个可能的状态变更流程,如下所示。
接下来,定义了其他几个成员变量,如下所示。
又看到我们所熟悉的Callable接口了,Callable接口那肯定就是用来调用call()方法执行具体任务了。
看一下WaitNode类的定义,如下所示。
可以看到,WaitNode类是FutureTask类的静态内部类,类中定义了一个Thread成员变量和指向下一个WaitNode节点的引用。其中通过构造方法将thread变量设置为当前线程。 (2)构造方法 接下来,是FutureTask的两个构造方法,比较简单,如下所示。
(3)是否取消与完成方法 继续向下看源码,看到一个任务是否取消的方法,和一个任务是否完成的方法,如下所示。
这两方法中,都是通过判断任务的状态来判定任务是否已取消和已完成的。为啥会这样判断呢?再次查看FutureTask类中定义的状态常量发现,其常量的定义是有规律的,并不是随意定义的。其中,大于或者等于CANCELLED的常量为CANCELLED、INTERRUPTING和INTERRUPTED,这三个状态均可以表示线程已经被取消。当状态不等于NEW时,可以表示任务已经完成。 通过这里,大家可以学到一点:以后在编码过程中,要按照规律来定义自己使用的状态,尤其是涉及到业务中有频繁的状态变更的操作,有规律的状态可使业务处理变得事半功倍,这也是通过看别人的源码设计能够学到的,这里,建议大家还是多看别人写的优秀的开源框架的源码。 (4)取消方法 我们继续向下看源码,接下来,看到的是cancel(boolean)方法,如下所示。
接下来,拆解cancel(boolean)方法。在cancel(boolean)方法中,首先判断任务的状态和CAS的操作结果,如果任务的状态不等于NEW或者CAS的操作返回false,则直接返回false,表示任务取消失败。如下所示。
接下来,在try代码块中,首先判断是否可以中断当前任务所在的线程来取消任务的运行。如果可以中断当前任务所在的线程,则以一个Thread临时变量来指向运行任务的线程,当指向的变量不为空时,调用线程对象的interrupt()方法来中断线程的运行,最后将线程标记为被中断的状态。如下所示。
这里,发现变更任务状态使用的是UNSAFE.putOrderedInt()方法,这个方法是个什么鬼呢?点进去看一下,如下所示。
可以看到,又是一个本地方法,嘿嘿,这里先不管它,后续文章会详解这些方法的作用。 接下来,cancel(boolean)方法会进入finally代码块,如下所示。
可以看到在finallly代码块中调用了finishCompletion()方法,顾名思义,finishCompletion()方法表示结束任务的运行,接下来看看它是如何实现的。点到finishCompletion()方法中看一下,如下所示。
在finishCompletion()方法中,首先定义一个for循环,循环终止因子为waiters为null,在循环中,判断CAS操作是否成功,如果成功进行if条件中的逻辑。首先,定义一个for自旋循环,在自旋循环体中,唤醒WaitNode堆栈中的线程,使其运行完成。当WaitNode堆栈中的线程运行完成后,通过break退出外层for循环。接下来调用done()方法。done()方法又是个什么鬼呢?点进去看一下,如下所示。
可以看到,done()方法是一个空的方法体,交由子类来实现具体的业务逻辑。 当我们的具体业务中,需要在取消任务时,执行一些额外的业务逻辑,可以在子类中覆写done()方法的实现。 (5)get()方法 继续向下看FutureTask类的代码,FutureTask类中实现了两个get()方法,如下所示。
没参数的get()方法为当任务未运行完成时,会阻塞,直到返回任务结果。有参数的get()方法为当任务未运行完成,并且等待时间超出了超时时间,会TimeoutException异常。 两个get()方法的主要逻辑差不多,一个没有超时设置,一个有超时设置,这里说一下主要逻辑。判断任务的当前状态是否小于或者等于COMPLETING,也就是说,任务是NEW状态或者COMPLETING,调用awaitDone()方法,看下awaitDone()方法的实现,如下所示。
接下来,拆解awaitDone()方法。在awaitDone()方法中,最重要的就是for自旋循环,在循环中首先判断当前线程是否被中断,如果已经被中断,则调用removeWaiter()将当前线程从堆栈中移除,并且抛出InterruptedException异常,如下所示。
接下来,判断任务的当前状态是否完成,如果完成,并且堆栈句柄不为空,则将堆栈中的当前线程设置为空,返回当前任务的状态,如下所示。
当任务的状态为COMPLETING时,使当前线程让出CPU资源,如下所示。
如果堆栈为空,则创建堆栈对象,如下所示。
如果queued变量为false,通过CAS操作为queued赋值,如果awaitDone()方法传递的timed参数为true,则计算超时时间,当时间已超时,则在堆栈中移除当前线程并返回任务状态,如下所示。如果未超时,则重置超时时间,如下所示。
如果不满足上述的所有条件,则将当前线程设置为等待状态,如下所示。
接下来,回到get()方法中,当awaitDone()方法返回结果,或者任务的状态不满足条件时,都会调用report()方法,并将当前任务的状态传递到report()方法中,并返回结果,如下所示。
看来,这里还要看下report()方法啊,点进去看下report()方法的实现,如下所示。
可以看到,report()方法的实现比较简单,首先,将outcome数据赋值给x变量,接下来,主要是判断接收到的任务状态,如果状态为NORMAL,则将x强转为泛型类型返回;当任务的状态大于或者等于CANCELLED,也就是任务已经取消,则抛出CancellationException异常,其他情况则抛出ExecutionException异常。 至此,get()方法分析完成。注意:一定要理解get()方法的实现,因为get()方法是我们使用Future接口和FutureTask类时,使用的比较频繁的一个方法。 (6)set()方法与setException()方法 继续看FutureTask类的代码,接下来看到的是set()方法与setException()方法,如下所示。
通过源码可以看出,set()方法与setException()方法整体逻辑几乎一样,只是在设置任务状态时一个将状态设置为NORMAL,一个将状态设置为EXCEPTIONAL。 至于finishCompletion()方法,前面已经分析过。 (7)run()方法与runAndReset()方法 接下来,就是run()方法了,run()方法的源代码如下所示。
可以这么说,只要使用了Future和FutureTask,就必然会调用run()方法来运行任务,掌握run()方法的流程是非常有必要的。在run()方法中,如果当前状态不是NEW,或者CAS操作返回的结果为false,则直接返回,不再执行后续逻辑,如下所示。
接下来,在try代码块中,将成员变量callable赋值给一个临时变量c,判断临时变量不等于null,并且任务状态为NEW,则调用Callable接口的call()方法,并接收结果数据。并将ran变量设置为true。当程序抛出异常时,将接收结果的变量设置为null,ran变量设置为false,并且调用setException()方法将任务的状态设置为EXCEPTIONA。接下来,如果ran变量为true,则调用set()方法,如下所示。
接下来,程序会进入finally代码块中,如下所示。
这里,将runner设置为null,如果任务的当前状态大于或者等于INTERRUPTING,也就是线程被中断了。则调用handlePossibleCancellationInterrupt()方法,接下来,看下handlePossibleCancellationInterrupt()方法的实现。
可以看到,handlePossibleCancellationInterrupt()方法的实现比较简单,当任务的状态为INTERRUPTING时,使用while()循环,条件为当前任务状态为INTERRUPTING,将当前线程占用的CPU资源释放,也就是说,当任务运行完成后,释放线程所占用的资源。 runAndReset()方法的逻辑与run()差不多,只是runAndReset()方法会在finally代码块中将任务状态重置为NEW。runAndReset()方法的源代码如下所示,就不重复说明了。
(8)removeWaiter()方法 removeWaiter()方法中主要是使用自旋循环的方式来移除WaitNode中的线程,比较简单,如下所示。
最后,在FutureTask类的最后,有如下代码。
关于这些代码的作用,会在后续深度解析CAS文章中详细说明,这里就不再探讨。 至此,关于Future接口和FutureTask类的源码就分析完了。 好了,今天就到这儿吧,我是冰河,我们下期见~~ |
Posted: 28 Mar 2022 08:01 AM PDT @[toc] 1. xxl-job松哥也在微信群里和小伙伴们讨论过各自到底用的是 xxl-job 还是 ElasticJob,讨论的结果就是,xxl-job 使用的人更多一些。 不说功能的优劣,我们单纯从数据上其实就能看出一些端倪: 这是 xxl-job 的 GitHub: 这是 ElasticJob 的 GitHub: 从这个数据比较上大概也能看出来 xxl-job 更火一些。注意我这里说的是更火一些,不是说 xxl-job 比 ElasticJob 更强。 xxl-job 出自大众点评,这是一个分布式轻量级的任务调度框架,其核心设计目标是开发迅速、学习简单、轻量级、易扩展。 xxl-job 通过一个中心式的调度平台,调度多个执行器执行任务,调度中心通过 DB 锁保证集群分布式调度的一致性,这样扩展执行器会增大 DB 的压力,然而大部分公司的任务数,执行器并不多;xxl-job 提供了非常好用的监控页面甚至还有任务失败的邮件告警功能。不同于 ElasticJob,xxl-job 在使用时依赖 MySQL,而不需要 ZooKeeper。 ElasticJob 则出自当当,设计 ElasticJob 的初衷是为了面对高并发以及复杂的业务,即使是在业务量大,服务器多的时候也能做好任务调度,尽可能的利用服务器的资源。ElasticJob 是无中心化的,如果主服务器挂了,会自动通过 ZooKeeper 的选举机制选举出新的主服务器。因此 ElasticJob 具有良好的扩展性和可用性。 所以,你打算用哪个? 2. 运行 xxl-job我们先把 xxl-job 跑起来,再写我们自己的代码。 首先我们先把 xxl-job 的代码搞下来,地址: 然后用 IDEA 打开项目,打开之后,有四个主要的文件夹:
由于 xxl-job 运行需要数据库,所以接下来我们就来配置数据库,先找到数据库脚本,在 找到数据库脚本后,导入到数据库中执行一下,执行完成后,生成如下库和表: 接下来找到
日志配置也要修改一下,在
如果你是 Windows 操作系统,这里肯定要改,如果你是 Mac 的话,也有可能没有这个目录的权限,因此我建议大家改一下这里的配置,改为如下这样:
改为在项目运行目录下生成这个日志文件。 修改完成后,接下来我们就可以启动 xxl-job-admin 项目了,这是一个 SpringBoot 项目,找到启动类,直接运行其 main 方法即可。 项目启动成功后,浏览器输入如下地址 默认的登录账号是 看到如下页面,就是登录成功了。 3. 开发定时任务3.1 项目创建及配置接下来我们来创建一个项目,跑一个定时任务看看。 首先创建一个 SpringBoot 项目,引入 Web 依赖,如下: 项目创建成功后,引入 xxl-job 的依赖:
然后在 resources 目录下加入 logback.xml,内容如下:
修改 application.properties 文件,内容如下:
各项配置的含义我已经加了注释了。 接下来再提供一个配置类,如下:
其实就是把刚刚 application.properties 中的属性都配置成一个 XxlJobSpringExecutor Bean,很奇怪官方为什么没把这个搞成一个自动化配置的 Bean。 接下来我们就可以创建一个具体的定时任务了。 3.2 定时任务开发方式对于我们 Java 工程师而言,有三种开发定时任务的方式。 3.2.1 BEAN 模式(类形式)Bean 模式任务,支持基于类的开发方式,每个任务对应一个 Java 类。 优点:不限制项目环境,兼容性好。即使是无框架项目,如 main 方法直接启动的项目也可以提供支持。 缺点:
开发方式:
这种方式用的不多,我就不给大家演示了,小伙伴们可以自行尝试。 3.2.2 BEAN模式(方法形式)Bean 模式任务,支持基于方法的开发方式,每个任务对应一个方法,一般推荐这种方式。 优点:
缺点:
基于方法开发的任务,底层会生成 JobHandler 代理,和基于类的方式一样,任务也会以 JobHandler 的形式存在于执行器任务容器中。 开发步骤:
这里的
在这个过程中,我们需要通过 默认任务结果为 "成功" 状态,不需要主动设置;如有诉求,比如设置任务结果为失败,可以通过 然后启动 SpringBoot 项目。
接下来打开配置调度中心,找到执行器管理,点击新增,如下: 如果是自动注册的话,可以不用填机器地址。 接下来找到任务管理,并点击新增: 基础配置都没啥好说的。 调度类型选择 CRON 表达式,CRON 表达式可以自己填,也可以点击后面的编辑按钮自动生成。 运行模式就选择 BEAN,JobHandler 的值就是我们前面 @XxlJob 注解中填的值,任务参数就是定时任务的方法参数。 配置完成后,回到执行器管理,点击查看,可以查看刚刚注册的节点信息: 再次回到任务管理,选择启动,就可以开始定时任务的执行了: 开启之后,点击调度日志,就可以看到我们系统任务执行的详细信息了: 可以看到,每隔 5 秒执行一次。 点击调度备注,可以查看一些调度细节: 在右边操作按钮中选择执行日志: 可以查看执行细节: 红色框出来的,就是我们刚刚自己打印的。 当然这里还有一些其他玩法,小伙伴们可以自行点击按钮尝试,我就不赘述了。 3.2.3 GLUE 模式(Java)任务以源码方式维护在调度中心,支持通过 Web IDE 在线更新,实时编译和生效,因此不需要指定 JobHandler。即在网页上写定时任务的代码,然后去执行。 这种方式个人感觉使用较少,小伙伴们了解一下即可。 开发流程如下:
编辑完成后,保存即可。接下来又和前面一样了,启动任务,后查看调度日志。 这里支持 30 个版本的版本回溯,在 GLUE 任务的 Web IDE 界面,选择右上角下拉框"版本回溯",会列出该 GLUE 的更新历史,选择相应版本即可显示该版本代码,保存后 GLUE 代码即回退到对应的历史版本。 4. 小结好啦,这就是我跟大家介绍的 xxl-job,感兴趣的小伙伴可以试试哦~ 公众号后台回复 |
Posted: 28 Mar 2022 05:30 PM PDT 读者反馈:今年这波裁员有点凶,但在我们公司去年已经有了些不好的苗头,为了不让自己那么拉胯🌶,躲不过各种坑坑洼洼。跟着小傅哥的博客内容补全了自己很多的知识,包括:中间件、字节码、DDD项目、设计模式以及面试手册等,终于算是有了一点点竞争力。赶在这波裁员时上岸了!可能也有运气的存在,继续努力吧! 技术是长期积累沉淀的,并不是一蹴而就的,更不是成为一堆没用资料的收藏家。只有跟随还在一线编码的硬核号主,吸收实战经验才能快速成长。-
有价值的干货资料介绍1 Java 面经手册
《Java 面经手册》是一本以面试题为入口讲解 Java 核心技术的 PDF 书籍,书中内容也极力的向你证实 编码只是在确定了研发设计后的具体实现,而设计的部分包括:数据结构、算法逻辑以及设计模式等,而这部分数据结构和算法逻辑在 Java 的核心 API 中体现的淋漓尽致。那么,也就解释了为什么这些内容成为了热点面试题,虽然可能我们都会觉得这样的面试像是造火箭。 2 重学Java设计模式 - PDF版
本书是作者小傅哥,基于互联网真实案例编写的Java设计模式实践图书。全书以解决方案为核心,从实际开发业务中抽离出交易、营销、规则引擎、中间件、框架源码等22个真实场景,对设计模式进行全面、彻底的分析。帮助读者灵活地使用各种设计模式,从容应对复杂变化的业务需求,编写出易维护、可扩展的代码结构。 3 字节码编程全书共计107页,11万7千字,20个章节涵盖三个字节码框架和JavaAgent使用并附带整套案例源码! 讲道理,市面上以及网络搜索中都基本很少有成体系的关于字节码编程的知识,这主要由于大部分开发人员其实很少接触这部分内容,包括;ASM、Javassist、Byte-buddy以及JavaAgent,没有很大的市场也就没有很多的资料。但大家其实已经从其他的框架或者中间件中使用到,就像你用到的;Cglib、混沌工程、非入侵的全链路监控以及你是否使用过jetbrains-agent.jar做了某项实验? 4 Spring 手撸专栏在写了部分关于 Spring核心源码 的面经内容后,我决定要去手撸一个Spring了。为啥这么干呢?因为所有我想写的内容,都希望它是以理科思维理解为目的方式学会,而不是靠着硬背记住。而编写面经的过程中涉及到的每一篇Spring源码内容分析,在即使去掉部分非主流逻辑后,依然会显得非常庞大。 此专栏是一本以开发简化版Spring学习其原理和内核的知识内容,不仅是代码编写实现也更注重内容上的需求分析和方案设计,所以在学习的过程要结合这些内容一起来实践,并调试对应的代码。粉丝伙伴在阅读的过程中,千万不要害怕在学习的过程中遇到问题,这些都是正常的! 希望你可以一直坚持把这些内容事必躬亲、亲历亲为的学完,加油! 5 IDEA Plugin 开发手册此开发手册,分为4章12节循序渐进的通过实践案例开发的方式,串联 IDEA Plugin 开发的各项常用技术点,为读者讲解如何开发一个 IDEA 插件。 IDEA 插件开发可以帮助研发人员提升能效,解决一些实际场景中的共性问题。但最近在折腾IDEA插件开发的时候,市面的资料确实不多,也没有成体系完整的开发指导手册,所以就遇到了很多不知道就不会的事情,需要一点点查询搜索源码、验证API接口,最终把各项功能实现,当然在这个过程中也确实踩了不少坑!接下来在这个专栏会把一些关于 IDEA 插件开发用到的各项知识做成案例输出出来,帮助有需要的研发伙伴,一起建设 IDEA Plugin。 6. Lottery 抽奖系统 - 基于领域驱动设计的四层架构实践
7. SpringBoot 中间件小册全小册19个章节,包括16个中间件的设计和开发,包括测试案例共30个代码库提供给读者学习使用。小册实现的中间件场景涵盖:技术框架、数据服务、数据组件、分布式技术、服务治理、字节码、IDEA插件七个方面,贯穿整个互联网系统架构中常用的核心内容。非常值得了解、学习、实践到掌握。
小傅哥所编写的这些技术资料,皆是亲自验证、体系梳理、逐步总结的技术内容,所以在学习的过程中一定要对照源码对应的案例进行学习,这样才能让你有更大的收获。 最后,我想说:能力,是你前行的最大保障。哪怕你是兢兢业业的工作者,也要拥有 |
Posted: 27 Mar 2022 07:38 PM PDT 0 起因前些日子,有位同学找我咨询求职问题。他本科专业其实不错,但是第一份工作没找好,所以只好报了家培训班,想社招之路走得更稳一些。结业之前,他担心培训班就业辅导不够,也找了几位外部导师帮忙。其中就包括我。 熟悉我的人都知道,我比较反对报培训班,因为:
这次咨询也基本印证了我的观点。所以我想再分享一下,到底招聘方的情况是怎样的,他们有哪些需求,准备简历的时候应该注意什么,面试的时候又应该注意什么。 1 企业要招人1.0 产生需求某天,老板想做个产品。我们假设公司里已经有靠谱的技术团队和技术管理,那么,技术主管很快就会梳理需求,落实到岗位和个人,如果他发现,目前的人力无法应对满足需求,就会启动招聘。 1.1 产生岗位描述(JD)岗位描述(简称 JD),即这个岗位职责是什么、需要哪些职业技能、工作地点和福利待遇等,是招聘前必须准备好的物料。 产品确定、需求确定,所以岗位需求和岗位职责基本也是确定的。 此时技术主管一般会找来以前的 JD,改一改职责和需求,完成新的 JD。然后 ta 会把 JD 发给招聘负责人(多半是 HR),上传到招聘网站、开发者社区、公司招聘库等等。 比如 Code.fun 加入我们 就是一份很典型的 JD。 1.2 筛简历一段时间后,HR、技术主管会从各种渠道获得一大批简历。于是他们就要从这一大堆简历快速筛选出能满足岗位需要的候选人。 怎么筛呢?一般是关键词。 拿我某项工作来举例,我当时负责开发的 Showman 产品,是浏览器插件,界面部分采用 Vue,需要兼容 Puppeteer API。那么在我招聘的时候,就会很重视候选人这几方面的经历。因为匹配度越高,他能顺利接手工作、快速度过适应期的可能性就越大。 找到关键词之后,HR 可能会接受简历,转发给岗位负责人,即初筛。而岗位负责人则会结合候选人的教育经历、职业经理、项目经验等,判断这些关键词的可靠性与含金量,最终再筛选出其中最合适的一些人进入面试环节。 1.3 面试招聘方从成百上千的简历中筛选出了若干的"看起来"比较合适的候选人,接下来,就要约过来面试聊聊,看看哪些人真正合适。 面试的过程,其实是验证简历的过程。 张三简历里说,他开发过浏览器插件,但是这个插件有多少用户、运营过多久,在开发过程中解决过多少问题,并没有写得非常详细;即使写得很细,也未必跟他有关系。所以作为面试官,我就需要再面试中弄清楚,这项职业经历对他来说,是加分还是减分,甚至是一票否决。 除了验证简历,面试还可以让招聘方初步了解候选人的非职业特性,比如沟通能力、语言概括能力、接人待物能力,等等。这些东西在招聘中占比不大,不过在诸多候选人的技术能力拉不开差距的时候,也会是必要的判断依据。 1.4 多轮面试单场面试时间有限,通常做不到面面俱到。所以面试通常不止一轮。每个面试官关注的点不一样,比如 A 关注项目经验、B 关注代码实现能力、C 关注职业规划等。最终 ABC 的观点会汇总到一起,给每个候选人一个总评。 接下来,面试进行到一定阶段,总评过关的人数积累到一定数目,公司就会给其中比较出色的几位发 offer,然后就是入职试用转正,略过不谈。 2 我们应该怎么做2.1 简历要有针对性正如前文所说,企业收到的简历量很大,筛简历的压力也很大。通常来说,招聘方负责人会花在每个简历上的时间很短,基本上就扫一遍,有必要的关键词就再看仔细一点,没有就直接扔掉。 很多同学只做一套简历,投所有企业所有岗位。在这种情况下希望渺茫:A 公司招数据可视化,B 公司招小程序开发,C 公司主做移动端,三家公司的岗位要求差异很大,一套简历很难覆盖不同公司的不同需求。 有同学说那我简历里把所有技术关键词都写上可以么?很遗憾,招聘方也不是傻子,你的工作年限和项目经验、技术专长不符,也难逃直接扔掉的命运。或者简历太长,重点不突出,要费力查找关键词,招聘方多半也会直接扔掉。 当然,为每家公司单独准备一份简历成本太高,也不现实。所以,推荐的做法是:
2.1.1 特殊技巧(我再想想要不要写……) 2.2 简历要尽可能真实,面试前也要做好准备前文也提过,面试是验证简历的过程。能够走到面试这一步,说明招聘方认为候选人的履历可以满足岗位需求,接下来就是要对简历验真,以及判断候选人的发展潜力和综合排名。 所以简历里的内容可以适度美化,但一定不要做假。为什么呢?面试时间有限,面试官不会针对简历中的每一项进行审查,而是对自己关注的、擅长的领域盘问,也就是大概抽查 20% 的内容,给整份简历打分。 如果候选人简历有做假,面试时被面试官发现,ta 可不会只扣这一条的分,而是整份简历都显得不可靠,都要扣分。即使剩下的部分都是真实的,但是没有被抽检到,就不会改变面试官的判断。甚至,如果连问两条都不符合预期,可能就会直接中止面试。 面试前的准备也要尽量做好。比如你参考上一条建议,优化了简历内容,突出以前的某段项目经历以匹配特定关键词。那么这个时候最好回顾一下,审视一下当时的技术方案有何得失,哪里值得改进,自己的工作有何值得称道的地方,等等。然后搜搜看该领域目前的发展状况。不要面试官问起来,这个想不起那个不知道,好好的加分项直接被干成减分项。 2.3 项目经验要择优表现通常来说,简历不要太长,因为筛简历的人不会有那么多时间认真仔细的读。有人说不要超过一页,我觉得不用这么极端,但是三页绝对是极限了。短简历更容易突出重点、突出优势。 所以通常来说,我们要对项目经验进行取舍,不要把每个项目都罗列出来,即使是海投简历,也要选择会增加自己竞争优势的项目,写到简历里。 比如,某位培训班同学的简历里,介绍 ta 做过的某个全栈项目,混用 MySQL + MangoDB,koa2 + express。这就很奇怪,这两组技术产品定位冲突,在实际生产中,几乎不会混用。所以这样的项目经验就只能减分。还有,在一些同学的简历里看到,他们这两年都还在用 easyui、jquery 做项目,我当然不否认这些传统框架也能完成产品需求,但是写到简历里,就不要指望它们还能帮你加分。 如果,极端情况,你的项目经验都很差,那我建议你抓紧时间参与一些能给自己加分的项目,不管是开源的、商用的、独立项目都可以,别憨憨的写一堆没价值的项目,然后抱怨拿不到面试机会。 2.4 职业经历/项目经历要能让面试官感知到你的优势很多同学写简历写到职业经历和项目经历时会写的特别平铺直叙、没有重点、缺少关键信息。比如,做过 OA 系统,就把自己负责的模块都列举一下,或者把流程叙述一遍。这样的信息对面试官来说毫无意义:OA 系统大部分人都用过或了解,公司内的工作流程基本也大同小异。这样的简历基本难逃扫一眼直接丢掉的命运。 提升面试官感知有三个方法:
提醒大家,如果你要用这个方法吸引招聘方注意,那就要做好相应的准备,避免偷鸡不成蚀把米。比如,你要写数字,就要知道数字是怎么统计出来的;你要写方案,就要写有说服力的方案。 2.5 面试时找机会突出自己的优势目前来看,招聘方占据优势,是买方市场。招聘方会在众多候选人当中选择最好的几个发出 offer。所以我们在面试时,不仅要证明简历上写的都是真实可靠的,还要抓住机会,展现自己的优势。不然,平平淡淡拿到一个中等分数,可能在招聘方的眼里,只是一块"鸡肋"。 别的岗位就不说了,只说技术开发岗。 通常来说,技术研发,简历外,通常需要在面试中展现的能力主要有:
这些条件可能不足以形成一票通过/否决,但会影响你在同一批候选人里的排名,也要尽量好好表现。另外,不同的公司、职级、岗位,可能也有不同的权重,我就不详细解说了。 总结大家在求职面试时,不仅考虑到自己,也要多多站在招聘方的角度想一想,对方的环境、对方的需求、对方的判断方式,你应该怎么应对。哪些东西可以不写/说,哪些东西应该强调,哪些东西会加分,哪些东西会减分。一件事情做与不做,是这么做还是那样做,一篇文章一个问答,我们是否要照做,都可以用这个方式来判断。 希望大家都能找到满意的工作。有任何想法和意见,也欢迎留言讨论。 本文参与了 SegmentFault 思否征文「如何"反杀"面试官?」,欢迎正在阅读的你也加入。 本文原载于 我的博客,两边同步沟通,欢迎访问。 |
Posted: 27 Mar 2022 07:10 AM PDT @[toc] 1. 什么是表分区小伙伴们知道,MySQL 数据库中的数据是以文件的形势存在磁盘上的,默认放在 我们进入到这个目录下,就可以看到我们定义的所有数据库了,一个数据库就是一个文件夹,一个库中,有其对应的表的信息,如下: 在 MySQL 中,如果存储引擎是 MyISAM,那么在 data 目录下会看到 3 类文件:
如果存储引擎是
无论是哪种存储引擎,只要一张表的数据量过大,就会导致 为了解决这个问题,我们可以利用 MySQL 的分区功能,在物理上将这一张表对应的文件,分割成许多小块,如此,当我们查找一条数据时,就不用在某一个文件中进行整个遍历了,我们只需要知道这条数据位于哪一个数据块,然后在那一个数据块上查找就行了;另一方面,如果一张表的数据量太大,可能一个磁盘放不下,这个时候,通过表分区我们就可以把数据分配到不同的磁盘里面去。 MySQL 从 5.1 开始添加了对分区的支持,分区的过程是将一个表或索引分解为多个更小、更可管理的部分。对于开发者而言,分区后的表使用方式和不分区基本上还是一模一样,只不过在物理存储上,原本该表只有一个数据文件,现在变成了多个,每个分区都是独立的对象,可以独自处理,也可以作为一个更大对象的一部分进行处理。 需要注意的是,分区功能并不是在存储引擎层完成的,常见的存储引擎如 2. 分区的两种方式不同于 MyCat 中既可以垂直切分又可以水平切分,MySQL 数据库支持的分区类型为水平分区,它不支持垂直分区。 2.1 水平切分先来一张简单的示意图,大家感受一下什么是水平切分: 假设我的 DB 中有 table-1、table-2 以及 table-3 三张表,水平切分就是拿着我 40 米大刀,对准黑色的线条,砍一剑或者砍 N 剑! 砍完之后,将砍掉的部分放到另外一个数据库实例中,变成下面这样: 这样,原本放在一个 DB 中的 table 现在放在两个 DB 中了,观察之后我们发现:
这就是数据库的水平切分,也可以理解为按照数据行进行切分,即按照表中某个字段的某种规则来将表数据分散到多个库之中,每个表中包含一部分数据,即水平切分不改变表结构。 2.2 垂直切分先来一张简单的示意图,大家感受一下垂直切分: 所谓的垂直切分就是拿着我 40 米大刀,对准了黑色的线条砍。砍完之后,将不同的表放到不同的数据库实例中去,变成下面这个样子: 这个时候我们发现如下几个特点:
这就是垂直切分。一般来说,垂直切分我们可以按照业务来划分,不同业务的表放到不同的数据库实例中。 MySQL 数据库支持的分区类型为水平分区。 此外,MySQL 数据库的分区是局部分区索引,即一个分区中既存放了数据又存放了索引,目前,MySQL数据库还不支持全局分区(数据存放在各个分区中,但是所有数据的索引放在一个对象中)。 3. 为什么需要表分区
分区的限制和缺点:
4. 分区实践说了这么多,来个例子看一下。 首先我们先来查看一下当前的 MySQL 是否支持分区。 在 MySQL5.6.1 之前可以通过命令 从 MySQL5.6.1 开始, 确认我们的 MySQL 支持分区后,我们就可以开始分区啦! 接下来我们来看几种不同的分区策略。 4.1 RANGE 分区RANGE 分区比较简单,就是根据某一个字段的值进行分区。不过这个字段有一个要求,就是必须是主键或者是联合主键中的某个字段。 例如根据 user 表的 id 进行分区:
上面的规则涉及到了 id 的所有范围了,如果没有第三条规则,那么插入一个 id 为 300 的记录时,就会报错。 建表 SQL 如下:
表创建成功后,我们进入到 可以看到,此时的数据文件分为好几个了。 在 也可以自己写个 SQL 去查询:
每一行展示一个分区的信息,包括分区的方式、该区的范围、分区的字段、该区目前有几条记录等等。 RANGE 分区有一个比较典型的使用场景,就是按照日期对表进行分区,例如同一年注册的用户放在一个分区中,如下:
注意,createDate 是联合主键的一员。如果 createDate 不是主键,只是一个普通字段,那么创建时就会抛出如下错误: 现在,如果我们要查询 2022 年注册的用户,系统就只会去搜索 p2022 这个分区,通过 explain 执行计划可以证实我们的想法: 如果想要删除 2022 年注册的用户,则只需要删除该分区即可:
由上图可以看到,删除之后,数据就没了。 4.2 LIST 分区LIST 分区和 RANGE 分区类似,区别在于 LIST 分区是基于列值匹配一个离散值集合中的某个值来进行选择,而非连续的。举个例子大家看下就明白了: 假设我有一个用户表,用户有性别,现在想按照性别将用户分开存储,男性存储在一个分区中,女性存储在一个分区中,SQL 如下:
这个表将来就两个分区,分别存储男性和女性,gender 的取值为 1 或者 0,gender 如果取其他值,执行就会出错,最终执行结果如下: 这样分区之后,将来查询男性或者查询女性效率都会比较高,删除某一性别的用户时删除效率也高。 4.3 HASH 分区HASH 分区的目的是将数据均匀地分布到预先定义的各个分区中,保证各分区的数据量大致都是一样的。在 RANGE 和 LIST 分区中,必须明确指定一个给定的列值或列值集合应该保存在哪个分区中;而在 HASH 分区中,MySQL 自动完成这些工作,用户所要做的只是基于将要进行哈希分区的列指定一个表达式,并且分区的数量。 使用 HASH 分区来分割一个表,要在 CREATE TABLE 语句上添加
4.4 KEY 分区KEY 分区和 HASH 分区相似,但是 KEY 分区支持除 text 和 BLOB 之外的所有数据类型的分区,而 HASH 分区只支持数字分区。 KEY 分区不允许使用用户自定义的表达式进行分区,KEY 分区使用系统提供的 HASH 函数进行分区。 当表中存在主键或者唯一索引时,如果创建 KEY 分区时没有指定字段系统默认会首选主键列作为分区字段,如果不存在主键列会选择非空唯一索引列作为分区字段。 举个例子:
4.5 COLUMNS 分区COLUMN 分区是 5.5 开始引入的分区功能,只有 RANGE COLUMN 和 LIST COLUMN 这两种分区;支持整形、日期、字符串;这种分区方式和 RANGE、LIST 的分区方式非常的相似。 COLUMNS Vs RANGE Vs LIST 分区:
COLUMNS 支持的类型
举个例子看下:
这是 RANGE COLUMNS,分区值是连续的。 再来看 LIST COLUMNS 分区,这个就类似于枚举了:
5. 常见分区命令
6. 小结不知道小伙伴们是否还记得松哥 2019 年写的 MyCat 教程(公众号江南一点雨后台回复 2019 有文章索引),这些分区策略是不是和 MyCat 中的策略非常相似呀?感兴趣的小伙伴赶紧去试一把吧~ 参考资料: |
Posted: 27 Mar 2022 10:13 AM PDT 前言没事翻了翻 为什么推出 v6
我们通过代码来感受下,这是
这是
这个例子表明
增加了哪些特性?
之前:
现在
就感觉更优雅一些
之前
现在
这个变化不是很大
`
体验 v6这里我们使用
打开
现在,仍在
运行 如何升级 v6官方的迁移指南在这里:React Router v6 迁移指南 参考文章结语如果你正在用 重要的事如果你觉得这篇内容对你挺有启发,别忘记点赞 + 关注 欢迎添加我的个人微信:Jiang9684,一起交流前端技术 我的博客地址 |
Posted: 25 Mar 2022 12:29 AM PDT 近期热播的电视剧《人世间》,讲述了70年代无数普通人的故事,细腻的人物形象和真实的故事感动着我们。原来在那个年代,我们的父母和祖辈都在为新中国的美好生活而奋斗着,为国家舍弃了小家团聚的机会;原来在那个年代,拥有一张合照也不是容易的事情。 多年来,随着影像技术的迭代更新,人们的多彩生活被即时记录着。同时,给黑白图像增添色彩的技术也层出不穷,HMS Core视频编辑服务便是其一。通过其AI着色能力,可以智能填充黑白照片及视频的色彩,让黑白图像变得鲜活多彩! 当然,除了AI着色能力外,视频编辑服务还提供了专属滤镜、人物跟踪、一键染发、动态图片和人脸遮挡功能。移动应用集成这些能力之后,用户便可以随心复刻心仪图片的滤镜效果,实现百变发色,还可以锁定视频中的特定人物,让静态图片鲜活生动…… 除此之外,视频编辑服务还支持多视频/图片的导入,可随时调整片段的顺序时长,实现多分辨率导出,最高支持输出4k的视频分辨率和60fps的帧率。 使用场景:随时随地剪大片 视频编辑服务的功能丰富,可以服务的场景也是各具特色,具体可以使用在以下场景:
集成方式:多种接口灵活选择 目前,HMS Core为开发者提供了两种视频编辑服务的集成方式: 1.视频编辑UI SDK,提供产品级UI界面,集成简单。 2.视频编辑原子能力SDK,提供数百个底层能力接口,包含多个AI算法能力接口,可根据业务场景灵活选择。 这两种方式均提供导入、编辑、渲染、导出、媒体资源管理等一站式视频编辑能力,提供性能优异、简单易用、高兼容性的接口,帮助您轻松构建应用。您可根据使用场景选择不同的集成方式获取视频编辑能力。 了解更多详情>> 访问华为开发者联盟官网 关注我们,第一时间了解 HMS Core 最新技术资讯~ |
You are subscribed to email updates from SegmentFault 最新的文章. To stop receiving these emails, you may unsubscribe now. | Email delivery powered by Google |
Google, 1600 Amphitheatre Parkway, Mountain View, CA 94043, United States |
No comments:
Post a Comment