feign和zuul组件的使用方法以及使用SpringCloud的feign和zuul组件搭建一个简单的微服务
feign和zuul组件的使用方法以及使用SpringCloud的feign和zuul组件搭建一个简单的微服务
才学习了SpringCloud 微服务的基本组件,做个笔记记录下。
1. 项目环境
IDEA 2019 3.1、jdk1.8、mysql 8.0.16、SpringBoot 2.2.1.RELEASE、SpringCloud Hoxton.SR6
SpringCloud的版本一定要适配SpringBoot的版本,如果不清楚,可以去官网查询。
https://cloud.spring.io/spring-cloud-static/Hoxton.SR5/reference/html/
2. 需求介绍
这个微服务是一个简单的查询,通过id查询用户的信息。系统架构如图:
3. 项目结构
3. 1 eureka-server:eureka注册中心,用于服务的注册和发现
3.1.1 导入依赖
创建项目后,导入依赖。只需要在pom文件中导入springcloud和eureka服务端组件即可,由于pom依赖篇幅较大,所以所有的pom依赖都放在了最后第5节。
3.1.2 application配置文件
eureka注册中心,需要配置eureka服务地址,如果有多个注册中心,则${server.port}可以书写为其他注册中心的端口,进行互相注册。
server:
port: 10086 # 微服务的端口号
spring:
application:
name: eureka-server # 将来作为微服务名称注入到eureka容器中
eureka:
client:
service-url:
defaultZone: http://localhost:${server.port}/eureka # eureka服务地址
server:
eviction-interval-timer-in-ms: 5000 # 默认剔除无效连接时间
enable-self-preservation: false # 关闭自我保护
3.1.3 启动类
注册中心可以什么都不用书写,只需要开启注册中心服务
@SpringBootApplication
@EnableEurekaServer //开启注册中心
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
3.2 service-consumer:服务的消费方
3.2.1 导入依赖
这个的pom文件和注册中心的类似,也需要进行版本管理。但作为服务,需要导入eureka的客户端。因为这个微服务使用了fegin进行服务的远程调用和熔断,所以需要导入熔断组件
3.2.2 application配置文件
作为eureka服务的注册方,需要进行服务的拉取,并开启feign的熔断机制
server:
port: 80
spring:
application:
name: service-consume
eureka:
client:
service-url:
defaultZone: http://localhost:10086/eureka
fetch-registry: true
registry-fetch-interval-seconds: 5 # 每次拉取服务列表时间
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 6000 # 设置hystrix的超时时间为6000ms
feign: # 开启feign的熔断机制
hystrix:
enable: true
3.2.3 启动类
@SpringBootApplication
@EnableDiscoveryClient //开启服务发现
@EnableCircuitBreaker //开启熔断机制,可使用@SpringCloudApplication注解代替上面三个注解:
@EnableFeignClients //开启feign功能,其集成了ribbon和hystrix
public class ServiceConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceConsumerApplication.class, args);
}
}
3.2.4 处理代码
controller:
@Controller
@RequestMapping("user")
public class UserController {
@Autowired
UserClient userClient; //注入一个feign对象
@RequestMapping("{uid}")
@ResponseBody
public String getUserById(@PathVariable("uid") String uid){
return userClient.getUserById(uid); //使用feign对象来进行远程调用
}
}
UserClient:一个接口,Feign会通过动态代理,帮我们生成实现类,@FeignClient
,声明这是一个Feign客户端。接口中的定义方法,完全采用SpringMVC的注解,Feign会根据注解帮我们生成URL,并访问获取结果改造原来的调用逻辑,调用UserClient接口。所以将Controller中的方法复制过来。
//value:指定服务id会自动解析成路径和端口号
//fallbcak:指定熔断方法所在的类
@FeignClient(value = "service-provide",fallback = UserClientCallBack.class)
public interface UserClient {
@RequestMapping("/user/{uid}")
public String getUserById(@PathVariable("uid") String uid);
}
UserClientCallBack:当在application配置文件中开启熔断功能时,就可以书写熔断方法了。只需要继承上面的接口即可
@Component
public class UserClientCallBack implements UserClient{
@Override
public String getUserById(String uid) {
return new String("请求超时,请稍后再试!");
}
}
3.3 service-provide:服务的提供方
和传统SSM项目一样的书写方式
3.3.1 导入依赖
由于提供方需要连接数据库,所以需要mysql的驱动;还要有注册到eureka中,所以需要eureka的客户端
3.3.2 application配置文件
服务提供者与服务消费者此配置类似。
server:
port: 8081
spring:
datasource: # 数据库配置
url: jdbc:mysql:///store?serverTimezone=UTC
username: root
password: 12345
application:
name: service-provide # 配置微服务名称
eureka:
client:
service-url:
defaultZone: http://localhost:10086/eureka # 配置服务器地址
instance:
lease-renewal-interval-in-seconds: 5 # 心跳间隔时间
lease-expiration-duration-in-seconds: 15 # 心跳过期时间
3.3.3 启动类
需要扫描pojo和mapper类,并开启服务发现
@SpringBootApplication
@MapperScan("com.hay.serviceprovide.mapper")
@EnableDiscoveryClient //开启服务发现
public class ServiceProvideApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceProvideApplication.class, args);
}
}
3.3.4处理代码
处理代码比较简单,就是一个查询根据id查询用户的请求。service和mapper代码和平时书写的没有区别所以省略掉。
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
UserService userService;
@RequestMapping("{uid}")
@ResponseBody
public User getUserById(@PathVariable("uid") String uid) {
return userService.getUserById(uid);
}
}
3.4 zuul-server:zuul网关,用于请求的路由和过滤
3.4.1 依赖文件
需要注册到eureka中,并导入zuul组件依赖
3.4.2 application配置文件
需要将服务路径注册到zuul网关,以便zuul进行路由和过滤。
其中配置服务请求路径方法有3种。分别是:
-
zuul: routes: 服务名: path: /xxx/** serviceId: 服务名
-
zuul: routes: 服务名: path: /xxx/**
-
zuul: routes: 服务名: /xxx/**
application.yml的配置:
server:
port: 10001
spring:
application:
name: api-gateway
zuul:
routes:
service-provide: /provide/** # 配置服务请求路径(服务提供方),将/provide/**路径映射到服务名为service-provide的服务中进行处理
service-consume: /consume/** # 配置服务请求路径(服务消费方)
prefix: /api # 设置zuul网关请求前缀
eureka:
client:
service-url:
defaultZone: http://localhost:10086/eureka
3.4.3 启动类
以为zuul也是一个服务,所以需要注册到eureka中
@SpringBootApplication
@EnableZuulProxy //开启zuul组件
@EnableDiscoveryClient //开启eureka客户端
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
}
3.4.4 处理代码
Zuul作为网关的其中一个重要功能,就是实现请求的鉴权。而这个动作我们往往是通过Zuul提供的过滤器来实现的。
ZuulFilter:是过滤器的*父类。在这里我们看一下其中定义的4个最重要的方法:
@Component
public class UserFilter extends ZuulFilter {
/**
* 过滤器的类型:
* pre(路由前) routing(路由时) post(路由后) error(错误)
* @return
*/
@Override
public String filterType() {
return "pre";
}
// 过滤器的执行级别
@Override
public int filterOrder() {
return 10;
}
// 是否开启此过滤器
@Override
public boolean shouldFilter() {
return true;
}
// 处理代码
@Override
public Object run() throws ZuulException {
// 获取Zuul的上下文对象
RequestContext context = RequestContext.getCurrentContext();
// 获取Request请求对象
HttpServletRequest request = context.getRequest();
// 获取参数
String token = request.getParameter("token");
// 如果没有token
if (StringUtils.isBlank(token)){
// 不转发请求
context.setSendZuulResponse(false);
// 设置状态码401 表示未认证
context.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
// 设置响应,打印字符串
context.setResponseBody("request error 401!");
}
return null;
}
}
-
shouldFilter
:返回一个Boolean
值,判断该过滤器是否需要执行。返回true执行,返回false不执行。 -
run
:过滤器的具体业务逻辑。 -
filterType
:返回字符串,代表过滤器的类型。包含以下4种:-
pre
:请求在被路由之前执行 -
route
:在路由请求时调用 -
post
:在route和errror过滤器之后调用 -
error
:处理请求时发生错误调用
-
-
filterOrder
:通过返回的int值来定义过滤器的执行顺序,数字越小优先级越高。
4. 测试
运行4个项目
启动成功后,在浏览器中输入:http://localhost:10001/api/consume/user/1
解释:(localhost:zuul端口/zuul设置的前缀/zuul中配置的服务名映射的路径/请求的路径/参数)
访问会失败,因为zuul的过滤器将此请求过滤了,请求参数种没有token参数,所以打印了zuul中ZuulFilter类里面run方法返回的字符串。
所以,加上token参数
查询成功了!!!!
同时也不仅仅可以通过服务消费者进行访问,也可以直接访问服务提供者,如输入http://localhost:10001/api/provide/user/1?token=1
也访问成功了!
我们以访问10086端口,查看eureka注册中心注册了哪些微服务
完成!!!!!
5. 各项目的pom依赖
5.1 eureka-server
<?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.2.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.hay.eureka</groupId>
<artifactId>eureka-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>eureka-server</name>
<description>Demo project for Spring Boot</description>
<!-- 依赖的版本号 -->
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR6</spring-cloud.version>
</properties>
<!-- 引入的依赖 -->
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</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>
<!-- 统一版本管理 -->
<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>
5.2 service-consume
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR6</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- eureka用户端 -->
<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-hystrix</artifactId>
</dependency>
<!-- feign组件 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</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>
5.3 service-provide
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR6</spring-cloud.version>
</properties>
<dependencies>
<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>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</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>
5.4 zuul-server
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR6</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</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>
<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>