面试项目介绍
自我介紹
面試官您好,我叫王云虎,來自東南大學自動化學院,專業是電子信息,目前是二年級碩士研究生。在研究生期間,擔任學院研會外聯部部長,同時獲得2021年學校三好研究生等榮譽稱號。熱愛學習,研究生期間主要研究方向為一些人工智能算法方面,目前已經發表一篇EI檢索會議論文和一篇被SCI期刊已經錄用的論文,期間也做了一些實驗室接的后端項目,同時自己也和同門一起做了一個小項目,想尋求一份Java后端開發工程師的職位。
威騰項目
項目介紹
威騰母線監控系統是和物聯網相關的項目。現在傳統行業都想和互聯網結合,來提升自身的競爭力。這樣的項目一般都有一些終端設備會采集并上傳運行狀況等信息,后臺讀取這些信息,進行處理,比如過限預警等,然后存儲在數據庫中。后臺除了和設備交互之外,還需要向前端提供分類查詢之類的服務,前端將數據以圖表等方式展現出來。
在這個項目中。我的主要工作是對項目進行完善,主要負責的是設備解析模塊,并且在這個過程中學習Web項目的組成部分和開發過程。我使用工廠和策略等設計模式重構設計了設備消息解析模塊。在我們這個項目中目前大約有三種不同類型的采集器,或者說傳感器,采集的數據不太相同,有的是電力數據,比如電壓電流,有的是溫度。之前這三種類型的消息都在一個函數內解析完成,用if-elif-else這樣的分支語句來寫的,造成整體代碼非常長,而且后續需要修改、添加功能時,都需要在大幾百行的代碼里來尋找,可讀性和可維護性都很差。因此考慮使用三個單獨的類實現消息解析功能。設備傳過來的每幀消息里都會帶有一個“類型”的字段,所以可以根據這一字段,通過工廠獲取相應的消息解析類。而且,我希望“類型”到實際處理類的轉換是通過配置文件來配置,而不是硬編碼在工廠的代碼里,于是采用策略模式,后續如果要添加新的消息類別,只需要添加一個新的消息處理接口實現類,再在配置文件中添加一行映射關系,這樣只需要一個工廠通過消息的類型就可以獲得相應的消息解析類,可以真正實現開閉原則。我對工廠的實現大致是,啟動時,工廠從配置文件中讀取這些對應關系,通過全限定名獲取消息解析類的Class對象,然后通過Spring工廠上下文獲取指定類型的Bean對象,通過實現Aplicationaware接口,獲得Spring工廠上下文對象。(這里之所以沒有使用newInstance()方法創建實例的原因是,消息處理類的內部除了要處理消息之外,還得做存儲,采集的數據不同,所以存儲肯定也是存到不同的表里嘛。所以消息解析類還需要依賴DAO等對象,如果用newInstance()手動創建出來,還得對每個字段進行賦值,非常麻煩,而注入依賴這件事,Spring IoC容器最擅長了,所以我通過Spring來獲取這些已經初始化完成的消息處理類。)獲取到這些類之后,存入HashMap,在運行時可以快速獲取消息處理程序。此外,由于Spring工廠上下文的創建比較慢,所以我不能在類實例化的時候就做剛剛所說的的初始化操作,而需要將初始化操作延遲到第一次使用工廠時,為了避免多線程競爭初始化的問題,我還采用了雙重鎖定判斷實現了延遲初始化。
(我使用模板模式則是因為抽象出了兩個變化的因素,一個是工廠生產的消息解析類的子包名可能會變化,一個是類型到消息解析類映射關系的獲取方式可能會變化。現在我是采用配置文件配置這種關系,也許某一天,我可能會采用數據庫來獲取這種關系,我先預留出了這個變化因素。)
因為設備上傳數據比較快,大概是5s一幀,而且同一時間會有多個設備上傳數據,考慮到項目上線時,可能會有上萬臺設備同時在線,數據庫寫入壓力過大,尤其是因為我們是單機部署,因此采用Redis緩存一分鐘內設備的消息,后臺啟用一個定時任務把緩存寫入數據庫。
如何處理設備端高并發網絡讀寫問題?
由于我們做的是物聯網類型的項目,所以不可避免地要與設備之間建立網絡連接通信。甲方聲稱它們未來會有幾千上萬臺設備,所以對于這么多的并發連接,我們這種單機部署的方式可能難以承受。因此,我們采用了阿里云物聯網平臺提供的消息隊列中間件來解決這一問題。設備將消息推送到阿里云上,我們的后端從阿里云一次性拉多條數據下來處理,這樣,與設備端相連的高并發網絡讀寫、維護長連接(什么是長連接?與設備相連時,設備需要時刻注意自己的網絡狀態,斷線時需要重連)、TCP粘包等問題就被解決了(或者說問題由專門的高并發中間件解決了,尤其我們還是用了阿里云的物聯網服務,所以相比于我們單機部署所有應用的情況,性能會更好),服務端只需要開少量的線程,處理并存取數據即可。
除了維持大量網絡連接的問題之外,還需要考慮合并插入的問題,也就是將高頻的設備數據插入進行緩存,然后定時插入數據庫中
為何使用Redis?項目中怎么使用的Redis
一面時的說辭
因為設備上傳數據比較快,大概是5s一幀,而且同一時間會有多個設備上傳數據,考慮到項目上線時,可能會有上萬臺設備同時在線,數據庫寫入壓力過大,尤其是因為我們是單機部署,因此采用Redis緩存一分鐘內設備的消息,后臺啟用一個定時任務把緩存寫入數據庫。這里存在一個問題,由于一次性插入一分鐘內的所有數據,這樣會丟掉每一幀消息的時間戳,所以前端獲取數據時,還需要通過時間插值還原出原始數據的時間戳——因為一分鐘一存,所以取出時,看看一幀里能拆分出多少次數據,做個除法得出大致的時間間隔,計算出每個數據點大致的時間,然后再推給前端面對大佬時的說辭
我們的項目中使用到了redis,一方面是用來緩存設備消息的,因為設備上傳數據比較快,大概是5s一幀,而且同一時間會有多個設備上傳數據,考慮到項目上線時,可能會有上萬臺設備同時在線,數據庫寫入壓力過大,尤其是因為我們是單機部署,因此采用Redis緩存一分鐘內設備的消息,后臺啟用一個定時任務把緩存寫入數據庫;第二個地方使用了redis是在一個郵件報警功能中,因為郵件發送相對來說比較慢,所以業務程序中將它放到了redis的一個列表鍵里,然后有一個報警推送業務不停地讀這個隊列,進行郵件發送的功能。那你們為何不使用hashmap?
隨著后續學習的深入,我也覺得,在當前這種單機部署的環境下,似乎沒有必要使用redis,設備消息緩存可以使用hashmap來做,郵件發送功能可以使用線程池來做。 但是redis還有一些功能,是hashmap和線程池給不了的。比如說,持久化,比如說數據相對來說更加安全,假如應用服務器程序死掉了,數據就全消失了,但是存在redis中呢,只要系統不宕機,數據就不會丟失,即使系統宕機,還能從持久化的數據庫中恢復一些數據。 不過我覺得這些功能應該有本地緩存框架可以實現,我沒有深入地了解過,只是印象里有一些本地緩存框架可以實現這種k-v存儲,以及持久化的功能。你覺得什么時候才有必要使用redis
我個人感覺,起碼要在分布式環境下,才有使用redis的必要,最起碼,redis不應該和應用服務器部署在一起吧? redis自身是集中式分布式緩存,所以用于共享一些狀態,比如分布式session可以存在redis中,這樣用戶就不會因為請求被路由到不同的服務實例上就重新登陸了。項目有哪些可以改進的地方
最重要的就是,部署的時候是不是可以使用Docker,簡化環境的配置;實際上也沒有必要使用Redis,都是單機部署,用本地緩存就足夠了
困難
最困難的事情就是剛接手這個項目,就是我剛剛說負責重構的那個的時候,一臉懵逼吧,代碼很亂,甚至還有bug,然后就是一點文檔都沒有。我連需求、功能都不知道是什么,反正就是很崩潰,不知道該從哪里下手。
這個困難其實也好解決,反正萬事開頭難嘛,開頭的一兩天,我基本上都比較暴躁,但是后來熟悉了整個系統的功能之后,就一點一點慢慢改。我找到了之前的負責人,問了問每個功能是怎么用的,然后一天補一點,就逐漸熟悉了。
在這個過程中,我又發現了一個bug,就是每次我啟動這個程序,我自己的本子跑起來風扇嗚嗚轉,我當時還到處去問,是不是springboot比較費cpu資源啊?師弟也沒給我肯定的答案,就說可能吧,然后我就一直以為是個正常現象。直到后來,我閑來無事翻代碼加注釋的時候,終于找到了這個bug在哪里,原來是因為,我們需要實現一個郵件報警功能,如果傳感器采集到的數據超限了,那就給項目的負責人發送一封郵件。當時也設計得挺好的,因為發郵件是耗時工作,就把任務放到了Redis的一個列表鍵里,開了一個線程,死循環地去讀Redis的這個列表鍵,當然一般情況下是不會有郵件報警的,所以這個線程就相當于在死循環。找到這個bug之后,我就做了修改,我想的辦法就是說,每次如果獲取結果是空的話,那就讓這個線程休眠1s,如果獲取有結果的話,那就死循環獲取,把消息都讀完。
現在再看這個事,感覺處理的很不好,應該第一時間查看哪個進程cpu的使用率是最高的,然后可以使用jdk自帶的java VisualVM查看具體的哪個線程使用率最高。
Linux系統中,先使用top指令找到占用CPU過高的進程pid。
首先顯示線程列表,并按照CPU占用高的線程排序:
[root@localhost ~]# ps -mp 41673 -o THREAD,tid,time | sort -rn將需要的線程TID轉換為16進制格式
[root@localhost ~]# printf "%x\n" 41846 a376最后使用jstack命令打印出該進程下面的此線程的堆棧信息:
[root@localhost ~]# jstack 41673 |grep "a376" -A 30現在再看這個事,其實我又覺得,根本用不到Redis,包括項目里的一些緩存,也完全沒必要用redis,都是單機部署,直接在java內部緩存一下不就好了嗎?對于發郵件這件事,我覺得,用線程池實現不就好了嗎?因為線程池是支持阻塞獲取的,甚至都不用考慮讓cpu暫停一會兒這樣的事了。
其實這個項目也還有挺多地方可以繼續整改的,因為這個學期我們的重心主要會放在找工作上面嘛,所以我都把它留給了師弟,并且把我認為更好的設計,寫在了文檔里。雖然說也有點像是留了一堆鍋給師弟,但是起碼我沒有留bug給師弟,我留的是對這個項目的思考,更好的設計給他吧,而且還是有文檔的系統。我覺得這個項目現在最大的不足,就在于部署非常麻煩,需要裝一堆東西,什么mysql啊,redis啊,mysql的導出表啊,redis的rdb啊,項目里的靜態資源文件啊,我發現漏了一個文件就傳一個,傳得都不好意思了,所以我把使用Docker部署項目,放在了寫給師弟的建議里的第一行。
問題
消息隊列用來流量削峰?
redis內部數據結構,哈希鍵,每個項目內部有一個哈希鍵用于存儲緩存的數據,哈希鍵內部的鍵是采集點的編號,名是拼接的字符串數組。
SSD和機械硬盤的區別?
能不能畫個圖跟我說說,你們項目,redis,消息隊列之間的關系?
你們項目中有沒有考慮到可靠性?為什么需要Redis做合并寫入
實驗室工具箱項目
為什么要設計實驗室工具箱?
因為我實驗室同門有一部分是做地震數據分析的,地震的一些數據需要使用爬蟲獲得,每次需要這些地震數據的時候其他人都會找我一個同門讓他幫忙爬一下,這樣每次都讓他有點煩。所以我們就想到建一個web,然后可以遠程調用這些使用Python寫的各種數據獲取,數據處理的代碼,之后還方便對這個系統進行擴展,增添一些其他的功能。
為什么要使用單點登錄?
因為我們有數據獲取、數據處理等這些不同的模塊,如果不使用單點登錄,每次使用一個模塊都要進行一次登錄驗證。使用單點登錄就可以把登錄模塊單獨拿出來,用戶登錄成功后將用戶的登錄信息永JWT生成token存在redis里面,并設置一個過期時間,然后后面前端如果有需要登錄驗證的需求的請求時,就可以使用一個攔截器將token取出來,查看redis里面有沒有當前用戶的登錄信息。
為什么要使用JWT生成token?
因為使用JWT更加安全,JWT由3部分組成:Header、有效載荷(Payload)和簽名,Header是一個JSON對象里面包含簽名所使用的算法,默認為HS256,生成的令牌屬性。Payload也是一個JSON對象,是主體部分,我在里面存放的userid,username和時間戳。Head和Payload使用base64進行編碼,簽名部分對使用base64編碼后的head和payload部分使用指定的算法進行加密,并且加上一個密鑰,該密鑰只存在服務器中。驗證登錄的時候首先判斷傳過來的token中的head和payload部分有沒有被篡改,然后再根據userid查詢用戶在不在redis里面。
為什么要使用springcloud?
因為之前也想過就使用socket進行連接,但是后面發現服務接口越來越多,就想使用一個框架對這些服務接口進行統一管理。而且我也想以后對這個實驗室工具箱進行拓展,加一個留言板等功能,大家可以自由的在上面討論問題。
怎么使用springcloud的?
主要使用了springcloud的eureke服務注冊中心,專門負責服務的注冊和發現,里面有一個注冊表,保存了各個服務在哪臺機器上,端口號是多少,有了服務注冊與發現,只需要使用服務的標識符,就可以訪問到服務。EureKa自我保護機制:默認情況下,當eureka server在一定時間內沒有收到實例的心跳,便會把該實例從注冊表中刪除(默認是90秒),但是,如果短時間內丟失大量的實例心跳,便會觸發eureka server的自我保護機制,該保護機制的目的是避免網絡連接故障,在發生網絡故障時,微服務和注冊中心之間無法正常通信,但服務本身是健康的,不應該注銷該服務,如果eureka因網絡故障而把微服務誤刪了,那即使網絡恢復了,該微服務也不會重新注冊到eureka server了,因為只有在微服務啟動的時候才會發起注冊請求。
AP可用性和一致性
Feign:實現服務調用的,只需要創建一個接口并使用注解的方式來配置它,即可完成對服務提供方的接口綁定,讓微服務之間的調用變得更簡單,類似controller調用service.(Feign Client會在底層根據你的注解,跟你指定的服務建立連接、構造請求、發起靕求、獲取響應、解析響應,等等。)如果你對某個接口定義了@FeignClient注解,Feign就會針對這個接口創建一個動態代理接著你要是調用那個接口,本質就是會調用 Feign創建的動態代理, Feign的動態代理會根據你在接口上的注解,來動態構造出你要請求的服務的地址,最后針對這個地址,發起請求、解析響應
Hystrix實現服務降級,當獲取數據的時候,如果請求獲取10年地震tec的數據,由于地震數據很龐大,使用爬蟲在網上獲取的,如果要獲取十年的數據很可能出現內存不夠的問題,所以就想到使用hystrix服務降級。使用hystrix的超時時間,如果時間超過60秒,就發生服務降級,返回null,并且通知發生服務降級,請適當縮小日期間隔,每次請求最好保證時間在60天內。
算法項目
項目背景:能源在人類發展史上扮演著極其重要的作用,但是目前能源緊缺與環境污染問題日漸突出,人們開始對太陽能、風能等可再生清潔綠色能源的利用方式進行研究。目前,城市智慧能源系統建設尚處于探索階段,對其進行研究,可以優化城市能源結構、提高能源利用效率、促進清潔能源開發利用,最終實現城市能源消費的低碳化。
首先從城市功能出發,調研各參與方的需求,總結出城市智慧能源的定義。然后對系統的信息物理系統進行研究,基于信息物理系統構建系統的信息物理架構。接著對系統的功能架構進行研究,系統架構由形式與功能的映射構成,其中,形式是指系統的物理體現與信息體現,功能通過形式來執行,形式對功能起著工具性的作用。因為任何事物的發展總是需要評價評估,因此對系統進行評價是很有必要的,所以最后建立城市智慧能源系統的綜合評價體系。在這個項目中,我主要的工作是對城市智慧能源系統功能架構的應用微電網配置優化進行研究,和建立城市智慧能源系統的綜合評價體系。
物理架構:通過對城市智慧能源系統各方需求進行分析,搭建以電熱冷氣交通為主要能源輸入,建立和可再生能源天陽能、風能等進行多能互補為重要物理特征的物理架構。在CPS中,信息網絡深深地嵌入在每個物理部件上,每個物理部件在覆蓋于其上的信息空間的作用下能夠實時地自我控制,同時,不同物理部件還可以在信息網絡的幫助下靈活通信。分為智能終端設備,能源管理站,能源智慧服務平臺。
功能架構:能源系統安全、能源協同控制、能源數據服務與能源交易市場。化石能源的枯竭和環保的訴求,使得綠色能源成為大家關注的話題,但是傳統電網對消納綠色能源顯得十分吃力,微電網被認為是緩解上述問題的一個解決方法。微電網協同優化屬于能源協同控制模塊,微電網配置優化是微電網協同優化運行的一個子問題,它屬于數據服務模塊的內容,微電網適當的配置更有利于微電網協同優化運行。
微電網配置優化主要是配置小型園區風力發電機、光伏發電機、柴油發電機和儲能設備的數量,目標函數為初始裝機成本、運行成本、維護成本、治理成本組成,有四個目標函數,同時考慮經濟性環保型等問題,是一個多目標優化問題。首先通過遺傳算法求出Pareto最優解集,然后將Pareto最優解集作為PCA的樣本數據,通過主成分分析法求出第一主成分的向量,然后確定各個目標函數的權重,將其轉化為單目標優化問題,然后在使用遺傳算法求解單目標優化問題。
問題:在仿真實驗的時候發現每次求出來的各個目標函數的權重都有一些細微差別,原因是,再多目標問題中,使用遺傳算法求出來對Pareto最優解具有一定的隨機性。解決方案:多做幾次實驗,然后再求平均值求出各個目標函數的權重。
對一個系統進行評價首先要建立系統的評價指標體系。通過對城市智慧能源系統需求進行分析,最后從高效性、經濟性、可靠性、社會性、互動性五個方面選取評價指標,建立系統的評價指標體系。并將城市智慧能源系統分成四個級別,然后將這些評價指標作為神經網絡的輸入,輸出為系統的級別。因為城市智慧能源系統剛剛處于起步發展階段,數據集較少,而訓練神經網絡需要大量的數據集,因此使用SeqGAN對數據集進行擴充.因為神經網絡權值和閾值的初始值會對網絡模型訓練產生影響,容易造成陷入局部最優問題,因此使用遺傳算法優化神經網絡權值和閾值的初始值。
問題:數據集獲取較困難,評價指標是自己建立的,很難獲得完整的數據集,而且城市智慧能源系統剛剛處于起步發展階段,數據集獲取更加困難。
解決:項目是和電科院合作的,電科院陸續提供了一部分未處理的數據,然后再通過調研互聯網收集等將這些數據轉變為建立的評價指標的數據,最后數據量還是有點不足,然后通過SeqGAN生成對抗網絡來擴充數據集。
城市智慧能源系統是以城市為運行范圍,結合材料、環境工程、電力電子、通信、人工智能等學科理論,合理應用信息通信技術和先進控制技術,向信息化、網絡化、智能化、平臺化轉型,充分利用多能互補和源網荷儲協同優化,提升系統穩定性、安全性、經濟性、高效性與環保性,提升城市能源服務質量
總結
- 上一篇: html个人简历网页
- 下一篇: subclipse用法