Table of contents
1. SpringBoot quickly integrates Feign
2. Add annotations to the startup class
2. Three ways to add Header to the request
2. Add header through interface signature
3. Add a timeout configuration to the request
4. Global timeout configuration
5. Set timeout configuration for individual services
4. Configure the client load balancing mode for the request
Seven, Feign and Springboot version
no suitable HttpMessageConverter found for response type
This chapter explains key goals and user-oriented
Objectives of this chapter | Understand the principle of feign Master the use of feign Learn how to set feign's various headers Learn to feign log control Understand feign advanced configuration items Other Development Considerations |
user-oriented | Beginner, middle and advanced R&D students |
Many times, we see that when some high-level architecture students are writing Http service calls, when debugging and tracking codes, they only have interface signatures, and they do not write some calling process code like okhttp. The code looks refreshing, elegant and logical. , after reading this article, you will master its implementation plan, and provide a demo to help you complete the actual combat. If you have technical questions, you can ask for advice by private message;
1. SpringBoot quickly integrates Feign
1. Add Pom dependencies
<properties>
<spring-cloud-feign.version>3.1.3</spring-cloud-feign.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.10</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>${spring-cloud-feign.version}</version>
</dependency>
</dependencies>
2. Add annotations to the startup class
@EnableFeignClients
3. Reference Feign service
@RestController
@RequestMapping("/stock")
public class StockController {
@Autowired
private LocalFeignService service;
@GetMapping("/get")
public String get(){
service.add("10");
return "Hello world";
}
}
2. Three ways to add Header to the request
1. Add a fixed header
@RestController
public class HomeController {
@Resource
private FeignClientService feignClientService;
@RequestMapping(value = "/add")
public String add(@RequestBody FeignReq req) {
return "ok";
}
@RequestMapping("/test-add")
public Object testAdd() {
FeignReq req = new FeignReq();
req.setId(System.currentTimeMillis());
return feignClientService.add(req);
}
}
Add a fixed header value to the headers parameter of the @PostMapping annotation, and also include some configuration items read from the configuration file as the header value
@Component
@FeignClient(name = "feignClientService", url = "http://localhost:8081")
public interface FeignClientService {
@PostMapping(value = "/add", headers = {"Authorization=myFeignSignToken", "Content-Type=text/plain", "App=${my.name}"})
public String add(@RequestBody FeignReq req);
}
Browser access: http://localhost:8081/test-add
2. Add header through interface signature
@RestController
public class HomeController {
@Resource
private FeignClientService feignClientService;
@PostMapping(value = "/query")
public String query(@RequestParam("queryName") String queryName) {
return queryName + "ok";
}
@RequestMapping("/test-query")
public Object testQuery() {
Map<String, String> map = new HashMap<>();
map.put("token", "ikong_token"+System.currentTimeMillis());
return feignClientService.query("ikong", map);
}
}
The focus is on the headers of the Map type. Here, the Map object is converted into a request header through the @RequestHeader annotation of the interface signature
@Component
@FeignClient(name = "feignClientService", url = "http://localhost:8081", configuration = TestRequestInterceptor.class)
public interface FeignClientService {
@PostMapping(value = "/query")
public String query(@RequestParam("queryName") String queryName, @RequestHeader Map<String, String> headers);
}
Browser access: http://localhost:8081/test-query
3. Add header dynamically
@RestController
public class HomeController {
@Resource
private FeignClientService feignClientService;
@RequestMapping("/search")
public Object search(@RequestBody FeignReq req) {
return "ok";
}
@RequestMapping("/test-search")
public Object testSearch() {
FeignReq req = new FeignReq();
req.setId(System.currentTimeMillis());
return feignClientService.search(req);
}
}
@Component
@FeignClient(name = "feignClientService", url = "http://localhost:8081", configuration = TestRequestInterceptor.class)
public interface FeignClientService {
@PostMapping("/search")
public String search(@RequestBody FeignReq req);
}
Inject configuration = TestRequestInterceptor.class through FeignClient annotation, and realize header dynamic addition capability in TestRequestInterceptor
public class TestRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
template.header(HttpHeaders.AUTHORIZATION, createApiSign());
}
/**
* 创建接口签名
*
* @return
*/
private String createApiSign() {
return UUID.randomUUID().toString();
}
}
Browser access: http://localhost:8081/test-search
3. Add a timeout configuration to the request
1. Default timeout
connectiontimeout : 10s,readtimeout : 60s
public static class Options {
private final int connectTimeoutMillis;
private final int readTimeoutMillis;
public Options(int connectTimeoutMillis, int readTimeoutMillis) {
this.connectTimeoutMillis = connectTimeoutMillis;
this.readTimeoutMillis = readTimeoutMillis;
}
public Options() {
this(10 * 1000, 60 * 1000);
}
/**
* Defaults to 10 seconds. {@code 0} implies no timeout.
*
* @see java.net.HttpURLConnection#getConnectTimeout()
*/
public int connectTimeoutMillis() {
return connectTimeoutMillis;
}
/**
* Defaults to 60 seconds. {@code 0} implies no timeout.
*
* @see java.net.HttpURLConnection#getReadTimeout()
*/
public int readTimeoutMillis() {
return readTimeoutMillis;
}
}
3. Timeout exception
exception:feign.RetryableException: connect timed out executing POST http://xxx.ik.com/your/api
4. Global timeout configuration
feign.client.config.default.connectTimeout=5000
feign.client.config.default.readTimeout=5000
5. Set timeout configuration for individual services
To implement different timeouts for different services, you can configure them as follows
feign.client.config.yourService.connectTimeout=5
feign.client.config.yourService.readTimeout=5
4. Configure the client load balancing mode for the request
OpenFeign is just a Rest client and does not have any load balancing operations. The bottom layer of OpenFeign still calls the Netflix Ribbon load balancing component, so how do we modify the load balancing strategy when we use OpenFeign to implement service calls?
Customize Ribbon's load balancing configuration
@Configuration
// name为服务名
@RibbonClient(name = "my-provider", configuration = MyLoadBalanceConfiguration.class)
public class MyLoadBalanceConfiguration {
@Bean
public IRule ribbonRule() {
return new RandomRule(); // 采用随机策略
}
}
We are using the random strategy here, and the default is the round-robin strategy.
Ribbon provides optional load balancing classification
- Random strategy - RandomRule
- Polling strategy - RoundRobinRule Note: Ribbon default strategy
- Retry strategy - RetryRule
- Minimum Concurrency Policy - BestAvailableRule
- Available filtering strategy - AvailabilityFilteringRule, filter out those back-end servers marked as circuit tripped because of continuous connection failure, and filter out those back-end servers with high concurrency (active connections exceed the configured threshold), the performance is second only to the lowest concurrency strategy.
- Response time weighting strategy ——WeightedResponseTimeRule, calculate the server response time every 30 seconds, and use the response time as the weight. The shorter the response time, the greater the probability of being selected.
- Zone Tradeoff Strategy ——ZoneAvoidanceRule
Suggestions for using Ribbon's load balancing strategy
In general, it is recommended to use the lowest concurrency strategy, which has much higher performance than the default polling strategy.
Five, Feign log
Feign's log is based on the debug log mode. Whether it is global configuration or local configuration, code configuration or property configuration, you need to first change the log mode to debug mode: (The following is an example of calling the log mode of the FeignClientService interface )
1. Log level
OpenFeign's log levels are
NONE: By default, no logs are displayed.
BASIC: Only log the request method, URL, response status code, and execution time.
HEADERS: In addition to the custom information in BASIC, there are request and response headers.
FULL: In addition to the information defined in HEADERS, there are request and response body and metadata.
2. Log configuration class
@Configuration
public class OpenFeignLogConfig {
@Bean
Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;
}
}
3. Configuration file
logging:
level:
*[feign日志以什么级别监控哪个接口]:
com.ik.ikong.service.FeignClientService: debug
Six, high-level configuration
Seven, Feign and Springboot version
The version comparison relationship is as follows, for details, you can also click the link here to view the latest version compatibility relationship on the official website
8. Technical issues
Idea reports an error when calling the interface
no suitable HttpMessageConverter found for response type
resolution process
1. Let go of the log
@Configuration
public class FeignConfiguration {
@Bean
public feign.Logger logger() {
return new Slf4jLogger();
}
@Bean
public Logger.Level level() {
return Logger.Level.FULL;
}
}
logging:
level:
feign.Logger: debug
log detailed
From the released log, in the header returned by the interface: content-type: text/html; charset=UTF-8
Although I added produces and consumes to the interface signature
@PostMapping(value = "/xxxxx/your/api", produces = "application/json;charset=utf-8", consumes = "application/json;charset=utf-8")
It can be seen that there is no effect, and it is still abnormal: no suitable HttpMessageConverter found for response type
It is guessed that the native http return value only performs generic deserialization processing on the application/json type, which is the content-type: text/html returned by the interface; it cannot perform json deserialization processing, so add this type of json Deserialization processing;
@Bean
public HttpMessageConverters customConverters() {
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
mappingJackson2HttpMessageConverter.setSupportedMediaTypes(Arrays.asList(MediaType.TEXT_HTML,MediaType.APPLICATION_JSON));
return new HttpMessageConverters(false, Arrays.asList(mappingJackson2HttpMessageConverter, new StringHttpMessageConverter()));
}
重点在mappingJackson2HttpMessageConverter.setSupportedMediaTypes(Arrays.asList(MediaType.TEXT_HTML,MediaType.APPLICATION_JSON));
Finally, the json deserialization with content-type: text/html was successfully completed;
Reference: Spring Cloud
Feign implements Http calls between services. _feign http call_Half-life i's Blog-CSDN Blog