高併發圖片(縮略圖)處理中間層服務架構設計

互聯網公司常常會有大量原始圖片上傳,並且一個原圖會在頁面以不一樣尺寸縮略圖顯示,通常有兩種策略生成縮略圖,一種在上傳圖片時,生成須要的多張不一樣縮略圖,另外一種是請求指定尺寸的圖片時實時生存縮略圖片,第一種方式有必定限制,就是須要提早知道全部尺寸的圖片,作雍餘存儲,無形中增長大量文件數量,若是文件系統設計很差,還有可能造成大量文件碎片,並且會消耗大量存儲空間,若是前端ui設計改變了圖片大小,須要從新生成。而第二種方式更加靈活,可是更消耗cpu資源,屬於cpu密集計算型 html

 

大吞吐量服務端架構設計要考慮四個技術點 前端

 

1 編程語言和編譯優化

技術選型,是單進程多線程模型(reactor事件機制),仍是多進程模型.java

 

2 圖片壓縮算法

高效分佈式文件存儲系統選型。mysql

Linux系統中sysctl參數優化(TCP高級選項設置)react

編程語言和編譯優化

互聯網行業用java開發語言比較多,並且開發人員成熟,並且經驗豐富。linux

高性能網絡框架:netty,mina等等,並且資料比較,社區比較活躍。並且有大量內置的圖像處理API和算法直接使用.對於jdk自帶的一套圖片處理庫,他的特色是穩定簡單,可是對圖片處理來講,性能確實不好!離實際線上要求差距很大。不過java方面也提供了相似jni方式支持GraphicsMagick+im4java處理圖像,可是要原生態支持openmpi,tbb,opencv等就比較繁瑣了,要用jni方式調用大量動態或靜態庫。一個性能問題,二是若是出現內存問題也很差控制。nginx

C語言:算法

1.有成熟圖像處理庫GraphicsMagick和opencv,sql

2.有能夠很容易實現多進程模式。數據庫

3.容易用其餘編譯器作優化,好比用intelicc編譯,能夠大幅度提升性能。

4.多進程中每一個進程方面綁定到每一個cpu核上,實現操做系統每一個cpu核上隊列相同,均衡調度,更容易發揮目前多核cpu性能!

 

下面說一下單進程多線程模型

主線程負責偵聽listen,註冊accept和新進來鏈接,而後把鏈接socket轉交給workthreadpool進行讀寫事件註冊,計算邏輯處理

reactor事件機制:

Reactor釋義「反應堆」,是一種事件驅動機制。和普通函數調用的不一樣之處在於:應用程序不是主動的調用某個API完成處理,而是偏偏相反,Reactor逆置了事件處理流程,應用程序須要提供相應的接口並註冊到Reactor上,若是相應的時間發生,Reactor將主動調用應用程序註冊的接口,這些接口又稱爲「回調函數」.

Reactor模式的優勢

Reactor模式是編寫高性能網絡服務器的必備技術之一,它具備以下的優勢:1)響應快,沒必要爲單個同步時間所阻塞,雖然Reactor自己依然是同步的;2)編程相對簡單,能夠最大程度的避免複雜的多線程及同步問題,而且避免了多線程/進程的切換開銷;3)可擴展性,能夠方便的經過增長Reactor實例個數來充分利用CPU資源;4)可複用性,reactor框架自己與具體事件處理邏輯無關,具備很高的複用性;

 

3 多進程服務器

1每一個進程處理多個connection,使用epoll事件驅動來管理這些鏈接,多個worker進程之間是對等的,他們同等競爭來自客戶端的請求,各進程互相之間是獨立的。

2master由信號驅動,worker由epoll驅動(固然信號會讓epoll_wait返回),有更好的容錯性,若是其中一個進程掛了或產生core,master收到相關信號後,會同時重啓一個進程,並同時發送出相關監控信息,也不會致使不能提供服務,。

3多進程用來利用多CPU硬件,因此按照業務邊界來劃分進程,或者就按CPU個數配置。

4每一個進程是單線程的:全部IO相關操做都是全異步處理方式,避免多線程切換和鎖機制開銷。

