I have already introduced the Ribbon remote call knowledge to you about how services communicate with each other in micro-services. Have you found any problems with the Ribbon?
The problem of Ribbon
In the Ribbon, if we wanted to make a call, it would look like this:
@Resource
private RestTemplate restTemplate
String result = restTemplate.getForObject("http://my-goods/goods/get", String.class);
Goods goods = JSONObject.parseObject(result, Goods.class);
This is just like a normal HTTP request, requiring manual processing of incoming and outgoing parameters.
At first glance, this seems to be all right, but when you think about it, it’s not: the interface being called is all written by ourselves, the input and output parameters are all fixed, and even the person who wrote the called interface is the same… Is there a better way, say, to call it directly as if it were a native method?
Like this:
Goods goods = goodsServer.get();
The term is remote method invocation
Today’s protagonist Feign for us to achieve such a function, the following please ~
What is Feign
Officially, Feign is a Java-to-HTTP client binder inspired by Retrofit, JAXRS-2.0, and WebSocket.
Why Binder? In terms of what Feign does and starts with, Feign doesn’t implement the HTPP client itself, and adds enhancements on top of other component clients. We can get a feel for what Feign brings to us first
The client
- java.net.URL
- Apache HTTP
- OK Http
- Ribbon
- Java 11 http2
- .
specification
- Feign
- JAX-RS
- JAX-RS 2
- SOAP
- Spring 4
- .
codec
- GSON
- JAXB
- Jackson
- .
other
- Hystrix
- SLF4J
- Mock
Check out GitHub for more:
https://github.com/OpenFeign/…
The basic use
1. Write CRUD in Goods and Services
Small partners can find a project to write, mainly is to make a few interfaces to call
@RestController @RequestMapping("/goods") public class GoodsController { @GetMapping("/get-goods") public Goods Return new Goods().setName(" apple ").setPrice(1.1).setNumber(2); return new Goods().setName(" apple ").setPrice(1.1).setNumber(2); } @GetMapping("/list") public List<Goods> list(){ ArrayList<Goods> goodsList = new ArrayList<>(); Goods apple = new Goods().setName(" apple ").setPrice(1.1).setNumber(2); Goods apple = new Goods().setName(" apple ").setPrice(1.1).setNumber(2); goodsList.add(apple); Goods lemon = new Goods().setName(" lemon ").setPrice(5.1).setNumber(3); Goods lemon = new Goods().setName(" lemon ").setPrice(5.1).setNumber(3); goodsList.add(lemon); return goodsList; } @PostMapping("save") public void save(@RequestBody Goods goods){ System.out.println(goods); } @DeleteMapping public void delete(String id){ System.out.println(id); }}
2. Build a demo and introduce dependencies
<dependencies> <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-core</artifactId> </dependency> <! --> <dependency> < grouppid >io.github. OpenFeign </ grouppid > <artifactId> feig-Jackson </artifactId> </dependency> </dependencies>
3. Write the interface of the Feign specification
public interface GoodsApi { @RequestLine("GET /goods/get-goods") Goods getGoods(); @RequestLine("GET /goods/list") List<Goods> list(); @RequestLine("POST /goods/save") @Headers("Content-Type: application/json") void save(Goods goods); @RequestLine("DELETE /goods? id={id}") @Headers("Content-Type: application/json") void delete(@Param("id") String id); }
What is the FEIGN specification
4. Test
Public class feignDemo {public static void main(String[] args) {public static void main(String[] args) { .encoder(new JacksonEncoder()) .decoder(new JacksonDecoder()) .target(GoodsApi.class, "http://localhost:8082"); Println (goodsapi.getGoods ()); // call System.out.println(goodsapi.getGoods ()); System.out.println(goodsApi.list()); goodsApi.save(new Goods().setName("banana")); goodsApi.delete("1"); }}
flashes
See here, I don’t know if you have a flash of inspiration?
So if I build the code like this
@Bean
public GoodsApi goodsApi(){
return Feign.builder()
.encoder(new JacksonEncoder())
.decoder(new JacksonDecoder())
.target(GoodsApi.class, "http://localhost:8082");
}
And then when you use it, you inject it directly. WOC, isn’t that awesome?
A common way to use it
In addition to the basic usage above, Feign also supports the Spring 4 specification, as well as various HTTP clients (such as OkHttp), retry timeouts, logging, etc. Let me introduce one of the more common methods
Increased reliance on
<! -- Spring4 specification --> <dependency> < grouppid >io.github. OpenFeign </ grouppid > <artifactId> feig-spring4 </artifactId> The < version > 10.10.1 < / version > < / dependency > <! -- Ribbon client --> <group dependency> IO. Github. OpenFeign </group dependency> <artifactId>feign-ribbon</artifactId> </dependency> <! -- okHttp client --> <dependency> < grouppid > IO. Github. OpenFeign </ grouppid > <artifactId> feig-okHttp </artifactId> </dependency> <! -- Dependency --> <dependency> < grouppid >io.github. OpenFeign </dependency> <artifactId> feig-slf4j </artifactId> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> </dependency>
Write the interface
public interface Spring4GoodsApi {
@GetMapping("/goods/get-goods")
Goods getGoods();
@GetMapping("/goods/list")
List<Goods> list();
@PostMapping(value = "/goods/save", consumes = MediaType.APPLICATION_JSON_VALUE)
void save(Goods goods);
@DeleteMapping("/goods")
void delete(@RequestParam(value = "id") String id);
}
test
public class Spring4FeignDemo { public static void main(String[] args) { Spring4GoodsApi goodsApi = Feign.builder() // Use Spring4 specification.contract(new SpringContract()) // use Jackson code.encoder (new JacksonEncoder()).decoder(new) Decoder() // OKHttpClient (new OKHttpClient ()) // Failed to retry the request, Options(new Request. options(10, TimeUnit.SECONDS, 60, TimeUnit.seconds, true)) // Logger will be printed before and after the request. Logger(new SLF4JLogger ()) // Log-level configuration, BASIC: Print only request path and response status code BASIC information. LogLevel (logger.level.basic). Target (Spring4GoodSApi.class, "http://localhost:8082"); System.out.println(goodsApi.getGoods()); System.out.println(goodsApi.list()); goodsApi.save(new Goods().setName("banana")); goodsApi.delete("1"); }}
The interceptor
An HTTP client will have an interceptor mechanism for doing things uniformly before a request, such as adding a request header
Feign’s interceptors are used as follows:
public class AuthFeignInterceptor implements RequestInterceptor { @Override public void apply(RequestTemplate template) {System.out.println(" Enter the interceptor "); HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest(); template.header("token", request.getHeader("token")); }}
Implement a RequestInterceptor
Add at build time
Feign.builder()
.requestInterceptor(new AuthFeignInterceptor())
.target(Spring4GoodsApi.class, "http://localhost:8082");
That’s how Feign works, and once you’ve learned how to integrate it into the Spring Cloud, it’s a lot easier to do.
Integration of Spring Cloud
Website document: https://docs.spring.io/spring…
The consolidation sample invokes the commodity service for the order service
Direct triple ax start
1. Add dependencies
Introduce a dependency in the order-server
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2. Add notes
Add the annotation EnableFeignClients to the Application class
@EnableFeignClients @SpringBootApplication public class OrderApplication { public static void main(String[] args) { SpringApplication.run(GoodsApplication.class, args); }}
Any configuration class can be used, but it is recommended to add it to the boot class, because by default the annotation scans all packages in the class path of the annotation, and then the boot class is at the top, so all packages can be scanned.
Of course, you can also scan a package directly, since the Feign interface is usually kept together
3. Write the configuration
See Configuration Section
The axe is finished, and the sample is written
4. Write samples
@FeignClient(name = "my-goods", path = "/goods", contextId = "goods") public interface GoodsApi { @GetMapping("/get-goods") Goods getGoods(); /** * @SpringQueryMap getMapping ("/goods") goods getGoods(@SpringQueryMap goods /goods); /** * @SpringQueryMap goods /goods; @GetMapping("/list") List<Goods> list(); @PostMapping(value = "/save") void save(Goods goods); @DeleteMapping void delete(String id); }
FeignClient:
Name: Name of the service being invoked
Path: A prefix for the path that all interfaces under this class inherit from
contextId: A contextId is used to distinguish between different Feign interfaces, since there is usually more than one Feign interface for a service, such as a GoodsDetailAPI, but their name attributes are the same and they are all commodity services, so a contextId is needed to distinguish between different business scenarios
The others are the same as in common use
5. Test
@RestController @RequestMapping("/order") public class OrderController { @Resource private GoodsApi goodsApi; @GetMapping("/get-goods") public Goods getGoods(){ return goodsApi.getGoods(); }}
configuration
In the usual way, we build a Feign interface with various properties that are hardcoded, and then integrated into Spring, can be configured to make it more flexible.
The log
Feign: client: config: # global configuration, default: loggerLevel: # global configuration, default: loggerLevel: # global configuration, default: loggerLevel: # global configuration, default: loggerLevel: # global configuration, default: loggerLevel: # global configuration Full # separate service configuration corresponds to a higher priority goods: loggerLevel: BASIC
Global configuration here special pit, do not look at the source code do not know how to match, small partners must pay attention to
The client
Feign uses the default HttpURLConnection as the client, but it can be replaced by any other client
Preface: All clients are implementation classes of the Client interface. To see if the replacement is successful, simply put a breakpoint in the corresponding implementation class
Use httpclient
Introduction of depend on
<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> </dependency> <dependency> < the groupId > IO. Making. Openfeign < / groupId > < artifactId > feign - httpclient < / artifactId > < version > 10.10.1 < / version > </dependency>
This is fine, without any configuration changes, because the Feign automatic configuration class of the HttpClient takes effect when the service contains the class of the ApacheHttpClient and has a higher priority than the default HttpURLConnection automatic configuration class. At this point the service will inject HttpClient as the client.
The source is as follows:
The @import Import order is HttpClient, OKHttp, Default(HttpURLConnection).
The configuration class is valid if the ApacheHttpClient class exists, and
feign.httpclient.enabled
The configuration default unfit also takes effect.
Using OkHttp
Introduction of depend on
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>
Since we introduced the HttpClient dependency in the previous step, HttpClient has a higher priority than OkHttp and is enabled by default, there are two ways to make OkHttp work:
- Remove HttpClient dependencies
- Shows to close HttpClient
Here I use the second method
Feign: # turn off HttpClient: enabled: false # turn on okHttpClient: enabled: true
GZIP compression
Sometimes, when the request data is too large, compressing the data can effectively improve the performance of the request, and Feign also provides this configuration
Note: Compression only takes effect when you are not an OKHttp client
Feign: HttpClient: enabled: true # Compression: Request: enabled: true # Compression: The default is these MIME-types: Text/XML, application/ XML, application/json # Minimum threshold for compression of bytes. Default: 1024 min-request-size: 10
I’m using HttpClient here, or I don’t want to write it, but I’m just letting you know that I’m not using OkHttp
Fault tolerant retry
One of the problems that Internet applications can’t solve: network partitioning. When the service provider is unable to respond to this situation, we cannot keep the service consumer waiting, so we can configure a timeout for the service, after a certain period of time the service is called does not respond, and then cut off the request.
Feign configuration:
Feign: client: config: # global configuration default: # connection timeout per millisecond default 10 seconds connectTimeout: 1000 # request timeout per millisecond default 60 seconds readTimeout: 5000
Internet applications can’t solve the second problem: network jitter. When this happens, we can only ask the service to retry.
Feign configuration:
Retryer: feign: client: config: # global configuration default: # retry 5 retries
The interceptor
The interceptor is implemented the RequestInterceptor interface in the same way as usual
@Component public class AuthFeignInterceptor implements RequestInterceptor { @Override public void apply(RequestTemplate Template) {System.out.println(" Enter the interceptor "); HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest(); template.header("token", request.getHeader("token")); }}
Add the @Component annotation to the Spring container, and the service will automatically join Feign’s interceptor chain when it starts
You can also use configuration
Feign: client: config: # Default: requestInterceptors: - com.my.micro.service.order.interceptor.AuthFeignInterceptor
summary
This article introduces a remote method call method in detail: Feign. Now, let’s review it briefly.
What is FEIGN?
A remote method calling client that integrates the Ribbon, HttpClient, and OkHttp with support for various specifications such as Spring4
The basic way to use Feign?
Edit the interface, add the canonical annotations supported by Feign, build the proxy class using Feign.Builder, and initiate the call.
How to integrate SpringCloud?
Introduce the dependency, add the @EnableFeignClients annotation, and add the configuration as required
This post has covered some of the common methods used in Feign, so I hope you found them out and I’ll see you next time
gittee: https://gitee.com/lzj960515/m…
Personal blog space: https://zijiancode.cn/archive…
After reading it, you must have some harvest ~ Want to know more exciting content, welcome to pay attention to the public number: programmer A Jian, A Jian in the public number to welcome your arrival ~