上海某游戲小廠面試,也扛不住了...
大家好,我是小林。
今天分享一位同學面試上海某游戲公司的面經,同學的技術棧是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 的生命周期 沒有讓面試官聽懂