5進程之間搶佔epoll資源時,僅用一個輕量級的共享內存鎖,循環依次把鏈接事件放入隊列,而後循環處理每一個客戶端的鏈接請求和邏輯處理。

6高性能:服務器若支持多CPU或超線程,多線程沒法徹底利用機器性能,多進程則可讓服務器滿載.

 

4 圖片壓縮算法(jpeg,png,gif)

目前圖像壓縮算法已經成型,並且基本上都是搞數學方面的大牛發明的,

關於圖像處理方面能夠參考以下:

圖片壓縮或處理是一個很是消耗cpu的操做計算量很是大,由於要進行大量矩陣,傅立葉變換、沃爾什變換、離散餘弦變換,圖像噪聲處理等變換或計算.目前高性能圖像處理開源軟件有2種GraphicsMagick和opecv。

GraphicsMagick:

GraphicsMagick號稱圖像處理領域的瑞士軍刀。短小精悍的代碼卻提供了一個魯棒、高效的工具和庫集合,來處理圖像的讀取、寫入和操做,支持超過88種圖像格式,包括重要的DPX、GIF、JPEG、JPEG-2000、PNG、PDF、PNM和TIFF等等。

經過使用OpenMP但是利用多線程進行圖片處理,加強了經過擴展CPU提升處理能力。

注意:可是GraphicsMagick啓動多線程時,處理速度雖然加快了,可是cpu確大幅飆升。

Opencv:

OpenCV於1999年由Intel創建,現在由WillowGarage提供支持。OpenCV是一個基於[1](開源)發行的跨平臺計算機視覺庫,能夠運行在Linux、Windows和MacOS操做系統上。它輕量級並且高效——由一系列C函數和少許C++類構成,同時提供了Python、Ruby、MATLAB等語言的接口,實現了圖像處理和計算機視覺方面的不少通用算法。[2]最新版本是2.4.5。

OpenCV擁有包括300多個C函數的跨平臺的中、高層API。它不依賴於其它的外部庫——儘管也可使用某些外部庫。

注意:opencv目前支持jpeg,tiff,png,可是因爲版權和法律方面緣由不支持gif圖像處理,png只是有限支持,圖像壓縮時會變形或變模糊。

GraphicsMagick與Opencv比較優缺點:

GraphicsMagick支持圖像多,覆蓋面全,幾乎全部常見圖像格式.壓縮質量高

Opencv支持有限的圖像處理,覆蓋面不全,通過大量壓力測試綜合比較,可是壓縮性能確比GraphicsMagick快一倍多。

綜合二者的優勢:須要把二者結合起來混合處理不一樣圖像,以達到圖像處理最佳性能。

 

5 高效分佈式文件存儲系統選型

互聯網圖片文件存儲,通常考慮帶寬,存儲空間方面壓力,通過壓縮大小不會2MB。所以存儲方案就有多種選擇,既能夠選擇傳統mysql數據庫,也能夠用成熟的分佈式文件系統.下面就來講說他們的不一樣和優缺點。

用mysql作存儲:

1.互聯網公司都用mysql的豐富經驗,技術成熟,衆多人都會用mysql,並且還有專業的DBA團隊來維護。

2.Mysq性能穩定,單臺機器加上內存,基本能知足QPS性能要求。

3.存儲圖片的表結構屬性少,結構簡單,通常訪問時只須要查詢主鍵就能夠了,不需求簡歷額外的索引。

4.去中心化設計,兩臺服務器爲一組,雙寫隨機讀(任意一臺服務器),服務器爲raid5模式。

5.系統擴容,每當當前服務器存儲空間不足,須要增長服務器擴容時,都須要成倍增長服務器數量.

 

6 用分佈式文件作存儲

1.通常是直接使用成熟開源產品或自主研發,使用開源產品,開發成本低,學習成本高,須要專門花費一些進行研究或學習。還要本身來維護。自主研發,時間週期長,投入成本更高,但可控性更強。能進行大量性能優化和調整,或許能節省一些服務器資源。

