1. 程式人生 > >軟件各種架構圖2

軟件各種架構圖2

alt handling 範圍 查詢 架構部署 數量 table 分布式 for

Spring MVC 核心架構圖

技術分享

架構圖對應的DispatcherServlet核心代碼如下:

技術分享
//前端控制器分派方法
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        int interceptorIndex = -1;

        try {
            ModelAndView mv;
            boolean errorView = false;

            try {
                   //檢查是否是請求是否是multipart(如文件上傳),如果是將通過MultipartResolver解析
                processedRequest = checkMultipart(request);
                   //步驟2、請求到處理器(頁面控制器)的映射,通過HandlerMapping進行映射
                mappedHandler = getHandler(processedRequest, false);
                if (mappedHandler == null || mappedHandler.getHandler() == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }
                   //步驟3、處理器適配,即將我們的處理器包裝成相應的適配器(從而支持多種類型的處理器)
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

                  // 304 Not Modified緩存支持
                //此處省略具體代碼

                // 執行處理器相關的攔截器的預處理(HandlerInterceptor.preHandle)
                //此處省略具體代碼

                // 步驟4、由適配器執行處理器(調用處理器相應功能處理方法)
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

                // Do we need view name translation?
                if (mv != null && !mv.hasView()) {
                    mv.setViewName(getDefaultViewName(request));
                }

                // 執行處理器相關的攔截器的後處理(HandlerInterceptor.postHandle)
                //此處省略具體代碼
            }
            catch (ModelAndViewDefiningException ex) {
                logger.debug("ModelAndViewDefiningException encountered", ex);
                mv = ex.getModelAndView();
            }
            catch (Exception ex) {
                Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
                mv = processHandlerException(processedRequest, response, handler, ex);
                errorView = (mv != null);
            }

            //步驟5 步驟6、解析視圖並進行視圖的渲染
//步驟5 由ViewResolver解析View(viewResolver.resolveViewName(viewName, locale))
//步驟6 視圖在渲染時會把Model傳入(view.render(mv.getModelInternal(), request, response);)
            if (mv != null && !mv.wasCleared()) {
                render(mv, processedRequest, response);
                if (errorView) {
                    WebUtils.clearErrorRequestAttributes(request);
                }
            }
            else {
                if (logger.isDebugEnabled()) {
                    logger.debug("Null ModelAndView returned to DispatcherServlet with name ‘" + getServletName() +
                            "‘: assuming HandlerAdapter completed request handling");
                }
            }

            // 執行處理器相關的攔截器的完成後處理(HandlerInterceptor.afterCompletion)
            //此處省略具體代碼


        catch (Exception ex) {
            // Trigger after-completion for thrown exception.
            triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
            throw ex;
        }
        catch (Error err) {
            ServletException ex = new NestedServletException("Handler processing failed", err);
            // Trigger after-completion for thrown exception.
            triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
            throw ex;
        }

        finally {
            // Clean up any resources used by a multipart request.
            if (processedRequest != request) {
                cleanupMultipart(processedRequest);
            }
        }
    }
技術分享

核心架構的具體流程步驟如下:

1、 首先用戶發送請求——>DispatcherServlet,前端控制器收到請求後自己不進行處理,而是委托給其他的解析器進行處理,作為統一訪問點,進行全局的流程控制;

2、 DispatcherServlet——>HandlerMapping, HandlerMapping將會把請求映射為HandlerExecutionChain對象(包含一個Handler處理器(頁面控制器)對象、多個HandlerInterceptor攔截器)對象,通過這種策略模式,很容易添加新的映射策略;

3、 DispatcherServlet——>HandlerAdapter,HandlerAdapter將會把處理器包裝為適配器,從而支持多種類型的處理器,即適配器設計模式的應用,從而很容易支持很多類型的處理器;

4、 HandlerAdapter——>處理器功能處理方法的調用,HandlerAdapter將會根據適配的結果調用真正的處理器的功能處理方法,完成功能處理;並返回一個ModelAndView對象(包含模型數據、邏輯視圖名);

5、 ModelAndView的邏輯視圖名——> ViewResolver, ViewResolver將把邏輯視圖名解析為具體的View,通過這種策略模式,很容易更換其他視圖技術;

6、 View——>渲染,View會根據傳進來的Model模型數據進行渲染,此處的Model實際是一個Map數據結構,因此很容易支持其他視圖技術;

7、返回控制權給DispatcherServlet,由DispatcherServlet返回響應給用戶,到此一個流程結束。

