preface

Recently, I planned to reconstruct a core business system of the company with Java. I didn’t write Java for half a year, and JDK has been updated to 14. In consideration of stability and other problems, I finally chose JDK11.

In the selection of the overall architecture, since it is a brand new system, there is no historical burden, and there are many talents in the team, so our choice is bold.

The end result is a direct jump to the next big thing: Service Mesh, which eliminates distributed frameworks like SpringCloud and Dubbo.

I’m not going to discuss what Service Mesh is, what it solves, or why I chose it. After all, I’m still in the learning stage, and I’ll talk to you when I know it’s stable.

The problem

Since the direction is set on the actual lu code, but just at the beginning of the verification of the “ideal is full, the reality is very skinny”;

Since we removed frameworks like SpringCloud and Dubbo, the registration, discovery, load balancing, and other requirements for services are all provided in the Service Mesh.

However, it is still desirable for developers to be able to invoke remote services by calling local methods, which is easy to implement in frameworks such as SpringCloud, which are well supported.

Going back to our scenario, the requirement is quite simple: you want a declarative + annotated invocation like Feign in SpringCloud.

    @Autowired
    private StoreClient client ;
    
    Store store = client.update(1, store)
Copy the code

Using the Spring-Cloud-OpenFeign package would do the trick, but it would introduce some SpringCloud dependencies that we wouldn’t use at all, and it would feel “dirty”; As opposed to the idea of Service Mesh, one of the goals is to make such frameworks less intrusive.


Feign is at the heart of Spring-Cloud-OpenFeign, and it works right out of the box, so try to see if Feign supports it.

According to the official documentation, it is possible to call the remote interface in the form of a defined interface, but it is essentially usable without relying on other libraries, so it is reasonable that it is not integrated with Spring, but there is no library available for us to use.

Naturally, we don’t want to write the code in the red box above. We want all interfaces to be directly injected and used.

use

Hence the library feign-Plus

The process for using it is essentially spring-Cloud-OpenFeign:

@FeignPlusClient(name = "github", url = "${github.url}")
public interface Github {

    @RequestLine("GET /repos/{owner}/{repo}/contributors")
    List<GitHubRes> contributors(@Param("owner") String owner, @Param("repo") String repo);
}
Copy the code

Scanning at the SpringBoot entry:

@SpringBootApplication
@EnableFeignPlusClients(basePackages = "top.crossoverjie.feign.test")
public class DemoApplication {

	public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); }}Copy the code

Directly injected in the Spring context:

    @Autowired
    private Github github ;
    
    List<GitHubRes> contributors = github.contributors("crossoverJie"."feign-plus");
    logger.info("contributors={}".new Gson().toJson(contributors));    
Copy the code

Therefore, when we need to call some external third-party interfaces (such as Alipay, external OpenAPI), we can define an interface like this, which shields all details of HTTP requests.

It is also suitable for intra-company service invocation, similar to what we used to do when we wrote SpringCloud or Dubbo; The service provider provides a Client package that the consumer can call directly upon. Service Mesh does the rest of the load balancing, fault tolerance and so on.

For internal interfaces, @requestMapping (“/path”) can also be annotated:

At request time, /order is concatenated after the URL, so when configuring feign.order.service.url, only the domain name or IP address of the service provider is required.


Feign-plus also supports switching the specific HttpClient, the default is OKhttp3, which can be changed with the following configuration.

# default(okhttp3)
feign.httpclient=http2Client
Copy the code

There are other configurations as well:

feign.plus.max-idle-connections = 520
feign.plus.connect-timeout = 11000
feign.plus.read-timeout = 12000
Copy the code

implementation

Finally, let’s talk a little bit about how this is done, which is essentially a condensed version of Spring-Cloud-OpenFeign.

One of the most core is top. Crossoverjie. Feign. Plus. Factory. FeignPlusBeanFactory class.

This class implements the org. Springframework. Beans. Factory. FactoryBean interface, and rewrite the getObject () method returns an object.

If this code sounds familiar, it’s actually Feign’s official demo.

The returned object is actually a proxy object for the interface we defined, and the object itself is Feign, so our HTTP request codec, request initiation, and other logic is proxyed by this Feign object.

This HardCodedTarget is the object inside Feign used to broker the final request.

There is a small snafus: such a custom Bean and then inject object Idea is not recognized, the current context does not have the Bean, but spring-Cloud-OpenFeign is recognized.


Since Feign supports multiple clients, the clients here can be dynamically specified through a configuration file.

Using the @conditionalonExpression annotation provided by SpringBoot, you can dynamically select which HttpClient to use based on the configuration, that is, which Bean to generate.

conclusion

The logic of this library is very simple. It essentially encapsulates Feign and provides SpringBoot support. Welcome to download and use this library.

Feign-plus source: github.com/crossoverJi…

Your likes and shares are the biggest support for me