本文是《
京东商品详情页服务闭环实践
》中未公开的一些细节,是15年内部培训的PPT,目前的内容也不过时,还适用现有系统架构设计。PPT下载地址:
https://pan.baidu.com/s/1K-Djkf6IFZ7qSEIlNqYPAw
。
单品页依赖服务众多,分布在各个部门。问题:
-
服务质量没有监控数据
-
出现问题不能及时降级
-
接口调用分散化
-
域名重复解析,没有长连接的优势
架构总体原则
-
设计上无状态
-
使用nginx+lua+tomcat7架构
-
充分利用localcache(proxycache or shared_dict orjava guava cache)
-
使用localtwemproxy做redis分片,且缓存分离(重要业务与其他业务分离)
-
分离线程池,核心业务与非核心业务分离,且有线程池监控和开关
-
异步化更新
-
Redis集群使用主从架构
-
使用unixdomain socket减少连接数
-
使用keepalive长连接
-
考虑好开关
-
缓存时间、是否调用后端服务、托底(托底恢复采用指数/随机数恢复机制)
Twemproxy+Redis
Nginx keep alive
Nginx timeout
Nginx proxy cache
Parameters of caching can also beset directly in the response header. This has higher priority than setting ofcaching time using the directive.
•
The“X-Accel-Expires” header field sets caching time of a response in seconds. Thezero value disables caching for a response. If the value starts with the
@
prefix,it sets an absolute time in seconds since Epoch, up to which the response maybe cached.
•
Ifthe header does not include the “X-Accel-Expires” field, parameters of cachingmay be set in the header fields “Expires” or “Cache-Control”.
•
Ifthe header includes the “Set-Cookie” field, such a response will not be cached.
•
Ifthe header includes the “Vary” field with the special value “
*
”,such a response will not be cached (1.7.7). If the header includes the “Vary”field with another value, such a response will be cached taking into accountthe corresponding request header fields (1.7.7).
Nginx DNS
Nginx Gzip
Nginx upstream
consistent_key是根据流量负载动态计算的,如根据ip计算:
local newval, err = ip_log:incr(ip, 1)
local toDelay = false
-- 单IP恶意请求 分流到固定机组
if newval > 50 then
toDelay = true
ngx_var.consistent_key = ngx_var.consistent_key .. '_' .. newval
--要在set_uri前执行,否则执行不到
end
Nginx Real ip
Nginx client header
限定请求头和请求体大小,在proxy pass到后端服务时不传输请求头和请求体,减少网络交互。
Nginx limit
参考
聊聊高并发系统之限流特技-1
、
聊聊高并发系统之限流特技-2
。
Nginx+Lua shared_dict
Nginx+Lua 接口合并
Nginx+Lua 记录日志
Java 架构
从Servlet3开始支持异步模型,Tomcat7/Jetty8开始支持,相同的概念是Jetty6的Continuations。我们可以把处理过程分解为一个个的事件。
通过这种将请求划分为事件方式我们可以进行更多的控制。如,我们可以为不同的业务再建立不同的线程池进行控制:
即我们只依赖tomcat线程池进行请求的解析,对于请求的处理我们交给我们自己的线程池去完成;这样tomcat线程池就不是我们的瓶颈,造成现在无法优化的状况。
通过使用这种异步化事件模型,我们可以提高整体的吞吐量,不让慢速的A业务处理影响到其他业务处理。慢的还是慢,但是不影响其他的业务。
通过这种将请求划分为事件方式我们可以进行更多的控制。如,我们可以为不同的业务再建立不同的线程池进行控制:
Java Tomcat
export JAVA_OPTS="-Djava.library.path=/usr/local/lib -server -XX:-UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:CMSInitiatingOccupancyFraction=80 -XX:+CMSParallelRemarkEnabled -XX:SoftRefLRUPolicyMSPerMB=0 -XX:MaxDirectMemorySize=512m -Xss256k -XX:NewRatio=1 -XX:SurvivorRatio=6 -Xms16384m -Xms16384m -XX:MaxPermSize=256m -Djava.awt.headless=true -Dsun.net.client.defaultConnectTimeout=60000 -Dsun.net.client.defaultReadTimeout=60000 -Djmagick.systemclassloader=no -Dnetworkaddress.cache.ttl=300 -Dsun.net.inetaddr.ttl=300 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=$CATALINA_BASE/logs -XX:ErrorFile=$CATALINA_BASE/logs/java_error_%p.log"
-XX:+UseConcMarkSweepGC 表示使用CMS
-XX:+CMSParallelRemarkEnabled 表示并行remark
-XX:+UseCMSCompactAtFullCollection 表示在FGC之后进行压缩,因为CMS默认不压缩空间的。
-XX:CMSInitiatingOccupancyFraction=80 设置阀值为80%,默认为68%。
-XX:SoftRefLRUPolicyMSPerMBsoftly reachable objects will remain alive for some amount of time after thelast time they were referenced. The default value is one second of lifetime perfree megabyte in the heap
-XX:NewRatio年轻代(包括Eden和两个Survivor区)与年老代的比值(不包括持久代)
-XX:SurvivorRatio Eden区与Survivor区的大小比值
以Tomcat 6为例,其Connector有几个关键配置:
BIO实现:
acceptCount:在超过最大连接数时,可接受的排队数量;超过这个值就直接拒绝连接;默认100;
maxThreads:tomcat可创建的最大线程数,没线程处理一个请求,它决定了tomcat最大线程阀值;默认200;
minSpareThreads:最小备用线程数,即tomcat一启动就创建的线程数;默认25;(使用Executor时配置)
maxQueueSize:最大备用线程数,一旦创建的线程超过这个值tomcat就会关闭不活动的线程;默认Integer.MAX_VALUE;(使用Executor时配置)
NIO实现(继承如上的配置):
acceptorThreadCount:接受连接的线程数;默认1,可以根据CPU核数调整;如果没有问题默认1个即可,基本不需要改;
pollerThreadCount:运行选择事件的线程个数;默认每核一个;
processorCache:协议处理器缓存Http11NioProcessor对象的个数,目的是提高性能,默认200,建议其值接近maxThreads;
对于tomcat7的相关配置可以参考官网http://tomcat.apache.org/tomcat-7.0-doc/config/http.html;核心差不多。
Java servlet3
Java thread pool
Java cache