Java处理互联网高并发(专题精讲)

引子:
高并发是互联网应用的一大特点,也是互联网应用不可避免的一个问题;比如 淘宝双11购物狂欢节,京东618购物促销节,12306春节火车票,促销,秒杀等;

解决高并发问题是一个系统工程,需要站在全局高度统筹谋划,从多个角度进行架构设计,在实践中,我们探索、总结和提炼出来了很多应对高并发的方案或者说手段,分别如下:

***A.硬件解决方案:
方式一: 单体应用----单体应用也叫集中式应用;
产品或者网站初期,通常功能较少,用户量也不多,所以一般按照单体应用进行设计和开发;
按照经典的MVC三层架构设计,使用单台数据库,缓存也不是必须的,应用系统和数据库部署在同一台服务器上;
随着应用系统功能的增加,访问用户的增多,单台服务器已无法承受那么多的访问流量;
此时,根据业务情况、访问流量等因素,在成本也不高的情况下,可以采用提升硬件配置的方式来解决;
能通过提升硬件解决的时候,也需要提升硬件性能;
方式二: 单体应用垂直扩容----单体应用垂直扩容:指的是提升服务器硬件配置;CPU从32位提升为64位;内存从64GB提升为256GB(比如缓存服务器);磁盘从HDD(Hard Disk Drive)提升为SSD(固态硬盘(Solid State Drives)),有大量读写的应用;磁盘扩容,1TB扩展到2TB,比如文件系统;千兆网卡提升为万兆网卡;但是不管怎么提升硬件性能,硬件性能的提升不可能永无止尽,所以最终还是要靠分布式解决;

*** B.缓存解决方案:缓存可以说是处理高并发,优化系统性能首先要考虑的一个因素;它是解决性能问题的利器,就像一把瑞士军刀,锋利强大;
方式一:HTTP缓存(包括 浏览器缓存、Nginx缓存、CDN缓存)
浏览器缓存-> 浏览器缓存是指当我们使用浏览器访问一些网站页面或者HTTP服务时,根据服务器端返回的缓存设置响应头将响应内容缓存到浏览器,下次可以直接使用缓存内容或者仅需要去服务器端验证内容是否过期即可,这样可以减少浏览器和服务器之间来回传输的数据量,节省带宽,提升性能;比如新浪:http://www.sina.com.cn/,第二次刷新访问,返回响应码为304,表示页面内容没有修改过,浏览器缓存的内容还是最新的,不需要从服务器获取,直接读取浏览器缓存即可;
DateFormat format = new SimpleDateFormat(“EEE,MMM yyyy HH: mm: ss ‘GMT’”, Locale. US);
//当前时间
long now = System.currentTimeMillis() * 1000 * 1000;
response.addHeader( “Date”, format.format(new Date()));
//过期时间http 1. 0支持
response.addHeader(“Expires”, format.format (new Date(now+ 20 * 1000)));
//文档生存时间http 1.1支持
response.addHeader(“Cache-Control”, “max-age=20”);

Nginx缓存->Nginx提供了expires指令来实现缓存控制,比如:
location /static {
root /opt/static/;
expires 1d;
}
当用户访问时,Nginx拦截到请求后先从Nginx本地缓存查询数据,如果有并且没有过期,则直接返回缓存内容;启用压缩,减少数据传输量;
gzip on; #开启gzip压缩输出

CDN缓存-> CDN的全称是Content Delivery Network,即内容分发网络。CDN的基本原理是广泛采用各种缓存服务器,将这些缓存服务器分布到用户访问相对集中的地区或网络中,在用户访问网站时,利用全局负载技术将用户的访问指向距离最近的工作正常的缓存服务器上,由缓存服务器直接响应用户请求。

纵观整个宽带服务的价值链,内容提供商和用户位于整个价值链的两端,中间依靠网络服务提供商将其串接起来。随着互联网工业的成熟和商业模式的变革,在这条价值链上的角色越来越多也越来越细分。其目的是使用户可就近取得所需内容,解决 Internet网络拥挤的状况,提高用户访问网站的响应速度。在这里插入图片描述著名的厂商:(帝联科技)http://www.dnion.com/

