related

  1. Spring Cloud Series 1 – Service Registration and Discovery Eureka

  2. Spring Cloud – Client calls Rest + Ribbon

  3. Spring Cloud Combat Series iii – Declarative client Feign

  4. Spring Cloud Series iv – Fuse Hystrix

  5. Spring Cloud Combat Series 5 – Service Gateway Zuul

  6. Spring Cloud Deployment Series (6) – Distributed Configuration center Spring Cloud Config

  7. Spring Cloud Combat Series (7) – Service Link Tracking Spring Cloud Sleuth

  8. Spring Cloud Combat Series (8) – Micro service monitoring Spring Boot Admin

  9. Spring Cloud OAuth 2.0

  10. Single sign-on JWT and Spring Security OAuth

preface

In the previous article, WE talked about how the RestTemplate works with the Ribbon to consume services. Feign is a declarative HTTP pseudo-client that provides interface oriented PROGRAMMING for HTTP client calls. This article goes further on how to consume services through Feign.

  • Feign can be invoked simply by creating an interface and providing annotations.

  • Feign has pluggable annotations that can be used with BOTH Feign annotations and JAX-RS annotations.

  • Feign supports pluggable encoders and decoders.

  • Feign integrates the Ribbon by default and can be used with Eureka to achieve load balancing by default.

The body of the

1. Create a service contract module

Create a service-contract project Module with the following pom.xml:

<?xml version="1.0" encoding="UTF-8"? >
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.3. RELEASE</version>
        <relativePath/> <! -- lookup parent from repository -->
    </parent>
    <groupId>io.ostenant.github.springcloud</groupId>
    <artifactId>service-contract</artifactId>
    <version>0.0.1 - the SNAPSHOT</version>
    <name>service-contract</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

        <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>io.ostenant.github.springcloud</groupId>
            <artifactId>service-contract</artifactId>
            <version>0.0.1 - the SNAPSHOT</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>
Copy the code

Service interfaces and related Dtos are defined in a service-contract as follows:

User.java

public class User implements Serializable {
    private String name;
    private int age;

    public User(a) {}public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void getName(a) {
        return this.name;
    }

    public String setName(a) {
        this.name = name;
    }

    public int getAge(a) {
        return this.age;
    }

    public void setAge(int age) {
        this.age = age; }}Copy the code

UserContract.java

The UserContract defines all the behavior of the User and is a declarative service interface with the @FeignClient annotation tag. Where the value of @feignClient specifies the service name of the service provider.

@FeignClient("service-provider")
public interface UserContract {
    @PostMapping("/user")
    void add(@RequestBody User user);

    @GetMapping("/user/{name}")  
    User findByName(@PathVariable String name);

    @GetMapping("/users")
    List<User> findAll(a);
}
Copy the code

For the service provider, you need methods that implement the UserContract interface; For service consumers, the UserContract can be injected directly as a client stake.

2. Create a service provider

Create a service-provider project Module and import the service contract Module dependencies as follows:

<?xml version="1.0" encoding="UTF-8"? >
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.3. RELEASE</version>
        <relativePath/> <! -- lookup parent from repository -->
    </parent>
    <groupId>io.ostenant.github.springcloud</groupId>
    <artifactId>service-provider</artifactId>
    <version>0.0.1 - the SNAPSHOT</version>
    <name>service-provider</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Dalston.SR1</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>io.ostenant.github.springcloud</groupId>
            <artifactId>service-contract</artifactId>
            <version>0.0.1 - the SNAPSHOT</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
Copy the code

Indicate that you are a EurekaClient by annotating @enableEurekaclient.

@SpringBootApplication
@EnableEurekaClient
@RestController
public class ServiceProviderApplication {
    public static void main(String[] args) { SpringApplication.run(ServiceProviderApplication.class, args); }}Copy the code

Create a class UserService to implement the specific services of the UserContract interface and provide user-related HTTP services externally.

@RestController
public class UserService implements UserContract {
    private static final Set<User> userSet = new HashSet<>();

    static {
        userSet.add(new User("Alex".28));
        userSet.add(new User("Lambert".32));
        userSet.add(new User("Diouf".30));
    }

