高性能架构优化
1、请求分发
什么是无状态?
指两个来自相同发起者的请求在服务器端是否具备上下文关系。
简单讲:同一个客户发起两个请求,服务器是否能判定是同一个客户请求,不能则无状态。
cookie 和 session 可以让服务器有记忆功能,从无状态变成有状态。分布式系统更适合无状态请求。
请求分发实现方式:
1、nginx proxy 配置简单,性能也能满足一般的业务场景(一般推荐)
2、HA proxy 配置简单,性能也能满足一般的业务场景(一般推荐)
3、LVS LVS比Nginx/HAproxy要更稳定,但成本高
4、硬件负载均衡器
5、dns 大型站点可以基于DNS域名解析
2、前置到接入层
目前比较推崇接入层(如nginx)直接返回数据,而无需调用后端程序(如go、php、java等程序)
openResty介绍
OpenResty® 是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。
nginx可以直接调用redis、mysql、mongodb、memcache等,这极大提升了响应速度和并发性能
nginx 调用mysql和memcache示例:
location /getinfo { default_type 'text/plain'; content_by_lua ' local args = ngx.req.get_uri_args() if args["appleid"] == nil then ngx.say("param appleid is nil") return end local memcached = require "memcached" local memc, err = memcached:new() if not memc then ngx.say("failed to instantiate memc: ", err) return end memc:set_timeout(1000) -- 1 sec local ok, err = memc:connect("172.16.18.114", 11211) if not ok then ngx.say("failed to connect: ", err) return end local res, flags, err = memc:get(args["appleid"]) if err then ngx.say("failed to get memc: ", err) return end if not res then local mysql = require "mysql" local db, err = mysql:new() if not db then ngx.say("failed to instantiate mysql: ", err) return end db:set_timeout(1000) -- 1 sec local ok, err, errno, sqlstate = db:connect{ host = "172.16.18.162", port = 3306, database = "test", user = "root", password = "cpyf", max_packet_size = 1024 * 1024 } if not ok then ngx.say("failed to connect: ", err, ": ", errno, " ", sqlstate) return end -- ngx.say("connected to mysql.") sql = "select * from tagval where tag = \'" .. args["appleid"] .. "\'" res, err, errno, sqlstate = db:query(sql) if not res then ngx.say("bad result: ", err, ": ", errno, ": ", sqlstate, ".") return end local json = require "json" ngx.say("mysql found") ngx.say(json.encode(res)) local ok, err = memc:set(args["appleid"], json.encode(res)) if not ok then ngx.say("failed to set memc: ", err) return end local ok, err = db:set_keepalive(0, 100) if not ok then ngx.say("failed to set keepalive: ", err) return end return end ngx.say("memc found") ngx.say(res) memc:set_keepalive(0, 100) '; }
3、拆分
3.1、服务拆分
挂号服务、订单服务、支付服务、敏感词、发短信服务
基于RPC的微服务框架 grpc、dubbo、swoft
3.2、插件拆分
皮肤购买插件、抽奖插件、文本处理插件
与服务拆分不同,插件拆分表现为前后端一整套镶入,而服务拆分是后端的拆分
3.3、技术组件拆分
js图片处理组件拆分,api加密组件拆分
3.4、数据库拆分
分库分表
哈希分表: 插入压力也分散,解决了集中插入的问题,存在拓展问题:5-10年后,可能又要分表了。
分段分表: 插入压力没分散,解决了数据库拓展问题
分布式事务
2PC: mysql自带的xa事务
3PC:增加一个健康检测间断
TCC: 基于业务层面用接口实现
MQ最终一致性: 消息队列:rabbitmq的消息可靠性传递(confirm、ack)
3.5、nosql以及搜索引擎拆分
redis哈希分槽
mongodb集群分片
elasticsearch分片机制
消息队列拆分
4、数据结构优化
目的是优化数据的空间利用率和查询效率,比如mysql索引优化等等
5、消息队列
消息队列可以实现服务解耦、异步处理、流量削峰/缓冲等: rabbitmq、kafka、redis(list,stream)
6、数据异构与聚合
7、缓存
7.1、浏览器缓存
nginx相关配置:
//# 配置 HTTP 强缓存静态文件,通过 Response Header 头返回 location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|js|css)$ { expires 3d; }
7.2、CDN缓存
7.3、反向代理缓存
nginx反向代理缓存示例:
http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; proxy_cache_path /usr/local/nginx/cache levels=1:2 keys_zone=cache_one:500m inactive=1d max_size=30g; server { listen 80; server_name http://192.168.0.36/; } location / { proxy_cache cache_one; proxy_cache_valid 200 304 302 24h; proxy_pass http://192.168.0.37/; } }
7.4、接入层缓存(nginx的shared_dict等)
通过存入缓存,减少对后端服务器的调用次数
示例:(必须安装lua以及nginx lua拓展)
function get_from_cache(key) local cache_ngx = ngx.shared.my_cache local value = cache_ngx:get(key) return value end function set_to_cache(key,value,exptime) if not exptime then exptime = 0 end local cache_ngx = ngx.shared.my_cache local succ,err,forrcible = cache_ngx:set(key,value,exptime) return succ end local args = ngx.req.get_uri_args() local id = args["id"] local item_model = get_from_cache("item_"..id) if item_model == nil then local resp = ngx.location.capture("/item/get?id="..id) item_model = resp.body ngx.log(ngx.ERR,resp.body) set_to_cache("item_"..id,item_model,1*60) end ngx.say(item_model)
7.5、nosql 缓存
常见的有mongodb、redis、memcache等,之所以选用nosql作为缓存,主要在于其性能优越,而在选择上,又会基于不同的场景使用不同的技术栈。目前使用较多的是mongodb和redis。
Redis和MongoDB的区别
redis,用来存储一些热数据,量也不大。现在项目中用的是MongoDB,目前是百万级的数据,将来会有千万级、亿级。
就Redis和MongoDB来说,大家一般称之为Redis缓存、MongoDB数据库。这也是有道有理有根据的,Redis主要把数据存储在内存中,其“缓存”的性质远大于其“数据存储“的性质,其中数据的增删改查也只是像变量操作一样简单;
MongoDB却是一个“存储数据”的系统,增删改查可以添加很多where条件,就像SQL数据库一样灵活,这一点在面试的时候很受用。
1、内存管理机制
Redis 数据全部存在内存,定期写入磁盘,当内存不够时,可以选择指定的 LRU 算法删除数据。
MongoDB 数据存在内存,由 linux系统 mmap 实现,当内存不够时,只将热点数据放入内存,其他数据存在磁盘。
2、支持的数据结构
Redis 支持的数据结构丰富,包括hash、set、list、订阅等等。
MongoDB 数据结构比较单一,但是支持丰富的数据表达,索引,最类似关系型数据库,支持的查询语言非常丰富。
mongodb查询列表:
db.sites.find() { "_id" : ObjectId("5f37de2f3330400c06716592"), "x" : 1, "name" : "hello", "url" : "http://www.hello.com" } { "_id" : ObjectId("5f37de2f3330400c06716593"), "x" : 2, "name" : "hello", "url" : "tool.hello.com" } { "_id" : ObjectId("5f37de2f3330400c06716594"), "x" : 3, "name" : "bbb", "url" : "http://www.bbb.com" }
3、数据量和性能:
当物理内存够用的时候,redis>mongodb>mysql
当物理内存不够用的时候,redis和mongodb都会使用虚拟内存。
实际上如果redis要开始虚拟内存,那很明显要么加内存条,要么你换个数据库了。
但是,mongodb不一样,只要,业务上能保证,冷热数据的读写比,使得热数据在物理内存中,mmap的交换较少。
mongodb还是能够保证性能。
4、性能
mongodb依赖内存,TPS较高;Redis依赖内存,TPS非常高。性能上Redis优于MongoDB。
5、可靠性
mongodb从1.8版本后,采用binlog方式(MySQL同样采用该方式)支持持久化,增加可靠性;
Redis依赖快照进行持久化;AOF增强可靠性;增强可靠性的同时,影响访问性能。
可靠性上MongoDB优于Redis。
6、数据分析
mongodb内置数据分析功能(mapreduce);而Redis不支持。
7、事务支持情况
Redis 事务支持比较弱,只能保证事务中的每个操作连续执行;mongodb不支持事务
8、集群
MongoDB 集群技术比较成熟,Redis从3.0开始支持集群。
7.6、文件缓存
以文件的形式保存的缓存。
1.7.5.1 供前端读取的缓存文件
前端静态化页面、动态json数据暂存
sendfile函数
1.7.5.2 供应用程序读取的缓存文件
8、程序优化
8.1、io模型
阻塞IO、非阻塞IO、多路复用IO、信号驱动IO、异步IO
8.2、并发处理
8.3、连接池
如redis连接池、mysql连接池等等
8.4、代码优化
框架结构优化,sql语句,对象,循环,执行逻辑等等的优化。
9、集群
redis、monggodb、es、solr、kafka、rabbitmq。。。等等基本上大多数的主流技术栈都是支持集群的。
10、前端优化
10.1、浏览器缓存,反向代理缓存
10.2、js、css、图片等的压缩
10.3、nginx
sendfile示例
sendfile on
sendfile: 设置为on表示启动高效传输文件的模式。sendfile可以让Nginx在传输文件时直接在磁盘和tcp socket之
间传输数据。如果这个参数不开启,会先在用户空间(Nginx进程空间)申请一个buffer,用read函数把数据从磁盘读到
cache,再从cache读取到用户空间的buffer,再用write函数把数据从用户空间的buffer写入到内核的buffer,最后到
tcp socket。开启这个参数后可以让数据不用经过用户buffer。
gzip压缩示例:
gzip` `on; ``#决定是否开启gzip模块,on表示开启,off表示关闭; gzip_min_length 1k; ``#设置允许压缩的页面最小字节(从header头的Content-Length中获取) ,当返回内 容大于此值时才会使用gzip进行压缩,以K为单位,当值为0时,所有页面都进行压缩。建议大于1k gzip_buffers 4 16k; ``#设置gzip申请内存的大小,其作用是按块大小的倍数申请内存空间,param2:int(k) 后面单位是k。这里设置以16k为单位,按照原始数据大小以16k为单位的4倍申请内存 gzip_http_version 1.1; ``#识别http协议的版本,早起浏览器可能不支持gzip自解压,用户会看到乱码 gzip_comp_level 2; ``#设置gzip压缩等级,等级越底压缩速度越快文件压缩比越小,反之速度越慢文件压缩 比越大;等级1-9,最小的压缩最快 但是消耗cpu gzip_types text``/plain` `application``/x-javascript` `text``/css` `application``/xml``; ``#设置需要压缩的MIME类型,非设置值不进行压缩,即匹配压缩类型 gzip_vary on; ``#启用应答头"Vary: Accept-Encoding"

浙公网安备 33010602011771号