面试题--多种技术在实际项目中的应用梳理 前言 Java基础/设计模式 计算机网络 数据库 框架使用 敏捷开发项目管理 优化

实际面试中,面试官会结合具体的项目经验,结合具体框架深入提问一些问题,最好能提前针对这部分做好准备,就能够更加从容地参加面试。

Java基础/设计模式

  1. 抽象类和接口具体项目中是如何实践的?

    • 抽象类最主要是为了解决代码复用的问题,子类和抽象父类是is-a的关系。(具体项目中,我把协议中的相同部分抽取出来,编写了AbstraceCommand和AbstraceMessage,里面包含了所有指令相同的部分,包括:消息头、类型字段等等,每个子类也有自己的成员属性,在子类构造方法中会调用一个父类的抽象方法来进行子类成员属性具体的解码;父类的构造方法中负责解码相同的部分。)
    • 接口最主要是用于适应需求变动带来的实现变化问题,实现类和接口是has-a关系。如果需求几乎不会改变或者只有一种实现,可以考虑直接用实现类。(具体项目中,发送指令的驱动类就设计成了接口,提供了一种发送指令的方法,因为需要兼容不同的协议;利用一个工厂类来实现根据协议类型自动选择不同的驱动发送类实现,或者也可以使用spring容器,利用不同的协议类型名来获取实现类也可以)
  2. 项目中如何使用ConcurrentHashMap的?

    我们的读写器上传过来的数据,需要根据某个key进行分组,这里我选择使用的是concurrentHashMap,因为还有一个线程负责从Map中获取数据,并进行后续的业务处理,在多线程的情况下,concurrentHashMap可以保证线程安全。

  3. 项目中是如何使用线程池的?

    项目中具体会使用两个线程池,一个是普通的线程池,一个是带定时任务的线程池,区别两个线程池的原因就是业务的隔离性,一个业务不要影响另外的业务。然后结合Spring框架,在配置文件中配置好相关的信息,然后通过ConfigurationProperties注入,使用的时候也通过Spring框架来获取线程池对象。

  4. 你们的业务中是如何设置线程池的任务拒绝机制的?

    因为要保证任务一定会执行,并且不能影响其他待排队的任务,所以采用了调用者线程执行的策略。

  5. 你们项目的JVM参数中的垃圾收集器使用的是哪种垃圾收集器?

    因为我们系统更关注吞吐量,所以使用了JDK1.8默认配置的垃圾收集器,新生代使用scanvenger,老年代使用Parrell old。

    可以用 PrintCommandLineFlags来查看具体的垃圾收集器

  6. 你做过JVM参数调优吗?能具体讲讲吗?

    我没做过JVM参数调优,但是通过JvisualVM排查内存泄露的问题

    当时服务器发现数据处理出现延迟,从网络层入手排查,最后发现是Netty层可能有问题,所以导出堆栈信息,然后通过分析软件查看具体是哪个对象存在内存泄漏,后来发现是Netty中的存储消息的队列出现积压,最后问题找到是业务处理逻辑中的redis出现慢查询拖慢了整个处理速度,导致生产远大于消费,出现延迟问题。

    最后的解决方案是,拆分非核心的redis业务数据进行了异步处理。

    核查框架如下:
    事前:HeapDumpOnOutofMemory参数一定要加上,这样出了问题才有章可循。
    核查问题有两大方向:
    检查是内存溢出还是内存泄漏,内存泄漏一般需要排查代码问题;内存溢出代码问题和参数大小设置都需要考虑。根据不同区域溢出,代码核查的方面也不同:
    - 堆溢出错误:需要根据对象具体查看
    - 栈溢出错误:核查是否有没有出口的递归
    - 方法区溢出:核查是否有大量创建的动态类
    - 直接内存溢出:核查NIO相关的代码

  7. 你们项目中都应用过哪些设计模式?为什么要这么用?

    工厂模式:不同硬件设备的底层驱动,获取实例的时候,是通过工厂模式获取的,一种可以自己实现工厂,另一种可以通过SpringIoc容器实现工厂

    模板模式:Netty的handler业务处理类,都包括类似的处理流程,包括解码,设置响应等等,我们把业务流程确定下来,不同的硬件设备继承这个模板类,实现自己的具体业务方法即可。

    代理模式:SpringAOP就是一种代理模式,我们项目中会使用ControllerAdvice和ExceptionHandler处理Controller的统一异常。

    Netty中有经典的责任链模式,Netty把处理器串成了一个链式结构,这样链上的处理器都可以针对一条消息做业务处理。

