36

zanePerfor在高流量项目下的架构配置建议实践说明

 4 years ago
source link: https://blog.seosiwei.com/detail/53?
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

zanePerfor在高流量项目下的架构配置建议实践说明

HI!,你好,我是zane,zanePerfor是一款我开发的一个前端性能监控平台,现在支持web浏览器端和微信小程序端。

我定义为一款完整,高性能,高可用的前端性能监控系统,这是未来会达到的目的,现今的架构也基本支持了高可用,高性能的部署。实际上还不够,在很多地方还有优化的空间,我会持续的优化和升级。

开源不易,如果你也热爱技术,拥抱开源,希望能小小的支持给个star。

项目的github地址:https://github.com/wangweianger/zanePerfor

项目开发文档说明:https://blog.seosiwei.com/performance/index.html

zanePerfor应用理论上能够支持千万级以上pv项目,但实际情况需要依赖于服务器和数据库的性能,以下项尽可能的从各种配置来提升应用的性能。

备注:以下内容以选择redis消息队列来阐述和说明。

一:相关项目配置项说明

config.default.js 配置说明

1、servers集群模式下服务器之间主要通过内网进行通信,因此在这里hostname我们需要配置成内网IP,做如下更改即可

// 集群配置(一般默认即可)
    config.cluster = {
        listen: {
            port: config.port,
            hostname: address.ip(),  // 此处替换127.0.0.1
            ip: address.ip(),
        },
    };

2、实时统计任务在大流量项目下时间尽可能的长一些,即能减轻数据库的压力也能提升实时统计的准确性 (定时任务时间间隔建议5-20分钟之间)

// 执行pvuvip定时任务的时间间隔 默认每分钟定时执行一次 (可更改)
config.pvuvip_task_minute_time = '0 */1 * * * *';
// 更改为
config.pvuvip_task_minute_time = '0 */10 * * * *';

3、上报和消费数据方式选择redis

// 上报原始数据使用redis存储、kafka储存、还是使用mongodb存储
config.report_data_type = 'redis'; // redis  mongodb  kafka

4、在数据库性能足够强悍的情况下,每次定时任务的时间尽量短,消费的数据尽量多,消息队列池尽量不做限制

config.redis_consumption = {
    // 定时任务执行时间
    task_time: '*/10 * * * * *',
    // 每次定时任务消费线程数(web端)
    thread_web: 2000,
    // 每次定时任务消费线程数(wx端)
    thread_wx: 2000,
    // 消息队列池限制数, 0:不限制 number: 限制条数,高并发时服务优雅降级方案
    total_limit_web: 0,
    total_limit_wx: 0,
};
  • task_time 消费消息定时任务间隔

  • thread_wx 每次消费数据条数

  • total_limit_wx 限制消息队列中总条数

以上配置表示:每10秒钟消费2000条数据,不对上报数据条数做限制 (如果定时任务设置了type: 'all',消费数据会成倍数增加)。

5、解析用户IP使用redis方式,并关闭文件缓存(备注:流量大时,本地文件存储的数据会比较大,每次加载会比较耗时)

// 解析用户ip地址为城市是使用redis还是使用mongodb
config.ip_redis_or_mongodb = 'redis'; // redus  mongodb

// 文件缓存ip对应地理位置(文件名)
config.ip_city_cache_file = {
    isuse: false, // 是否开启本地文件缓存(数据量太大时建议不开启)
    web: 'web_ip_city_cache_file.txt',
    wx: 'wx_ip_city_cache_file.txt',
};

6、mongodb集群模式下url链接需要更改为内网ip,连接池可以稍微调大一些

// mongodb 服务
const dbclients = {
    db3: {
        // 集群分片
        url: 'mongodb://192.168.1.10:30000/performance',
        options: {
            autoReconnect: true,
            poolSize: 50,
        },
    },
};

二:HTTP层面说明(以下内容已在程序中实现)

1、设置Connection,关闭keep-alive

为什么要关闭?

  • 在高并发项目下,tcp保持连接时间不要太长,因此nginx的keepalive_timeout尽量设置的更短
  • 同一域下请求频率低、请求次数少的http链接尽量减少tcp链接时间,因此keepalive_timeout尽量设置低

而对于本应用来说,上面两条都已满足,因此关闭keep-alive选项。

// node服务实现:
ctx.set('Connection', 'close');

// nginx服务实现:
http {
    keepalive_timeout: 0;
}

2、返回空body信息

  • 上报接口接收到请求就尽快返回状态码,逻辑处理放到后面处理
  • body返回内容尽量简短或者为空
// 代码实现:
async wxReport() {
    const { ctx } = this;
    ctx.set('Access-Control-Allow-Origin', '*');
    ctx.set('Connection', 'close');
    ctx.status = 200;

    // 后续逻辑处理...
}

三:node单机集群

使用node.js的Cluster 模块开启多进程,尽可能的榨干服务器资源,利用上多核 CPU 的并发优势。同时也保证单机服务的稳定性。

egg.js提供多进程模型和进程间通讯。

传送门:https://eggjs.org/zh-cn/core/cluster-and-ipc.html

开启方式:

// 应用package.json
// 案例中开启两个worker进程,默认会开启服务器cpu核数个worker进程
"scripts": {
    "start": "egg-scripts start --daemon --workers=2 --title=performance",
}

四:Mongodb集群搭建

