optimize: alibaba-seata: Transfer XID using `feign.RequestInterceptor` (#3171)

pull/3180/head
WangLiang/王良 2 years ago committed by GitHub
parent d57ff89748
commit 10b08a94ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,61 +0,0 @@
/*
* Copyright 2013-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.seata.feign;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.cloud.openfeign.FeignClientFactory;
/**
* @author xiaojing
*/
public class SeataContextBeanPostProcessor implements BeanPostProcessor {
private final BeanFactory beanFactory;
private SeataFeignObjectWrapper seataFeignObjectWrapper;
SeataContextBeanPostProcessor(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof FeignClientFactory && !(bean instanceof SeataFeignContext)) {
return new SeataFeignContext(getSeataFeignObjectWrapper(),
(FeignClientFactory) bean);
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
private SeataFeignObjectWrapper getSeataFeignObjectWrapper() {
if (this.seataFeignObjectWrapper == null) {
this.seataFeignObjectWrapper = this.beanFactory
.getBean(SeataFeignObjectWrapper.class);
}
return this.seataFeignObjectWrapper;
}
}

@ -1,47 +0,0 @@
/*
* Copyright 2013-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.seata.feign;
import java.io.IOException;
import feign.Client;
import feign.Request;
import feign.Response;
import org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalancerClient;
/**
* @author yuhuangbin
*/
public class SeataFeignBlockingLoadBalancerClient
extends FeignBlockingLoadBalancerClient {
public SeataFeignBlockingLoadBalancerClient(Client delegate,
BlockingLoadBalancerClient loadBalancerClient,
LoadBalancerClientFactory loadBalancerClientFactory,
SeataFeignObjectWrapper seataFeignObjectWrapper) {
super((Client) seataFeignObjectWrapper.wrap(delegate), loadBalancerClient, loadBalancerClientFactory);
}
@Override
public Response execute(Request request, Request.Options options) throws IOException {
return super.execute(request, options);
}
}

@ -1,35 +0,0 @@
/*
* Copyright 2013-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.seata.feign;
import feign.Feign;
import org.springframework.beans.factory.BeanFactory;
/**
* @author xiaojing
*/
final class SeataFeignBuilder {
private SeataFeignBuilder() {
}
static Feign.Builder builder(BeanFactory beanFactory) {
return Feign.builder().client(new SeataFeignClient(beanFactory));
}
}

@ -16,30 +16,28 @@
package com.alibaba.cloud.seata.feign;
import feign.Feign;
import feign.Retryer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
/**
* @author xiaojing
* @author wang.liang
* @since 2.2.5
*/
public class SeataBeanPostProcessor implements BeanPostProcessor {
private final SeataFeignObjectWrapper seataFeignObjectWrapper;
public class SeataFeignBuilderBeanPostProcessor implements BeanPostProcessor {
SeataBeanPostProcessor(SeataFeignObjectWrapper seataFeignObjectWrapper) {
this.seataFeignObjectWrapper = seataFeignObjectWrapper;
}
private static final Logger LOGGER = LoggerFactory.getLogger(SeataFeignBuilderBeanPostProcessor.class);
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
return this.seataFeignObjectWrapper.wrap(bean);
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof Feign.Builder) {
((Feign.Builder) bean).retryer(Retryer.NEVER_RETRY);
LOGGER.info("change the retryer of the bean '{}' to 'Retryer.NEVER_RETRY'", beanName);
}
return bean;
}
}

@ -1,81 +0,0 @@
/*
* Copyright 2013-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.seata.feign;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.alibaba.cloud.commons.lang.StringUtils;
import feign.Client;
import feign.Request;
import feign.Response;
import io.seata.core.context.RootContext;
import org.springframework.beans.factory.BeanFactory;
/**
* @author xiaojing
*/
public class SeataFeignClient implements Client {
private final Client delegate;
private final BeanFactory beanFactory;
private static final int MAP_SIZE = 16;
SeataFeignClient(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
this.delegate = new Client.Default(null, null);
}
SeataFeignClient(BeanFactory beanFactory, Client delegate) {
this.delegate = delegate;
this.beanFactory = beanFactory;
}
@Override
public Response execute(Request request, Request.Options options) throws IOException {
Request modifiedRequest = getModifyRequest(request);
return this.delegate.execute(modifiedRequest, options);
}
private Request getModifyRequest(Request request) {
String xid = RootContext.getXID();
if (StringUtils.isEmpty(xid)) {
return request;
}
Map<String, Collection<String>> headers = new HashMap<>(MAP_SIZE);
headers.putAll(request.headers());
List<String> seataXid = new ArrayList<>();
seataXid.add(xid);
headers.put(RootContext.KEY_XID, seataXid);
return Request.create(request.httpMethod(), request.url(), headers, request.body(),
request.charset(), null);
}
}

@ -17,62 +17,27 @@
package com.alibaba.cloud.seata.feign;
import feign.Client;
import feign.Feign;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
/**
* @author xiaojing
* @author wang.liang
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Client.class)
@AutoConfigureBefore(FeignAutoConfiguration.class)
public class SeataFeignClientAutoConfiguration {
@Bean
@Scope("prototype")
@ConditionalOnClass(name = "com.alibaba.csp.sentinel.SphU")
@ConditionalOnProperty(name = "feign.sentinel.enabled", havingValue = "true")
Feign.Builder feignSentinelBuilder(BeanFactory beanFactory) {
return SeataSentinelFeignBuilder.builder(beanFactory);
public static SeataFeignBuilderBeanPostProcessor seataFeignBuilderBeanPostProcessor() {
return new SeataFeignBuilderBeanPostProcessor();
}
@Bean
@ConditionalOnMissingBean
@Scope("prototype")
Feign.Builder feignBuilder(BeanFactory beanFactory) {
return SeataFeignBuilder.builder(beanFactory);
}
@Configuration(proxyBeanMethods = false)
protected static class FeignBeanPostProcessorConfiguration {
@Bean
static SeataBeanPostProcessor seataBeanPostProcessor(
SeataFeignObjectWrapper seataFeignObjectWrapper) {
return new SeataBeanPostProcessor(seataFeignObjectWrapper);
}
@Bean
static SeataContextBeanPostProcessor seataContextBeanPostProcessor(
BeanFactory beanFactory) {
return new SeataContextBeanPostProcessor(beanFactory);
}
@Bean
SeataFeignObjectWrapper seataFeignObjectWrapper(BeanFactory beanFactory) {
return new SeataFeignObjectWrapper(beanFactory);
}
public SeataFeignRequestInterceptor seataFeignRequestInterceptor() {
return new SeataFeignRequestInterceptor();
}
}

@ -1,69 +0,0 @@
/*
* Copyright 2013-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.seata.feign;
import java.util.HashMap;
import java.util.Map;
import feign.Client;
import org.springframework.cloud.openfeign.FeignClientFactory;
/**
* @author xiaojing
*/
public class SeataFeignContext extends FeignClientFactory {
private final SeataFeignObjectWrapper seataFeignObjectWrapper;
private final FeignClientFactory delegate;
SeataFeignContext(SeataFeignObjectWrapper seataFeignObjectWrapper,
FeignClientFactory delegate) {
this.seataFeignObjectWrapper = seataFeignObjectWrapper;
this.delegate = delegate;
}
@Override
public <T> T getInstance(String name, Class<T> type) {
T object = this.delegate.getInstance(name, type);
if (object instanceof Client) {
return object;
}
return (T) this.seataFeignObjectWrapper.wrap(object);
}
@Override
public <T> Map<String, T> getInstances(String name, Class<T> type) {
Map<String, T> instances = this.delegate.getInstances(name, type);
if (instances == null) {
return null;
}
Map<String, T> convertedInstances = new HashMap<>();
for (Map.Entry<String, T> entry : instances.entrySet()) {
if (entry.getValue() instanceof Client) {
convertedInstances.put(entry.getKey(), entry.getValue());
}
else {
convertedInstances.put(entry.getKey(),
(T) this.seataFeignObjectWrapper.wrap(entry.getValue()));
}
}
return convertedInstances;
}
}

@ -1,55 +0,0 @@
/*
* Copyright 2013-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.seata.feign;
import feign.Client;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalancerClient;
/**
* @author xiaojing
*/
public class SeataFeignObjectWrapper {
private static final Logger LOG = LoggerFactory
.getLogger(SeataFeignObjectWrapper.class);
private final BeanFactory beanFactory;
SeataFeignObjectWrapper(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
Object wrap(Object bean) {
if (bean instanceof Client client && !(bean instanceof SeataFeignClient)) {
if (bean instanceof FeignBlockingLoadBalancerClient feignBlockingLoadBalancerClient) {
return new SeataFeignBlockingLoadBalancerClient(feignBlockingLoadBalancerClient.getDelegate(),
beanFactory.getBean(BlockingLoadBalancerClient.class),
beanFactory.getBean(LoadBalancerClientFactory.class),
this);
}
return new SeataFeignClient(this.beanFactory, client);
}
return bean;
}
}

@ -16,23 +16,29 @@
package com.alibaba.cloud.seata.feign;
import com.alibaba.cloud.sentinel.feign.SentinelFeign;
import feign.Feign;
import feign.Retryer;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.BeanFactory;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import io.seata.core.context.RootContext;
import org.springframework.util.StringUtils;
/**
* @author xiaojing
* @author wang.liang
*/
final class SeataSentinelFeignBuilder {
public class SeataFeignRequestInterceptor implements RequestInterceptor {
private SeataSentinelFeignBuilder() {
}
@Override
public void apply(RequestTemplate template) {
String xid = RootContext.getXID();
if (!StringUtils.hasLength(xid)) {
return;
}
static Feign.Builder builder(BeanFactory beanFactory) {
return SentinelFeign.builder().retryer(Retryer.NEVER_RETRY)
.client(new SeataFeignClient(beanFactory));
List<String> seataXid = new ArrayList<>();
seataXid.add(xid);
template.header(RootContext.KEY_XID, xid);
}
}
Loading…
Cancel
Save