Feign Declarative service invocation
Learning goals
What is Feign
Feign is a lightweight RESTful HTTP client for Spring Cloud Netflix. Feign is an open source framework that implements load balancing and Rest invocation. It encapsulates Ribbon and RestTemplate. The interface – oriented programming of WebService is realized, which further reduces the coupling degree of the project. The article is reprinted with music bytes
Feign has a built-in Ribbon for client load balancing calls to services in the service registry.
Feign itself does not support Spring MVC annotations, it has its own annotations, and for ease of use, Spring Cloud incubates OpenFeign.
Feign is a declarative, templated HTTP client (available only in Consumer).
Feign supports annotations and usage in the official documentation: github.com/OpenFeign/f… Or spring. IO official documentation
Feign is used by using Feign’s annotations to define an interface that can be invoked to invoke the services of the service registry.
What problems does Feign solve
Feign is designed to make it easier to write JAVA HTTP clients. Feign simplifies RestTemplate code and implements Ribbon load balancing, making code simpler and less client-called. Feign is the preferred solution for load balancing. Just create an interface and add annotations to it.
Feign is a declarative service invocation component that, at its core, invokes remote methods as if they were local methods, with no awareness of remote HTTP requests.
-
It solves the problem of having a developer call a remote interface as if it were a local method, with no sense that it was a remote method, much less an HTTP request. You don’t have to worry about the details of remote interactions, and you don’t have to worry about distributed environment development.
-
Like Dubbo, the Consumer calls the Provider interface methods directly, without having to parse the returned data through the normal Http Client construction request.
Feign vs OpenFeign
OpenFeign is a Spring Cloud annotation that supports Spring MVC on top of Feign, such as @requestMapping, @PathVariable, etc.
OpenFeign’s @FeignClient can parse the interface under @RequestMapping annotation of SpringMVC and generate implementation classes through dynamic proxy, which can perform load balancing and invoke services.
Feign Primer case
Feign-demo polymerization project. SpringBoot 2.2.4.RELEASE, Spring Cloud hoxton.sr1
Feign can be used in the following steps:
- Service consumers add Feign dependencies;
- To create a service layer interface, add
@FeignClient
The annotation declares the service that needs to be invoked; - The business-layer abstraction method uses SpringMVC annotations to configure service addresses and parameters.
- Start class addition
@EnableFeignClients
Annotations activate the Feign component.
Create a project
PS: Service consumers access services through the Eureka registry, or through the Ribbon point-to-point direct connection mode.
We create an aggregation project and use the Eureka registry to illustrate Feign, first creating a POM parent project.
Add the dependent
pom.xml
<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>
<groupId>com.xxxx</groupId>
<artifactId>feign-demo</artifactId>
<version>1.0 the SNAPSHOT</version>
<! -- Inherits spring-boot-starter-parent dependency -->
<! -- Use inheritance mode, implement reuse, conform to inheritance can be used -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.4. RELEASE</version>
</parent>
<! -- Define the dependency version number centrally, but do not introduce the dependency version number, when the subproject uses the declared dependency, can not add the dependency version number, so that the unified management project uses the dependency version number.
<properties>
<! -- Spring Cloud hoxton.sr1 dependencies -->
<spring-cloud.version>Hoxton.SR1</spring-cloud.version>
</properties>
<! Project dependency management The parent project only declares dependencies. The child project needs to specify the required dependencies (omit version information).
<dependencyManagement>
<dependencies>
<! -- Spring Cloud 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>
</project>
Copy the code
Registry Eureka-Server
The registry is constructed in a cluster mode. In this paper, two nodes are eureka-Server and Eureka-Server02.
Create a project
The procedure for creating eureka-Server and Eureka-server02 is the same.
Add the dependent
The dependency configurations of eureka-server and Eureka-server02 are the same.
pom.xml
<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>
<groupId>com.xxxx</groupId>
<artifactId>eureka-server</artifactId>
<version>1.0 the SNAPSHOT</version>
<! -- Inheriting parent dependencies -->
<parent>
<groupId>com.xxxx</groupId>
<artifactId>feign-demo</artifactId>
<version>1.0 the SNAPSHOT</version>
</parent>
<! -- Project dependencies -->
<dependencies>
<! -- Netflix Eureka Server dependencies -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<! Spring Boot Web dependency -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<! -- Spring Boot test dependency -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>
Copy the code
The configuration file
Eureka – server application. Yml
server:
port: 8761 # port
spring:
application:
name: eureka-server # Application name (same under cluster)
Configure the Eureka Server registry
eureka:
instance:
hostname: eureka01 If the host name is not specified, the host name will be obtained based on the OS host name
prefer-ip-address: true Whether to register with an IP address
instance-id: ${spring.cloud.client.ip-address}:${server.port} # ip:port
client:
Set service registry address to point to another registry
service-url: The registered address exposed by the registry
defaultZone: http://localhost:8762/eureka/
Copy the code
Eureka – server02 application. Yml
spring:
application:
name: eureka-server # Application name (same under cluster)
# port
server:
port: 8762
Configure the Eureka Server registry
eureka:
instance:
hostname: eureka02 If the host name is not specified, the host name will be obtained based on the OS host name
prefer-ip-address: true Whether to register with an IP address
instance-id: ${spring.cloud.client.ip-address}:${server.port} # ip:port
client:
Set service registry address to point to another registry
service-url: The registered address exposed by the registry
defaultZone: http://localhost:8761/eureka/
Copy the code
Start the class
The startup classes of Eureka-server and Eureka-server02 are the same.
EurekaServerApplication.java
package com.xxxx;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
// Enable EurekaServer annotations
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) { SpringApplication.run(EurekaServerApplication.class, args); }}Copy the code
Service-provider Indicates the service provider
The service provider is constructed in cluster mode, and two nodes are used in this paper, namely service-provider and service-provider02.
Create a project
The creation process of service-provider and service-provider02 is the same.
Add the dependent
The dependency configurations of service-provider and service-provider02 are the same.
pom.xml
<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>
<groupId>com.xxxx</groupId>
<artifactId>service-provider</artifactId>
<version>1.0 the SNAPSHOT</version>
<! -- Inheriting parent dependencies -->
<parent>
<groupId>com.xxxx</groupId>
<artifactId>feign-demo</artifactId>
<version>1.0 the SNAPSHOT</version>
</parent>
<! -- Project dependencies -->
<dependencies>
<! -- Netflix eureka client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<! Spring Boot Web dependency -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<! -- Lombok dependency -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<! -- Spring Boot test dependency -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>
Copy the code
The configuration file
Service – the provider application. Yml
server:
port: 7070 # port
spring:
application:
name: service-provider # Application name (same under cluster)
Configure the Eureka Server registry
eureka:
instance:
prefer-ip-address: true Whether to register with an IP address
instance-id: ${spring.cloud.client.ip-address}:${server.port} # ip:port
client:
service-url: Set service registry address
defaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/
Copy the code
Service – provider02 application. Yml
spring:
application:
name: service-provider # Application name (same under cluster)
# port
server:
port: 7071
Configure the Eureka Server registry
eureka:
instance:
prefer-ip-address: true Whether to register with an IP address
instance-id: ${spring.cloud.client.ip-address}:${server.port} # ip:port
client:
service-url: Set service registry address
defaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/
Copy the code
Entity class
Service-provider and service-Provider02 have the same entity class.
Product.java
package com.xxxx.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Product implements Serializable {
private Integer id;
private String productName;
private Integer productNum;
private Double productPrice;
}
Copy the code
Write the service
The service codes of service-provider and service-provider02 are the same.
ProductService.java
package com.xxxx.service;
import com.xxxx.pojo.Product;
import java.util.List;
/** ** ** /
public interface ProductService {
/** ** query the product list **@return* /
List<Product> selectProductList(a);
}
Copy the code
ProductServiceImpl.java
package com.xxxx.service.impl;
import com.xxxx.pojo.Product;
import com.xxxx.service.ProductService;
import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.List;
/** ** ** /
@Service
public class ProductServiceImpl implements ProductService {
/** ** query the product list **@return* /
@Override
public List<Product> selectProductList(a) {
return Arrays.asList(
new Product(1.Huawei Mobile.1.5800D),
new Product(2."Lenovo Notebook".1.6888D),
new Product(3."Xiaomi Tablet".5.2020D)); }}Copy the code
Control layer
Service-provider and service-provider02 have the same control layer.
ProductController.java
package com.xxxx.controller;
import com.xxxx.pojo.Product;
import com.xxxx.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/product")
public class ProductController {
@Autowired
private ProductService productService;
/** ** query the product list **@return* /
@GetMapping("/list")
public List<Product> selectProductList(a) {
returnproductService.selectProductList(); }}Copy the code
Start the class
Service-provider and service-Provider02 have the same startup class.
ServiceProviderApplication.java
package com.xxxx;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
// Enable the EurekaClient annotation, which is enabled by default in the current version if the Eureka registry is configured
//@EnableEurekaClient
public class ServiceProviderApplication {
public static void main(String[] args) { SpringApplication.run(ServiceProviderApplication.class, args); }}Copy the code
Service-consumer
Create a project
Create a service-consumer service consumer project under the parent project.
Add the dependent
Service consumers add OpenFeign dependencies.
pom.xml
<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>
<groupId>com.xxxx</groupId>
<artifactId>service-consumer</artifactId>
<version>1.0 the SNAPSHOT</version>
<! -- Inheriting parent dependencies -->
<parent>
<groupId>com.xxxx</groupId>
<artifactId>feign-demo</artifactId>
<version>1.0 the SNAPSHOT</version>
</parent>
<! -- Project dependencies -->
<dependencies>
<! -- Netflix eureka client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<! -- Spring Cloud OpenFeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<! Spring Boot Web dependency -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<! -- Lombok dependency -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<! -- Spring Boot test dependency -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>
Copy the code
The configuration file
application.yml
server:
port: 9090 # port
spring:
application:
name: service-consumer # app name
Configure the Eureka Server registry
eureka:
client:
register-with-eureka: false # Whether to register yourself in the registry. Default is true
registry-fetch-interval-seconds: 10 # indicates how often the Eureka Client pulls registration information from the server. The default is 30 seconds
service-url: Set service registry address
defaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/
Copy the code
Entity class
Product.java
package com.xxxx.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Product implements Serializable {
private Integer id;
private String productName;
private Integer productNum;
private Double productPrice;
}
Copy the code
Order.java
package com.xxxx.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Order implements Serializable {
private Integer id;
private String orderNo;
private String orderAddress;
private Double totalPrice;
private List<Product> productList;
}
Copy the code
Consumer services
ProductService.java
package com.xxxx.service;
import com.xxxx.pojo.Product;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.List;
// Declare the service to be invoked
@FeignClient("service-provider")
public interface ProductService {
/** ** query the product list **@return* /
// Configure the service address and parameters to be invoked
@GetMapping("/product/list")
List<Product> selectProductList(a);
}
Copy the code
OrderService.java
package com.xxxx.service;
import com.xxxx.pojo.Order;
public interface OrderService {
/** * select order ** from primary key@param id
* @return* /
Order selectOrderById(Integer id);
}
Copy the code
OrderServiceImpl.java
package com.xxxx.service.impl;
import com.xxxx.pojo.Order;
import com.xxxx.service.OrderService;
import com.xxxx.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private ProductService productService;
/** * select order ** from primary key@param id
* @return* /
@Override
public Order selectOrderById(Integer id) {
return new Order(id, "order-001"."China".22788D, productService.selectProductList()); }}Copy the code
Control layer
OrderController.java
package com.xxxx.controller;
import com.xxxx.pojo.Order;
import com.xxxx.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private OrderService orderService;
/** * select order ** from primary key@param id
* @return* /
@GetMapping("/{id}")
public Order selectOrderById(@PathVariable("id") Integer id) {
returnorderService.selectOrderById(id); }}Copy the code
Start the class
ServiceConsumerApplication.java
package com.xxxx;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
// Enable the EurekaClient annotation, which is enabled by default in the current version if the Eureka registry is configured
//@EnableEurekaClient
// Enable the FeignClients annotation
@EnableFeignClients
public class ServiceConsumerApplication {
public static void main(String[] args) { SpringApplication.run(ServiceConsumerApplication.class, args); }}Copy the code
access
The current operating results are as follows:
Feign Load balancing
Feign encapsulates the Ribbon and naturally integrates load balancing functions. By default, the Ribbon uses polling. How do I modify a load balancing policy? This is consistent with the configuration explained in the previous section of the Ribbon.
global
Inject load balancing policy objects into the startup class or configuration class. All service requests use this policy.
@Bean
public RandomRule randomRule(a) {
return new RandomRule();
}
Copy the code
local
Modify the load balancing policy specified in the configuration file. Format: service application name. Ribbon. NFLoadBalancerRuleClassName
Load balancing policy
# service-provider specifies the name of the invoked service
service-provider:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
Copy the code
Feign Requests parameter transmission
GET
Accept request parameters using the @PathVariable annotation or the @requestParam annotation.
Service provider
ProductService.java
/** * select * from primary key@param id
* @return* /
Product selectProductById(Integer id);
Copy the code
ProductServiceImpl.java
/** * select * from primary key@param id
* @return* /
@Override
public Product selectProductById(Integer id) {
return new Product(id, "Refrigerator".1.2666D);
}
Copy the code
ProductController.java
package com.example.controller;
import com.example.pojo.Product;
import com.example.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/product")
public class ProductController {
@Autowired
private ProductService productService;
/** * select * from primary key@param id
* @return* /
@GetMapping("/{id}")
public Product selectProductById(@PathVariable("id") Integer id) {
returnproductService.selectProductById(id); }}Copy the code
Service consumer
ProductService.java
package com.example.service;
import com.example.pojo.Product;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import java.util.List;
// Declare the service to be invoked
@FeignClient("service-provider")
public interface ProductService {
/** * select * from primary key@return* /
@GetMapping("/product/{id}")
Product selectProductById(@PathVariable("id") Integer id);
}
Copy the code
OrderServiceImpl.java
package com.example.service.impl;
import com.example.pojo.Order;
import com.example.service.OrderService;
import com.example.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Arrays;
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private ProductService productService;
/** * select order ** from primary key@param id
* @return* /
@Override
public Order selectOrderById(Integer id) {
return new Order(id, "order-003"."China".2666D,
Arrays.asList(productService.selectProductById(5))); }}Copy the code
access
The results are as follows:
POST
Use the @requestBody annotation to receive request parameters.
Service provider
ProductService.java
/** * select * from primary key@param id
* @return* /
Product queryProductById(Integer id);
/** ** new merchandise **@param product
* @return* /
Map<Object, Object> createProduct(Product product);
Copy the code
ProductServiceImpl.java
/** * select * from primary key@param id
* @return* /
@Override
public Product queryProductById(Integer id) {
return new Product(id, "Refrigerator".1.2666D);
}
/** ** new merchandise **@param product
* @return* /
@Override
public Map<Object, Object> createProduct(Product product) {
System.out.println(product);
return new HashMap<Object, Object>() {{
put("code".200);
put("message"."New success");
}};
}
Copy the code
ProductController.java
package com.example.controller;
import com.example.pojo.Product;
import com.example.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/product")
public class ProductController {
@Autowired
private ProductService productService;
/** * select * from primary key@param id
* @return* /
@PostMapping("/single")
public Product queryProductById(@RequestBody Integer id) {
return productService.queryProductById(id);
}
/** ** new merchandise **@param product
* @return* /
@PostMapping("/save")
public Map<Object, Object> createProduct(@RequestBody Product product) {
returnproductService.createProduct(product); }}Copy the code
The article is reprinted with music bytes