高流量,高并发项目少不了mongodb集群的搭建,关于mongodb集群搭建请参考以下两篇文章:

zanePerfor前端性能监控平台高可用之Mongodb集群分片架构

zanePerfor前端性能监控平台高可用之Mongodb副本集读写分离架构

案例:现在有3台服务器,内网ip分别为:(10.1.0.86、10.1.0.97、10.1.0.70),现在我们来搭建由三台服务器组建的集群。

1、分别在每台服务器上创建如下3个文件

kdir -p /data/mongod/s0
mkdir -p /data/mongod/log
mkdir -p /data/mongod/c0

2、分别在每台服务器上启动Shard Server

mongod --dbpath /data/mongod/s0 --logpath /data/mongod/log/s0.log --fork --smallfiles --port 27020 --bind_ip 10.1.0.86 --shardsvr
mongod --dbpath /data/mongod/s0 --logpath /data/mongod/log/s0.log --fork --smallfiles --port 27020 --bind_ip 10.1.0.97 --shardsvr
mongod --dbpath /data/mongod/s0 --logpath /data/mongod/log/s0.log --fork --smallfiles --port 27020 --bind_ip 10.1.0.70 --shardsvr

3、分别在每台服务器上启动Config Server

mongod --dbpath /data/mongod/c0 --logpath  /data/mongod/log/c0.log --fork --smallfiles --port 27100 --bind_ip 10.1.0.86 --replSet rs1 --configsvr
mongod --dbpath /data/mongod/c0 --logpath  /data/mongod/log/c0.log --fork --smallfiles --port 27100 --bind_ip 10.1.0.97 --replSet rs1 --configsvr
mongod --dbpath /data/mongod/c0 --logpath  /data/mongod/log/c0.log --fork --smallfiles --port 27100 --bind_ip 10.1.0.70 --replSet rs1 --configsvr

4、配置副本集

// 进入97的mongo
mongo --port 27100 --host 10.1.0.97

// 使用admin账户
use admin

// 初始化副本集
rs.initiate({_id:"rs1",members:[{_id:0,host:"10.1.0.97:27100"},{_id:1,host:"10.1.0.86:27100"},{_id:2,host:"10.1.0.70:27100"}]})

// 查看副本集状态
rs.status()

5、启动Route Process服务

mongos  --logpath /data/mongod/log/mongo.log --port 30000 --bind_ip 10.1.0.97 --fork --configdb rs1/10.1.0.97:27100,10.1.0.86:27100,10.1.0.70:27100

6、配置Sharding分片

// 进入路由服务器
mongo --port 30000 --host 10.1.0.97

// 添加分片
sh.addShard("10.1.0.97:27020")
sh.addShard("10.1.0.86:27020")
sh.addShard("10.1.0.70:27020")

// 查看分片信息
sh.status();

7、设置分片数据库与片键

//指定需要分片的数据库
sh.enableSharding("performance")

//创建索引(需要对片建创建索引)
db.wx_ajaxs_wx3feeea844b1d03ffs.ensureIndex({"path":1})

//设置分片(对performance数据库的wx_ajaxs_wx3feeea844b1d03ffs表按照path字段以hashed的方式分片)
sh.shardCollection("performance.wx_ajaxs_wx3feeea844b1d03ffs", { "path": "hashed"})

//查看分片信息
sh.status()

其他表分片重复以上步骤即可, 至此一个简单的3台服务器集群搭建完毕。

注意事项:对mongodb进行分片时需要绑定内网IP,不然请求无法连通。

备注:应用中对浏览器端 | 微信小程序端 默认使用 _id字段 进行分片, 尽量不要更改。

S4kNmhQxt3c7aGf4fCWYks4Ck2yXwZK5.png?imageslim

五:servers负载均衡

servers负载:即把所有上报的请求分发到不同的servers进行处理,减小单个servers服务的压力,也减轻了单个服务器的压力。

  • zanePerfor做到了一套代码多服务部署的方案

  • 应用保证了多服务同时运行时,task任务不重复执行

此处servers负载均衡采用nginx方案,ng配置如下:

upstream ps-servers {
    server 10.1.0.86:7001 weight=1 max_fails=4 fail_timeout=5;
    server 10.1.0.97:7001 weight=1 max_fails=4 fail_timeout=5;
    server 10.1.0.70:7001 weight=2 max_fails=4 fail_timeout=5;
}

参数说明:

weight=number:设置服务器的权重,默认情况下为1
max_conns=number:限制到代理服务器的同时活动连接的最大数量,默认为0
max_fails=number:服务器通信的失败尝试次数
fail_timeout=number:服务器通信失败超时时间,默认为10
backup:将服务器标记为备份服务器。它将在主服务器不可用时传递请求
down:将服务器标记为永久不可用

六:redis集群

应用中有很多功能都会依赖于redis的缓存能力,因此一台高性能的redis或者redis集群显得很有必要。

官方如此介绍redis的强劲性能:性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s ,理论上来说单台redis就能满足绝大部分的应用。

至于redis是否需要集群的支持,需要根据各个应用的情况来决定,博主暂时用的单台redis,500w PV (1千多万ajax请求量)内暂未遇见性能问题。

至于redis集群的搭建此处暂不做介绍,暂时挂个官网参考链接:https://redis.io/topics/cluster-tutorial

zanePerfor在高流量项目下的架构配置建议实践说明(完)。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK