最近的一次面试经历

最近项目解散了,我离开了公司,出来找工作。在我更新简历后,有hr找到我面试,是我喜欢的公司,我欣然接受了。hr给我定了岗位,我觉得也可以。

本来我想等面几家公司后,有经验了再面这家公司,可就这么巧合。

一面

很快,hr帮我预约了一面,是视频面试。

等到快面试的时候,hr联系我改期,说是其中一位面试官临时有事。

我电脑关机,准备去吃饭。有人打电话给我,原来是另外一位面试官,问我没来面试的事,我跟他说hr改期了。他表示不好意思,他没有收到通知,误会了。我跟他说没事的。

第二天,hr帮我重新预约了一面。

面试真的要开始了,我提前5分钟进入视频会议室,看到已有一位面试官在里面。他看到我,主动跟我打招呼,跟我抱歉之前的事。

紧接着,他说,另外一个面试官应该是准点才来,让我稍等下。

我点点头,说,“没事的,我也是提前进来的”。

很快,另外一位面试官进来了。

我们互相问候一声后,我开始自我介绍:“我叫陈伟琪,我…”

突然,他打断了我,说:“跳过这个环节吧,我们提问题,你回答。我看过你简历,简历附带的博客我也看了,我就先问你了解的这些技术吧”。

我有点惊愕,但很快反应过来,说:“好的”。虽然我以前附带博客地址,但几乎很少有人注意到。

一面开始

问:你们数据库怎么设计的
答:使用mysql做持久化,redis做缓存,主要以玩家的id做主键,同时以主键第一个字段做分区键,数据分区记录。

问:数据如何落地的
答:定时落地,还有下线时也会检查落地一次
问:全部数据还是部分数据落地
答:部分变更的数据
问:如何确定哪些数据变更的
答:每个功能模块各自管理变更
问:有没有办法知道哪些数据变更了?
答:(思考中)
问:lua有没办法知道哪些数据变更了?
答:使用metatable,可以标记哪些字段变动了

问:lua如何做热更新?
答:lua可以遍历获取函数所有外部变量,先遍历取得模块,再将函数替换成新函数,外部变量就用debug.gettraceback获取。
问:gettraceback?
答:getupvalue

问:哪些socket的方法会阻塞
答:read,write,accept
问:还有吗?
答:(思考中)
问:过吧

问:线上怎么排查高cpu和高内存使用?
答:我们项目会打印协议处理、cmd处理的次数跟耗时,定时打印当前内存使用,lua死循环时会打印当前执行哪个函数和行数,出问题可以看log排查

问:cpu占用非常高不一定是死循环,有没办法知道是哪些代码?
答:(思考中)
问:高内存呢
答:(思考中)
问:过吧

问:skynet有哪些线程
答:monitor,socket,处理消息的worker线程
问:还有吗
答:想不起来了

问:skynet怎么处理消息
答:skynet实现了两级队列,其中有一个全局队列,以及每个服务都有自己的队列。当有消息时,会先将消息插入目标服务的队列,然后将消息队列引用插入全局队列。实现消息处理,是由多个worker线程从全局队列取消息,然后分别从对应队列取消息内容处理,回调特定的callback函数。
问:到协程怎么处理
答:skynet会先设置lua回调函数,等消息处理时回调,唤醒协程,或者创建协程池处理消息

问:skynet怎么判定服务卡死了
答:想不起来了,如果是我实现的话,消息队列长度超过某个阈值,或者间隔查消息队列当前消息标记是否变化

问:erlang热更新原理
答:erlang会保留新旧两份代码,热更新时,将还在使用旧代码的进程kill掉,将原来的新代码变成旧代码,重新加载新的代码。

问:进程怎么切换到新代码
答:进程以外部调用形式就可以调用到新代码,新代码更新时会更新模块导出函数表,指向新代码。
问:怎么算外部调用
答:以mfa形式,模块:函数名(参数1,…)
问:没其他了?
答:没了【扣分】

问:erlang进程给另一个进程发消息,是怎么处理的
答:erlang会复制消息数据到目标进程堆,对于比较大的二进制,会复制引用,再给目标进程投递新消息。

问:有没研究过哪些高性能框架
答:(思考中)
问:过吧
答:好

问:一个服务端框架需要具备哪些东西
答:数据库接口支持,通讯协议支持,网络接口支持,加密,集群支持
问:还有吗
答:想不起来了
问:好,过吧

问:好的程序设计是怎样的
答:好的设计就是模块解耦性,减少冗余,保持代码简单,大家风格接近
问:这是原则,还不是设计
答:(思考中)
问:过吧

第一位面试官问完了,交给第二位面试官。

问:table怎么实现的
答:lua使用数组跟hash表记录,数字key主要用数组记录,其他用hash表记录
问:#的作用是什么
答:取得table数组的大小。
问:那我问你,我屏幕这道题结果是什么
答:不好意思,我看不到你屏幕
问:好,那我念出来,像 #{1,nil,nil,1} 结果是多少 【估计是听错了,后来想想应该是问 #{1,nil,1,nil}】
答:4
问:真的是4吗
答:是的。
问:那{1,2,3},插入key 1000,#结果是什么
答:3
问:为什么
答:记录1000个下标会有空间浪费,所以将1000记录到hash表
问:判定条件是什么
答:我没有深入研究

