related
-
Spring Cloud Series 1 – Service Registration and Discovery Eureka
-
Spring Cloud – Client calls Rest + Ribbon
-
Spring Cloud Combat Series iii – Declarative client Feign
-
Spring Cloud Series iv – Fuse Hystrix
-
Spring Cloud Combat Series 5 – Service Gateway Zuul
-
Spring Cloud Deployment Series (6) – Distributed Configuration center Spring Cloud Config
-
Spring Cloud Combat Series (7) – Service Link Tracking Spring Cloud Sleuth
-
Spring Cloud Combat Series (8) – Micro service monitoring Spring Boot Admin
-
Spring Cloud OAuth 2.0
-
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:
-
Start FeignClient with the @enableFeignClients annotation. Only if this annotation exists will the package scan for the @FeignClient annotation start at program startup.
-
The service provider implements the FeIGN-based contract interface and annotates it with the @FeignClient annotation.
-
When the service consumer starts, it does a package scan, scans all @FeignClient’s annotated classes, and injects this information into the Spring context.
-
When the interface’s methods are called, the JDK’s proxy generates a concrete RequestTemplate template object.
-
A Request object that is regenerated from the RequestTemplate as an HTTP Request.
-
The Request object is handed to the Client for processing. The network Request framework embedded in the Client can be HTTPURLConnection, HttpClient, or OkHttp.
-
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.