原文出处:http://bbs.pconline.cn/topic-2065.html
R 系统白皮书[[br]]
============
本文介绍R 系统的各种使用方法和背后的原因,是使用R系统前必须看明白的一份文档,
也是使用过程中的参考,同时也是HTTP调用各种问题的一个总结
是什么?[[br]]
=======[[br]]
R 系统是系统之间进行HTTP调用的一个统一的接口,封装了调用网络路由,HTTP调用和
结果缓存三大模块,是对于系统之间HTTP调用多年经验的一个整理总结的结果
为什么?[[br]]
=======[[br]]
在日常的开发中,系统之间进行HTTP调用是经常遇到的情况,但是要用好它并不是一件容
易的事情,因为实际情况中WEB 是一个[非可靠]的[分布式网络]系统,调用过程中要解决
各种不可控的情况需要经过深思熟虑才能做到,R 系统就是经过深思熟虑的一个设计
需求[[br]]
====[[br]]
经过慎密的分析,R 系统要解决一下问题:
1. 端到端的调用路由和负载均衡,自动化的路由表更新
2. 方便的调用方式和超时及出错/响应过慢处理
3. 对结果的缓冲和并发及故障保护,支持过期缓存使用扩展
下面的使用场景说明会一步一步的说明各需求的应对情况
使用前准备[[br]]
==========[[br]]
R 系统需要JDK6的版本[[br]]
resin/lib下需要放r-route-1.2.jar
应用的WEB-INF/lib下需要放r-1.2.jar[[br]]
还要:[[br]]
memcached客户端2.0.1以上[[br]]
log4j-1.2.x以上[[br]]
commons-codec-1.4以上[[br]]
commons-logging-1.1.1以上[[br]]
httpcore-4.1以上[[br]]
httpclient-4.1.1以上[[br]]
通常和spring一起使用
使用场景[[br]]
========[[br]]
1. 路由配置[[br]]
{{{
------------
+----------+
+-------| server-1 | ip:port
+--------+ domain | +----------+
| client |----------+ ...
+--------+ | +----------+
+-------| server-n | ip:port
+----------+
}}}
上图是最基本的调用结构,需求1 主要解决基本的路由和负载均衡问题,R 系统使用
route 模块来解决相关的问题,route 会将域名调用对应到相关的ip:port 并进行轮询
来实现负载均衡,并采用扫描四层交换机的方式来自动更新路由表,对于用户来说,只要
使用适当的配置就可以解决需求1 的问题,下面我们说明一下路由相关配置方法
在resin 的配置文件中加入进行R 系统的生产环境路由配置:
{{{
------------------------------------------------------------------------------
192.168.238.75
http://192.168.237.61/route.txt
private.pcauto.com.cn=192.168.74.5:8888
------------------------------------------------------------------------------
}}}
routeOverwrite用于指定特殊的路由映射,用于虚拟域名等星空
在resin 的配置文件中加入进行R 系统的开发、测试环境路由配置:
{{{
------------------------------------------------------------------------------
192.168.11.90:8080
ks.pcauto.com.cn=192.168.74.10:8081,192.168.47.11:8081
bbs.pcauto.com.cn=192.168.74.5:8888
------------------------------------------------------------------------------
}}}
proxy 可以将对于域名的调用代理到公网,方便开发测试环境[[br]]
routes用于指定要使用开发、测试环境的那些机器来进行测试
spring applicationContext.xml 配置:
{{{
------------------------------------------------------------------------------
------------------------------------------------------------------------------
}}}
2. HTTP调用(无缓冲)[[br]]
cn.pconline.r.client.SimpleHttpTemplate 是HTTP调用的核心类,以模版方法的模式方便
的进行HTTP调用,通常需要配置为采用route 进行路由,有多种get 和post方法提供使用,
本模块会对于超时和错误进行合理的处理,用户配置好就可以方便使用
spring applicationContext.xml 配置:
{{{
------------------------------------------------------------------------------
------------------------------------------------------------------------------
}}}
五种get 方法:
{{{
public String get(String uri, String refererUri);
public String get(String uri, String refererUri, int readTimeout);
public T get(String uri, String refererUri,
ResponseExtractor responseExtractor);
public T get(String uri, String refererUri,
RequestCallback requestCallback,
ResponseExtractor responseExtractor);
public T get(String uri, String refererUri,
RequestCallback requestCallback,
ResponseExtractor responseExtractor, int readTimeout);
}}}
三种post方法:
{{{
public T post(String uri, String refererUri,
ResponseExtractor responseExtractor, HttpEntity entity);
public T post(String uri, String refererUri,
RequestCallback requestCallback,
ResponseExtractor responseExtractor, HttpEntity entity);
public T post(String uri, String refererUri,
RequestCallback requestCallback,
ResponseExtractor responseExtractor, HttpEntity entity,
int readTimeout);
}}}
3. RClient 缓冲get 调用结果
{{{
----------------------------
+----------+
+-------| server-1 | Cache-Control: max-age=900
+--------+ domain | +----------+
| client |----+---------+ ...
+--------+ | | +----------+
| +-------| server-n | Cache-Control: max-age=900
| +----------+
+-----+-----+
| memcached |
+-----------+
}}}
服务端指定缓冲时间,RClient 按照缓冲时间用memcached 进行结果缓冲,并处理并发
调用相同uri 的情况
resin 配置:(注意线上环境)
{{{
---------------------------------------------------------------------------
MemCahcedClient config
memcachedConfig
java.lang.String
servers=127.0.0.1:11211
initConn=20
minConn=10
maxConn=50
maintSleep=30
nagle=false
socketTO=3000
------------------------------------------------------------------------------
}}}
spring applicationContext.xml 配置:
{{{
------------------------------------------------------------------------------
------------------------------------------------------------------------------
public String get(final String uri, final String refererUri,
final int timeout, final TimeUnit timeUnit);
}}}
4. RClient 持久化缓冲(后端故障时使用过期缓冲内容)
{{{
----------------------------------------------------
+----------+
| mongodb | 持久化缓冲KV存储
+----------+
| +----------+
| +-------| server-1 |
+--------+ | domain | +----------+
| client |----+---------+ ...
+--------+ | | +----------+
| +-------| server-n |
| +----------+
+-----+-----+
| memcached |
+-----------+
}}}
对于某些比较重要的内容,当后端失败时需要采用最近的旧数据提供服务,可以使用持久
化缓冲的方式进行
实现RClientHelper 扩展进行持久化缓冲
{{{
------------------------------------------------------------------------------
public interface RClientHelper {
void update(String uri, String key, String content,
long resourceTimeMillis, long cacheLifeMillis);
R get(String uri, String key);
}
------------------------------------------------------------------------------
}}}
spring applicationContext.xml 配置:
{{{
------------------------------------------------------------------------------
...
------------------------------------------------------------------------------
}}}
RClient 调用get 方法时指定persistence 参数为true表示采用持久缓冲:
{{{
public String get(final String uri, final String refererUri,
final boolean persistence,
final int timeout, final TimeUnit timeUnit);
}}}
故障情景分析[[br]]
============[[br]]
以前我曾经将HTTP服务的状态分为四种情况:死、慢、错、对,只有第四种才是好的服务
状态,前三种都属于故障,下面我们分析一下R 系统是怎么应对这三种故障状态的
我们还是按照SimpleHttpTemplate和RClient 两种情况进行分析会清楚一点
{{{
+----------+
+-------| server-1 | ip:port
+--------+ domain | +----------+
| client |----------+ ...
+--------+ | +----------+
+-------| server-n | ip:port
+----------+
}}}
对于SimpleHttpTemplate,当全部后端死掉时,将返回最后一台访问状态,否则,返回
成功请求的结果
情况表
{{{
----------------------------+------+------+------+------+------+------+
| all | some | all | some | all | some |
Back failed status | die | die | slow | slow | err | err |
--------------------+-------+------+------+------+------+------+------+
| GET | fail | OK | fail | OK | fail | OK |
SimpleHttpTemplate +-------+------+------+------+------+------+------+
| POST | fail | OK | fail | O/F | fail | O/F |
--------------------+-------+------+------+------+------+------+------+
}}}
O/F: 轮询时如果碰到好的就OK,碰到有问题的就失败
要和大家再集体商量一次,最终做个了断
{{{
+----------+
| mongodb | 持久化缓冲KV存储
+----------+
| +----------+
| +-------| server-1 |
+--------+ | domain | +----------+
| client |----+---------+ ...
+--------+ | | +----------+
| +-------| server-n |
| +----------+
+-----+-----+
| memcached |
+-----------+
}}}
对于RClient, 当后端全部死掉时,还要看缓存的情况,有没有持久缓存也不同
情况表
{{{
------------------------------------+-----------+-----------+
Back server failed status | all fail | some fail |
------------------------+-----------+-----------+-----------+
memcached |persistence|-----------------------|
------------------------+-----------+-----------+-----------+
mc valid | | OK | OK |
------------------------+ +-----------+-----------+
mc expiried | no | BLANK | OK |
------------------------+ +-----------+-----------+
mc LRU out | | BLANK | OK |
------------------------+-----------+-----------+-----------+
mc valid | | OK | OK |
------------------------+ +-----------+-----------+
mc expiried | yes | OK/OLD | OK |
------------------------+ +-----------+-----------+
mc LRU out | | OK/OLD | OK |
------------------------+-----------+-----------+-----------+
}}}
OK/OLD: 如果缓存没有过期就是OK,已经过期就是旧的
内部流程
========
分别说明SimpleHttpTemplate和RClient的内部流程
SimpleHttpTemplate
------------------
本模块是对Apache HttpClient 的简单封装,没有使用另外的线程进行异步处理,整个
调用对于应用来说是同步的,下面的配置除了clientUri外,都是直接的设置
{{{
p:clientUri="http://myapp.pconline.com.cn"
p:connectTimeout="10000"
p:readTimeout="60000"
p:maxTotalConnections="300"
p:maxPerRoute="3"
}}}
实际的流程是:
1 用户请求uri
2 模块根据uri获得后端服务器的列表
3 向一个轮询出的服务器请求
4 请求成功返回结果
4.1 请求不成功,判断是否请求下一个服务器
4.1.1不需要请求下一个,则返回成功结果
4.1.2需要请求下一个,则从列表中拿出下一个,到第4布进行请求
* 是否请求下一个服务器的标准按照前文的情况表决定
RClient
-------
本模块采用异步的方式,有专门的线程进行异步的HTTP请求,对于请求的结果,采用
memcached 进行缓冲,必要时采用mongodb 之类的KV存储对结果进行持久化存储,
对于相同的uri 请求进行并发请求标志保护,同一个uri过期时理论上只有一个HTTP请求
会到后端去请求,配置如下,前四与simpleHttpTemplate 相同,都是直接设置
Apache HttpClient, 最后三个用于缓冲时间设置
{{{
p:clientUri="http://app.pconline.com.cn"
p:connectTimeout="10000"
p:soTimeout="60000"
p:maxPerRoute="3"
p:httpThreads="10"
p:maxRetry="3"
p:cacheMillis="3600000"
p:errorCacheMillis="300000"
p:slowMillis="10000"
}}}
流程:
细节看代码好点...
1 请求memcached,有结果返回
2 请求mongodb,结果未过期返回,同时设置memcached
3 异步请求后端,正常时返回,同时设置memcached和mongodb
后端访问为轮询(maxRetry会限制轮询的次数),
遇到正常结果终止轮询,或者返回最后的结果
{{{
+-------+
|mongodb|
+---+---+
|
+---+-------------------------------+ +--------+
| | RClient | +------|server-1|
+------+ | | +------+ | | +--------+
|client|--------+---+--$|Thread| +----------+ | |
+------+ | | | Pool |------|HttpClient|--+----+ ...
| | +------+ +----------+ | |
| | | | +--------+
+---+-------------------------------+ +------|server-n|
| +--------+
+---+-----+
|memcached|
+---------+
------------------------------------------------------------------------------
}}}