如题,本篇我们介绍下spring cloud中的路由组件zuul。
zuul是什么?
在微服务架构中,需要几个关键的组件,服务注册与发现、服务消费、负载均衡、断路器、智能路由、配置管理等,zuul就是起到路由网关功能的组件。
zuul具有以下的功能 :
Authentication
Insights
Stress Testing
Canary Testing
Dynamic Routing
Service Migration
Load Shedding
Security
Static Response handling
Active/Active traffic management
我们新建两个maven工程sim-serviceA 、 sim-serviceB ,再建立zuul网关工程sim-zuul 。
sim-serviceA 工程 :
application.yml
server:
port: 6001
context-path: /
#devtool 热加载工具
spring:
devtools:
restart:
enabled: true
exclude: resources/**
#spring应用名称 、实例id配置
application:
name: sim-serviceA
eureka:
instance:
hostname: localhost #eureka客户端主机实例名称
prefer-ip-address: true
client:
service-url:
defaultZone: http://localhost:8761/eureka
pom.xml中引入
<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>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
springboot启动类
package com.tingcream.simServiceA;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@EnableEurekaClient
@SpringBootApplication
public class SimServiceAApp {
public static void main(String[] args) {
SpringApplication.run(SimServiceAApp.class, args);
}
}
ServiceAController.java
package com.tingcream.simServiceA.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ServiceAController {
@Value("${spring.application.name}")
private String applicationName;
@Value("${server.port}")
private String port ;
@GetMapping("/")
public String index(){
return "ServiceAController: 你好,我是"+applicationName+",服务端口:"+port;
}
@GetMapping("/hi")
public String hi(String name){
return "ServiceAController: hi "+name+",我是"+applicationName+",服务端口:"+port;
}
}
sim-serviceB 工程 :
application.yml
server:
port: 6002
context-path: /
#devtool 热加载工具
spring:
devtools:
restart:
enabled: true
exclude: resources/**
#spring应用名称 、实例id配置
application:
name: sim-serviceB
eureka:
instance:
hostname: localhost #eureka客户端主机实例名称
prefer-ip-address: true
client:
service-url:
defaultZone: http://localhost:8761/eureka
pom.xml中引入
<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>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
springboot启动类
package com.tingcream.simServiceB;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@EnableEurekaClient
@SpringBootApplication
public class SimServiceBApp {
public static void main(String[] args) {
SpringApplication.run(SimServiceBApp.class, args);
}
}
ServiceBController.java
package com.tingcream.simServiceB.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ServiceBController {
@Value("${spring.application.name}")
private String applicationName;
@Value("${server.port}")
private String port ;
@GetMapping("/")
public String index(){
return "ServiceBController: 你好,我是"+applicationName+",服务端口:"+port;
}
@GetMapping("/hi")
public String hi(String name){
return "ServiceBController: hi "+name+",我是"+applicationName+",服务端口:"+port;
}
}
sim-zuul工程:
application.yml
server:
port: 80
context-path: /
spring:
application:
name: sim-zuul
eureka:
instance:
hostname: localhost #eureka客户端主机实例名称
prefer-ip-address: true
client:
service-url:
defaultZone: http://localhost:8761/eureka
zuul:
ignoredServices: sim-provider
routes:
serviceA:
path: /serviceA/**
serviceId: sim-serviceA
serviceB:
path: /serviceB/**
serviceId: sim-serviceB
pom.xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</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>
springboot启动类
package com.tingcream.simZuul;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@EnableZuulProxy
@EnableEurekaClient
@SpringBootApplication
public class SimZuulApp {
public static void main(String[] args) {
SpringApplication.run(SimZuulApp.class, args);
}
}
依次启动sim-eureka 、sim-zuul 、 sim-serviceA 、 sim-serviceB,访问http://localhost/8761
注: 由于使用到了zuul网关(提供外部用户访问的入口),我们将sim-zuul的端口设置为了80,将sim-consumer应用的端口修改为了6003
zuul有一些默认的路由配置规则,如果映射规则我们什么都不写,zuul也给我们提供了一套默认的配置规则,默认配置规则如下:
zuul.routes.serviceXXX.path=/serviceXXX/**
zuul.routes.serviceXXX.serviceId=serviceXXX
因此,即使我们没有配置 sim-consumer服务的路由规则,当我们请求 http://localhost/sim-consumer/student/list 也是可以访问成功的。还有点需要注意的是,zuul默认会给所有注册到eureka server上的应用配置默认的映射路由。而sim-provider是我们内部系统的一个微服务,是不能让外部用户直接访问,因而可以使用zuul.ignoredServices= xxx 来忽略掉特定服务的路由映射。如果使用zuul.ignoredServices= '*' 则表示忽略所有的微服务的路由映射,设置后我们需要再手动添加需要映射的服务。
------------------
zuul中的过滤器 ,例如自定义过滤器MyFilter,对所有请求进行拦截,判断是否有传token参数。
package com.tingcream.simZuul;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Component;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
@Component
public class MyFilter extends ZuulFilter{
@Override
public Object run() throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
System.out.println("请求方法:"+request.getMethod());
System.out.println("请求URL:"+request.getRequestURL().toString());
Object accessToken = request.getParameter("token");//获取param中 token, token不能为空
if(accessToken == null) {
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(401);
try {
ctx.getResponse().getWriter().write("token is empty");
}catch (Exception e){
e.printStackTrace();
}
return null;
}
System.out.println("token值为:"+accessToken);
System.out.println("zuul 中 MyFilter: 过滤器执行通过 ");
return null;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public int filterOrder() {
return 0;
}
@Override
public String filterType() {
return "pre";
}
}
filterType返回一个字符串代表过滤器的类型,在zuul中定义了四种不同生命周期的过滤器类型,具体如下:
pre:路由之前
routing:路由之时
post: 路由之后
error:发送错误调用
filterOrder:过滤的顺序
shouldFilter: 是否要过滤,true过滤,false不过滤
run:过滤器的具体逻辑。
浏览器访问 http://localhost/serviceA/hi?name=小明&token=1212 ,http://localhost/serviceB/?token=1111 访问成功。如果没加token参数则无法成功访问 。
关于zuul的更多配置信息,可以参见官网文档
http://cloud.spring.io/spring-cloud-static/Edgware.SR4/single/spring-cloud.html
或者 http://projects.spring.io/spring-cloud/spring-cloud.html#_router_and_filter_zuul