1. 添加依赖
<!-- spring cloud openfeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
2. 启动类添加注解
@EnableFeignClients(basePackages={"com.demo.feign"})
启用feign客户端。
3. 添加Feign接口
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import com.demo.entity.GetTokenRequest;
import com.demo.entity.TokenResponse;
// @FeignClient(contextId = "demo-authserver-token", value = "demo-authserver")
@FeignClient(name = "demo-authserver",
configuration = FeignConfiguration.class, fallback=GetTokenServiceFallback.class)
public interface GetTokenService {
@GetMapping(value = "/getToken")
public String getTokenInfo(@RequestBody GetTokenRequest tokenRequest);
}
@FeignClient的注解参数说明:
value和name:value和name的作用一样,如果没有配置url那么配置的值将作为服务名称,用于服务发现。反之只是一个名称;
serviceId:serviceId已经废弃了,直接使用name即可。
contextId:一个相同的服务定义的接口比较多,当需要定义多个feign接口进行分组时,就可以添加该参数;获取客户端名称时,如果配置了contextId就会用contextId,如果没有配置就会去value,然后是name,最后是serviceId。默认都没有配置,当出现一个服务有多个Feign Client的时候就会报错了。
String name = getClientName(attributes);
registerClientConfiguration(registry, name,
attributes.get("configuration"));
private String getClientName(Map<String, Object> client) {
if (client == null) {
return null;
}
String value = (String) client.get("contextId");
if (!StringUtils.hasText(value)) {
value = (String) client.get("value");
}
if (!StringUtils.hasText(value)) {
value = (String) client.get("name");
}
if (!StringUtils.hasText(value)) {
value = (String) client.get("serviceId");
}
if (StringUtils.hasText(value)) {
return value;
}
throw new IllegalStateException("Either 'name' or 'value' must be provided in @"
+ FeignClient.class.getSimpleName());
}
url:url用于配置指定服务的地址,相当于直接请求这个服务,不经过Ribbon的服务选择。像调试等场景可以使用;
configuration:指定feign的配置类,可以自定义Feign的Encoder、Decoder、LogLevel(定义日志)、Contract;
fallback:定义容错的处理类,当调用远程接口失败或超时时,会调用对应接口的容错逻辑,fallback指定的类必须实现@FeignClient标记的接口;
path:定义当前FeignClient的统一前缀,当我们项目中配置了server.context-path,server.servlet-path时使用
4. feign配置类
public class FeignConfiguration {
@Bean
public Logger.Level getLoggerLevel() {
return Logger.Level.FULL;
}
@Bean
public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
return new BasicAuthRequestInterceptor("user", "password");
}
@Bean
public Request.Options options(){
return new Request.Options(5000, 3000);
}
@Bean
public CustomRequestInterceptor customRequestInterceptor() {
return new CustomRequestInterceptor();
}
// Contract,feignDecoder,feignEncoder.....
}
5. 定义容错的处理类
@Component
public class GetTokenServiceFallback implements GetTokenService {
@Override
public String getTokenInfo(@RequestBody GetTokenRequest tokenRequest);
{
TokenResponse tokenResponse = new TokenResponse();
tokenResponse.setCode("9999");
tokenResponse.setMessage("令牌获取失败");
String jsonTokenResponse = JSONObject.toJSONString(tokenResponse);
return jsonTokenResponse;
}
}
fallback的类必须实现Feign Client的接口。
6. 执行调用
@RestController
public class GetTokenController {
@Autowired
private GetTokenService getTokenService;
/**
* 获取当前用户信息
*/
@GetMapping("/tokeninfo/{username}")
public Object tokeninfo(@PathVariable("username") String username)
{
GetTokenRequest tokenRequest = new GetTokenRequest();
tokenRequest.setAppid("appftcsp001");
tokenRequest.setDatetime("20200818160101");
tokenRequest.setNonce("98908999");
tokenRequest.setTokentype("md5");
String data = tokenRequest.getDatetime() + tokenRequest.getAppid() + tokenRequest.getNonce() + tokenRequest.getTokentype();
String appSecret = "appkeyftcsp001";
String hash = Md5Utils.hash(data + appSecret);
tokenRequest.setSign(hash);
String tokenInfo = getTokenService.getTokenInfo(tokenRequest);
return tokenInfo;
}
}
7. 通过配置文件配置参数
logging:
level:
com.demo.openfeign.feignapi: debug
feign:
client:
config:
demo-authserver:
# 日志级别配置
loggerLevel: FULL
# 连接超时时间
connectTimeout: 5000
# 请求处理超时时间
readTimeout: 300
使用openfeign进行简单的调用还是比较容易的,相比固定的接口地址调用,使用服务名进行调用,配合ribbon的负载均衡策略,可以实现负载均衡功能。需要关注的是调用失败处理,调用日志,超时时间,自定义拦截器等内容。