面试受挫
一直以来没有总结的习惯,今天听取冰河大佬的建议说按照自己的思路对面试进行一个总结,但是因为自己的知识储备太少,想来想去没有一个很好地想法来组织对于这场面试的一些思考,就总结一下这40天来平均一天一场面试的经历吧,记录一下一个非科班的惨痛转码路(半程)。
大概是3个月前完成小论文后开始全身心投入开发知识的学习,那段时间经历了可能是所有转码人要经历的阵痛:不知从何学起。当时心里想的是,管他三七二十一搞个项目来看看开发到底是多难。于是就选了一个后来才知道已经被称作“烂大街”的RPC项目。跟着做完之后心情是舒畅的,想着这样就算开发一个框架了,满心欢喜地向心仪的大厂投出了人生第一份简历。面试官人很好,很委婉地表达了我怎么能这么菜的疑问,我张着嘴说不出一句话。
遇见bhrpc
一落千丈的心情不必多言,悲伤的我在搜索那些我回答不上来的问题的时候发现了 bhrpc,我可能才第一次明白了什么叫系统设计,没有什么所谓烂大街,只是你去实现的方式有没有触碰到开发最核心的能力而已。
其实反思学习bhrpc的这一个月,最大的问题是难免陷入依葫芦画瓢的照搬中去。虽然代码是一行一行敲出来的,但是根本没有多思考为什么会是这样。对于我们这些基本没有实战经验的学生而言,我觉得主动抓住所有产生的疑惑然后去求索对于加深理解是很重要的。
印象很深刻的是有一次我连着做了几个SPI扩展序列化方式的章节,在我写完后进行测试时忘了重启服务端直接运行了客户端(以前我一直会重启),发现换了序列化方式之后服务端竟然会按照新的序列化方式返回,我在想服务端启动的时候不是就在里面设置好了encoder和decoder吗,那序列化方式不应该是固定的吗?
结果首先证明我的想法是错的,再进一步考虑服务端如果启动后只能支持一种序列化方式,客户端怎么会发送序列化方式过来呢?于是就一步步找,找到BaseServer启动Netty的地方,去研究这个经常被使用的Netty的模版启动方式到底是怎么工作的。
阅读Netty的文档后才明白在指定了生成channel的类型后,channel的注册是在连接后才发生的,从发送来的protocol中获取序列化方式,然后才进行消息体的反序列化。明白了之后觉得就是这么简单的一件事啊,可是弄懂之前又会觉得怎么可能是这样哈哈。
面试总结
其次我觉得面试对我而言算是一种很重要的进步途径,因为我们本身缺乏实践,当面试官从实践的角度去提出一些问题的时候常常能让我有一种豁然开朗的感觉,印象很深刻的一次面试里,面试官对我说了无数次“思路要打开,不要局限在自己学的知识里”。
其实就是想问我分布式ID的相关问题,可是我根本没有了解过,面试官循循善诱,指出我现在的解决方案的问题会出现在哪里,从哪个方面可以去解决。当从实际的需求中一步步得到解决方案后,我觉得我可能再也不会忘记了。
引用冰河大佬的口头禅,“纸上得来终觉浅,绝知此事要躬行。”学了忘,忘了再学的情况大抵也是因为连这个技术的出现是因为什么需求都没想明白,就稀里糊涂地学了一通。
后续规划
从过往的经验规划以后的学习的话,我觉得对于我而言首先就是重视知识间的连通性,比如TCP的流量控制,避免浪费网络资源的滑动平均法也可以用在其他可能会出现资源浪费的情况等。
其次多阅读文档,多看源码,争取不再给面试官说“如果你看了源码你就知道了”这句话的机会。
中间件方面,还是要先会用,通过实践去锻炼动手能力。
最后也最重要的是,继续巩固Java的基础,甚至计算机基础,这样在看到一些复杂设计的时候才能看到更本质的东西,也才能触类旁通、举一反三地去学习、思考和总结。
到现在学习的动力已经由“想找到一份心仪的工作”转变成了真的被这些框架设计的精巧所吸引,想去深入地了解。这或许就是入门的时候有一个好师傅的重要性吧哈哈,感谢冰河大佬,后面的路也要认真走下去,加油!
注:bhrpc是冰河技术知识星球的《RPC手撸专栏》,带着大家从零开始手写的RPC框架,《RPC手撸专栏》目前已经更新120+篇文章,已提交120+项目工程,120+项目源码Tag分支,真正的分布式、高并发、高性能、可扩展的企业级RPC框架项目。
面试真题
综合性问题
- 讲一讲对自己这个 RPC 项目的想法,你是怎么设计这个项目的,想要实现那些功能?
- 你认为一个好的 RPC 框架最重要的特性有哪些?或者说什么是一个好的 RPC 框架?
- 就稳定性而言,如果在一个不稳定的网络情况下,连接经常出现闪断,那你这个 RPC 框架会怎么做?
- 那发生了闪断,你这次的请求会视作失败的还是成功的?
- 那这里会出现问题,因为网络是不可靠的,有可能服务端已经处理过这次请求了,只是客户端没有收到回复而已,如果服务端不是幂等的,就会出现业务的错误
- 如果你看过一些 RPC 的实现就会知道一般对于这种问题都会使用请求 ID 来解决,拿着这个请求 ID 服务端就可以进行一些追溯之类的操作,不能完全依靠方法名称和请求参数,因为这些是可以重复的
- 请求 ID 是怎么生成的?
- 是存在内存里的还是数据库里的?你在内存里怎么保证不重复呢,服务一重启不是马上又从 1 开始了?还有如果你的 RPC 框架在集群里,那你怎么保证每个服务器的 id 不一样呢?
Java
- Java 学习有多久了
- 简历里面为什么要单独列出来了解集合框架、JVM 和并发体系,你是怎么理解的
- HashMap 里的 key 可以为 null 吗,value 可以为 null 吗?
- 那如果 get(null) == null 的话,可以判断是没有这个键值对还是 key,value 同时为 null 吗
- 如果确切地知道 key 用什么方法?
- TreeMap 了解吗?有什么特性
- 什么时候需要用到 TreeMap
- ConcurrentHashMap 的 key 可以为 null 吗?value 可以为 null 吗?
- 你在写代码的时候对 ConcurrentHashMap 进行 try-catch 吗?
- 那为什么 value 不能为 null 而 HashMap 可以?
并发
- 你的项目里肯定涉及到了并发,在你处理并发的时候都用到了什么工具,什么类?
- Java 7 里提供了 fork join,是为了解决什么问题?
- 那用锁、信号量不行吗?
- CountdownLatch 用过吗?
场景
- 有一个 8G 大的文本文件,存的字符串,你只有 2G的内存,找出重复的字符串以及出现的次数
- 需要你生成一个在时间和空间维度全局唯一的 ID,你怎么生成?
- 机器重启之后就要重新申请,那怎么保证请求的 id 不被浪费呢?
编程
模拟计算器,包含 “加减乘除” 和 “括号” 的操作,处理一个字符串,求这个字符串对应的结果
- 实现基本功能
- 用面向对象的思想设计程序
- 能够识别字符串是否有效
- 能够处理大数乘法