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.enabledThe 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 ~