如题,本篇我们介绍下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

springcloud项目相关组件依赖版本推荐_路由

注: 由于使用到了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参数则无法成功访问 。 

springcloud项目相关组件依赖版本推荐_spring_02

 

关于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