此處我們只是講了核心流程,沒有考慮攔截器、本地解析、文件上傳解析等,後邊再細述。

近段時間以來,通過接觸有關海量數據處理和搜索引擎的諸多技術,常常見識到不少精妙絕倫的架構圖。除了每每感嘆於每幅圖表面上的繪制的精細之外,更為架構 圖背後所隱藏的設計思想所嘆服。個人這兩天一直在搜集各大型網站的架構設計圖,一為了一飽眼福,領略各類大型網站架構設計的精彩之外,二來也可供閑時反復 琢磨體會,何樂而不為呢?特此,總結整理了諸如國外wikipedia,Facebook,Yahoo!,YouTube,MySpace,Twitter,國內如優酷網等大型網站的技術架構(本文重點分析優酷網的技術架構),以饗讀者。

本文著重凸顯每一幅圖的精彩之處與其背後含義,而圖的說明性文字則從簡從略。ok,好好享受此番架構盛宴吧。當然,若有任何建議或問題,歡迎不吝指正。謝謝。

  • 1、WikiPedia 技術架構

技術分享

WikiPedia 技術架構圖Copy @Mark Bergsma

  1. 來自wikipedia的數據:峰值每秒鐘3萬個 HTTP 請求 每秒鐘 3Gbit 流量, 近乎375MB 350 臺 PC 服務器。
  2. GeoDNSA :40-line patch for BIND to add geographical filters support to the existent views in BIND", 把用戶帶到最近的服務器。GeoDNS 在 WikiPedia 架構中擔當重任當然是由 WikiPedia 的內容性質決定的--面向各個國家,各個地域。
  3. 負載均衡:LVS,請看下圖:

技術分享

  • 2、Facebook 架構

技術分享

Facebook 搜索功能的架構示意圖

細心的讀者一定能發現,上副架構圖之前出現在此文之中:從幾幅架構圖中偷得半點海裏數據處理經驗。本文與前文最大的不同是,前文只有幾幅,此文系列將有上百幅架構圖,任您盡情觀賞。

  • 3、Yahoo! Mail 架構

技術分享

Yahoo! Mail 架構

Yahoo! Mail 架構部署了 Oracle RAC,用來存儲 Mail 服務相關的 Meta 數據。

  • 4、twitter技術架構

技術分享

twitter的整體架構設計圖

twitter平臺大致由twitter.com、手機以及第三方應用構成,如下圖所示(其中流量主要以手機和第三方為主要來源):

技術分享

緩存在大型web項目中起到了舉足輕重的作用,畢竟數據越靠近CPU存取速度越快。下圖是twitter的緩存架構圖:

技術分享

關於緩存系統,還可以看看下幅圖:

技術分享

  • 5、Google App Engine技術架構

技術分享

GAE的架構圖

簡單而言,上述GAE的架構分為如圖所示的三個部分:前端,Datastore和服務群。

  1. 前端包括4個模塊:Front End,Static Files,App Server,App Master。
  2. Datastore是基於BigTable技術的分布式數據庫,雖然其也可以被理解成為一個服務,但是由於其是整個App Engine唯一存儲持久化數據的地方,所以其是App Engine中一個非常核心的模塊。其具體細節將在下篇和大家討論。

  3. 整個服務群包括很多服務供App Server調用,比如Memcache,圖形,用戶,URL抓取和任務隊列等。

  • 6、Amazon技術架構

技術分享

Amazon的Dynamo Key-Value存儲架構圖

可能有讀者並不熟悉Amazon,它現在已經是全球商品品種最多的網上零售商和全球第2大互聯網公司。而之前它僅僅是一個小小的網上書店。ok,下面,咱們來見識下它的架構。

Dynamo是亞馬遜的key-value模式的存儲平臺,可用性和擴展性都很好,性能也不錯:讀寫訪問中99.9%的響應時間都在300ms內。按分布 式系統常用的哈希算法切分數據,分放在不同的node上。Read操作時,也是根據key的哈希值尋找對應的node。Dynamo使用了 Consistent Hashing算法,node對應的不再是一個確定的hash值,而是一個hash值範圍,key的hash值落在這個範圍內,則順時針沿ring找,碰 到的第一個node即為所需。

Dynamo對Consistent Hashing算法的改進在於:它放在環上作為一個node的是一組機器(而不是memcached把一臺機器作為node),這一組機器是通過同步機制保證數據一致的。

下圖是分布式存儲系統的示意圖,讀者可觀摩之:

技術分享

