最近在看Apache httpclient 源码,在发送请求的执行过程中,采用的是责任链模式。责任链设计模式在开源框架中很常见,比如netty 的pipeLine设计,servlet的filter。
责任链
以下引用网络释义,我觉得比较好理解的
职责链模式(Chain of Responsibility):使多个对象都有机会处理同一个请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
用责任链有什么好处呐?就是为了让代码更好的拥抱变化
开闭原则:对扩展开放、对修改关闭。
当功能需要变化的时候,我们应该是通过扩展的方式来实现,而不是通过修改已有的代码来实现。
执行步骤
// 创建client对象,类似于我们打开一个浏览器
CloseableHttpClient closeableHttpClient = HttpClients.custom().build();
// 创建post方式请求对象,这个类似于我们打开的浏览器,再开个页面
// http请求包括:请求行(request line)、请求头部(header)、空行和请求数据(body的数据)
// 这个就是构造一个http请数据
HttpPost httpPost = new HttpPost(url);
// 解析http请求的body数据
// http响应也由四个部分组成,分别是:状态行、消息报头、空行和响应正文
CloseableHttpResponse response = closeableHttpClient.execute(httpPost);
HttpEntity httpResponseEntity = response.getEntity();
String responseBody = EntityUtils.toString(httpResponseEntity, HttpConstants.UTF_8);
// 分析CloseableHttpClient.execute();方法,最后执行指向的是
InternalHttpClient.doExecute( final HttpHost target,final HttpRequest request,final HttpContext context);
// InternalHttpClient的成员变量ClientExecChain execChain执行execute方法
CloseableHttpResponse execute(
HttpRoute route, //路由对象
HttpRequestWrapper request, //请求包裹对象(request请求+目的地址)
HttpClientContext clientContext, //上下文对象
HttpExecutionAware execAware //接收线程中断信号的处理类
) throws IOException, HttpException;
ClientExecChain接口下面的实现类
默认的httpClient构造实现和调用过程
1.实例过程,从上到下,下面的exec都把上面的exec实例作为属性实例化
2.调用过程,从下而上调用
实例过程和调用过程
// HttpClientBuilder的builder方法关键代码
ClientExecChain execChain = createMainExec(
requestExecCopy,
connManagerCopy,
reuseStrategyCopy,
keepAliveStrategyCopy,
new ImmutableHttpProcessor(new RequestTargetHost(), new RequestUserAgent(userAgentCopy)),
targetAuthStrategyCopy,
proxyAuthStrategyCopy,
userTokenHandlerCopy);
execChain = decorateMainExec(execChain);
execChain = new ProtocolExec(execChain, httpprocessorCopy);
execChain = decorateProtocolExec(execChain);
// Add request retry executor, if not disabled
if (!automaticRetriesDisabled) {
HttpRequestRetryHandler retryHandlerCopy = this.retryHandler;
if (retryHandlerCopy == null) {
retryHandlerCopy = DefaultHttpRequestRetryHandler.INSTANCE;
}
execChain = new RetryExec(execChain, retryHandlerCopy);
}
HttpRoutePlanner routePlannerCopy = this.routePlanner;
if (routePlannerCopy == null) {
SchemePortResolver schemePortResolverCopy = this.schemePortResolver;
if (schemePortResolverCopy == null) {
schemePortResolverCopy = DefaultSchemePortResolver.INSTANCE;
}
if (proxy != null) {
routePlannerCopy = new DefaultProxyRoutePlanner(proxy, schemePortResolverCopy);
} else if (systemProperties) {
routePlannerCopy = new SystemDefaultRoutePlanner(
schemePortResolverCopy, ProxySelector.getDefault());
} else {
routePlannerCopy = new DefaultRoutePlanner(schemePortResolverCopy);
}
}
// Add redirect executor, if not disabled
if (!redirectHandlingDisabled) {
RedirectStrategy redirectStrategyCopy = this.redirectStrategy;
if (redirectStrategyCopy == null) {
redirectStrategyCopy = DefaultRedirectStrategy.INSTANCE;
}
execChain = new RedirectExec(execChain, routePlannerCopy, redirectStrategyCopy);
}
return new InternalHttpClient(
execChain,
connManagerCopy,
routePlannerCopy,
cookieSpecRegistryCopy,
authSchemeRegistryCopy,
defaultCookieStore,
defaultCredentialsProvider,
defaultRequestConfig != null ? defaultRequestConfig : RequestConfig.DEFAULT,
closeablesCopy);
执行过程,拿重试RetryExec示例
@Override
public CloseableHttpResponse execute(
final HttpRoute route,
final HttpRequestWrapper request,
final HttpClientContext context,
final HttpExecutionAware execAware) throws IOException, HttpException {
Args.notNull(route, "HTTP route");
Args.notNull(request, "HTTP request");
Args.notNull(context, "HTTP context");
final Header[] origheaders = request.getAllHeaders();
for (int execCount = 1;; execCount++) {
try {
// 调用下游exec的实例对象的execute方法
return this.requestExecutor.execute(route, request, context, execAware);
} catch (final IOException ex) {
if (execAware != null && execAware.isAborted()) {
this.log.debug("Request has been aborted");
throw ex;
}
// 这里判断是否重试,如果重试,还是走到上面的for循环
if (retryHandler.retryRequest(ex, execCount, context)) {
if (this.log.isInfoEnabled()) {
this.log.info("I/O exception ("+ ex.getClass().getName() +
") caught when processing request to "
+ route +
": "
+ ex.getMessage());
}
request.setHeaders(origheaders);
} else {
// 不重试,则抛出异常
if (ex instanceof NoHttpResponseException) {
final NoHttpResponseException updatedex = new NoHttpResponseException(
route.getTargetHost().toHostString() + " failed to respond");
updatedex.setStackTrace(ex.getStackTrace());
throw updatedex;
} else {
throw ex;
}
}
}
}
}
至此httpclient的执行模式则被讲完了,如果不用责任链的模式,举个例子,如果一个请求不用重试,或者自己新加某种责任。则只需要实现ClientExecChain,并在构造httpclient的时候,自己组装责任链,对原有代码无需修改。
网友评论