This is the sixth day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021

This series code address: github.com/JoJoTec/spr…

In many micro-services that use cloud native, a relatively small number may directly rely on the load balancer in the cloud service to map the internal domain name to the service, judge the health status of instances through the health check interface, and then directly use OpenFeign to generate Feign Clients corresponding to the domain name. In the Spring Cloud ecosystem, OpenFeign is encapsulated and each component of Feign Client is customized to realize the integration of service discovery and load balancing in OpenFeign Client. On this basis, we also combined the Resilience4J component to implement thread isolation at microservice instance level, circuit breakers at microservice method level and retry.

Let’s take a look at Spring Cloud OpenFeign

Spring Cloud OpenFeign parsing

HTTP codec, combined with the codec in Spring-Boot

Any component in the Spring Cloud is implemented based on Spring Boot. Since the HTTP codec is already available in Spring Boot, it is possible to implement the HTTP codec independently for OpenFeign. Instead, consider implementing OpenFeign’s codec interface with Spring Boot’s HTTP codec.

In FeignClientsConfiguration, provides a default implementation:

// Due to the initialization order and NamedContextFactory Configuration initialization, @autoWired Private ObjectFactory<HttpMessageConverters> messageConverters; @Bean @ConditionalOnMissingBean public Decoder feignDecoder() { return new OptionalDecoder(new ResponseEntityDecoder(new  SpringDecoder(this.messageConverters))); } @bean@conditionalonmissingbean // we ignore the existence of Pageable class here // the compatible implementation of Pageable for Spring Data is also simple, You ignore @ ConditionalOnMissingClass (" org. Springframework. Data. Domain. Pageable ") public Encoder feignEncoder(ObjectProvider<AbstractFormWriter> formWriterProvider) { return springEncoder(formWriterProvider, encoderProperties); } private Encoder springEncoder(ObjectProvider<AbstractFormWriter> formWriterProvider, FeignEncoderProperties encoderProperties) { AbstractFormWriter formWriter = formWriterProvider.getIfAvailable(); if (formWriter ! = null) { return new SpringEncoder(new SpringPojoFormEncoder(formWriter), this.messageConverters, encoderProperties); } else { return new SpringEncoder(new SpringFormEncoder(), this.messageConverters, encoderProperties); }}Copy the code

Decoder based decoder decoder

Through the source code can be seen, the default Decoder is through several layers of packaging Decoder, including:

  • OptionalDecoder: a decoder for handling Optional wrapper classes in the Java JDK
  • ResponseEntityDecoder: used to handle the spring-Web decoder for the request response wrapper class HttpEntity
  • SpringDecoder: Feign’s Decoder implemented using Spring’s Decoder

The HttpMessageConverters object passed to SpringDecoder is a collection of all The HttpMessageConverters of Spring-Web. HttpMessageConverter is a spring-Web tool that encodes and decodes the body of an HTTP request and response. Its interface structure is as follows:

Public interface HttpMessageConverter<T> {// Check whether clazz can be read by the current HttpMessageConverter Boolean canRead(Class<? > clazz, @Nullable MediaType mediaType); Boolean canWrite(Class<? > clazz, @Nullable MediaType mediaType); // Get all supported MediaType List<MediaType> getSupportedMediaTypes(); // The default implementation is that if the type can be read or written by the current HttpMessageConverter, GetSupportedMediaTypes default List<MediaType> getSupportedMediaTypes(Class<? > clazz) { return (canRead(clazz, null) || canWrite(clazz, null) ? getSupportedMediaTypes() : Collections.emptyList()); } // An object of Type clazz is read from inputMessage and parsed. T read(Class<? extends T> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException; // Serialize object t to HttpOutputMessage, Void write(T T, @nullable MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException; }Copy the code

Spring Boot has a lot of HttpMessageConverter built in. We can also implement our own HttpMessageConverter to implement our own custom MediaType. For example, we define one here:

public class CustomizedHttpMessageConverter implements HttpMessageConverter<Student> { @Override public boolean canRead(Class<? > clazz, MediaType mediaType) { return clazz.equals(Student.class); } @Override public boolean canWrite(Class<? > clazz, MediaType mediaType) { return clazz.equals(Student.class); } public static class StudentMediaType extends MediaType { public StudentMediaType() { super("application", "student", StandardCharsets.UTF_8); } } @Override public List<MediaType> getSupportedMediaTypes() { return List.of(new StudentMediaType()); } @Override public Student read(Class<? extends Student> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { String temp = StreamUtils.copyToString(inputMessage.getBody(), StandardCharsets.UTF_8); String[] split = temp.split(","); return new Student( Long.parseLong(split[0]), split[1]); } @Override public void write(Student student, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { outputMessage.getBody().write((student.getId() + "," + student.getName()).getBytes(StandardCharsets.UTF_8)); }}Copy the code

Then, as before, configure it into a Spring Boot-compatible MVC configuration:

@Configuration(proxyBeanMethods = false)
public class TestConfig implements WebMvcConfigurer {
	@Override
	public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
		converters.add(new CustomizedHttpMessageConverter());
	}
}
Copy the code

Write Controller, test:

@RestController @RequestMapping("/test") public class TestController { @PostMapping("/post-to-student") public Student postToStudent(@RequestBody Student student) { return student; }}Copy the code

Using a tool like Postman, specify the HTTP request header:

Content-Type:application/student
Accept:application/student
Copy the code

The Body is:

1,zhx
Copy the code

After the request, will go to the read of the CustomizedHttpMessageConverter parsed into Student object, Response after the student will also be CustomizedHttpMessageConverter write write in response to the Body

SpringEncoder allows you to reuse Spring’s built-in HttpMessageConverter and extend and customize your own HttpMessageConverter.

ResponseEntityDecoder (); ResponseEntityDecoder (); ResponseEntityDecoder;

@Override public Object decode(final Response response, Type type) throws IOException, FeignException {// Is HttpEntity< with a tangible parameter? > if (isParameterizeHttpEntity(type)) {type = ((ParameterizedType) type).getActualTypeArguments()[0]; DecodedObject = this.decoder.decode(response, type); // Decode response Object decodedObject = this.decoder.decode(response, type); Return createResponse(decodedObject, response); return createResponse(decodedObject, response); Return createResponse(null, response);} else if (isHttpEntity(type)) {return createResponse(null, response); } else {return this.decoder.decode(response, type); }}Copy the code

In order to be compatible with the RestTemplate response, RestTemplate can return an HttpEntity, but the body returned by the underlying HTTP request does not actually wrap this type.

Similarly, the Optional wrapper class in the JDK needs to do the same thing, which is implemented through OptionalDecoder.

SpringEncoder based encoder

The SpringEncoder coder is also very simple and is also based on HttpMessageConverter in Spring. We will not repeat it here.

Wechat search “my programming meow” public account, a daily brush, easy to improve skills, won a variety of offers