上海某游戲小廠面試,也扛不住了...
大家好,我是小林。
今天分享一位同學(xué)面試上海某游戲公司的面經(jīng),同學(xué)的技術(shù)棧是Java后端,雖然不是大廠,但是一面面試也被問了 25 多個問題,時長也接近 1 小時了
面試過程中,也問到了 Linux socket 編程,游戲公司都會對網(wǎng)絡(luò)協(xié)議和網(wǎng)絡(luò)編程這一塊要求比較高,所以投游戲公司的同學(xué),需要重點(diǎn)準(zhǔn)備網(wǎng)絡(luò)方面的知識。
還有一點(diǎn),游戲公司的開發(fā)崗除了技術(shù)要求之外,可能還會問你一下你對游戲的興趣,平常玩什么游戲,對游戲有什么看法,因?yàn)楣ぷ鲀?nèi)容就是開發(fā)游戲,如果對游戲沒有熱情,會覺得工作缺失了激情。
問題記錄
介紹你的項(xiàng)目
balabal 了幾分鐘
Redis 緩存一致性
說了旁路緩存策略
如果這個時候一波海量請求,你怎么保證他們能讀到數(shù)據(jù)
- 數(shù)據(jù)延遲肯定是有的
- 我個人認(rèn)為可以做流量控制,限制讀請求數(shù)量
- 當(dāng)然,如果非的讀的話??梢圆捎冒褎h除緩存策略改為更新緩存策略
說說Redis 數(shù)據(jù)結(jié)構(gòu)
Redis 有五大基本數(shù)據(jù)類型和四大新類型
五大基本類型是:
- String
- Hash
- List
- Set
- zset
每一種數(shù)據(jù)結(jié)構(gòu)根據(jù)自身的特性有不同的使用場景:
- string:
計數(shù)器,因?yàn)?Redis 是單線程模型的,所以redis執(zhí)行命令時原子性,所以他可以用來做計數(shù)器,例如 點(diǎn)贊計數(shù)、轉(zhuǎn)發(fā)、庫存數(shù)量等
分布式鎖:setnx key value ex 時間
- hash:
Hash 是 key-value 鍵值對,類似與 Java 的 HashMap, 查找時間復(fù)雜度是 o(l)
Hash 的底層數(shù)據(jù)結(jié)構(gòu)是hashtale 和壓縮表
當(dāng) 元素個小于 512 并且所有元素大小小于 64 字節(jié),采用壓縮列表作為底層數(shù)據(jù)結(jié)構(gòu)
反之采用 hashtable
它適合做購物車,用戶作為 id、商品 id 位 field、商品數(shù)量為 value
List (說到 List 被面試面試官打斷了,下一個)
事務(wù)了解嗎?
了解,acid 事務(wù)四大特性說了一遍
事務(wù)隔離級別有哪幾種
- 四種
讀未提交
讀已提交
可重復(fù)讀
串行話
- 讀未提交就是一個A事務(wù)能讀到另一個B事務(wù)未提交的事務(wù),當(dāng)這個B事務(wù)發(fā)生回滾時, A 事務(wù)讀到的是臟數(shù)據(jù)。它有臟讀、不可重復(fù)讀、幻讀問題
- 讀已提交就是只能讀到對方事務(wù)已經(jīng)提交的事務(wù),它解決了臟讀問題,但是有不可重復(fù)讀和幻讀問題(說到這里突然被面試官打斷)
追問:隔離級別是由啥保證的
- mvvc 機(jī)制 和 鎖機(jī)制
可重復(fù)讀為什么完全不能解決幻讀
在可重復(fù)讀隔離級別下,事務(wù) A 第一次執(zhí)行普通的 select 語句時生成了一個 ReadView,之后事務(wù) B 向表中新插入了一條 id = 5 的記錄并提交。接著,事務(wù) A 對 id = 5 這條記錄進(jìn)行了更新操作,在這個時刻,這條新記錄的 trx_id 隱藏列的值就變成了事務(wù) A 的事務(wù) id,之后事務(wù) A 再使用普通 select 語句去查詢這條記錄時就可以看到這條記錄了,于是就發(fā)生了幻讀。
圖片
因?yàn)檫@種特殊現(xiàn)象的存在,所以我們認(rèn)為 MySQL Innodb 中的 MVCC 并不能完全避免幻讀現(xiàn)象。
進(jìn)程與線程的區(qū)別
常規(guī)八股
線程池有哪幾個類型的
- newSingleExecutor
只有一個核心線程,也是最大線程數(shù)。隊(duì)列采用的是 LinkedblockingQueue 無界阻塞隊(duì)列。極端情況下會有 OOM 問題
它的工作原理是當(dāng)提交任務(wù)是當(dāng)沒有工作線程時,會將任務(wù)放入到阻塞隊(duì)列中,
有核心線程時,獲取阻塞隊(duì)列取任務(wù)執(zhí)行,執(zhí)行完了接著從阻塞隊(duì)列執(zhí)行
Keepalive存活時間是 0,因?yàn)楸緛砭蜎]有非核心線程
它的場景是串行化的場景,因?yàn)樗挥幸粋€工作線程
- newCacheExecutor
核心線程數(shù)是 0,隊(duì)列采用的是 SynchrousQueue 阻塞隊(duì)列。最大線程數(shù)是 Integer.Max_value 的默認(rèn)值,KeepAiveTime 是 60 s,也就是線程執(zhí)行完了處于空閑狀態(tài)時,過 60 s 就會銷毀,如果頻繁的創(chuàng)建線程會產(chǎn)生 OOM 問題
它的工作原理是提交任務(wù),沒有線程時,任務(wù)放到阻塞隊(duì)列
創(chuàng)建核心線程時取隊(duì)列執(zhí)行任務(wù),插入一個元素必須等工作線程取出消費(fèi),如果隊(duì)列沒有任務(wù)則會阻塞
它的吞吐量比 newFixedExecutor 更高,它適用于并發(fā)量大但是任務(wù)執(zhí)行周期短的場景
newFixedExecutor
SheculedExecutor
周期性去執(zhí)行任務(wù)。隊(duì)列是 DeayQueue 延遲隊(duì)列,
它的工作原理是 當(dāng)工作線程數(shù)小于最大線程數(shù),首先會去創(chuàng)建線程去執(zhí)行任務(wù)
當(dāng)達(dá)到核心線程數(shù)時,會將任務(wù)放入到阻塞隊(duì)列,
所謂周期性就是 他去任務(wù)隊(duì)列取出任務(wù)時,會修改一個 time 變量 位下次要執(zhí)行的時間
然后放入到隊(duì)列中
說說各層有哪些協(xié)議
- 應(yīng)用層:DNS、HTTP、IP
- 傳輸層:TCP、UDP
- 網(wǎng)絡(luò)層:IP、ICMP
- 數(shù)據(jù)鏈路層:ARP
- 物理層:不記得了
說說一個數(shù)據(jù)怎么在網(wǎng)絡(luò)各層分割報文的
發(fā)送數(shù)據(jù)方
- 傳輸層:加上 TCP 報文頭
- 網(wǎng)絡(luò)層:加上 IP 頭
- 數(shù)據(jù)鏈路層:加上幀頭和幀尾
- 物理層:則是轉(zhuǎn)換為包含0、1的二進(jìn)制比特流
講講 TCP 三次握手
- 首先剛開始雙方處于關(guān)閉連接狀態(tài),服務(wù)端處于監(jiān)聽端口狀態(tài),也就是 Listen 狀態(tài)
- 第一次握手:客戶端首先生成隨機(jī)初始化序列號seq = x,并放到 TCP 頭部的32位序號字段中,同時將 SYN 標(biāo)志設(shè)置為 1,表示這是一個 SYN 報文,然后發(fā)送給服務(wù)端,接著客戶端處于 SYN_SENT
- 第二次握手:服務(wù)端收到客戶端發(fā)送過來的 SYN 報文后,首先也會生成隨機(jī)初始化序列號seq = y,并放到 TCP 頭部的32位序號字段中, 并對客戶端的序列化 seq = x + 1 作未確認(rèn)應(yīng)答號,然后放到 TCP 頭部的確認(rèn)應(yīng)答字段中,同時將 SYN 和 ACK 標(biāo)志設(shè)置為 1,表示這是一個 SYN-ACK 報文。把該報文發(fā)送給客戶端后,服務(wù)端處于 SYC_RCVD
- 第三次握手:客戶端收到服務(wù)端發(fā)送過來的 SYN_ACK 報文,會發(fā)送確認(rèn)報文給服務(wù)端,這個確認(rèn)報文是對服務(wù)端的初始序列化 seq = y + 1, 客戶端進(jìn)入 ESATBLISH 狀態(tài)
- 服務(wù)端收到后,也進(jìn)入 ESTABLISHED 狀態(tài)
你剛剛說的 Listen、SYN_SENT、SYN_RCVD、ESTABLISHED 狀態(tài)有什么含義?
- LISTEN 狀態(tài)表示監(jiān)聽是否有連接到來,當(dāng)有連接到來時,它獲得已經(jīng)連接的 socket
- SYN_SENT 表示 客戶端具備發(fā)送數(shù)據(jù)能力。但還不具備接受數(shù)據(jù)能力, 此時需要等待服務(wù)端的確認(rèn)
- SYN_RCVD 表示服務(wù)端具備接受數(shù)據(jù)的能力和發(fā)送數(shù)據(jù)的能力,此時需要等待客戶端的確認(rèn)
- ESTABLISHED 表示我已經(jīng)建立連接了,我可以發(fā)送數(shù)據(jù)了
客戶端發(fā)送了數(shù)據(jù)給服務(wù)端,服務(wù)端返回對方成功確認(rèn)收到的確認(rèn)信息,這個時候是否可以肯定服務(wù)端收到了數(shù)據(jù)
- 不一定,服務(wù)端有一個接受緩存區(qū),此時服務(wù)端還在處理前面的數(shù)據(jù),有可能服務(wù)端發(fā)生異常了,導(dǎo)致接收緩沖區(qū)的數(shù)據(jù)未被處理
那怎么解決這個問題呢?
- 嗯,觸發(fā)重傳機(jī)制,客戶端重新發(fā)送數(shù)據(jù)?(懵逼)
客戶端想盡快關(guān)閉連接,應(yīng)該怎么辦?
- 發(fā)送 FIN 報文?
- RST 報文好像也可以斷開連接
Socket 編程了解過嗎,什么是 socket
- 了解過
- Socket 是一個套接字
socket 的流程
不會(我搞 java 的,沒研究過 socket 編程,完了)
基本 socket 做好了封裝,你了解嗎
剛開始懵逼,后來想到才是 Netty 這個框架
Socket 和 http 有什么區(qū)別?
- Socket 是一個套接字接口
- Http 是請求連接,http 是 tcp 連接的管理器
你說說 spring 的生命周期?
大致分為五個階段,創(chuàng)建前準(zhǔn)備階段、實(shí)例化階段、依賴注入階段、容器緩存階段、實(shí)例銷毀階段
后面從說了每個階段是干嘛的(面試官反應(yīng)邏輯講的不夠清楚,這里我就不列出來的)
事后復(fù)習(xí)總結(jié)如下:
- 創(chuàng)建前準(zhǔn)備階段:
Spring 啟動后,掃描 @ComponentScan 注解配置的路徑下的所有 .class 文件,
類加載其根據(jù)類名加載獲取類的 Class 對象
判斷類上是否有 @Component、Service 等注解找出 bean 對象
給每個符合條件的 bean 創(chuàng)建 BeanDefintion 對象用于存放 Class 對象、作用域等信息,作用域包括 singletion、prototype、request 等,然后添加進(jìn) beanDefinitionMap, key 值存放 bean 的名字,value 是對應(yīng)的 BeanDefition
掃描 bean 對象
遍歷 beanDefinitionMap,創(chuàng)建
MyBatis 中 ${} 與 #{} 的區(qū)別
- 無法防止注入攻擊,在開發(fā)中盡量使用{}
- #{} 是占位符,預(yù)編譯處理,${} 是拼接符,字符串替換,沒有預(yù)編譯處理
感覺
面試官說我們是做游戲開發(fā),然后又問我你平時喜歡打游戲嗎,我說近些年很少打游戲的。以前很喜歡玩,后面覺得膩了,然后面試官說做游戲開發(fā)需要對游戲很了解的,對游戲很熱愛的,否則難干下去
反問環(huán)節(jié)說我基礎(chǔ)還算行,就是可能在業(yè)務(wù)方面可能不匹配
不足之處
socket編程不太熟悉,計網(wǎng)還需加強(qiáng)學(xué)習(xí)
Spring bean 的生命周期 沒有讓面試官聽懂