问:lua gc的过程?
答:lua使用三色标记法,初始化所有对象标记白色,gc时将对象标记灰色,并且加入灰色链表。遍历灰色链表,将可达的对象标记黑色,gc结束时,将还是白色的对象删除掉。另外,使用两种白色,一个是当前白色,一种非当前白色,用于标记gc过程中新产生的对象,新产生的对象放到下次gc处理。

问:lua哪些会引起gc卡顿
答:有很多大table,还有使用大量的弱表。

问:怎么减少gc卡顿
答:减少大table,可以拆分成多个小table,还有,减少弱表的使用。

问:还有吗
答:减少大table跟大table之间的联系,(思考中)想不到了

问:lua gc是深度优先,还是广度的
答:没注意到

问:配置占了大部分内存,该怎么解决
答:我们项目会对配置分类,设置哪些是客户端配置,哪些是服务端的,哪些是备注的;然后加载代码的时候,会预处理配置,生成实际需要的结构,这个过程也可以只保留部分需要的配置。
问:那不是要一个配置一个处理
答:是的
问:还有没其他办法?
答:暂时想不到了

一面结束了。

三面

没错,是三面。

hr经理电话联系我,表示我要求的工资过高,如果不改工资很难进入二面,所以三面提前,问我愿不愿意降工资。我说好的,按公司的要求就好。

谈到为什么在一家公司呆6年?我没怎么思考,索性就说,公司按时发工资,没有拖欠,工资隔一段时间都有涨,跟同事相处也比较融洽。

最后,她跟我详细介绍了公司福利和项目的一些情况。

二面

hr帮我预约了二面,让我复习一面的面试题。

面试复习

在面试前,我复习了一面的题目:
1、Lua 如何做热更新?
lua运行时,会将全局变量记录到一个table,模块加载结果也记录到一个table,函数外部变量也以upvalue形式记录。如果想替换掉某个函数,只要从模块table遍历取得这个函数,然后利用debug.getupvalue取得这个函数的原来的外部变量,赋值到新的函数中,或者利用debug.upvaluejoin、debug.setupvalue更新

2、lua 的正式服如何处理 CPU 和内存占用异常高?
lua可以利用debug.sethook设置统计函数,记录函数调用次数,确定哪些函数调用频繁
lua遍历Registry,然后查找所有table,计算每个table占用大小,超过多大的输出一下。(sizeof(TValue)* h->sizearray + sizeof(Node) * sizenode(h))
查找内存泄漏,就遍历Registry,统计所有table,function,thread,userdata,等待一段时间后,再统计所有数据,对比两次不同有哪些数据。

3、erlang如何判定一个函数调用是本地调用,还是外部调用?
外部调用通常是M:F(),但如果是F(),代码编译的时候,会查找是否有 import 记录,没有则认为是本地调用。

4、skynet怎么判定服务卡死了
skynet在处理服务消息时,每处理一条消息会将定时器加1,如果monitor进程发现定时器数值没变,则认为该服务卡死了。

5、lua gc过程
lua使用三色标记法,初始化所有对象标记白色,gc时将对象标记灰色,并且加入灰色链表,可达的对象标记黑色,gc结束时,将还是白色的对象删除掉。另外,使用两种白色,一个是当前白色,一种非当前白色,用于标记gc过程中新产生的对象,新产生的对象放到下次gc处理。

6、lua哪些会引起gc卡顿、gc卡顿怎么解决
① 巨大table,②太多大table的弱表,③整个系统的对象过多,而且在伴随着gc过程中有大量table key/value新增或修改
gc卡顿怎么解决? 减少大table,减少弱表使用,使用c模块来管理常量数据

7. lua table实现
lua使用数组跟hash表记录,紧凑的数字key用数组记录,其他用hash表记录。对于数字key,如果超过原来数组大小,且空间利用率低于50%,会放到hash表

8. skynet消息怎么处理,到协程怎么处理
skynet实现了两级队列,其中有一个全局队列,以及每个服务都有自己的队列。当有消息时,会先将消息插入目标服务的队列,然后将消息引用插入全局队列。实现消息处理,是由多个worker线程从全局队列取消息,然后分别从对应队列取消息内容处理,回调特定的callback函数。
到协程怎么处理?首先,skynet会先设置lua回调函数,记录到registry,等待消息处理时回调,根据消息类型分别处理,唤醒协程,或者创建协程池处理消息

9.skynet有哪些线程
消息处理worker、socket处理、monitor、timeout

二面开始

二面开始了,也是视频面试。

这回面试我的是新的两位面试官,其中一位跟我介绍,“你可以在视频下方看到我们名字,我是xxx,另一位是xx部门的技术大神”。

他继续说,“你先自我介绍吧”。

我开始自我介绍:“我叫陈伟琪,我…”

