在樂視風口浪尖的時候,敢於站出來說我是樂視的而不怕被打臉的,也就是我了。就算我以后不在樂視了,提起來在樂視工作過,我也還是挺驕傲的。因為這是一個有理想,敢拼敢干的公司。想起復仇者聯盟里Fury指揮官的一句話:Until such time as the world ends, we will act as though it intends to spin on. 上周我們去懷柔團建,人家都是兩個大人住一間,帶小孩子的是三個人一間。我帶着我家小王子兩人住了1380一晚的別墅,據說是最好的房間。像我說過的,每每好事兒都讓我攤上了,所以還是該干啥干啥。
我們部門叫基礎業務平台部,負責基本管理樂視視頻的視頻,音頻及所在的專輯數據。單台QPS幾千,業內人士表示並發量不大,只是公司的集中緩存差強人意。
開放平台的系統框架是這樣的:
這是一個很規范的網站系統框架,基本可以滿足目前大部分SOA垂直拆分網站架構的需求。項目依賴關系是這樣的:
客戶層ope-web采用的是標准的spring mvc架構。定義了三個視圖解析器:
1>InternalResourceViewResolver 這個是UrlBasedViewResolver的方便子類。因為前端頁面采用的是JSP,這個必然是首選。
2>CommonsMultipartResolver 涉及上傳視頻和圖片,這個必不可少。需要注意最大上傳大小和最大占用內存大小。
3>SimpleMappingExceptionResolver 定義統一異常處理。我們這個項目中配置的默認跳轉頁面defaultErrorView是404,異常時攜帶的屬性exceptionAttribute是ex。在exceptionMappings只定義了一個叫AccessException的異常,跳轉到errors頁面。
說到Spring MVC還是先放一張架構圖吧
由圖中可以看到整個spring mvc核心是dispatcherServlet,客戶端將請求提交給它,它查詢web.xml里的mapping定義找到Controller。我們項目mapping定義是
<servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/applicationContext.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
所有的請求都走這個dispatcherServlet,按照applicationContext.xml的配置處理,這里面配置了自動掃描的controller路徑和上面提到的視圖解釋器。來看一眼dispatcherServlet的核心源碼:
protected void initStrategies(ApplicationContext context) { initMultipartResolver(context); //文件上傳解析,如果請求類型是multipart將通過MultipartResolver進行文件上傳解析; initLocaleResolver(context); //本地化解析 initThemeResolver(context); //主題解析 initHandlerMappings(context); //通過HandlerMapping,將請求映射到處理器 initHandlerAdapters(context); //通過HandlerAdapter支持多種類型的處理器 initHandlerExceptionResolvers(context); //如果執行過程中遇到異常將交給HandlerExceptionResolver來解析 initRequestToViewNameTranslator(context); //直接解析請求到視圖名 initViewResolvers(context); //通過ViewResolver解析邏輯視圖名到具體視圖實現 initFlashMapManager(context); //flash映射管理器 }
從命名就可以看出,這里面主要用到了策略模式,對不同的視圖采用不同的策略。ApplicationContext是一個抽象接口,spring的上下文將框架內部的各個組件信息都通過一個context暴露給外部,是一個門面模式。
dispatcherServlet在進行請求分發的時候還提供了一些服務:
1>保存現場:保存request熟悉的快照,以便能在必要時恢復。
2>將框架需要的對象放入request中,以便view和handler使用。
3>在請求分發后恢復現場。
建議大家看看DispatcherServlet的源碼,經常做spring mvc項目的話,相信源碼不難看懂。大家可能經常遇到的問題,比如看了<Java並發編程實踐>這本書,感覺這些東西項目中用不到啊。其實不是這樣,這本書很基礎,里面的東西都用到了,只是封裝在框架里了,很多人沒仔細研究而已。記得書里講安全發布的時候講到使用Collections.unmodifiableMap來發布一個只讀的map。這個在DispatcherServlet源碼里對它的運用非常的典型:將flashmap的一個快照保存在request的屬性里以備查看用。
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response); if (inputFlashMap != null) { request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap)); }
從源碼到原理,最基礎的,比如:調用構造器來創建一個Java類的時候,要知道這個構造器實際上是一個靜態方法。所以第一次調用構造器創建對象的時候,或者訪問這個類的靜態方法或者靜態成員的時候,Java解釋器先要定位其字節碼(class)文件,加載了字段文件后,要進行所有的靜態初始化工作,這個工作只進行一次。
多想想,從編碼到排查問題,相信都不會無從下手。
上面說了在spring里可以配置異常處理頁面,這個不通過spring直接走servlet也可以,只要在web.xml里配置一下:
DispatcherServlet還實現了一個很重要的功能:攔截器,我們項目中主要用它來做用戶身份驗證。用戶身份驗證要走樂視網統一的SSO,在隔着我工位4,5排的用戶中心組那邊。只是一個外部接口的調用,但是總不能每次用戶發一個請求就調一次sso啊,外部調用network hops延時很嚴重的,所以這時候就用到了集中式緩存。取了一次之后將驗證身份的token存於redis里,有效期24小時。
我們組和SSO組中間隔着前端組。JSP的靜態頁和JS都是前端提供的。為了進一步解耦前后端的工作,數據的加載都走的是js異步調用,數據由前端去渲染。為了應對前端的修改,jSP中大量引入靜態分片。這個靜態分片由一個后台服務定時將最新的分配刷新到本地。路徑保存在本地緩存中。本地緩存用的google的guava工具包。
再說攔截器,攔截器和servlet的過濾器很像,它們都是AOP變成思想的體現。這地方要注意:在web.xml配置的都是servlet的功能,在applicationContext里配置的是spring mvc的功能。它們的區別也在這個地方。因為Filter是Servlet的規范,僅能在Servlet前后起作用。而interceptor和spring可以親密互動,能夠深入到方法前后,異常拋出前后等,可以訪問Action上下文,值棧里的對象,可多次被調用。
一般項目中用過濾器的就是utf8字符過濾器,在web.xml的配置如下:
<filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
Servlet規范中還定義了一種特殊類,監聽器,用於監聽域對象的創建與銷毀,以及這些域對象屬性的修改事件。我們項目中它來配置logback日志的監聽。