上海某游戏小厂面试,也扛不住了…

2023年 9月 8日 52.6k 0

大家好,我是小林。

今天分享一位同学面试上海某游戏公司的面经,同学的技术栈是Java后端,虽然不是大厂,但是一面面试也被问了 25 多个问题,时长也接近 1 小时了

面试过程中,也问到了 Linux socket 编程,游戏公司都会对网络协议和网络编程这一块要求比较高,所以投游戏公司的同学,需要重点准备网络方面的知识。

还有一点,游戏公司的开发岗除了技术要求之外,可能还会问你一下你对游戏的兴趣,平常玩什么游戏,对游戏有什么看法,因为工作内容就是开发游戏,如果对游戏没有热情,会觉得工作缺失了激情。

问题记录

介绍你的项目

balabal 了几分钟

Redis 缓存一致性

说了旁路缓存策略

如果这个时候一波海量请求,你怎么保证他们能读到数据

  • 数据延迟肯定是有的
  • 我个人认为可以做流量控制,限制读请求数量
  • 当然,如果非的读的话。可以采用把删除缓存策略改为更新缓存策略
  • 说说Redis 数据结构

    Redis 有五大基本数据类型和四大新类型

    五大基本类型是:

  • String
  • Hash
  • List
  • Set
  • zset
  • 每一种数据结构根据自身的特性有不同的使用场景:

  • string:
  • 计数器,因为 Redis 是单线程模型的,所以redis执行命令时原子性,所以他可以用来做计数器,例如 点赞计数、转发、库存数量等

    分布式锁:setnx key value ex 时间

  • hash:
  • Hash 是 key-value 键值对,类似与 Java 的 HashMap, 查找时间复杂度是 o(l)

  • Hash 的底层数据结构是hashtale 和压缩表

  • 当 元素个小于 512 并且所有元素大小小于 64 字节,采用压缩列表作为底层数据结构

  • 反之采用 hashtable

  • 它适合做购物车,用户作为 id、商品 id 位 field、商品数量为 value

  • List (说到 List 被面试面试官打断了,下一个)

  • 事务了解吗?

    了解,acid 事务四大特性说了一遍

    事务隔离级别有哪几种

  • 四种
  • 读未提交

    读已提交

    可重复读

    串行话

  • 读未提交就是一个A事务能读到另一个B事务未提交的事务,当这个B事务发生回滚时, A 事务读到的是脏数据。它有脏读、不可重复读、幻读问题
  • 读已提交就是只能读到对方事务已经提交的事务,它解决了脏读问题,但是有不可重复读和幻读问题(说到这里突然被面试官打断)
  • 追问:隔离级别是由啥保证的

  • mvvc 机制 和 锁机制
  • 可重复读为什么完全不能解决幻读

    在可重复读隔离级别下,事务 A 第一次执行普通的 select 语句时生成了一个 ReadView,之后事务 B 向表中新插入了一条 id = 5 的记录并提交。接着,事务 A 对 id = 5 这条记录进行了更新操作,在这个时刻,这条新记录的 trx_id 隐藏列的值就变成了事务 A 的事务 id,之后事务 A 再使用普通 select 语句去查询这条记录时就可以看到这条记录了,于是就发生了幻读。

    图片图片

    因为这种特殊现象的存在,所以我们认为 MySQL Innodb 中的 MVCC 并不能完全避免幻读现象。

    进程与线程的区别

    常规八股

    线程池有哪几个类型的

  • newSingleExecutor
  • 只有一个核心线程,也是最大线程数。队列采用的是 LinkedblockingQueue 无界阻塞队列。极端情况下会有  OOM 问题

    它的工作原理是当提交任务是当没有工作线程时,会将任务放入到阻塞队列中,

    有核心线程时,获取阻塞队列取任务执行,执行完了接着从阻塞队列执行

    Keepalive存活时间是 0,因为本来就没有非核心线程

    它的场景是串行化的场景,因为他只有一个工作线程

  • newCacheExecutor
  • 核心线程数是 0,队列采用的是 SynchrousQueue 阻塞队列。最大线程数是 Integer.Max_value 的默认值,KeepAiveTime 是 60 s,也就是线程执行完了处于空闲状态时,过 60 s 就会销毁,如果频繁的创建线程会产生 OOM 问题

  • 它的工作原理是提交任务,没有线程时,任务放到阻塞队列

  • 创建核心线程时取队列执行任务,插入一个元素必须等工作线程取出消费,如果队列没有任务则会阻塞

  • 它的吞吐量比 newFixedExecutor 更高,它适用于并发量大但是任务执行周期短的场景

  • newFixedExecutor

  • SheculedExecutor

  • 周期性去执行任务。队列是 DeayQueue 延迟队列,

  • 它的工作原理是 当工作线程数小于最大线程数,首先会去创建线程去执行任务

  • 当达到核心线程数时,会将任务放入到阻塞队列,

  • 所谓周期性就是 他去任务队列取出任务时,会修改一个 time 变量 位下次要执行的时间

  • 然后放入到队列中

  • 说说各层有哪些协议

  • 应用层:DNS、HTTP、IP
  • 传输层:TCP、UDP
  • 网络层:IP、ICMP
  • 数据链路层:ARP
  • 物理层:不记得了
  • 说说一个数据怎么在网络各层分割报文的

    发送数据方

  • 传输层:加上 TCP 报文头
  • 网络层:加上 IP 头
  • 数据链路层:加上帧头和帧尾
  • 物理层:则是转换为包含0、1的二进制比特流
  • 讲讲 TCP 三次握手

  • 首先刚开始双方处于关闭连接状态,服务端处于监听端口状态,也就是 Listen 状态
  • 第一次握手:客户端首先生成随机初始化序列号seq = x,并放到 TCP 头部的32位序号字段中,同时将 SYN 标志设置为 1,表示这是一个 SYN 报文,然后发送给服务端,接着客户端处于 SYN_SENT
  • 第二次握手:服务端收到客户端发送过来的 SYN 报文后,首先也会生成随机初始化序列号seq = y,并放到 TCP 头部的32位序号字段中, 并对客户端的序列化 seq = x + 1 作未确认应答号,然后放到 TCP 头部的确认应答字段中,同时将 SYN 和 ACK 标志设置为 1,表示这是一个 SYN-ACK 报文。把该报文发送给客户端后,服务端处于 SYC_RCVD
  • 第三次握手:客户端收到服务端发送过来的 SYN_ACK 报文,会发送确认报文给服务端,这个确认报文是对服务端的初始序列化 seq = y + 1,  客户端进入 ESATBLISH 状态
  • 服务端收到后,也进入 ESTABLISHED 状态
  • 你刚刚说的 Listen、SYN_SENT、SYN_RCVD、ESTABLISHED 状态有什么含义?

  • LISTEN 状态表示监听是否有连接到来,当有连接到来时,它获得已经连接的 socket
  • SYN_SENT 表示 客户端具备发送数据能力。但还不具备接受数据能力, 此时需要等待服务端的确认
  • SYN_RCVD 表示服务端具备接受数据的能力和发送数据的能力,此时需要等待客户端的确认
  • ESTABLISHED 表示我已经建立连接了,我可以发送数据了
  • 客户端发送了数据给服务端,服务端返回对方成功确认收到的确认信息,这个时候是否可以肯定服务端收到了数据

  • 不一定,服务端有一个接受缓存区,此时服务端还在处理前面的数据,有可能服务端发生异常了,导致接收缓冲区的数据未被处理
  • 那怎么解决这个问题呢?

  • 嗯,触发重传机制,客户端重新发送数据?(懵逼)
  • 客户端想尽快关闭连接,应该怎么办?

  • 发送 FIN 报文?
  • RST 报文好像也可以断开连接
  • Socket 编程了解过吗,什么是 socket

  • 了解过
  • Socket 是一个套接字
  • socket 的流程

    不会(我搞 java 的,没研究过 socket 编程,完了)

    基本 socket 做好了封装,你了解吗

    刚开始懵逼,后来想到才是 Netty 这个框架

    Socket 和 http 有什么区别?

  • Socket 是一个套接字接口
  • Http 是请求连接,http 是 tcp 连接的管理器
  • 你说说 spring 的生命周期?

    大致分为五个阶段,创建前准备阶段、实例化阶段、依赖注入阶段、容器缓存阶段、实例销毁阶段

    后面从说了每个阶段是干嘛的(面试官反应逻辑讲的不够清楚,这里我就不列出来的)

    事后复习总结如下:

  • 创建前准备阶段:
  • Spring 启动后,扫描 @ComponentScan 注解配置的路径下的所有 .class 文件,

    类加载其根据类名加载获取类的 Class 对象

    判断类上是否有 @Component、Service 等注解找出 bean 对象

    给每个符合条件的 bean 创建 BeanDefintion 对象用于存放 Class 对象、作用域等信息,作用域包括 singletion、prototype、request 等,然后添加进 beanDefinitionMap, key 值存放 bean 的名字,value 是对应的 BeanDefition

    扫描 bean 对象

    遍历 beanDefinitionMap,创建

    MyBatis 中 ${} 与 #{} 的区别

  • 无法防止注入攻击,在开发中尽量使用{}
  • #{} 是占位符,预编译处理,${} 是拼接符,字符串替换,没有预编译处理
  • 感觉

    面试官说我们是做游戏开发,然后又问我你平时喜欢打游戏吗,我说近些年很少打游戏的。以前很喜欢玩,后面觉得腻了,然后面试官说做游戏开发需要对游戏很了解的,对游戏很热爱的,否则难干下去

    反问环节说我基础还算行,就是可能在业务方面可能不匹配

    不足之处

    socket编程不太熟悉,计网还需加强学习

    Spring bean 的生命周期 没有让面试官听懂

    相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论