计算机网络

  1. 你们项目使用的是什么线程模型?

    现在第一版本的读写器做成了读写器是server模式,服务端时client模式,所以使用一个多线程的EventlooGroup来对接很多读写器。

    后续版本会升级成读写器为client端,这样的话我们会使用主从模式

  2. 你们项目中是如何处理粘包拆包的?

    通过使用Netty自带的解码器,LengthFiledBasedFrameDecode来实现通过识别每条消息中的消息长度来分割消息。

  3. 你们项目如何做长连接的?

    具体采用应用层定时发送心跳保活包来实现,并且通过多次验证来防止误判。具体项目中使用了BNetty提供的IdleStateHandler,这个handler可以实现定时机制,并回调一个方法,我们把心跳保活机制写到这个方法里面即可

  4. 你们项目的web管理后台,前后端是如何交互的?

    前后端通过定义了统一的JSON格式文档来实现交互。JSON格式主要包括Status,message和Data,前端可以通过Status判断此次调用是否成功,通过messsage查看具体的报错信息,data获取本地需要的数据。

  5. 你们项目是如何做登录校验和权限校验的?用到cookie了吗?

    通过使用一个进入controller之前的拦截器来实现权限校验,具体是通过继承WebMvcConfigurationSupport,实现addInteceptor方法来实现添加拦截器和拦截路径的。

    项目中用到cookie了,cookie中保存用户的userId,作为是否登陆超时的依据

    如果是登录相关的url,直接放行,登陆成功后会设置redis和cookie,redis中存放的是权限等其他信息,利用redis的过期时间,可以实现登录超时机制。

    权限校验逻辑是,获取cookie中的userId,然后查询redis,获取权限信息。

数据库

  1. 做过SQL调优吗?

    做过。管理后台页面有一个对大表的查询比较慢,正常来说,分页查询不应该这么慢,我先开启了MyBatis的debug模式,本地跑起来后,发现count(0)统计总条数很慢,查看对应的条件语句,发现范围查询字段选择的不合理。使用explain查看执行计划后,使用合理的范围查询字段后,count(0)统计变快了,并且也可以利用到对应的索引。

  2. 你们数据库中使用索引了吗?如何建立的?

    针对业务查询SQL,针对区分度比较好的字段做索引,并优先建立联合索引,能充分利用到最左前缀的索引匹配规则,并且在orderby的时候也可以使用索引。

  3. 你们项目中使用redis了,如何使用的?

    • redis中保存了登录信息,包括权限,利用redis的过期实现登录超时
    • redis中保存了一些分布式系统之间的交互数据,比如,上层业务系统传递过来的数据既需要持久化,又需要延迟低,处理过后存储到redis中,当下层系统时候时,直接查询,解耦了双方。

框架使用

敏捷开发项目管理

  1. 你们项目是敏捷开发?使用的什么项目管理软件?

    使用的是禅道,通过禅道建立需求,建立项目或者产品,建立任务把需求匹配起来,然后分配给项目组成员,作为管理者,需要经常查看工时消耗的情况,及时把控项目进度。

  2. 你们开站立式早会吗?

    之前项目组人少的时候会开早会,后来项目组人多了之后,拆分出具体的项目和产品小组,就不再开全员早会了,但是我们中间件组会开一个简短的早会,各自汇报一下昨天做了什么,今天要做什么,需要什么支持等等。

优化

  1. 针对redis做更详细的调研,思考redis的数据结构设计是否合理,应用的是否合理等等
  2. 网络层的负载均衡服务。这块内容我后续编写了自己的负载均衡服务,因为目前默认只开启一个实例,所以算法这部分的具体实现还没有做,默认实现为调用第一个实例
  3. 重构了网络层的Netty框架的操作,形成了一个小框架,为后续网络层业务开发提供了坚实的保障
  4. 逐步构建了博客系统,公司内部的信息交流系统(权衡之后使用了有道云笔记来实现)
  5. 设计并实现了解放物流项目的大数据框架改造工程,对storm的框架有了基本的应用经验。