Amazon的雲架構圖如下:

技術分享

Amazon的雲架構圖

  • 7、優酷網的技術架構

從一開始,優酷網就自建了一套CMS來解決前端的頁面顯示,各個模塊之間分離得比較恰當,前端可擴展性很好,UI的分離,讓開發與維護變得十分簡單和靈活,下圖是優酷前端的模塊調用關系:

技術分享

這樣,就根據module、method及params來確定調用相對獨立的模塊,顯得非常簡潔。下圖是優酷的前端局部架構圖:

技術分享

優酷的數據庫架構也是經歷了許多波折,從一開始的單臺MySQL服務器(Just Running)到簡單的MySQL主從復制、SSD優化、垂直分庫、水平sharding分庫。

  1. 簡單的MySQL主從復制。 MySQL的主從復制解決了數據庫的讀寫分離,並很好的提升了讀的性能,其原來圖如下:技術分享

    其主從復制的過程如下圖所示:技術分享

    但是,主從復制也帶來其他一系列性能瓶頸問題:

    1. 寫入無法擴展
    2. 寫入無法緩存
    3. 復制延時
    4. 鎖表率上升
    5. 表變大,緩存率下降

    那問題產生總得解決的,這就產生下面的優化方案。

  2. MySQL垂直分區

    如果把業務切割得足夠獨立,那把不同業務的數據放到不同的數據庫服務器將是一個不錯的方案,而且萬一其中一個業務崩潰了也不會影響其他業務的正常進行,並且也起到了負載分流的作用,大大提升了數據庫的吞吐能力。經過垂直分區後的數據庫架構圖如下:技術分享

    然而,盡管業務之間已經足夠獨立了,但是有些業務之間或多或少總會有點聯系,如用戶,基本上都會和每個業務相關聯,況且這種分區方式,也不能解決單張表數據量暴漲的問題,因此為何不試試水平sharding呢?

  3. MySQL水平分片(Sharding)

    這是一個非常好的思路,將用戶按一定規則(按id哈希)分組,並把該組用戶的數據存儲到一個數據庫分片中,即一個sharding,這樣隨著用戶數量的增加,只要簡單地配置一臺服務器即可,原理圖如下:技術分享

    如何來確定某個用戶所在的shard呢,可以建一張用戶和shard對應的數據表,每次請求先從這張表找用戶的shard id,再從對應shard中查詢相關數據,如下圖所示:技術分享 但是,優酷是如何解決跨shard的查詢呢,這個是個難點,據介紹優酷是盡量不跨shard查詢,實在不行通過多維分片索引、分布式搜索引擎,下策是分布式數據庫查詢(這個非常麻煩而且耗性能)。

  4. 緩存策略

    貌似大的系統都對“緩存”情有獨鐘,從http緩存到memcached內存數據緩存,但優酷表示沒有用內存緩存,理由如下:

    1. 避免內存拷貝,避免內存鎖
    2. 如接到老大哥通知要把某個視頻撤下來,如果在緩存裏是比較麻煩的

    而且Squid 的 write() 用戶進程空間有消耗,Lighttpd 1.5 的 AIO(異步I/O) 讀取文件到用戶內存導致效率也比較低下。

    但 為何我們訪問優酷會如此流暢,與土豆相比優酷的視頻加載速度略勝一籌?這個要歸功於優酷建立的比較完善的內容分發網絡(CDN),它通過多種方式保證分布 在全國各地的用戶進行就近訪問——用戶點擊視頻請求後,優酷網將根據用戶所處地區位置,將離用戶最近、服務狀況最好的視頻服務器地址傳送給用戶,從而保證 用戶可以得到快速的視頻體驗。這就是CDN帶來的優勢,就近訪問。

附註:1、此段優酷網的技術架構整理於此處:http://www.itivy.com/ivy/archive/2011/8/13/the-architecture-of-youku.html;2、同時推薦一個非常好的站點:http://www.dbanotes.net/)。從上百幅架構圖中學得半點大型網站建設經驗(上),完。

後記

此篇文章終於寫完了,從昨日有整理此文的動機後,到今日上午找電腦上網而不得,再到此刻在網吧完成此文。著實也體味了一把什麽叫做為技術狂熱的感覺。大型 網站架構是一個實戰性很強的東西,而你我或許現在暫時還只是一個在外看熱鬧的門外漢而已。不過,沒關系,小魚小蝦照樣能暢遊汪汪大洋,更何況日後亦能成長 為大魚大鯊。

軟件各種架構圖2