摘要
大家好,我是新手小编小八(上一篇说错了),上一篇我简要的分享了我学习feign源码了解到的启动内容,这一篇我将分享一下feign发起请求和响应处理的流程的成果!
代理类处理
上篇讲到最后feign客户端的实质最后就是代理对象(不清楚的可以回顾认识feign(一)启动流程),根据是否启用hystrix,他的处理器为可分为FeignInvocationHandler(未启用)和HystrixInvocationHandler(启用);
主要区别:未启用hystrix直接进行请求,而启用了hystrix则是将其请求方法委托到hystrixCommand对象中进行请求处理,我主要讲启动了hystrix的情况
hystrix封装
HystrixInvocationHandler的invoke方法为代理处理方法,前面的处理逻辑和FeignInvocattionHandler的一摸一样,就是简单处理基于object的原生方法,主要的逻辑从下面开始
// hystrixCommand对象里面进行了hystrix处理和配置,如熔断、隔离等
// 调用自定义业务方法run()
// 调用出现各种异常则调用自定义异常方法getFallback
HystrixCommand<Object> hystrixCommand = new HystrixCommand<Object>(setterMethodMap.get(method)) {
@Override
protected Object run() throws Exception {
try {
// 从方法集合中获取相应的方法处理器SynchronousMethodHandler开始请求处理
return HystrixInvocationHandler.this.dispatch.get(method).invoke(args);
} catch (Exception e) {
throw e;
} catch (Throwable t) {
throw (Error) t;
}
}
@Override
protected Object getFallback() {
//
if (fallbackFactory == null) {
return super.getFallback();
}
try {
// 获取熔断对象(可自定义FallbackFactory, 一般我们都直接用一个fallback,它默认熔断所有异常,除了HystrixBadRequestException)
Object fallback = fallbackFactory.create(getExecutionException());
// 获取熔断返回信息
Object result = fallbackMethodMap.get(method).invoke(fallback, args);
// 根据返回值类型进行不同的操作
if (isReturnsHystrixCommand(method)) {
return ((HystrixCommand) result).execute();
} else if (isReturnsObservable(method)) {
// Create a cold Observable
return ((Observable) result).toBlocking().first();
} else if (isReturnsSingle(method)) {
// Create a cold Observable as a Single
return ((Single) result).toObservable().toBlocking().first();
} else if (isReturnsCompletable(method)) {
((Completable) result).await();
return null;
} else {
// 直接返回信息
return result;
}
} catch (IllegalAccessException e) {
// shouldn't happen as method is public due to being an interface
throw new AssertionError(e);
} catch (InvocationTargetException e) {
// Exceptions on fallback are tossed by Hystrix
throw new AssertionError(e.getCause());
}
}
};
这里hystrixCommand里面有很多知识点,主要是通过RxJava技术实现,大家如果要想去看懂里面的处理逻辑先得学习下RxJava,不然看起来比较困难
我这边就不细讲了,大概的逻辑为(现将讲一下,这里面的基本对象都是RxJava的观察者对象):
- 它先去判断是否有缓存信息,有缓存直接拿缓存的;如果没有,则进行请求处理,然后将其放在缓存中
- 进行请求的化先隔离处理,主要隔离策略有信号量隔离和线程隔离(默认),先进行信号量隔离的判断,如果是线程隔离默认通过,线程隔离主要通过RxJava的subscribeOn方法实现
- 同时hystrix也会判断当前是否开启过期熔断(默认开启),如果开启通过lift方法进行处理
请求参数解析
进到SynchronousMethodHandler对象中,第一步要处理的就是将参数信息拼接到请求信息中,最后组装成request对象供client进行请求,我们从buildTemplateFromArgs.create(argv) 进入,解析信息后可获得一个请求模板对象
// 从请求模板中复制请求信息
RequestTemplate mutable = new RequestTemplate(metadata.template());
// 根据参数对url进行拼接,没怎么用过
if (metadata.urlIndex() != null) {
int urlIndex = metadata.urlIndex();
checkArgument(argv[urlIndex] != null, "URI parameter %s was null", urlIndex);
mutable.insert(0, String.valueOf(argv[urlIndex]));
}
// 根据参数名(@RequestParam的value值),对参数的信息进行分组
Map<String, Object> varBuilder = new LinkedHashMap<String, Object>();
for (Entry<Integer, Collection<String>> entry : metadata.indexToName().entrySet()) {
int i = entry.getKey();
Object value = argv[entry.getKey()];
if (value != null) { // Null values are skipped.
if (indexToExpander.containsKey(i)) {
value = expandElements(indexToExpander.get(i), value);
}
for (String name : entry.getValue()) {
varBuilder.put(name, value);
}
}
}
// 1.对复合型对象参数用EnCoder编码器解析成为请求的body体(如果复合型对象用了@RequestParam进行装饰,则不进行解析)
// 2.对上面的简单参数对象进行请求拼接
RequestTemplate template = resolve(argv, mutable, varBuilder);
// 对用了@RequestParam进行装饰的复合型对象进行地址拼接处理
if (metadata.queryMapIndex() != null) {
template = addQueryMapQueryParameters((Map<String, Object>) argv[metadata.queryMapIndex()], template);
}
// 进行@ReqyestHeader参数的进行header的添加
if (metadata.headerMapIndex() != null) {
template = addHeaderMapHeaders((Map<String, Object>) argv[metadata.headerMapIndex()], template);
}
return template;
}
关于编码器如何解析复合型对象,在resolve(argv, mutable, varBuilder)进行复合对象的解析,除了一开始的校验代码,定位到 encoder.encode(body, metadata.bodyType(), mutable)这一行,请求的实现类为 SpringEncoder, 主要是匹配相应的HttpMessageConverters转换器(跟mvc共用)进行对象解析,一般我们都匹配到jackson或者fastjson的转换器
请求处理
获得请求模板后,先是用克隆的方式生成一个Retryer重试器对象(默认不重试,可自定义),在循环体中进行请求和响应解析,报错了调用重试器进行重试处理,(不重试的处理策略为直接报错),不报错的会继续进行重试。接下来进入请求方法executeAndDecode
第一步:获取请求对象,targetRequest(template)
Request targetRequest(RequestTemplate template) {
// 拦截器链式调用,对请求信息进行最后处理
for (RequestInterceptor interceptor : requestInterceptors) {
// RequestInterceptor接口只有apply一个方法
interceptor.apply(template);
}
// 获取请求对象,target的实现类为HardCodedTarget,对请求地址进行最后处理
return target.apply(new RequestTemplate(template));
}
第二步:调用client的execute方法进行请求,委托给ribbon或者client对象处理(如果用了服务名,需要委托给ribbon,反之用了url真实地址,直接委托给client.Default)
public Response execute(Request request, Request.Options options) throws IOException {
try {
// 获取请求地址
URI asUri = URI.create(request.url());
// 获取服务名称
String clientName = asUri.getHost();
// 清除服务名称 ,例如:http://user-service/test -> http:///test
URI uriWithoutHost = cleanUrl(request.url(), clientName);
// 获取ribbon请求对象
FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(
this.delegate, request, uriWithoutHost);
// 获取ribbon客户端配置对象
IClientConfig requestConfig = getClientConfig(options, clientName);
return
// 获取ribbon客户端
lbClient(clientName)
// 委托给ribbon进行请求处理
.executeWithLoadBalancer(ribbonRequest, requestConfig)
// 返回响应
.toResponse();
}
catch (ClientException e) {
IOException io = findIOException(e);
if (io != null) {
throw io;
}
throw new RuntimeException(e);
}
}
第三步:ribbon调用executeWithLoadBalancer进行请求处理
// 构建ribbon执行器(主要是进行一些配置,如ribbon的重试机制,有兴趣可以去学习ribbon)
LoadBalancerCommand<T> command = buildLoadBalancerCommand(request, requestConfig);
try {
return command.submit(
new ServerOperation<T>() {
@Override
public Observable<T> call(Server server) {
// 获取本次请求的服务地址
URI finalUri = reconstructURIWithServer(server, request.getUri());
// 将服务地址插入到之前的地址中去
S requestForServer = (S) request.replaceUri(finalUri);
try {
// 调用请求
return Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig));
}
catch (Exception e) {
return Observable.error(e);
}
}
})
.toBlocking()
.single();
} catch (Exception e) {
Throwable t = e.getCause();
if (t instanceof ClientException) {
throw (ClientException) t;
} else {
throw new ClientException(e);
}
}
第四步:ribbon调用execute进行请求
public RibbonResponse execute(RibbonRequest request, IClientConfig configOverride)
throws IOException {
Request.Options options;
// 设置ribbon请求参数设置,连接超时时间和读取超时时间
if (configOverride != null) {
RibbonProperties override = RibbonProperties.from(configOverride);
options = new Request.Options(
override.connectTimeout(this.connectTimeout),
override.readTimeout(this.readTimeout));
}
else {
options = new Request.Options(this.connectTimeout, this.readTimeout);
}
// 这里调用了client对象进行请求,这里的client才是真正的client.Default对象
Response response = request.client().execute(request.toRequest(), options);
return new RibbonResponse(request.getUri(), response);
}
第五步(用真实直接跳到这一步):client的execute方法请求处理
public Response execute(Request request, Options options) throws IOException {
// 将请求对象是信息转换成HttpUrlConnection对象所需要的信息,并进行请求 (不多讲解,就是一些处理赋值,有兴趣可以看看)
HttpURLConnection connection = convertAndSend(request, options);
// 获取响应信息,并封装
return convertResponse(connection).toBuilder().request(request).build();
}
到此就算已经成功向目标地址发出了请求,并收到了响应信息
响应处理
拿到响应信息后,我们回到请求方法executeAndDecode,直接定位到请求后的处理流程
try {
if (logLevel != Logger.Level.NONE) {
// 打印响应信息
}
// 判断响应对象是否为Response
if (Response.class == metadata.returnType()) {
if (response.body() == null) {
return response;
}
// 无需解码或者无法解码直接返回
if (response.body().length() == null ||
response.body().length() > MAX_RESPONSE_BUFFER_SIZE) {
shouldClose = false;
return response;
}
// 字节数据返回
byte[] bodyData = Util.toByteArray(response.body().asInputStream());
return response.toBuilder().body(bodyData).build();
}
// 判断响应状态是否为正确响应
if (response.status() >= 200 && response.status() < 300) {
if (void.class == metadata.returnType()) {
return null;
} else {
// 成功响应返回正确数据,则用解码器进行数据解码(常用)
return decode(response);
}
} else if (decode404 && response.status() == 404 && void.class != metadata.returnType()) {
// 如果响应状态为404,但是设置为需要解码,正常用解码器进行数据解码
return decode(response);
} else {
// 进行错误信息的解码,默认为报错(常用)
throw errorDecoder.decode(metadata.configKey(), response);
}
} catch (IOException e) {
// 日志输出
throw errorReading(request, response, e);
} finally {
if (shouldClose) {
ensureClosed(response.body());
}
}
Decoder解码器的最终调用类为SpringDncoder,解码逻辑就是跟编码差不多,也是匹配相应的HttpMessageConverters转换器对响应数据里面的body进行解析,但是这里不是直接用HttpMessageConverter进行解析,而是靠一个新的对象HttpMessageConverterExtractor,这个对象专门是用于从response转换出具体的对象,这里不做太多说明,有兴趣可以去了解下
结束词
小编的第二篇原理分享到这里就结束了,这次讲的主要还是请求处理流程,很多细节的处理没怎么讲,有问题的可以评论区跟我互动,共同进步哦!希望能帮到大家!!!