Spring Cloud Getting Started tutorial (VI): with a declarative REST client Feign call as the remote HTTP service
First briefly explain what is declarative achieve?
Do one thing, you need to know three elements, where, what, how. That is what (where) do (what) where what way (how). When to do it (when) we included in the scope of how.
1) programmatically implemented: each element (where, what, how) are required to achieve a specific code is represented. The traditional way is generally programmatic implementation, developers need to be concerned about every business logic
2) declarative realization: only need to declare what to do and where (where) (what), without concern for how (how). Spring AOP is a kind of declarative implementations, such as Web site to check whether the login page logic of development, you can just needs to be done to check whether the user logs on (what) a statement by AOP configuration page loads (where), without concern for how to check whether the user Login (how). How to check this logic to achieve the AOP mechanism, AOP login to check implementation mechanism is being developed with the logic of the page itself is irrelevant.
In Spring Cloud Netflix stack, each micro services are exposed to their service in the form of an HTTP interface, so when calling a remote service must use the HTTP client. Feign Spring is a declarative REST client Cloud provides. REST interface can be called micro distal services provided by Feign access. Now we will use Feign to call SERVICE-HELLOWORLD exposed REST interface to get to the "Hello World" message. When using Feign, Spring Cloud integrates Ribbon Eureka to provide load balancing and HTTP client.
Here we are using Feign way to call the Hello World service cluster.
1. Create a Maven project, add spring-cloud-starter-feign dependent
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency>
Complete 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>
<groupId>com.chry</groupId>
<artifactId>springcloud.helloworld.feign.client</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>springcloud.helloworld.feign.client</name>
<description>Demo Feigh client application</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<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.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</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>Dalston.RC1</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>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
2. Create a startup class, you need to use it together with @EnableFeignClients comment Feign , automatic service discovery using open @EnableDiscoveryClient
1 package springcloud.helloworld.feign.service; 2 3 import org.springframework.boot.SpringApplication; 4 import org.springframework.boot.autoconfigure.SpringBootApplication; 5 import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 6 import org.springframework.cloud.netflix.feign.EnableFeignClients; 7 8 @SpringBootApplication 9 @EnableDiscoveryClient 10 @EnableFeignClients 11 public class ServiceFeignApplication { 12 public static void main(String[] args) { 13 SpringApplication.run(ServiceFeignApplication.class, args); 14 } 15 }
3. Add Profile application.yml, using port 8902, the name is defined as a service-feign, and registered with the eureka service center
1 eureka: 2 client: 3 serviceUrl: 4 defaultZone: http://localhost:8761/eureka/ 5 server: 6 port: 8902 7 spring: 8 application: 9 name: service-feign
4. Define Feign: an interface class annotated with @FeignClient,
@FeignClient
Feign components used to notify the agent interface (no need to write interface), the user can directly through the @Autowired
injection; This interface defines SERVICE-HELLOWORLD service (auto-discovery mechanism will locate specific URL through service centers) need to call by value ; @RequestMapping defined URL sERVICE-HELLOWORLD Feign need to access the service (in this example, the root "/")
1 package springcloud.helloworld.feign.service; 2 3 import org.springframework.cloud.netflix.feign.FeignClient; 4 import org.springframework.web.bind.annotation.RequestMapping; 5 import org.springframework.web.bind.annotation.RequestMethod; 6 7 @FeignClient(value = "SERVICE-HELLOWORLD") 8 public interface HelloWorldService { 9 @RequestMapping(value = "/",method = RequestMethod.GET) 10 String sayHello(); 11 }
Spring Cloud application at startup, Feign scans the marked @FeignClient
annotation interface, proxy generation, and registered to Spring container. Feign created for each interface, a method to produce a proxy RequetTemplate
object that encapsulates all the information required for the HTTP request, the request parameter name, the request method and other information is determined in this process, the template of the reflected here Feign
5. Define a WebController.
Before implantation defined by the @FeignClient generated bean,
sayHello () is mapped to http: // localhost: 8902 / hello, here, I modified the mapping Hello World Service, the root "/" modified into "/ hello".
1 package springcloud.helloworld.feign.service; 2 3 import org.springframework.beans.factory.annotation.Autowired; 4 import org.springframework.web.bind.annotation.RequestMapping; 5 import org.springframework.web.bind.annotation.RequestMethod; 6 import org.springframework.web.bind.annotation.RestController; 7 8 @RestController 9 public class WebController { 10 @Autowired HelloWorldService helloWorldFeignService; 11 @RequestMapping(value = "/hello",method = RequestMethod.GET) 12 public String sayHello(){ 13 return helloWorldFeignService.sayHello(); 14 } 15 }
6. Start Feign application, visit http: // localhost: 8902 / hello, repeatedly refresh, and you can see the previous chapter Ribbon inside the application, the output of two alternating Hello World service. DESCRIPTION Feign access services, Spring Cloud has been used by default Ribbon load balancing.
6. In the Apache HTTP Client Feign
Feign using default JDK is native URLConnection
HTTP request, the pool is not connected, but are connected to each address a long gwai remains, namely the use of HTTP persistence connection
. We may be replaced with the Apache HTTP Client Feign original http client, in order to gain control and performance is closely related to the connection pool timeout time. Spring Cloud from Brixtion.SR5
version began to support this alternative, the first statement in Apache HTTP Client and project feign-httpclient
-dependent:
1 <!-- 使用Apache HttpClient替换Feign原生httpclient --> 2 <dependency> 3 <groupId>org.apache.httpcomponents</groupId> 4 <artifactId>httpclient</artifactId> 5 </dependency> 6 <dependency> 7 <groupId>com.netflix.feign</groupId> 8 <artifactId>feign-httpclient</artifactId> 9 <version>${feign-httpclient}</version> 10 </dependency>
Then add:application.properties
feign.httpclient.enabled=true
7. Feign the Encoder, Decoder and ErrorDecoder
The method signature Feign method parameters into request parameters into the target sequence during the HTTP request, by an encoder (Encoder) completed. Similarly, the HTTP response data is deserialized into Java objects by a decoder (Decoder) completed. By default, will be marked with Feign @RequestParam
conversion parameter annotated to add a string to the URL, the parameter will not be converted by the annotation to Jackson json into the request body. Note that if you @RequetMapping
are method
designated as the request mode POST
, then all solutions of unlabeled parameters will be ignored, for example:
@RequestMapping(value = "/group/{groupId}", method = RequestMethod.GET) void update(@PathVariable("groupId") Integer groupId, @RequestParam("groupName") String groupName, DataObject obj);
At this time, because the declaration is no request body GET request, the obj
parameter is ignored.
In Spring Cloud environment, Feign the Encoder will not add parameters used to encode annotation. If you customize the Encoder, then only the coding obj
parameter will call your Encoder. For Decoder, the default will be entrusted to the SpringMVC MappingJackson2HttpMessageConverter
decode class. Only when the state code absence ErrorDecoder between 200 and 300 will be called. ErrorDecoder action is to return a response message exception that can be captured at the local call Feign interface according to HTTP. We have come to the Feign by ErrorDecoder interface to throw a business exception for the caller process.
First briefly explain what is declarative achieve?
Do one thing, you need to know three elements, where, what, how. That is what (where) do (what) where what way (how). When to do it (when) we included in the scope of how.
1) programmatically implemented: each element (where, what, how) are required to achieve a specific code is represented. The traditional way is generally programmatic implementation, developers need to be concerned about every business logic
2) declarative realization: only need to declare what to do and where (where) (what), without concern for how (how). Spring AOP is a kind of declarative implementations, such as Web site to check whether the login page logic of development, you can just needs to be done to check whether the user logs on (what) a statement by AOP configuration page loads (where), without concern for how to check whether the user Login (how). How to check this logic to achieve the AOP mechanism, AOP login to check implementation mechanism is being developed with the logic of the page itself is irrelevant.
In Spring Cloud Netflix stack, each micro services are exposed to their service in the form of an HTTP interface, so when calling a remote service must use the HTTP client. Feign Spring is a declarative REST client Cloud provides. REST interface can be called micro distal services provided by Feign access. Now we will use Feign to call SERVICE-HELLOWORLD exposed REST interface to get to the "Hello World" message. When using Feign, Spring Cloud integrates Ribbon Eureka to provide load balancing and HTTP client.
Here we are using Feign way to call the Hello World service cluster.
1. Create a Maven project, add spring-cloud-starter-feign dependent
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency>
Complete 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>
<groupId>com.chry</groupId>
<artifactId>springcloud.helloworld.feign.client</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>springcloud.helloworld.feign.client</name>
<description>Demo Feigh client application</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<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.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</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>Dalston.RC1</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>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
2. Create a startup class, you need to use it together with @EnableFeignClients comment Feign , automatic service discovery using open @EnableDiscoveryClient
1 package springcloud.helloworld.feign.service; 2 3 import org.springframework.boot.SpringApplication; 4 import org.springframework.boot.autoconfigure.SpringBootApplication; 5 import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 6 import org.springframework.cloud.netflix.feign.EnableFeignClients; 7 8 @SpringBootApplication 9 @EnableDiscoveryClient 10 @EnableFeignClients 11 public class ServiceFeignApplication { 12 public static void main(String[] args) { 13 SpringApplication.run(ServiceFeignApplication.class, args); 14 } 15 }
3. Add Profile application.yml, using port 8902, the name is defined as a service-feign, and registered with the eureka service center
1 eureka: 2 client: 3 serviceUrl: 4 defaultZone: http://localhost:8761/eureka/ 5 server: 6 port: 8902 7 spring: 8 application: 9 name: service-feign
4. Define Feign: an interface class annotated with @FeignClient,
@FeignClient
Feign components used to notify the agent interface (no need to write interface), the user can directly through the @Autowired
injection; This interface defines SERVICE-HELLOWORLD service (auto-discovery mechanism will locate specific URL through service centers) need to call by value ; @RequestMapping defined URL sERVICE-HELLOWORLD Feign need to access the service (in this example, the root "/")
1 package springcloud.helloworld.feign.service; 2 3 import org.springframework.cloud.netflix.feign.FeignClient; 4 import org.springframework.web.bind.annotation.RequestMapping; 5 import org.springframework.web.bind.annotation.RequestMethod; 6 7 @FeignClient(value = "SERVICE-HELLOWORLD") 8 public interface HelloWorldService { 9 @RequestMapping(value = "/",method = RequestMethod.GET) 10 String sayHello(); 11 }
Spring Cloud application at startup, Feign scans the marked @FeignClient
annotation interface, proxy generation, and registered to Spring container. Feign created for each interface, a method to produce a proxy RequetTemplate
object that encapsulates all the information required for the HTTP request, the request parameter name, the request method and other information is determined in this process, the template of the reflected here Feign
5. Define a WebController.
Before implantation defined by the @FeignClient generated bean,
sayHello () is mapped to http: // localhost: 8902 / hello, here, I modified the mapping Hello World Service, the root "/" modified into "/ hello".
1 package springcloud.helloworld.feign.service; 2 3 import org.springframework.beans.factory.annotation.Autowired; 4 import org.springframework.web.bind.annotation.RequestMapping; 5 import org.springframework.web.bind.annotation.RequestMethod; 6 import org.springframework.web.bind.annotation.RestController; 7 8 @RestController 9 public class WebController { 10 @Autowired HelloWorldService helloWorldFeignService; 11 @RequestMapping(value = "/hello",method = RequestMethod.GET) 12 public String sayHello(){ 13 return helloWorldFeignService.sayHello(); 14 } 15 }
6. Start Feign application, visit http: // localhost: 8902 / hello, repeatedly refresh, and you can see the previous chapter Ribbon inside the application, the output of two alternating Hello World service. DESCRIPTION Feign access services, Spring Cloud has been used by default Ribbon load balancing.
6. In the Apache HTTP Client Feign
Feign using default JDK is native URLConnection
HTTP request, the pool is not connected, but are connected to each address a long gwai remains, namely the use of HTTP persistence connection
. We may be replaced with the Apache HTTP Client Feign original http client, in order to gain control and performance is closely related to the connection pool timeout time. Spring Cloud from Brixtion.SR5
version began to support this alternative, the first statement in Apache HTTP Client and project feign-httpclient
-dependent:
1 <!-- 使用Apache HttpClient替换Feign原生httpclient --> 2 <dependency> 3 <groupId>org.apache.httpcomponents</groupId> 4 <artifactId>httpclient</artifactId> 5 </dependency> 6 <dependency> 7 <groupId>com.netflix.feign</groupId> 8 <artifactId>feign-httpclient</artifactId> 9 <version>${feign-httpclient}</version> 10 </dependency>
Then add:application.properties
feign.httpclient.enabled=true
7. Feign the Encoder, Decoder and ErrorDecoder
The method signature Feign method parameters into request parameters into the target sequence during the HTTP request, by an encoder (Encoder) completed. Similarly, the HTTP response data is deserialized into Java objects by a decoder (Decoder) completed. By default, will be marked with Feign @RequestParam
conversion parameter annotated to add a string to the URL, the parameter will not be converted by the annotation to Jackson json into the request body. Note that if you @RequetMapping
are method
designated as the request mode POST
, then all solutions of unlabeled parameters will be ignored, for example:
@RequestMapping(value = "/group/{groupId}", method = RequestMethod.GET) void update(@PathVariable("groupId") Integer groupId, @RequestParam("groupName") String groupName, DataObject obj);
At this time, because the declaration is no request body GET request, the obj
parameter is ignored.
In Spring Cloud environment, Feign the Encoder will not add parameters used to encode annotation. If you customize the Encoder, then only the coding obj
parameter will call your Encoder. For Decoder, the default will be entrusted to the SpringMVC MappingJackson2HttpMessageConverter
decode class. Only when the state code absence ErrorDecoder between 200 and 300 will be called. ErrorDecoder action is to return a response message exception that can be captured at the local call Feign interface according to HTTP. We have come to the Feign by ErrorDecoder interface to throw a business exception for the caller process.