openfeign的使用
一、项目目录结构
1、craft微服务(服务提供者):
包含craft-core模块、craft-facade模块;core模块是craft微服务的核心模块,facade是craft微服务对外提供远程调用模块;
2、order微服务(服务使用者):
包含order-core模块、order-facade模块;core模块是order微服务的核心模块,facade模块是order微服务对外提供远程调用的模块
二、简单使用
添加在各种模块引入openfeign依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> <version>2.1.RELEASE</version> </dependency>
1、服务提供者craft
1.1craft-facade模块创建远程调用接口
package com.atxgl.craft.facade.feignClient; import com.atxgl.craft.facade.entity.CraftVersionDto; import com.atxgl.craft.facade.request.CraftVersionRequest; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.cloud.openfeign.SpringQueryMap; import org.springframework.stereotype.Service; import org.springframework.web.bind.annotation.GetMapping; import java.util.List; @Service @FeignClient(value = "craft-service", fallback = CraftVersionFeignFallback.class) public interface CraftVersionFeign { @GetMapping("/queryOrderCraft") List<CraftVersionDto> queryOrderCraft(@SpringQueryMap CraftVersionRequest craftVersionRequest); }
2、craft-core模块实现对外提供的接口服务CraftVersionFeign
package com.atxgl.craft.core.contorller; import com.atxgl.common.exception.BusinessException; import com.atxgl.craft.facade.entity.CraftVersionDto; import com.atxgl.craft.facade.feignClient.CraftVersionFeign; import com.atxgl.craft.facade.request.CraftVersionRequest; import org.springframework.web.bind.annotation.RestController; import springfox.documentation.annotations.ApiIgnore; import java.util.ArrayList; import java.util.List; @RestController @ApiIgnore public class CraftVersionFeignController implements CraftVersionFeign { @Override public List<CraftVersionDto> queryOrderCraft(CraftVersionRequest craftVersionRequest) { if ("123".equals(craftVersionRequest.getOrderId())){ throw new BusinessException(10020, "订单id不能为123"); } ArrayList<CraftVersionDto> craftVersionDtos = new ArrayList<>(); craftVersionDtos.add(new CraftVersionDto().setId("1212132")); return craftVersionDtos; } }
2、服务使用者orde微服务
2.1、引入craft-facade模块的依赖
<dependency> <groupId>com.atxgl</groupId> <artifactId>craft-facade</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
2.2、在启动类上添加@EnableFeignClients
package com.atxgl.order.core; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.ComponentScan; import org.springframework.core.env.Environment; import java.net.InetAddress; import java.net.UnknownHostException; @SpringBootApplication
/**
* 扫描标记了@FeignClient的接口并创建实例bean,默认扫描并创建所在工程下的包,
* 如果要扫描其他路径的包需要在basePackage属性添加路径,
* 注意如果自己包路径下也有,需要加上本工程的
*/
@EnableFeignClients(basePackages = {"com.atxgl.craft.facade.*"})
@Slf4j
@EnableDiscoveryClient
@ComponentScan(basePackages = {"com.atxgl.order.*","com.atxgl.craft.facade.*"})
public class OrderApplication {
public static void main(String[] args) throws UnknownHostException {
ConfigurableApplicationContext application = SpringApplication.run(OrderApplication.class, args);
}
}
2.3、使用@Autowired注解标注CraftVersionFeign参数
package com.atxgl.order.core.service.impl; import com.atxgl.craft.facade.entity.CraftVersionDto; import com.atxgl.craft.facade.feignClient.CraftVersionFeign; import com.atxgl.craft.facade.request.CraftVersionRequest; import com.atxgl.order.core.service.OrderCraftService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; @Slf4j @Service public class OrderCraftServiceImpl implements OrderCraftService { @Autowired private CraftVersionFeign craftVersionFeign; @Override public List<CraftVersionDto> queryCraftVersion(String orderId) { CraftVersionRequest craftVersionRequest = new CraftVersionRequest(); craftVersionRequest.setOrderId(orderId); List<CraftVersionDto> craftVersionDtos = craftVersionFeign.queryOrderCraft(craftVersionRequest); log.info("result---" + craftVersionDtos.toString()); return craftVersionDtos; } }
三、扩展
1、feign拦截器RequestInterceptor
使用feign在服务间调用时,可以实现RequestInterceptor接口的apply方法,对请求进行处理
package com.atxgl.common.feign; import com.atxgl.common.base.ResponseEnum; import com.atxgl.common.context.request.RequestContext; import com.atxgl.common.exception.BaseException; import feign.RequestInterceptor; import feign.RequestTemplate; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Configuration; import org.springframework.util.StringUtils; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; /** * @program: springCloud230416 * @description: feign拦截器: * 在调用feign接口请求的时候进行拦截,实现对请求进行处理 * @author: xiaogl * @create: 2023-04-20 17:17 */ @Configuration @Slf4j public class FeignClientInterceptorConfig implements RequestInterceptor { @Override public void apply(RequestTemplate requestTemplate) { ServletRequestAttributes requestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes(); HttpServletRequest request = requestAttributes.getRequest(); String requestId = request.getHeader("requestId"); log.info("requestId:{}", requestId); if (!StringUtils.hasLength(requestId)){ throw new BaseException(ResponseEnum.REQUEST_ID_NOT_EMPTY); }else { requestTemplate.header(RequestContext.REQUEST_ID, requestId); } requestTemplate.header(RequestContext.REQUEST_ID, requestId); } }
2、自定义feign异常解析器ErrorDecoder
feign默认的错误处理程序ErrorDecode.default总是抛出FeignException,我们可以通过实现ErrorDecode接口的decode方法自定义异常返回信息
package com.atxgl.common.feign; import com.alibaba.fastjson.JSON; import com.atxgl.common.base.ResponseEnum; import com.atxgl.common.base.Result; import com.atxgl.common.exception.BaseException; import com.atxgl.common.exception.BusinessException; import com.atxgl.common.exception.ParameterValidException; import feign.Response; import feign.Util; import feign.codec.ErrorDecoder; import lombok.extern.slf4j.Slf4j; /** * 自定义openfeign客户端接收请求返回异常信息 */ @Slf4j public class FeignClientErrorDecoder implements ErrorDecoder { @Override public Exception decode(String s, Response response) { try { String s1 = Util.toString(response.body().asReader()); log.info("执行了FeignClientErrorDecoder:{}", s1 ); Result result = JSON.parseObject(s1, Result.class); if (BaseException.STATUS.equals(response.status())){ return new BaseException(result.getCode(), result.getMessage()); } else if (BusinessException.STATUS.equals(response.status())) { return new BusinessException(result.getCode(), result.getMessage()); }else if (ParameterValidException.STATUS.equals(response.status())) { return new ParameterValidException(result.getCode(), result.getMessage()); } } catch (Exception e) { e.printStackTrace(); } return new BaseException(ResponseEnum.INVALID_PARAM_ERROR); } }
3、容错机制
开启feign对熔断器的支持,在配置文件添加feign.hystrix.enabled=true
- 使用fallback属性
- 使用fallbackFactory属性
3.1 fallback属性
在对接口使用@FeignClient声明feign客户端后,可以使用属性fallback指定异常处理类,这个类必须实现@FeignClinet作用的接口,且被注入到容器中;
@Service @FeignClient(value = "craft-service", fallback = CraftVersionFeignFallback.class) public interface CraftVersionFeign { @GetMapping("/queryOrderCraft") List<CraftVersionDto> queryOrderCraft(@SpringQueryMap CraftVersionRequest craftVersionRequest); } @Component @Slf4j public class CraftVersionFeignFallback implements CraftVersionFeign{ @Override public List<CraftVersionDto> queryOrderCraft(CraftVersionRequest craftVersionRequest) { log.info("执行了CraftVersionFeignFallback"); return new ArrayList<>(); } }
3.2 fallbackFactory属性
在对接口使用@FeignClient声明feign客户端后,可以使用属性fallbackFactory指定异常处理类,这个类必须实现FallbackFactory接口,且被注入到容器中;
@Service @FeignClient(value = "craft-service", fallbackFactory = CraftVersionFeignFallbackFactory.class) public interface CraftVersionFeign { @GetMapping("/queryOrderCraft") List<CraftVersionDto> queryOrderCraft(@SpringQueryMap CraftVersionRequest craftVersionRequest); } @Slf4j @Component public class CraftVersionFeignFallbackFactory implements FallbackFactory<CraftVersionFeign> { @Override public CraftVersionFeign create(Throwable cause) { log.error("CraftVersionFeign远程调用错误:{}", cause.getMessage()); return new CraftVersionFeign() { @Override public List<CraftVersionDto> queryOrderCraft(CraftVersionRequest craftVersionRequest) { return new ArrayList<CraftVersionDto>(); } }; } }