從一道真實的面試題,聊聊消息機制。

前言

上週一同事面試遇到一個比較有趣的面試題,大意是,我們在項目中,像分佈式消息隊列,比如RocketMQKafka等都用得很多了,他們的優點比如解耦大家都很熟了,那麼,有沒有在項目中,用到一些單機的隊列,或者事件通知機制?

坦白說,絕大部分同學平時開發,都是CRUD爲主,除了分佈式隊列外,能和隊列沾上邊的,還是比較少的。可能比較好想到的是,線程池中有隊列這麼一個概念。但是這種生拉硬扯的隊列“遠方親戚”可能也不是面試官想聽的,肥朝就根據自己的經驗,說一下單機的隊列,或者事件通知機制的場景。

場景描述

先簡單交代背景,每個公司肯定有一些前後端交互的規範,比如常見的請求頭參數響應json格式等等,這個很好理解。所以我們會在spring-boot-starter-web的基礎上,進行二次開發,做一些增強,這裏命名爲feichao-boot-starter-web。(不要問我feichao是什麼)

特別聲明,我們之所以做二次開發,並不僅僅是爲了上述的功能,增強的全部的功能不是本文重點,這裏暫不介紹。

但是我們知道,肯定有一些不需要校驗請求頭的url,比如我們也對Swagger做了一些增強feichao-boot-starter-swagger。因此,就需要在feichao-boot-starter-web做一些忽略校驗的URL。

public class FeiChaoMVCProperties {

   /**
    * 忽略校驗請求頭的url
    */


   private List<String> filter = new ArrayList<>();
}




我們遇到了什麼問題?

稍微熟悉swagger的原理都知道,比如我們打開swagger的html界面的時候,那肯定要訪問你的web項目來進行獲取swagger的相關注解參數信息,那麼問題來了,這個請求,就明顯是不需要校驗請求頭的URL。也就是說swagger的html界面發起的請求,是不需要做任何請求頭的校驗處理的。但是我們在feichao-boot-starter-web做了請求頭的攔截校驗處理。

那麼對於這個功能,肥朝認爲有常見的幾個解決辦法。

常見的問題解決思路分析

1.依賴api

要麼feichao-boot-starter-swagger依賴feichao-boot-starter-web然後往FeiChaoMVCPropertiesfilter裏面set進相關的swagger請求URL,從而達到忽略校驗的目的。但是這樣就會出現一個問題,就是feichao-boot-starter-swagger就會依賴feichao-boot-starter-web。我們都知道,starter就相當於一個組件,組件和組件之間就不應該有依賴關係,再說,我們寫代碼,提倡的不就是低耦合。其實生活也是一樣的,舔狗不就是因爲過度依賴對方,一直舔,最後一無所有?

當然你可能會說,那我把這個FeiChaoMVCProperties放在一個公共的common.jar不就行了?當然這個也可以,但是你想FeiChaoMVCProperties的字段以及後續維護,肯定是和該starter-web相關的,如果放在common.jar,和我們面向對象的想法,總還是有些違背。

2.配置

那麼還有一種方案,就是FeiChaoMVCProperties還是放在starter-web,但是FeiChaoMVCProperties的內容,是可以通過配置文件配置的方式來設置值,這樣,把swagger的相關URL,放在配置文件不就行了。看似沒問題,但是你想啊,我現在用絲襪哥(swagger)你就配置一下url,那萬一我後面又多增加一個肥朝哥,難道你配置文件又要配置一下url?

如何解決遇到的問題?

那麼這個問題如何解決呢?我們可以利用Spring的事件機制,當然有些同學可能用到的是GuavaEventBus。但是爲了儘量減少依賴,我們就直接用Spring的。

我們在feichao-boot-starter-web的模塊就可以進行監聽

@Component
public class FilterUrlListener implements ApplicationListener<UrlFilterEvent>{

   @Autowired
   private SpringMVCProperties springMVCProperties;

   @Override
   public void onApplicationEvent(UrlFilterEvent event) {
       List<String> urls = event.getSource();
       springMVCProperties.getFilter().addAll(urls);
   }

}












然後feichao-boot-starter-swagger進行發送

@Component
@Slf4j
public class SwaggerFilterUrlEvent implements ApplicationContextAware {

   @Override
   public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
       applicationContext.publishEvent(new UrlFilterEvent(SwaggerConstant.SWAGGER_URLS));
   }
}








這樣,兩個組件之間就徹底解耦開來。後續除了絲襪哥(swagger)外,即使增加一個肥朝哥的組件,都不需要改動feichao-boot-starter-web的代碼。只需要在肥朝哥的組件中,發送一個消息即可。

因爲肥朝平常的日常工作主要是基礎架構相關的,任何一個耦合的設計,一旦改動起來,影響還是比較大的。當然除了上述場景,還有一些其他場景,比如目前公司做的是海外業務,就會涉及到國際化,那麼就會有多個服務會用到語言這個東西。怎麼把語言這個字段,在多個業務之間傳遞,就會是個問題。如果你直接在業務的DTO,比如訂單DTO加個語言的字段,那麼就明顯很不友好了。如何做到業務方隨時隨地都能獲取語言,但是語言這個字段又不和具體業務DTO耦合?那麼這個肥朝下期再講,防止一些假粉老是白嫖,每次看完了就取關!!!

知識拓展

關注肥朝公衆號的老粉絲想必都知道,肥朝向來不解決某一個問題,對於這個問題的思考,我們到這裏就截止了嗎?當然不是,比如,我們文中提到的過濾URL。這個過濾URL,我們希望做成類似SpringMVC,或者Tomcat這樣,能配置/*/feichao/*等形式,那麼怎麼實現這個需求呢?

其實這個問題,常見的錯誤做法是,花費大量的時間寫一個這樣的匹配URL邏輯,甚至惡補一下正則表達式之類的。但是這些做法都有如下缺點:

1.如果你寫的這個規則比較蹩腳,業務方的同事需要了解你這個配置的規則,需要一定的學習成本

2.自己寫的,bug多多不說,正則這個出問題了,不好debug

3.花費的時間巨大,得不償失

那麼,根據肥朝的愚見,比較合適的做法是怎麼樣呢?其實這個你直接參考Tomcat或者SpringMVC中的這個功能,看一下他們是怎麼實現的,然後把他們的代碼拷貝過來,這樣,業務方的同學,需要配相關URL的時候,他就根本不需要重新學習,就按照之前SpringMVC的配置習慣來即可,另外,經過SpringMVC、Tomcat等知名項目考驗,穩定性沒問題,最重要的是,你如果有閱讀源碼的優秀習慣,找到這段功能代碼,基本是半個小時之內,比你自己寫個類似邏輯,簡直好太多,所以,不要再問我,看源碼有沒有用了。

當然這個功能,Spirng提供了一個好的工具類做類似的事,你直接使用即可,AntPathMatcher。什麼,你不知道這個工具類,早喊你關注肥朝的公衆號你就是不聽!

除此之外,還有哪些使用場景?

當然對於事件機制,還有很多的使用場景,比如,肥朝要求每個項目配置文件必須設置appName,那麼我就需要在Spring容器啓動的時候,做相應的校驗,如果沒設置,就不允許項目啓動。當然還有今天羣裏朋友討論的,一些CRUD記錄操作日誌的場景,除了註解+AOP的方式來記錄操作日誌,也可以在執行完操作後,發出一個消息,然後在監聽消息做相關的日誌記錄。

當然,相信日常工作內容豐富多彩極具挑戰性的你,也肯定還有一些其他的使用場景,歡迎留言告訴肥朝。



圖片


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章