2.同等條件下分佈式文件系統性能通常會比mysql等關係型數據庫高3-5倍,由於它不需求進行B+Tree(時間複雜度)分頁查找,文件在上傳時,其生成的文件名就包含了大量文件具體位置信息,通常o(1)時間就能準備定位。並且是順序一次性讀取。不想B+Tree按頁式存儲,可能要屢次讀取多頁數據,並且每條記錄需求存儲額外信息,進行事物回滾處理,比較浪費存儲空間。

3.中心化設計(通常爲metaserver和dataserver兩類服務器組集羣),兩或三臺服務器爲一組,雙寫隨機讀(任意一臺服務器),能夠不用raid5模式。

4.系統擴容,每當當前服務器存儲空間不足,能夠輕易作到線性擴展,只須要增長一組服務器就能夠了。明顯在成本上具備優點。

Linux系統中sysctl參數優化(TCP高級選項設置)

服務器在高併發時,會建立大量鏈接,這就須要設置TCP相關參數來提供服務器性能。

1.文件描述符最大數調整。

修改vi/etc/security/limits.conf值

在裏面添加一行

*-nofile65535

保存重啓,再用命令ulimit-n可發現文件描述符由默認變成65535了

2.高負載linux服務器的內核調優

vi/etc/sysctl.conf,修改內核參數:

kernel.shmall=268435456

net.ipv4.tcp_syncookies=1

net.ipv4.tcp_tw_reuse=1

net.ipv4.tcp_tw_recycle=1

net.ipv4.tcp_fin_timeout=30

net.ipv4.tcp_keepalive_time=1200

net.ipv4.ip_local_port_range=102465000

net.ipv4.tcp_max_tw_buckets=5000

net.ipv4.tcp_max_tw_buckets=5000

net.ipv4.tcp_fin_timeout=30

net.ipv4.tcp_keepalive_time=300

net.ipv4.tcp_syncookies=1

net.ipv4.tcp_tw_reuse=1

net.ipv4.tcp_tw_recycle=1

net.ipv4.ip_local_port_range=500065000

net.ipv4.tcp_mem=78643210485761572864

net.core.wmem_max=873200

net.core.rmem_max=873200

net.ipv4.tcp_wmem=8192436600873200

net.ipv4.tcp_rmem=32768436600873200

net.core.somaxconn=256

net.core.netdev_max_backlog=1000

net.ipv4.tcp_max_syn_backlog=2048

net.ipv4.tcp_retries2=5

net.ipv4.tcp_keepalive_time=500

net.ipv4.tcp_keepalive_intvl=30

net.ipv4.tcp_keepalive_probes=3

net.ipv4.conf.lo.arp_ignore=0

net.ipv4.conf.lo.arp_announce=0

net.ipv4.conf.all.arp_ignore=0

net.ipv4.conf.all.arp_announce=0

3.參數說明:net.ipv4.tcp_syncookies=1

#表示開啓SYNCookies。當出現SYN等待隊列溢出時,啓用cookies來處理,可防範少許SYN攻擊,默認爲0,表示關閉;

net.ipv4.tcp_tw_reuse=1

#表示開啓重用。容許將TIME-WAITsockets從新用於新的TCP鏈接,默認爲0,表示關閉;

net.ipv4.tcp_tw_recycle=1

#表示開啓TCP鏈接中TIME-WAITsockets的快速回收,默認爲0,表示關閉。

net.ipv4.tcp_fin_timeout=30

#表示若是套接字由本端要求關閉,這個參數決定了它保持在FIN-WAIT-2狀態的時間。

net.ipv4.tcp_keepalive_time=1200

#表示當keepalive起用的時候,TCP發送keepalive消息的頻度。缺省是2小時,改成20分鐘。

net.ipv4.ip_local_port_range=102465000

#表示用於向外鏈接的端口範圍。缺省狀況下很小:32768到61000,改成1024到65000。

net.ipv4.tcp_max_tw_buckets=5000

#表示系統同時保持TIME_WAIT套接字的最大數量,若是超過這個數字,

#TIME_WAIT套接字將馬上被清除並打印警告信息。默認爲180000,改成5000。

 

 

7 架構

總體架構以下:

高併發圖片(縮略圖)處理中間層服務架構設計,by 5lulu.com

能夠看到,筆者採用了通用的分層架構設計模式。

  • file storage存放着原始的圖片數據。
  • image server用於圖片的處理,同時進行圖片的cache。
  • nginx做爲統一的入口,同時也做爲cache。

當用戶請求一張圖片的縮略圖的時候,若是該圖片不存在於nginx的緩存中,則nginx根據圖片的fileid 經過consistent hash路由到對應的image server上面去處理,若是image server仍然沒有該圖片,則會從file storage下載。

分層架構有一個很好的地方在於系統的可擴展性,同時咱們也能夠在加入一些中間層,提升cache的命中率,譬如咱們就能夠在image server與nginx之間引入一個cache層。不過鑑於咱們的系統主要用於企業內部,不會出現圖片數據量過大的狀況,因此上面這套分層設計已經足夠了。

nginx try_files

若是本地cache不存在,則去後臺服務器取數據。對於這套邏輯,nginx能夠經過try_files很好的處理,譬如:

location /abc.png {
    root /data/image/;
    try_files $uri @fetch;
}

location @fetch {
    proxy_pass http://up_imageserver$request_uri;
}

首先try_files會嘗試在本地獲取對應的文件,若是沒有找到,則會內部跳轉到fetch這個location去遠程獲取數據。

 

8 什麼時候處理縮略圖

既然是縮略圖,那麼什麼時候生成縮略圖就是須要考慮的問題了。一般來講,縮略圖的生成會有兩種方式:

  • 上傳生成

    當用戶上傳一張圖片以後,系統自動爲該圖片生成對應的固定格式縮略圖,而後將原圖與縮略圖一塊兒存放到file storage裏面去。這方面主要有facebook的Haystack系統。

  • 實時生成

    當用戶上傳一張圖片以後,只保留該圖片的原始數據,當請求該圖的縮略圖時,若是cache中不存在,由image server動態生成。這方面能夠參考淘寶的圖片存儲介紹。

對於筆者來講,實際使用的是第二種方法,主要有如下幾個緣由的考量:

  • 對於實時生成的縮略圖咱們能夠靈活的指定其大小,而不像上傳生成那樣只有預先定義的width和height。
  • 存儲成本,額外存儲縮略圖會佔用很大的存儲空間,並且存放到file storage裏面還會有冗餘備份的問題,更加浪費。
  • 協同圖片的冷熱性問題,最近最熱的圖片鐵定是最頻繁訪問的,尤爲是在多人協同狀況下面,而這些圖片縮略圖是有緩存的,不須要每次都經過原圖生成,性能有保證。

 

9 如何處理縮略圖

既然選擇實時生成縮略圖,那麼如何快速生成縮略圖就是筆者須要考慮的問題了。這裏筆者使用graphicsmagick來生成縮略圖,網上有太多介紹,這裏再也不累述。

 

10 安全

生成縮略圖以後,如何保證該圖片的安全訪問也是一個須要關注的問題。筆者考慮了以下解決方案:

  • 簽名,任何縮略圖的url都是通過簽名,由於簽名是經過登錄用戶自身的access id和security key進行的,而且有時效性,因此外界很難僞造。或者,可使用簡單的HttpAccessKeyModule來進行訪問控制。

  • nginx HttpRefererModule,只容許特定domain的請求訪問。

 

11 存儲

對於如何存儲大量的圖片小文件,筆者以爲能夠以下考慮:

  • 對於文件最終存放的file storage,業界有不少好的分佈式解決方案,譬如TFS,mogilefs等,若是想本身造一個輪子,也很不錯。
  • 對於圖片的cache,由於cache的存儲文件量級咱們是能夠控制的,因此這裏能夠考慮直接使用一般的文件系統存儲。

    但須要注意的是,單個目錄下面文件數量不能過多,目錄的層次也不能過深,否則會致使很嚴重的性能瓶頸。爲了解決上述問題,筆者創建了三層目錄結構,首層100個文件夾,以1 - 100命名,每一個文件夾下面1000個文件夾,以1 - 1000命名,對於任意的圖片文件,根據其實際的文件名經過兩次hash到特定的目錄下。

高併發圖片(縮略圖)處理中間層服務架構設計

本文連接 http://tec.5lulu.com/detail/105k2n1e6z65g8s6c.html

相關文章
相關標籤/搜索