方式二:应用缓存(包括:内存缓存、磁盘缓存、代码组件、服务器)
内存缓存->在内存中缓存数据;效率高,速度快;应用重启缓存丢失
磁盘缓存->在磁盘缓存数据;读取效率较之内存缓存稍低;应用重启缓存不会丢失;
代码组件-> Guava、Ehcache
服务器->Redis、MemCache
方式三:多级缓存,在整个应用系统的不同层级进行数据的缓存,多层次缓存,来提升访问效率;比如:CDN -> Nginx -> Redis -> DB
(补充小知识,前端缓存分为浏览器缓存和cdn缓存)
内容包括:经常需要读取的数据、频繁访问的数据、热点数据缓存、IO瓶颈数据、计算昂贵的数据、无需实时更新的数据。
***C:集群解决方案
集群部署,也叫单体应用水平扩容;
1. 应用层面:原来通过部署一台服务器提供服务,现在就多部署几台,那么服务的能力就会提升;部署了多台服务器,但是用户访问入口只能是一个,比如www.web.com,所以就需要负载均衡;负载均衡是应用集群扩容后的必须步骤;集群部署后,用户的会话session状态要保持的话,就需要实现session共享;
2. 数据库层面:将数据库服务器和应用服务器分离;
看应用是多读还是多写的应用,如果是多读的应用,可通过数据库主从架构解决,写数据时操作主库,读数据时操作从库;如果应用是多写的应用,可通过互为主从的模式进行解决;
***D.拆分解决方案
1.应用拆分: 分布式—>单体应用,随着业务的发展,应用功能的增加,单体应用就逐步变得非常庞大,很多人维护这么一个系统,开发、测试、上线都会造成很大问题,比如代码冲突,代码重复,逻辑错综混乱,代码逻辑复杂度增加,响应新需求的速度降低,隐藏的风险增大,所以需要按照业务维度进行应用拆分,采用分布式开发(所以在互联网公司架构师是很重要的);应用拆分之后,就将原来在同一进程里的调用变成了远程方法调用,此时就需要使用到一些远程调用技术:htppClient、hessian、dubbo、webservice等;
可能面临的挑战:
随着调用的错综复杂,我们需要对应用进行服务化,比如SOA,注册中心、服务治理。
随着访问流量进一步增大,那么解决方案也就越来越复杂,限流、降级、基础化、通用化等;
中等互联网公司大约发展到服务化、注册中心就差不多了;
通过应用拆分和服务化之后,扩容就变得容易,如果此时系统处理能力跟不上,只需要增加服务器即可;
2.数据库拆分
分为:垂直拆分和水平拆分
按照业务维度把相同类型的表放在一个数据库,另一些表放在另一个数据库,这种方式的拆分叫垂直拆分,
也就是在不同库建不同表,把表分散到各个数据库;比如 产品、订单、用户 三类数据以前在一个数据库中,现在可以用三个数据库,分别为 产品数据库、订单数据库、用户数据库;这样可以将不同的数据库部署在不同的服务器上,提升单机容量和性能问题,也解决多个表之间的IO竞争问题;
根据数据行的特点和规则,将表中的某些行切分到一个数据库,而另外的某些行又切分到另一个数据库,这种方式的拆分叫水平拆分;
单库单表在数据量和流量增大的过程中,大表往往会成为性能瓶颈,所以数据库要进行水平拆分;
主从复制/读写分离/SQL优化/索引优化;
一主多从:读多写少,读多的应用前面还需要添加缓存,比如Redis/Memcache;互为主从:写多读少的应用;采用MyCat分库分表/读写分离,降低开发难度;
E.静态化
对于一些访问量大,更新频率较低的数据,可直接定时生成静态html页面,供前端访问,而不是访问jsp;(用到的技术:freemaker、velocity、thymeleaf),页面静态化首先可以大大提升访问速度,不需要去访问数据库或者缓存来获取数据,浏览器直接加载html页即可;页面静态化可以提升网站稳定性,如果程序或数据库出了问题,静态页面依然可以正常访问;
F.动静分离
采用比如Nginx实现动静分离,Nginx负责代理静态页面;Nginx的效率极高,利用它处理静态资源,可以为后端服务器分担压力;
G.队列
队列是数据结构中的一种线性表,从一端插入数据,从另一端删除数据,先进先出;在实际项目中:
不是所有的处理都必须要实时处理;
不是所有的请求都必须要实时告诉用户结果;
不是所有的请求都必须100%一次性处理成功;
不知道哪个系统需要我的协助来实现它的业务处理,保证最终一致性,不需要强一致性;这些情况下,我们都可以考虑使用队列来处理;
队列的作用就是:
异步处理/系统解耦/流量削峰;
异步处理是使用队列的一个主要原因,比如注册成功了,发优惠券/送积分/送红包/发短信/发邮件等操作都可以异步处理;
使用队列实现系统解耦,比如支付成功了,发消息通知物流系统,发票系统,库存系统等,而无需直接调用这些系统;
使用队列流量削峰,比如并发下单、秒杀等,可以考虑使用队列将请求暂时入队,通过缓存+队列的方式将流量削平,变成平缓请求进行处理;使用队列的削峰,主要是将高峰流量变成平缓流量进行异步处理,避免应用系统因瞬间的巨大压力而压垮;
请求队列,比如web访问,我们可以采用请求队列对请求进行限流,当请求流量大于队列长度后,抛弃请求,特别是在一些流量高峰时段,保护后端服务不会被突然的巨大流量压垮服务;常见的消息队列产品:ActiveMQ/kafka/RabbitMQ/RocketMQ/Redis队列
H.池化
在实际开发中,我们经常会采用一些池化技术,减少资源消耗,提升系统性能;比如(对象池、数据库连接池、Redis连接池、HttpClient连接池、线程池)。
1.对象池:通过复用对象,减少对象创建和垃圾收集器回收对象的资源开销;可以采用commons-pool2实现;
2. 数据库连接池:Druid/DBCP/C3P0
3. Redis连接池:JedisPool(内部基于commons-pool2)
4. HttpClient连接池:PoolingClientConnectionManager
5.线程池: Executors.newFixedThreadPool(8);
Executors.newSingleThreadExecutor();
Executors.newScheduledThreadPool(10);
I.优化
(包括:JVM优化、Tomcat优化、Java程序优化、数据库优化、Nginx优化、Linux优化、网络优化)
1.JVM优化:设置JVM参数
-server -Xmx4g -Xms4g -Xmn256m -XX:PermSize=128m -Xss256k -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70(解释说明
:-server VM有两种运行模式Server与Client,两种模式的区别在于,Client模式启动速度较快,Server模式启动较慢;但是启动进入稳定期长期运行之后Server模式的程序运行速度比Client要快很多;

-Xmx2g 最大堆大小

-Xms2g 初始堆大小

-Xmn256m 堆中年轻代大小;

-XX:PermSize设置非堆内存初始值,默认是物理内存的1/64;由XX:MaxPermSize设置最大非堆内存的大小,默认是物理内存的1/4.

-Xss 每个线程的Stack大小

-XX:+DisableExplicitGC,这个参数作用是禁止代码中显示调用GC。代码如何显示调用GC呢,通过System.gc()函数调用。如果加上了这个JVM启动参数,那么代码中调用System.gc()没有任何效果,相当于是没有这行代码一样。

-XX:+UseConcMarkSweepGC 并发标记清除(CMS)收集器,CMS收集器也被称为短暂停顿并发收集器;

-XX:+CMSParallelRemarkEnabled 降低标记停顿;

-XX:+UseCMSCompactAtFullCollection:使用并发收集器时,开启对年老代的压缩.

-XX:LargePageSizeInBytes 指定 Java heap 的分页页面大小

-XX:+UseFastAccessorMethods 原始类型的快速优化

-XX:+UseCMSInitiatingOccupancyOnly 使用手动定义的初始化定义开始CMS收集

-XX:CMSInitiatingOccupancyFraction 使用cms作为垃圾回收使用70%后开始CMS收集;

2.设置JVM参数,可以参考JVM优化参数:
在tomcat的bin目录下的startup.sh中设置jvm参数:

JAVA_OPTS="-server -XX:+PrintGCDetails -Xmx4g -Xms4g -Xmn256m -XX:PermSize=128m -Xss256k -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70"
3.Java程序优化
不要重复创建太多对象;流/文件/连接 一定要记得在finally块中关闭;
少用重量级同步锁synchronized;不要在循环体中使用try/catch;多定义局部变量,少定义成员变量;…
4.数据库优化
数据库服务器优化:数据库服务器的参数设置,偏DBA;
数据库索引优化:建立索引的字段尽量的小,最好是数值;尽量在唯一性高的字段上创建索引,不要在性别这种唯一性很低的字段上创建索引;
SQL优化:数据搜索引擎solr/elasticsearch
5.Nginx优化
调整配置文件参数 :
worker_processes 16;

events {
worker_connections 4096;
multi_accept on;
use epoll;
}
6.Linux优化:优化内核参数
7.网络优化:带宽、路由器等方面
J.压测
在系统上线前,需要对系统各个环节进行压力测试,发现系统的瓶颈点,然后进行调优;
即便我们的优化工作已经做得很好了,但依然也会存在一些风险因素,比如网络不稳定,机房故障,所以我们需要提前有故障预备方案,比如多机房部署容灾、路由切换等;
即便我们的故障预备方案做好了,还需要提前进行演练,以确保预案的有效性;
压力测试工具:Apache JMeter/Webbench等;
技术总监、架构师牵头,测试团队、技术团队、运维团队 共同完成;

后语******
以上就是全部了,觉得不错,点个赞拿走不谢!

猜你喜欢

转载自blog.csdn.net/qq_30347133/article/details/83628684