`

日百万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存用户信息, 一个个取的问题以待优化.望高手指点.
分享到:
评论

相关推荐

    也谈架构:百万pv项目与虚拟化

    本文将从架构设计和虚拟化两个方面对百万PV项目进行分析和总结。我们将讨论项目的架构设计、虚拟化的必要性、虚拟化后的服务器数量和资源分配,以及虚拟化注意事项。 架构设计 本项目的架构设计分为五层:CDN层、...

    屌丝程序员如何打造日PV百万的网站架构.pdf

    如何打造一个日PV百万的网站架构对于个人程序员来说无疑是一项艰巨的挑战。从给定文件的内容中,我们可以提炼出以下知识点和实践步骤: 1. 低成本运营:由于资源有限,网站架构需要在较低成本的前提下进行设计和...

    pv_serie5.rar_Boost_PV modeling_pv_pv PANNEL

    在这个模型中,我们可以预期看到PV面板的电压-电流特性(I-V曲线)、Boost转换器的电路结构以及MPPT算法的实现。用户可能通过这个模型来模拟不同光照条件、温度变化以及负载需求对系统性能的影响,进行优化设计。 ...

    亿级 pv 网站架构的技术细节与套路

    在构建能够支撑亿级页面访问量(PV)的大型网站架构过程中,需要关注多个方面的技术细节与实践经验。以下内容将围绕这一主题展开,详细介绍包括但不限于后端架构设计、服务治理、流量预估与压力测试、日志收集与分析...

    S5PV210源码

    理解ARM体系结构是掌握S5PV210的基础。ARM Cortex-A8是ARMv7架构的一部分,支持多线程、硬件浮点运算和虚拟化技术。此外,了解各种外设接口如I2C、SPI、UART等,以及它们在源码中的实现,能帮助开发者灵活地设计和...

    PV3D中文手册

    1. **对象层次结构**: PV3D中的3D对象可以通过嵌套结构来组织,形成层次化的场景树。 2. **事件驱动编程**: PV3D支持ActionScript的事件系统,可以监听和响应用户交互或其他对象的状态变化。 3. **动画系统**: 可以...

    PV3D学习资料-----PV3D Essentials(汉语)

    1. **PV3D基础**:首先,你需要了解PV3D的基本架构,包括渲染器、摄像机、场景、对象和材质等基本元素。PV3D使用ActionScript 3作为编程语言,因此熟悉AS3语法是必要的。 2. **3D坐标系统**:PV3D使用右手坐标系统...

    pv3d flash 3d源码

    **PV3D的架构** PV3D主要由以下几个组件组成: 1. **渲染器(Renderer)**:负责将3D场景转换为2D像素,以便在屏幕上显示。PV3D通常使用GPU加速的硬件渲染,以提高性能。 2. **加载器(Loader)**:用于加载3D...

    C语言实现pv操作演示程序

    在计算机科学领域,操作系统是...在分析和调试过程中,也能锻炼对操作系统原理的理解和C语言的实践能力。通过阅读和运行"PV操作演示程序",可以直观地观察到不同进程间的交互,帮助加深对并发编程中同步机制的认识。

    pv3d安装,简单运用的实例

    总的来说,这个压缩包提供了一个完整的PV3D入门示例,涵盖了安装、基本代码结构、资源管理和最终输出等方面,非常适合对PV3D感兴趣的初学者进行实践和学习。通过深入研究这些文件,你将能够掌握PV3D的基础知识,并...

    S5PV210芯片数据手册 cortex-A8内核

    在配置和优化S5PV210芯片时,数据手册是至关重要的参考资料,它详细阐述了芯片的架构、功能特性和寄存器配置。 Cortex-A8是ARM公司推出的一种微处理器内核,属于ARMv7架构系列,是高端移动设备和嵌入式计算的理想...

    S5PV210 myled.rar

    对于S5PV210,这可能涉及到设备树(Device Tree)配置,它用来描述硬件结构,帮助内核初始化硬件资源。 此外,为了调试和测试,开发者还需要熟悉相关的开发工具,如交叉编译器、JTAG调试器、GDB等。这些工具能帮助...

    PV3D源码包(1.5版本和1.7版本)

    《深入理解PV3D:1.5与1.7版本源码解析》 Papervision3D(PV3D)是Flex平台上的一个开源3D引擎,它为Adobe Flash和...在实际项目中,结合源码学习和实践,我们可以充分利用PV3D的功能,实现更高效、更逼真的3D效果。

    s5pv210部分中文资料

    1. **处理器架构**:S5PV210基于ARM Cortex-A8核心,这是一种广泛应用的32位RISC(精简指令集计算)处理器架构,具有高性能和低功耗的特点。资料可能会介绍Cortex-A8的核心特性,如NEON媒体处理引擎和多线程能力。 ...

    S5PV210裸奔程序

    1. **处理器架构理解**:S5PV210基于ARM Cortex-A8内核,采用冯·诺依曼结构,具备超标量和乱序执行能力。开发者需要熟悉Cortex-A8的寄存器配置、指令集以及流水线操作,这对于编写高效的汇编代码至关重要。 2. **...

    S5PV210-Lcd_test.zip

    S5PV210是一款基于ARM Cortex-A8架构的高性能微处理器,由三星公司研发,广泛应用于智能手机、平板电脑以及嵌入式系统等设备中。其强大的处理能力使得它在处理图形和视频时表现出色,尤其是对于LCD(液晶显示器)的...

    PV.rar_pv

    总的来说,这个例子为我们提供了一个学习和实践并发控制的宝贵资源,尤其是对于理解PV操作如何在实际程序中通过链表实现提供了直观的示例。通过分析和运行“PV.cpp”代码,我们可以更好地掌握PV操作的工作原理及其在...

    S5PV210 Usermanual 用户手册 pdf samsung

    《S5PV210用户手册》是三星公司为开发者和用户提供的一份详细的技术文档,主要涵盖了S5PV210处理器的相关知识和技术规格。...通过深入学习和实践,开发者可以充分利用S5PV210的强大功能,构建高效、稳定的嵌入式系统。

    PV_model_.rar_matlab pv_pv MATLAB_pv matlab

    标题 "PV_model_.rar_matlab pv_pv MATLAB_pv matlab" 暗示着这是一个与光伏(PV)系统相关的MATLAB仿真项目。在这个项目中,开发者可能使用MATLAB来建模和模拟太阳能光伏系统的性能。MATLAB是一种强大的计算环境,...

    S5PV210 u-boot移植详细教程及移植好工程

    5. **创建设备树**:根据S5PV210的硬件配置编写或修改设备树源文件(.dts),描述硬件结构。设备树文件将被编译为二进制blob,用于u-boot加载。 6. **烧录u-boot**:将编译好的u-boot二进制文件烧录到S5PV210的存储...

Global site tag (gtag.js) - Google Analytics