高性能架构优化

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"

 

posted @ 2021-05-24 20:12  08ruanjian306  阅读(197)  评论(0)    收藏  举报