    @Override
    public void add(@RequestBody User user) {
        userSet.add(user);
    }

    @Override
    public User findByName(@PathVariable String name) {
        return userSet.stream().filter(user -> {
            return user.getName().equals(name);
        }).findFirst();
    }

    @Override
    public List<User> findAll(a) {
        return newArrayList<>(userSet); }}Copy the code

The address of the service registry indicated in the configuration file, application.yml configuration file is as follows:

spring:
  active:
    profiles: sp1 # sp2

---

spring:
  profiles: sp1
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/
server:
  port: 8770
spring:
  application:
    name: service-provider

---

spring:
  profiles: sp2
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/
server:
  port: 8771
spring:
  application:
    name: service-provider
Copy the code

Active = SP1 and spring.profiles. Active =sp2 are used as the Boot command parameters of spring Boot respectively, and two service provider instances are started on port 8770 and 8771.

3. Create service consumers

Create a new project Module, call it Service-Consumer, and introduce Feign’s starter dependencies and service contract modules into its POM file as follows:

<?xml version="1.0" encoding="UTF-8"? >
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.3. RELEASE</version>
        <relativePath/> <! -- lookup parent from repository -->
    </parent>
    <groupId>io.ostenant.github.springcloud</groupId>
    <artifactId>service-consumer</artifactId>
    <version>0.0.1 - the SNAPSHOT</version>
    <name>service-consumer</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Dalston.SR1</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-feign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>io.ostenant.github.springcloud</groupId>
            <artifactId>service-contract</artifactId>
            <version>0.0.1 - the SNAPSHOT</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
Copy the code

In the project application configuration file. Yml file, specify the name of the application for service – consumer, the port number is 8772, the registered service address For http://localhost:8761/eureka/, the code is as follows:

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/
server:
  port: 8772
spring:
  application:
    name: service-feign
Copy the code

On the application startup class ServiceConsumerApplication plus @ EnableFeignClients annotate the function to Feign.

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class ServiceConsumerApplication {

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

Define a UserController controller that invokes the service provided by the service provider and responds to the front end.

@RestController
public class UserController {
    @Autowired
    private UserContract userContract;

    @PostMapping("/user")
    public void add(@RequestBody User user) {
        userContract.add(user);
    }

    @GetMapping("/user/{name}")
    public User findByName(@PathVariable String name) {
        return userContract.findByName(name);
    }

    @GetMapping("/users")
    public List<User> findAll(a) {
        returnuserContract.findAll(); }}Copy the code

The Feign interface is introduced in the control layer UserController to specify which service is invoked via @feignClient (service name).

Start the service consumer applications, visit http://localhost:8772/users connectivity testing service access to the consumer, the response content as follows:

[{"name": "Alex"."age": 28
    },
    {
      "name": "Lambert"."age": 32
    },
    {
      "name": "Diouf"."age": 30}]Copy the code

4. Feign source code implementation process

In general, the Feign source code implementation process is as follows:

  1. Start FeignClient with the @enableFeignClients annotation. Only if this annotation exists will the package scan for the @FeignClient annotation start at program startup.

  2. The service provider implements the FeIGN-based contract interface and annotates it with the @FeignClient annotation.

  3. When the service consumer starts, it does a package scan, scans all @FeignClient’s annotated classes, and injects this information into the Spring context.

  4. When the interface’s methods are called, the JDK’s proxy generates a concrete RequestTemplate template object.

  5. A Request object that is regenerated from the RequestTemplate as an HTTP Request.

  6. The Request object is handed to the Client for processing. The network Request framework embedded in the Client can be HTTPURLConnection, HttpClient, or OkHttp.

  7. Finally, the Client is packaged into the LoadBalanceClient class, which implements load balancing with the Ribbon.

reference

  • An In-depth Understanding of Spring Cloud and Microservice Construction by Zhipeng Fang

Welcome to pay attention to the technical public number: Zero one Technology Stack

This account will continue to share learning materials and articles on back-end technologies, including virtual machine basics, multithreaded programming, high-performance frameworks, asynchronous, caching and messaging middleware, distributed and microservices, architecture learning and progression.