与HttpClient和RestTemplate相比,使用springcloud的feign调用远程接口更为简便,可以通过配置的方式实现远程接口调用。但是有时我们并不想使用springcloud,而只是想在springboot中使用feign,我在网上搜了很多springboot单独集成feign的文章,但都不能用。在通过学习、开发后简单整理了一个教程。详细介绍如何在springboot中单独集成feign,以简便的方式调用远程接口。
<groupId>com.examplegroupId>
<artifactId>demoartifactId>
<version>0.0.1-SNAPSHOTversion>
<packaging>jarpackaging>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.2.2.RELEASEversion>
<relativePath/>
parent>
<properties>
<java.version>1.8java.version>
<maven.compiler.target>1.8maven.compiler.target>
<maven.compiler.source>1.8maven.compiler.source>
<spring-cloud.version>Hoxton.SR1spring-cloud.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-dependenciesartifactId>
<version>2.2.2.RELEASEversion>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring-cloud.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
@EnableFeignClients //开启feign
@SpringBootApplication
public class SpringbootApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootApplication.class, args);
}
}
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import feign.Response;
@FeignClient(name = "userService", url = "${feign.rpc.systemServiceOne}", configuration = FeignConfig.class, fallbackFactory = RemoteUserRpcFallbackFactory.class)
public interface RemoteUserRpc {
@PostMapping(value = "/person",consumes = MediaType.APPLICATION_JSON_VALUE)
R<UserEntity> getUserInfo(@RequestBody UserEntity user);
//另外一种用Response接收
@PutMapping(value = "/x_organization_assemble_personal/jaxrs/person/icon", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
Response updateIcon(@RequestPart("file") MultipartFile file, @RequestParam("fileName") String fileName);
}
@FeginClient中的name是自己给这个rpc起的名字, ur为调用的地址
/**
* 获取用户信息
*
* @return the user info
*/
public UserEntity getUserInfo(UserEntity user) {
R<UserEntity> r = remoteUserRpc.getUserInfo(user);
if (r.getCode() == HttpStatus.ERROR) {
throw new RuntimeException(r.getMessage());
}
return r.getData();
}
/**
* 更新用户头像
*
* @param file the file
* @param fileName the file name
*/
public void updateIcon(MultipartFile file, String fileName) {
Response response = remoteUserRpc.updateIcon(file, fileName);
String str = HttpUtils.inputStreamConvertStr(response);
R r = JSONObject.parseObject(str, R.class);
String type = r.getType();
if (r.getCode() == HttpStatus.ERROR || !type.equals(ThirdStatus.SUCCESS)) {
throw new RuntimeException(r.getMessage());
}
}
response 转str 工具类
/**
* 流转str
*
* @param response the response
* @return string string
*/
public static String inputStreamConvertStr(Response response) {
Response.Body body = response.body();
String str = null;
try {
InputStream inputStream = body.asInputStream();
StringBuilder sb = new StringBuilder();
String line;
BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
while ((line = br.readLine()) != null) {
sb.append(line);
}
str = sb.toString();
} catch (IOException e) {
e.printStackTrace();
}
return str;
}
/**
* 响应信息主体
*
* @author lxy
*/
@Data
public class R<T> implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 成功
*/
public static final int SUCCESS = Constants.SUCCESS;
/**
* 失败
*/
public static final int FAIL = Constants.FAIL;
private int code;
private String msg;
private T data;
public static <T> R2<T> ok() {
return restResult(null, SUCCESS, null);
}
public static <T> R2<T> ok(T data) {
return restResult(data, SUCCESS, null);
}
public static <T> R2<T> ok(T data, String msg) {
return restResult(data, SUCCESS, msg);
}
public static <T> R2<T> fail() {
return restResult(null, FAIL, null);
}
public static <T> R2<T> fail(String msg) {
return restResult(null, FAIL, msg);
}
public static <T> R2<T> fail(T data) {
return restResult(data, FAIL, null);
}
public static <T> R2<T> fail(T data, String msg) {
return restResult(data, FAIL, msg);
}
public static <T> R2<T> fail(int code, String msg) {
return restResult(null, code, msg);
}
private static <T> R2<T> restResult(T data, int code, String msg) {
R2<T> apiResult = new R2<>();
apiResult.setCode(code);
apiResult.setData(data);
apiResult.setMsg(msg);
return apiResult;
}
}
import feign.hystrix.FallbackFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class RemoteUserRpcFallbackFactory implements FallbackFactory<RemoteUserRpc> {
@Override
public RemoteUserRpc create(Throwable throwable) {
log.error("调用用户数据接口出错:{}", throwable.getMessage());
return new RemoteUserRpc() {
@Override
public R<UserEntity> getUserInfo(UserEntity user) {
return R.fail("调用用户数据接口出错:" + throwable.getMessage());
}
@Override
public Response updateIcon(MultipartFile file, String fileName) {
return null;
}
};
}
}
@PostMapping(value = "/person",consumes = MediaType.APPLICATION_JSON_VALUE)
R getUserInfo(@RequestHeader Map header, @RequestBody UserEntity user);
/**
* feign转发header参数
*/
@Configuration
public class FeignConfig implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
HttpServletRequest httpServletRequest = ServletUtils.getRequest();
if (StringUtils.isNotNull(httpServletRequest)) {
Map<String, String> headers = ServletUtils.getHeaders(httpServletRequest);
String authentication = headers.get(Constants.AUTHORIZATION_HEADER);
if (StringUtils.isNotEmpty(authentication)) {
requestTemplate.header(Constants.AUTHORIZATION_HEADER, authentication);
}
}
}
}
/**
* 客户端工具类
*/
public class ServletUtils {
/**
* 获取request
*/
public static HttpServletRequest getRequest() {
try {
return getRequestAttributes().getRequest();
} catch (Exception e) {
return null;
}
}
public static ServletRequestAttributes getRequestAttributes() {
try {
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
return (ServletRequestAttributes) attributes;
} catch (Exception e) {
return null;
}
}
}
import com.netflix.hystrix.HystrixThreadPoolKey;
import com.netflix.hystrix.HystrixThreadPoolProperties;
import com.netflix.hystrix.strategy.HystrixPlugins;
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
import com.netflix.hystrix.strategy.concurrency.HystrixRequestVariable;
import com.netflix.hystrix.strategy.concurrency.HystrixRequestVariableLifecycle;
import com.netflix.hystrix.strategy.eventnotifier.HystrixEventNotifier;
import com.netflix.hystrix.strategy.executionhook.HystrixCommandExecutionHook;
import com.netflix.hystrix.strategy.metrics.HystrixMetricsPublisher;
import com.netflix.hystrix.strategy.properties.HystrixPropertiesStrategy;
import com.netflix.hystrix.strategy.properties.HystrixProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* 自定义Feign的隔离策略:
* 在转发Feign的请求头的时候, 如果开启了Hystrix,
* Hystrix的默认隔离策略是Thread(线程隔离策略), 因此转发拦截器内是无法获取到请求的请求头信息的,
* 可以修改默认隔离策略为信号量模式:hystrix.command.default.execution.isolation.strategy=SEMAPHORE,
* 这样的话转发线程和请求线程实际上是一个线程, 这并不是最好的解决方法, 信号量模式也不是官方最为推荐的隔离策略;
* 另一个解决方法就是自定义Hystrix的隔离策略:
* 思路是将现有的并发策略作为新并发策略的成员变量,在新并发策略中,
* 返回现有并发策略的线程池、Queue;将策略加到Spring容器即可;
*/
@Component
public class FeignHystrixConcurrencyStrategyIntellif extends HystrixConcurrencyStrategy {
Logger log = LoggerFactory.getLogger(this.getClass());
private HystrixConcurrencyStrategy delegate;
public FeignHystrixConcurrencyStrategyIntellif() {
try {
this.delegate = HystrixPlugins.getInstance().getConcurrencyStrategy();
if (this.delegate instanceof FeignHystrixConcurrencyStrategyIntellif) {
// Welcome to singleton hell...
return;
}
HystrixCommandExecutionHook commandExecutionHook =
HystrixPlugins.getInstance().getCommandExecutionHook();
HystrixEventNotifier eventNotifier = HystrixPlugins.getInstance().getEventNotifier();
HystrixMetricsPublisher metricsPublisher = HystrixPlugins.getInstance().getMetricsPublisher();
HystrixPropertiesStrategy propertiesStrategy =
HystrixPlugins.getInstance().getPropertiesStrategy();
this.logCurrentStateOfHystrixPlugins(eventNotifier, metricsPublisher, propertiesStrategy);
HystrixPlugins.reset();
HystrixPlugins instance = HystrixPlugins.getInstance();
instance.registerConcurrencyStrategy(this);
instance.registerCommandExecutionHook(commandExecutionHook);
instance.registerEventNotifier(eventNotifier);
instance.registerMetricsPublisher(metricsPublisher);
instance.registerPropertiesStrategy(propertiesStrategy);
} catch (Exception e) {
log.error("Failed to register Sleuth Hystrix Concurrency Strategy", e);
}
}
private void logCurrentStateOfHystrixPlugins(HystrixEventNotifier eventNotifier,
HystrixMetricsPublisher metricsPublisher,
HystrixPropertiesStrategy propertiesStrategy) {
if (log.isDebugEnabled()) {
log.debug("Current Hystrix plugins configuration is [" + "concurrencyStrategy ["
+ this.delegate + "]," + "eventNotifier [" + eventNotifier + "]," + "metricPublisher ["
+ metricsPublisher + "]," + "propertiesStrategy [" + propertiesStrategy + "]," + "]");
log.debug("Registering Sleuth Hystrix Concurrency Strategy.");
}
}
@Override
public <T> Callable<T> wrapCallable(Callable<T> callable) {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
return new WrappedCallable<>(callable, requestAttributes);
}
@Override
public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey,
HystrixProperty<Integer> corePoolSize,
HystrixProperty<Integer> maximumPoolSize,
HystrixProperty<Integer> keepAliveTime,
TimeUnit unit, BlockingQueue<Runnable> workQueue) {
return this.delegate.getThreadPool(threadPoolKey, corePoolSize, maximumPoolSize, keepAliveTime,
unit, workQueue);
}
@Override
public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey,
HystrixThreadPoolProperties threadPoolProperties) {
return this.delegate.getThreadPool(threadPoolKey, threadPoolProperties);
}
@Override
public BlockingQueue<Runnable> getBlockingQueue(int maxQueueSize) {
return this.delegate.getBlockingQueue(maxQueueSize);
}
@Override
public <T> HystrixRequestVariable<T> getRequestVariable(HystrixRequestVariableLifecycle<T> rv) {
return this.delegate.getRequestVariable(rv);
}
static class WrappedCallable<T> implements Callable<T> {
private final Callable<T> target;
private final RequestAttributes requestAttributes;
public WrappedCallable(Callable<T> target, RequestAttributes requestAttributes) {
this.target = target;
this.requestAttributes = requestAttributes;
}
@Override
public T call() throws Exception {
try {
RequestContextHolder.setRequestAttributes(requestAttributes);
return target.call();
} finally {
RequestContextHolder.resetRequestAttributes();
}
}
}
}
import feign.hystrix.FallbackFactory;
这个包路径下的,否则会有问题#是否开启hystrix。true:开启,false:不开启, 不开启的话feign调用失败不回走到回调函数里面
feign:
hystrix:
enabled: true
rpc:
systemServiceOne: http://192.168.0.182:20020
#hystrixf超时时间, 不设置feign调用会很快超时
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 10000