`

日百万PV的架构实践

阅读更多

最近在做一个手机App项目. 需求如下. 

   1: 查看最近登录用户列表以及其当前状态(在线/离线/忙碌/密聊)

   2: 根据用户状态以及语音时长排序, 显示用户以及该用户发表的最后一个帖子列表.(固定显示前50名)

       如: 张三+张三最后帖子--> 李四+李四最后帖子..........

   3: 显示所有用户信息以及用户发表帖子. 按时间顺序排列.

 

1和3就不说了. 解决了2, 其余的都是渣渣. 先讲讲我的实现过程. 

 

阶段1: 

因为用户状态是实时变化的, 语音时长也是实时变化. 因此在根据这种方式进行排序. 每次查询都会有不同的结果. 存放数据库当然扛不住. 涉及到数据库的频繁读写. 因此考虑到将用户信息以及状态存放到 Redis中. 

 

根据业务需求,  认证空闲>认证忙碌>认证挂机>普通在线>认证离线> 普通离线.

 

解决思路: 定义各状态的基础值, 再加上语音时长作为一个集合的score值. 根据该score排序. (灵感来源于做炸金花时各种牌力值大小比较.)

 

	public static final BigDecimal RENZHENG_KONGXIAN = new BigDecimal("900000000"); // 认证空闲
	public static final BigDecimal RENZHENG_MANGLU   = new BigDecimal("800000000"); // 认证忙碌
	public static final BigDecimal RENZHENG_GUAJI    = new BigDecimal("700000000"); // 认证挂机
	public static final BigDecimal PUTONG_ZAIXIAN    = new BigDecimal("600000000"); // 普通在线
	public static final BigDecimal RENZHENG_LIXIAN   = new BigDecimal("500000000"); // 认证离线
	public static final BigDecimal PUTONG_LIXIAN     = new BigDecimal("400000000"); // 普通离线

 

 

	/**
	 * @Notes : 更新用户排序
	 * @Author: songzj
	 * @Date : 2015年5月14日 下午7:53:24
	 * @param uin
	 * @param state
	 */
	private static void updateUserSort(int uin, byte state) {
		JedisCluster jc = JedisPoolUtil.getJedisCluster();

		// 获取该用户帖子总数.
		String topics = jc.hget(LOGON_USER_INFO + uin, "topic");
		if (topics != null && Integer.parseInt(topics) > 0) {
			String time = jc.hget(LOGON_USER_INFO + uin, "times"); // 获取语音聊天总时长
			time = Utils.isBlank(time) ? "0" : time;
			BigDecimal times = new BigDecimal(time);
			String vip = jc.hget(LOGON_USER_INFO + uin, "vip");// 认证用户
			vip = Utils.isBlank(vip) ? "0" : vip;
			if ("0".equals(vip)) {// 非认证
				switch (state) {
				case 0:
				case 1:
				case 2:
				case 3:
					times = PUTONG_ZAIXIAN.add(times);
					break;
				case 4:
					times = PUTONG_LIXIAN.add(times);
					break;
				}
			} else {// 认证
				switch (state) {
				case 0:
					times = RENZHENG_KONGXIAN.add(times);
					break;
				case 1:
					times = RENZHENG_MANGLU.add(times);
					break;
				case 2:
					times = RENZHENG_GUAJI.add(times);
					break;
				case 4:
					times = RENZHENG_LIXIAN.add(times);
					break;
				}
			}
			// 更新用户状态列表score
			jc.zadd(USER_SORT_LIST, times.doubleValue(), "" + uin);

			// 更新标签内排序.
			String tagId = jc.hget(LOGON_USER_INFO + uin, "tagId");
			if (Utils.isNotBlank(tagId)) {
				jc.zadd(USER_TAG_LIST + tagId, times.doubleValue(), uin + "");
			}
		} else {
			jc.zadd(USER_SORT_LIST, 0, "" + uin);
		}

	}

 

好了, 根据用户状态实时排序已经OK.

 

但是取用户列表和帖子列表的时候有点坑. 由于使用的是集群, 不同的用户数据, 帖子数据存放在不同的机器上. 且使用Jedis时不能使用管道. (如有大神有好的方案,请赐教). 需要一个个用户一条条帖子去读取. 中间网络传输占了很多的资源和时间. 因此效率低下. 

 

简单用ab测了一下, 单台tomcat. 10000次请求. 100个并发才 300~500 tps. 严重慢. 

再搭两个tomcat. 前面用 nginx 做代理. 使用多个tomcat来扛. 

 

nginx配置如下. 

 

 

upstream mfs_server{
   server 1.251.192.37;
   server 1.251.192.47:8080;
   server 1.251.192.47:8085;
   keepalive 128;
}
 

 

 

location /mfs{
        proxy_set_header Host  $host;
        proxy_set_header X-Forwarded-For  $remote_addr;
        proxy_pass http://mfs_server;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
 }
 

 

重启nginx. 同样的条件ab跑一次, 性能提升了3被. 但是传输率还不够. 网卡没跑满最大10M. 对tomcat和nginx都进行了优化. 提升量还不是很大. 

 

从业务角度考虑. 用户的基本信息和帖子信息是没必要实时更新和显示的. 15s~30s的延时是用户可以容忍的. 

因此考虑到使用nginx做缓存. 这样过来请求直接nginx处理. 

 

java代码处理如下. 返回页面时设置过期时间. 让nginx在这段时间内不要再来骚扰tomcat.

 

 

		long cur = System.currentTimeMillis() + (8 * 60 * 60 * 1000);
		response.setHeader("ETag", Long.valueOf(cur).toString());
		response.setHeader("Cache-Control", "public,max-age=60");
		response.setHeader("Cache-Control", "max-age=60");
		long adddaysM = 60 * 1000;
		response.addDateHeader("Last-Modified", cur);
		response.addDateHeader("Expires", cur + adddaysM);
		String requestUrl = request.getRequestURI();
 

 

nginx配置如下.

 

 

  client_body_buffer_size  512k;
  proxy_connect_timeout    60;
  proxy_read_timeout       60;
  proxy_send_timeout       60;
  proxy_buffer_size        16k;
  proxy_buffers            8 128k;
  proxy_busy_buffers_size 128k;
  proxy_temp_file_write_size 128k;
  gzip on;
 #nginx开启缓存. 内存中开启一块10m的空间, 取名Z.
  proxy_cache_path /home/songzj/cache levels=1:2 keys_zone=Z:10m inactive=1 max_size=1g;


 

 

 

location /mfs/topic/topicList{
      proxy_pass http://mfs_server; //去请求后台tomcat. 默认round_robin
      proxy_set_header X-Forwarded-For $remote_addr;

      proxy_cache Z;  //去Z的缓存里取
      proxy_cache_min_uses 1;
      proxy_cache_valid 200 302 1m;
      proxy_cache_valid 404 1m;
 
}
 
重新部署项目. 重启nginx. OK. 达到预期效果. 网卡跑满. 可以达到12000 tps.
还有一个问题, 当缓存页面失效的时候, 大量的请求又到了后台tomcat. tomcat表达压力好大. 这就是传说中的dogpile. 解决办法很简单. 
 location /mfs/topic/topicList{
        proxy_pass http://mfs_server;
        proxy_set_header X-Forwarded-For $remote_addr;

        proxy_cache Z;
        proxy_cache_min_uses 1;
        proxy_cache_valid 200 302 1m;
        proxy_cache_valid 404 1m;
        proxy_cache_use_stale updating invalid_header error timeout http_500;
}
 
这样, 在缓存update. 的时候, 只会有一个线程去后台更新新数据. 剩余的用户照样读取老的缓存. 当然, 数据量大的情况下, 可以开启Gzip压缩 .
好了. 百万pv几乎没什么问题了.  对于redis存用户信息, 一个个取的问题以待优化.望高手指点.
分享到:
评论

相关推荐

    云计算架构知识培训视频.rar

    L013老男孩高级架构师-2016最新亿级PV大型电商网站架构综合详解 L014老男孩高级架构师-架构师DNS实战 1015-老男孩高级架构师-架构师反向代理 1016老男孩高级架构师 keepalive nginx及面试题压力测试 017-老男孩高级...

    Linux高级架构师12期-视频教程网盘链接提取码下载.txt

    L001-高级架构师12期-zabbix深度实践-13节 L002-高级架构师12期-zabbix深度实践2-2节 L003-高级架构师12期-SaltStack深度...L013-高级架构师-2016最新亿级PV大型电商网站架构综合详解 L014-高级架构师-架构师DNS实战

    大型网站架构与自动化运维

    本书针对具备Linux 基础的人群...项目案例包括阳S 分布式文件系统、百万/千万PV 网站架构、Ansible 、SaltStack 、Puppet 自动化运维,通过以上项目案例的训练, 读者能够理解大型网站的架构,达到运维自动化的高度。

    Kubernetes部署-使用kubernetes部署Mysql主从结构(Kubernetes工作实践类)

    该资源是使用kubernetes部署Mysql主从结构,整个步骤包括: 1. 编写namespace脚本,创建专门的namespace 2. 编写configmap,将mysql的配置文件配置到里面 3. 编写secret脚本,将需要的密码配置在里面 4. 编写init...

    第四讲-大型互联网高并发网站业务架构设计实践.docx

    什么是高并发? 高并发(High ...7、PV 8、IP 什么是高并发? 高并发(High Concurrency)是互联网分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计保证系统能够同时并行处理很多请求。  高并

    Storm入门教程 之Storm原理和概念详解

    4、讲师Cloudy具有丰富的电商云平台架构经验,对流计算更是涉足早、沉淀深,课程依然沿用重实践、重实战的风格。 学习此课程需要具有: Java基础、Linux基础 学习Storm视频教程可以提升的技能(Storm除外):...

    kubernetes-learning.pdf

    k8s学习 介绍 序⾔ 课程介绍 Docker 基础 ...⽇志收集架构 搭建 EFK ⽇志系统 CI/CD: 动态 Jenkins Slave Jenkins Pipeline 部署 Kubernetes 应⽤ Jenkins BlueOcean Harbor Gitlab Gitlab CI Devops

    Kubernetes 系列教程

    并通过对PV Controller等关键存储模块的剖析展示了K8s持久化存储平台的实现细节。 7.Kubernetes的日志与监控 介绍ELK日志分析平台及其与K8s/ICp的集成,从而能够在K8s平台上实现日志分析 8.Kubernetes的应用部署 ...

    Android 教学实验箱 CES-EDU210B

    理论与实践的结合,透过详尽的实验例程,更深层次、系统化的学习Android系统的相关技术,从系统架构介绍、开发环境搭建,再到系统到平台的移植,应用软件的开发,整个课程系统采用由浅及深、循序渐进的学习模式,...

    wps2019数据分析加载项-数据分析的思维和方法.pdf

    >分析并输出结论(实现数据的管理、分析、聚类等) 数据分析岗位:项⽬经历(能⼒+思考+定位)、理论知识体系+实践(项⽬和能⼒范围) 数据岗位要求:四点:运营策略、客户需求、业务增长点、产品改进点 理解公司...

    ParagraphVec:在 CUDA 上实现段落向量

    #ParagraphVec# CSCI-GA.3033-004 图形处理单元 (GPU) 的项目提交:架构和编程。 段落向量算法的CUDA实现。 只是实现了 PV-DBOW 模型(Paragraph Vector Distributed Bag of Words)。 预计您已经使用 word2vec 的 ...

    Excel公式与函数大辞典.宋翔(带书签高清文字版).pdf

    本书采用理论与实践相结合的方式,提供了457 个案例,涉及多个行业,读者可以根据书中的案例举一反三,将其直接应用到实际工作中,有效提高学习效果与实际应用能力。 本书既可以作为函数速查工具手册,又可以作为...

Global site tag (gtag.js) - Google Analytics