互联网公司常常会有大量原始图片上传,并且一个原图会在页面以不一样尺寸缩略图显示,通常有两种策略生成缩略图,一种在上传图片时,生成须要的多张不一样缩略图,另外一种是请求指定尺寸的图片时实时生存缩略图片,第一种方式有必定限制,就是须要提早知道全部尺寸的图片,作雍余存储,无形中增长大量文件数量,若是文件系统设计很差,还有可能造成大量文件碎片,并且会消耗大量存储空间,若是前端ui设计改变了图片大小,须要从新生成。而第二种方式更加灵活,可是更消耗cpu资源,属于cpu密集计算型 html
大吞吐量服务端架构设计要考虑四个技术点 前端
技术选型,是单进程多线程模型(reactor事件机制),仍是多进程模型.java
高效分布式文件存储系统选型。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释义“反应堆”,是一种事件驱动机制。和普通函数调用的不一样之处在于:应用程序不是主动的调用某个API完成处理,而是偏偏相反,Reactor逆置了事件处理流程,应用程序须要提供相应的接口并注册到Reactor上,若是相应的时间发生,Reactor将主动调用应用程序注册的接口,这些接口又称为“回调函数”.
Reactor模式是编写高性能网络服务器的必备技术之一,它具备以下的优势:1)响应快,没必要为单个同步时间所阻塞,虽然Reactor自己依然是同步的;2)编程相对简单,能够最大程度的避免复杂的多线程及同步问题,而且避免了多线程/进程的切换开销;3)可扩展性,能够方便的经过增长Reactor实例个数来充分利用CPU资源;4)可复用性,reactor框架自己与具体事件处理逻辑无关,具备很高的复用性;
1每一个进程处理多个connection,使用epoll事件驱动来管理这些链接,多个worker进程之间是对等的,他们同等竞争来自客户端的请求,各进程互相之间是独立的。
2master由信号驱动,worker由epoll驱动(固然信号会让epoll_wait返回),有更好的容错性,若是其中一个进程挂了或产生core,master收到相关信号后,会同时重启一个进程,并同时发送出相关监控信息,也不会致使不能提供服务,。
3多进程用来利用多CPU硬件,因此按照业务边界来划分进程,或者就按CPU个数配置。
4每一个进程是单线程的:全部IO相关操做都是全异步处理方式,避免多线程切换和锁机制开销。
5进程之间抢占epoll资源时,仅用一个轻量级的共享内存锁,循环依次把链接事件放入队列,而后循环处理每一个客户端的链接请求和逻辑处理。
6高性能:服务器若支持多CPU或超线程,多线程没法彻底利用机器性能,多进程则可让服务器满载.
目前图像压缩算法已经成型,并且基本上都是搞数学方面的大牛发明的,
关于图像处理方面能够参考以下:
图片压缩或处理是一个很是消耗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快一倍多。
综合二者的优势:须要把二者结合起来混合处理不一样图像,以达到图像处理最佳性能。
互联网图片文件存储,通常考虑带宽,存储空间方面压力,通过压缩大小不会2MB。所以存储方案就有多种选择,既能够选择传统mysql数据库,也能够用成熟的分布式文件系统.下面就来讲说他们的不一样和优缺点。
用mysql作存储:
1.互联网公司都用mysql的丰富经验,技术成熟,众多人都会用mysql,并且还有专业的DBA团队来维护。
2.Mysq性能稳定,单台机器加上内存,基本能知足QPS性能要求。
3.存储图片的表结构属性少,结构简单,通常访问时只须要查询主键就能够了,不需求简历额外的索引。
4.去中心化设计,两台服务器为一组,双写随机读(任意一台服务器),服务器为raid5模式。
5.系统扩容,每当当前服务器存储空间不足,须要增长服务器扩容时,都须要成倍增长服务器数量.
1.通常是直接使用成熟开源产品或自主研发,使用开源产品,开发成本低,学习成本高,须要专门花费一些进行研究或学习。还要本身来维护。自主研发,时间周期长,投入成本更高,但可控性更强。能进行大量性能优化和调整,或许能节省一些服务器资源。
2.同等条件下分布式文件系统性能通常会比mysql等关系型数据库高3-5倍,由于它不需求进行B+Tree(时间复杂度)分页查找,文件在上传时,其生成的文件名就包含了大量文件具体位置信息,通常o(1)时间就能准备定位。并且是顺序一次性读取。不想B+Tree按页式存储,可能要屡次读取多页数据,并且每条记录需求存储额外信息,进行事物回滚处理,比较浪费存储空间。
3.中心化设计(通常为metaserver和dataserver两类服务器组集群),两或三台服务器为一组,双写随机读(任意一台服务器),能够不用raid5模式。
4.系统扩容,每当当前服务器存储空间不足,能够轻易作到线性扩展,只须要增长一组服务器就能够了。明显在成本上具备优点。
服务器在高并发时,会建立大量链接,这就须要设置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。
总体架构以下:
能够看到,笔者采用了通用的分层架构设计模式。
当用户请求一张图片的缩略图的时候,若是该图片不存在于nginx的缓存中,则nginx根据图片的fileid 经过consistent hash路由到对应的image server上面去处理,若是image server仍然没有该图片,则会从file storage下载。
分层架构有一个很好的地方在于系统的可扩展性,同时咱们也能够在加入一些中间层,提升cache的命中率,譬如咱们就能够在image server与nginx之间引入一个cache层。不过鉴于咱们的系统主要用于企业内部,不会出现图片数据量过大的状况,因此上面这套分层设计已经足够了。
若是本地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去远程获取数据。
既然是缩略图,那么什么时候生成缩略图就是须要考虑的问题了。一般来讲,缩略图的生成会有两种方式:
上传生成
当用户上传一张图片以后,系统自动为该图片生成对应的固定格式缩略图,而后将原图与缩略图一块儿存放到file storage里面去。这方面主要有facebook的Haystack系统。
实时生成
当用户上传一张图片以后,只保留该图片的原始数据,当请求该图的缩略图时,若是cache中不存在,由image server动态生成。这方面能够参考淘宝的图片存储介绍。
对于笔者来讲,实际使用的是第二种方法,主要有如下几个缘由的考量:
既然选择实时生成缩略图,那么如何快速生成缩略图就是笔者须要考虑的问题了。这里笔者使用graphicsmagick来生成缩略图,网上有太多介绍,这里再也不累述。
生成缩略图以后,如何保证该图片的安全访问也是一个须要关注的问题。笔者考虑了以下解决方案:
签名,任何缩略图的url都是通过签名,由于签名是经过登录用户自身的access id和security key进行的,而且有时效性,因此外界很难伪造。或者,可使用简单的HttpAccessKeyModule来进行访问控制。
nginx HttpRefererModule,只容许特定domain的请求访问。
对于如何存储大量的图片小文件,笔者以为能够以下考虑:
对于图片的cache,由于cache的存储文件量级咱们是能够控制的,因此这里能够考虑直接使用一般的文件系统存储。
但须要注意的是,单个目录下面文件数量不能过多,目录的层次也不能过深,否则会致使很严重的性能瓶颈。为了解决上述问题,笔者创建了三层目录结构,首层100个文件夹,以1 - 100命名,每一个文件夹下面1000个文件夹,以1 - 1000命名,对于任意的图片文件,根据其实际的文件名经过两次hash到特定的目录下。
本文连接 http://tec.5lulu.com/detail/105k2n1e6z65g8s6c.html