某芯片生产相关的智能制造外包面试经历
好的,我来重新整理一份更详细、保留更多口语化细节的版本~
面试录音文字整理(24分17秒)
开场
面试官:行,那我们现在开始呗。你先做个简单的自我介绍,然后重点介绍一下自己的项目经验。尤其是可以讲一讲自己最熟悉的一两个项目,它是什么样子的项目和业务,逻辑大概是什么样子的?以及你在项目中负责哪部分?然后自己的一些贡献。之后我们再聊一下用到的哪些技术。
自我介绍 & 项目介绍
候选人:嗯,好的......此除省略1900字自我介绍
全流程参与度
面试官:我看你简历里面有写,有全程参与了从需求到设计开发测试以及上线。整个流程的那端都有参与吗? 候选人:对,开发这边就是,比如说像抽样这个需求。可能我们这边跟组长需要敲定,比如说组长那边可能业务侧的要求就是要抽多少多少订单,我们讨论,比如说业务侧他可能抽取订单的条数经常变化,我们给他做一个灵活配置化的功能,相当于做这种,这样的话用户业务侧想要修改他们这个抽取需求的时候,就改前台的配置页面。我们又讨论,在具体实施的时候又讨论发现分片多进程之后,存在数量不准的问题,之后讨论用Redis来中间做一个分布式计数,来保证最最后的数量是相当于与他们要抽取的数量没有太大差异。
面试官:所以你说的讨论是你们team的所有人一起参与这部分的分析讨论。 候选人:讨论的话,需求分析阶段是跟组长一起,跟业务侧市场部拉会,相当于跟提出需求方,讨论需求。设计阶段,相当于编写设计文档,在设计评审里面跟组长具体再根据设计文档再讨论。讨论有没有需要填充的地方,最后代码开发阶段,相当于就执行代码开发,之后再进行单元测试,提测,最后在上线的时候可能要把这些脚本准备好,然后把对应的代码分支合并到生产分支执行构建发布,然后到生产才能跟测试一起,走这个过程,如果有问题可能要拉一起分析。
面试官:那其中哪些环节是你主导的人员?或者独立完成的? 候选人:独立完成,这个其实设计阶段,还有开发阶段,还有单元测试,这三个阶段是我独立完成。需求分析是我跟组长一起,相当于一起,跟业务侧那边去拉重新会议讨论。上线阶段的话就是测试那边负责发布,由发布组负责发布,测试负责测试,我负责在旁边监控吧,跟踪一下,跟测试如果有问题,测试把日志发出来,我去分析日志,找原因。
需求分析 & 设计经验
面试官:嗯,行,你觉得跟业务去做需求分析以及方案设计的这些这两个阶段,需要注意一些什么点呢? 候选人:需求分析阶段,需求分析阶段的话,主要就是要确定需求,客户业务侧提出的需求合不合理,比如说能不能实现,或者主要是合不合理。比如说他提出来要把一些值写死之类的,这些像这种代码里面写死这种过多的因素肯定是不行的,要搞那种灵活配置化,相当于让他提一次需求,尽量把问题都给解决了,就是不要反复提需求。
设计阶段的话,这个我就比较了解的多一点,因为主要我就负责很多设计,就相当于把过程中的一些表结构基本上都设计出来,表结构表名涉及的数据库要不要分库分表,用什么字段分库分表,大概的数据量估算一下,具体的状态流转级,状态级流转设计出来,把相关的需要多少个定时任务,每个定时任务的参数如何配置梳理出来,再梳理出来前端的一些,比如说一些核心的接口的逻辑也梳理出来,相当于最后再把一些相关的,比如说异常处理的办法,比如说这个进程,如果哪一天出了异常,我们怎么去分析日志,怎么去排查,把这些都给定死,然后再进行讨论,这是需求跟设计阶段。
业务复杂度 & 性能优化
面试官:你觉得你们的业务复不复杂? 候选人:业务不复杂,业务的话,业务的话根据具体的跟需求来看吧,有一些需求,就比如说有一些需求是比较简单,业务相对简单,比较复杂的业务就像,像这种,客户资料类,要查询客户资料,查询一些认证相关的表,判断这个用户是什么代办、经办之类的,来获取不同的信息。他细化的东西很复杂,但是针对这种复杂的需求,我觉得可以先尝试,把这个主流程给开发出来,针对细化的东西再慢慢去迭代优化,相当于这样的话,就可以把复杂的业务流程,就是一步步做出来。
面试官:行,你们的业务量有多大?业务量。系统的。系统负载有人做了吗? 候选人:系统负载的话,我们这边相当于,其实相对来说,前台发起的QPS不是很多,大概平时也就只有两三千左右,月底业务人员赶业务的时候,可能可以达到四五千。我们这边消息队列的话,一天差不多是处理百万级别的消息量。针对百万级别的消息量,我们可能把这个消息,再相当于具体一个消息可能匹配多个规则,再分发多个工单,可能数据一天的数据量可以达到千万级别。
面试官:那对于这种数据量大,或者性能需求会要比较高的,那你有没有一些实践经验,或者是一些方案设计的方法? 候选人:这个是,就比如说我们这个消息消费,比我们一天要消费几百万条消息,如果在消费者里面相当于太多的查询,查询这个规则还是一些反复的查询操作。这一步如果要是每一次都查表,它这个就其实很影响性能。如果不查,相当于这一步一定要设计Redis缓存,所有的规则统一走Redis缓存去获取,可以基于生效版本的机制,相当于保证这个查询Redis缓存可以获取到当前生效版本的数据,保证它这个查询缓存就能保证这个数据一致性,跳过所有的查数据库表的获取规则的步骤。这一步从规则方面来降低,还有就是相当于合理的使用线程池吧,相当于有一些可以并发执行的任务,可以用ThreadPoolExecutor加CountDownLatch去做。
还有的一些,比如说数据库的话,前台一般免不了一些数据库查询,虽然一部分的查询可以用Redis缓存几分钟,相当于来减少频繁查库,但是查库是不可避免,所以说一定要设计分库分表。相当于我们这边,它处理多个地市的数据,所以我们可以按照业务,也可以按照地市,相当于做数据库做表的那个水平拆分,拆分多张表。针对于用户,比如说像推送工单,基本上一个人他按月去查也可以,相当于再按照月份再进行水平分表,每一个月每个地市查一张表做分表,同时再合理的添加数据库索引,相当于用户,我们这个派单最好是精确到派给指定的人,这样的话可以根据他这个用户ID加索引,这样优化查询。相当于总共大概就是这几个,一是并发,二是分表,三是加缓存,实在不行就扩资源,大概这几个步骤去处理。
Redis 数据类型
面试官:嗯,你刚才说到了Redis缓存嘛,也就是其实项目中有使用过Redis。 候选人:对。 面试官:Redis,那就说一下Redis常见的数据类型。 候选人:常见的数据类型一般就是它的字符串类型,直接set进去,一般就是set key1 v1,这种就是字符串,然后字符串的话,它可以比较常用的基本就是set、setNX、setEX。还有比如说increase by,decrease by。还有就是list,list也是常用的类型,一般基于查询条件比较少的列表,可以直接基于list缓存到Redis上面,查询的时候可以直接用这个range,去根据用户的分页条件,直接从Redis的list里面返回。
还有就是用哈希,哈希的话,一般规则配置信息我们都会用哈希去存,相当于某一个,针对某一个配置表,我们就会用一个哈希去存,它这里面某个哈希对应的值,是这个规则小的这个JSON的值。还有就是,bitmap位图。Bitmap位图,这个我们主要用的就是相当于布隆过滤器有时候会用的。就是说针对某一个key,计算它几种计算几次哈希值,然后把这几次哈希值都对这个位图的大小去按位与,塞进去。在下一个值,下一次请求到来的时候,再计算哈希值,看那位图上面有没有,如果有的话就相当于这个才是有效数据,如果没有直接给过滤掉,用一个布隆过滤器。还有就是zset,zset的话可以,这zset可以记录一个score得分,还有它的具体的元素的一个值,它可以支持根据分数去排序,然后取前几名,也可以支持批量去获取到某一个区间内的全部元素,删除某一个区间内的全部元素的。
设计模式
面试官:嗯嗯,你刚才说到了策略模式。那其实是属于加了设计模式中的一种嘛。那你可以再列举几个你熟悉的那种设计模式,以及它们大概是怎么去实现的? 候选人:OK,它还有常见比较常见的,跟策略模式很像,是模板方法模式。比如说我们系统要做一个文件上传,用户可以根据URL上传,也可以根据直接上传具体的文件。但是上传具体步骤就不是很像,都是处理字节流,然后字节流上传对应的调用方法,上传对应的服务器。基本上可以把字节流上传,还有调用接口这些逻辑,拆写到abstract的template里,相当于具体的URL上传和文件上传,它只需要继承这个类,把具体的URL解析成byte数组,还有byte流和文件解析成byte流的方法实现一下,其他都不用动,这就是模板方法模式。
还有就是装饰器模式,Java的IO流就是装饰器模式。比如说它原本的FileInputStream就是只能处理比较简单的文件读写,然后在它外层你有一个BufferedInputStream,BufferedOutputStream,FileOutputStream刚才讲,然后再你有一个BufferedOutputStream,这样你去写的时候,它就会用这个Buffer去缓冲。然后的话还有比较常见的就是我想想策略模式,模板方法,还有就是工厂模式,像Spring的IOC容器,它其实就是工厂模式,相当于它有一个BeanFactory去获取具体的Bean对象的时候,它把复杂的Bean对象的创建逻辑全部封装到BeanFactory里面,我们再去获取对象,只需要用BeanFactory点getBean就可以。
多线程 & 线程池
面试官:嗯,然后也前面也说到了多线程,那要多线程就必须介绍一下线程池了。就你在你们在项目中就线程池一般是怎么? 候选人:一般项目中配置,就用那个ThreadPoolExecutor,之后它基本上有几个重要的参数,一个是核心线程,一个是最大线程,还有阻塞队列大小,还有拒绝策略,还有这个线程工厂名字。以及最大线程空闲时间,一般来说我们比如说像我们配置一个线程池的时候,如果是IO密集型,可能就给它设置成CPU核心数加一,最大线程数设置成CPU核心数线程数乘二,但是是比较理想的情况。一般对线程池一定要添加一个工具类动态去设置值,比如说可以监听ZK上面某个节点变化,动态去设置线程池的参数。等到上线之后,先大批量数据压测,压一把,发现这个处理能力不够的时候,相当于调整线程池参数,比如说发现它设置成CPU核心线程数,最大,核心线程数设置成CPU同样的大小不太合适,相当于给它再调两倍,再压一把试一下,发现不行的话,相当于再把这核心线程数再调小一点,再压一把。
还有就是比如说记录日志的时候,我们也会用线程池,但是记录日志这都属于非核心操作,如果使用太多的线程,相当于可能会影响到这服务器性能,一般这个就这个两三个左右,在业务上再压测一下,基本上如果可以的话,就设置很少的线程数。
面试官:那你在项目中有使用哪一种这种多线程的方法去做开发? 候选人:一般我用的比较多的其实就是两种,一种就是直接,比如说我获取到一组,比如说我要一下执行很多条,执行很多任务,我可能把任务拆分成一个个小任务,把这个小任务就是交给这个线程池去处理,每处理完或者抛异常,直接countDownLatch countDown,然后外层要countDown wait,只等待一些时间,等待所有的线程执行完,再去下,走下面一个。还有一种就是比如说记日志,记日志这种就没必要等,相当于直接处理完,直接调Execute的方法给它传进去,直接我这个请求就结束,给客户端响应。这一般是这两种。
收尾
面试官:行。行,我没其他问题了。嗯,好的。然后因为我们这边是做那种跟芯片生产相关的智能制造的。所以他对这个信息安全啊,他用的比较重,所以一般我们如果到我们这边的话,你在进办公室之前,大部分地方的手机是不允许带进办公室的,是需要放到外面的密码箱的。 候选人:嗯,好的。 面试官:因为有很多人可能比较介意这种。 候选人:这个不介意,接受。 面试官:对,另外就是我们的业务会比较复杂。可能是你接触过的所有业务里面最复杂的一种。就这边可能会有点压力。 候选人:可以接受。 面试官:你是离职的状态吗?还是? 候选人:目前是在职。 面试官:目前是在职,那是为啥是想想要换一个工作? 候选人:因为相当于我对这个公司的技术栈,还有常用的业务的处理方法都比较熟悉,相当于后面的提升可能就比较有限,人可能就偏向懒散。就是后面不会有很多成长,所以一定要。就类似于走出舒适圈。 面试官:嗯,你就想要一些新的挑战。好,了解。行,那我这边没其他问题了,那我们今天先这样。好的,谢谢。 候选人:嗯,好的,谢谢。