等我介绍完,他说,“我先问你问题,等我问完,另外一位面试官再问你”,我表示好的。

问:你对c熟练吗
答:我写c主要写lua的c扩展。
问:我问你printf,如果参数过多或过少,会怎样?
答:没研究过

问:算法题【我想问题的时候忘记题目说什么了,现在想想,应该是问,一大堆数字,一个数字出现1次,其他都出现2次,怎么找到出现1次的】
答:过吧
问:你别想着用最优算法,先实现再说
答:过吧,我忘记题目了。

问:skynet socket怎么实现的
答:skynet socket用了3种异步socket实现,epoll,kqueue,还有一个,具体没研究过

问:异步socket底层实现
答:我没研究过

问:tcp、udp熟悉吗
答:没研究,目前有做的是http/https开发,写lua的c模块,接入第三方接口。

问:全局邮件你怎么实现
答:如果每个玩家发一样或类似的内容,可以将内容记录为模版,不同的抽取成参数,以邮件id和参数发给玩家。
问:每个玩家一封邮件吗
答:可以进一步优化,如果所有玩家内容一样,玩家可以只记录阅读过的邮件id,打开邮件时获取下邮件。

问:排行榜算法怎么实现
答:如果是几百万人排,取前面一百名的话,可以用小根堆记录100名数据,堆顶是分数最低的,先填满堆,后面插入就跟堆顶比较,比他小就跳过,比他大就移走堆顶,再将数据插入堆。
问:堆是稳定的吗?
答:稳定的。

问:你们服务器分服吗
答:游戏对外不分服,但玩家会由负载均衡分配到不同的游戏服
问:那就是内分服咯
答:是的
问:怎么设计的
答:我们使用的是skynet的框架,根据玩家人数启动N个游戏服,可以动态伸缩,再搭配其他特定的业务服,组成一个集群。游戏对外不分服,但实际上,玩家会由负载均衡分配到不同的游戏服,在玩家登录游玩到下线的生命周期里面,都会在这个游戏服玩。大概一个游戏服支撑3000个玩家。如果全服同时在线的玩家数量过多,将会启动新服,将新玩家导向新服上。

第一位面试官问完了,交给第二位面试官。

问:你们项目有多少人?
答:简历第一个项目,人最多的时候,服务端有8-10人,客户端有10-12人,策划有15-20人

问:你们使用什么数据库,使用innodb引擎吗
答:tspider
问:没听过,是什么
答:基于mariadb开发的分布式数据库
问:那就不是innodb引擎,怎么分布式的
答:使用主键第一个字段作为分区键,建表的时候可以设置分区函数,基于分区键分布式存储
问:能跨表查询吗
答:能,但不建议,效率不高
问:能实现事务吗
答:不能
问:那你们怎么实现事务
答:利用skynet服务实现,或者将一段脚本插入redis执行
问:redis锁是吧
答:是

问:c++有用吗
答:主要写c,很少用到c++

问:算法题【我想问题的时候忘记题目说什么了】
答:过吧
问:别想着用最优算法,先实现再说
答:我忘记题目了
问:先进先出队列,怎么取得队列最大值
答:插入队列的时候记录下最大值就好
问:算法复杂度和空间复杂度多少?
答:过吧,算法是我的弱项,我对lua和skynet比较擅长。
问:我看过上次问的问题,那我问lua的吧。
答:好。

问:怎么确定lua死循环
答:我说下skynet的处理吧,skynet在处理服务消息时,每处理一条消息会将定时器加1,如果monitor进程发现定时器数值没变,则认为该服务卡死了,可以大致确定是死循环。
问:怎么定位代码位置
答:lua可以取得当前调用函数指针,可以获取函数名字和代码行数,还可以获取上一个函数调用,打印函数名字和代码行数
问:怎么取得上一个函数调用
答:我没细看,可以参考traceback的实现

问:lua栈帧怎么实现的
答:没研究过

问:你要求工资多少?
答:xxx

二面结束了。

结果

我以为hr会通知我结果,等了几天后还没有消息,我找了hr。她表示,hr经理和我谈好了工资是xxx,但我二面改口,面试官吓到了。我表示其中有误会,我都是说按公司的标准,其实我不知道标准是多少。hr问我能否接受这个预期工资,我说可以的。她帮我和经理谈了。

后来,hr没有联系我了,我也不再问了。

总结

我准备不够充分,在我被问问题的时候,我会不自觉思考问题的问点在哪,当我抓不住问点的时候,我会表现出紧张不安,大脑容易出现短时间的空白。我的弱项很明显,但我的强项没有表现出来。

我在思考,我的强项在哪?
这么多年来,我都是力求独立解决问题,主动承担项目主要开发;
做事认真,对自己的工作负责,每次都确保线上开发没有出现被刷的问题,也做到了;
刨根问底,发现和解决项目存在的重大问题。

我始终都觉得,人都会有短板,不让别人看到是运气,也是能力,我也要努力补齐短板。

发表评论

电子邮件地址不会被公开。 必填项已用*标注