由前面springcloud的例子可以实现服务发现与服务注册,但是对于服务消费者来说,调用远程服务则需要通过RestTmplate解析URL从而获取相应的服务。这种方式针对参数较多的情况下代码冗余度过高,不易于维护。因此,本文主要讲解springcloud中用于解决这种问题的组件--Feign。
1.Feign的工作原理
Feign主要将远程服务通过代理的形式与本地的Feign客户端进行关联起来,使得服务消费者调用远程服务如同调用本地方法一样。
1.启动类中通过@EnableFeignClients注解对FeignClient进行加载处理。
2.将所有包含@FeignClient的类注入到Spring IOC容器中。当Feign接口中的方法被调用时,通过JDK代理的方式,来生成具体的RequestTemplate。当生成代理时,Feign会为每个接口方法创建一个RequestTemplate对象,该对象封装了HTTP请求需要的全部信息。
3.由RequestTemplate生成的Request,然后把Request交给Client去处理(Client可以是JDK自带的URL connection、Apache的HttpClient、Okhttp)。最后Client被封装到LoadBalanceClient类,这个类结合Ribbon负载均衡发起服务之间的调用。
2.Feign使用例子
接下来通过一个实际例子来讲解Feign的作用。该案例涉及三个工程,分别是Eureka工程、服务提供者工程、服务消费者工程。接下来分别介绍三个工程。
2.1 Eureka工程
Eureka工程则使用 服务注册与发现一文中的Eureka的工程。
2.2服务提供者工程
服务提供者则使用 服务提供者与服务消费者一文中的服务提供者,只需要将该服务提供者工程中的application.yaml文件用以下内容替换,将该服务注册到Eureka服务注册中心。
server:
port: 8087
spring:
application:
name: microservice-student
jpa:
generate-ddl: false
show-sql: true
hibernate:
ddl-auto: none
datasource:
platform: h2
schema: classpath:schema.sql
data: classpath:data.sql
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/ #Eureka Server
instance:
prefer-ip-address: true #将自己的ip注册到Eureka Server上
logging:
level:
root: INFO
org.hibernate: INFO
org.hibernate.type.descriptor.sql.BasicBinder: TRACE
org.hibernate.type.descriptor.sql.BasicExtractor: TRACE
2.3服务消费者工程
该工程也是基于服务注册与发现一文中的服务消费者的基础上针对引入Feign做的修改。
1.pom文件
<?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 https://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>2.1.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.carson</groupId>
<artifactId>microservice-teacher-feign</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>microservice-teacher-feign</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR2</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</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>
其中 spring-cloud-starter-openfeign是Feign组件的依赖包。
2.POJO类
Student类:
public class Student {
private Long studentid;
private String studentname;
private String grade;
private Integer age;
private String sex;
public Long getStudentid() {
return studentid;
}
public void setStudentid(Long studentid) {
this.studentid = studentid;
}
public String getStudentname() {
return studentname;
}
public void setStudentname(String studentname) {
this.studentname = studentname;
}
public String getGrade() {
return grade;
}
public void setGrade(String grade) {
this.grade = grade;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
3.FeignClient类:
@FeignClient(name="microservice-student")
@Repository
public interface StudentFeignClient {
@RequestMapping(value="/{studentid}" ,method = RequestMethod.GET)
public Student findById(@PathVariable("studentid") Long id);
}
其中@FeignClient注解是将远程服务代理到本地客户端,name指服务注册中心对应的服务名称。@RequestMapping注解则对应远程服务相应方法的注解。 @Repository注解则是springmvc注解,不要忽略,因为该客户端在Controller类中需要使用。
4.TeacherController
@RestController
public class TeacherController {
@Autowired
private StudentFeignClient studentFeignClient;
@RequestMapping(value="/student/{studentid}",method = RequestMethod.GET)
public Student findStudentInfo(@PathVariable Long studentid){
return studentFeignClient.findById(studentid);
}
}
该controller中通过引入FeignClient对象,可以像调用本地方法一样调用服务。
5.启动类
@SpringBootApplication
@EnableFeignClients
public class MicroserviceTeacherFeignApplication {
public static void main(String[] args) {
SpringApplication.run(MicroserviceTeacherFeignApplication.class, args);
}
}
@EnableFeignClients是引入Feign组件的重要注解,这是意味着服务启动,就会全局扫描包含@FeignClient的类。
2.4 启动
依次启动Eureka工程,服务提供者工程和服务消费者工程。然后访问:http://localhost:8071/student/10003 得到如下图所示结果。说明Feign组件可以实现声明式Rest调用。