学习springCloud(九)之zuul路由

前面介绍了微服务都是通过Eureka找到的,但是在很多开发中为了规范微服务的使用,提供有一个处理控制器Zuul

Zuul其实是一个API网关,类似于设计模式里面的Facade门面模式,他的存在就像是整个微服务的门面,所有的外部客户端访问都需要经过它来进行调度与过滤

zuul的基本使用

新建立一个模块【springcloud-zuul-gateway】
【springcloud-zuul-gateway】 的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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>springcloud</artifactId>
        <groupId>enjoy</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>springcloud-zuul-gateway</artifactId>

    <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-actuator</artifactId>
        </dependency>
    </dependencies>
</project>

【springcloud-zuul-gateway】修改application.yml文件

server:
  port: 9501

eureka:
  client: # 客户端进行Eureka注册的配置
    service-url:
      defaultZone: http://admin:enjoy@eureka1:7001/eureka,http://admin:enjoy@eureka2:7002/eureka,http://admin:enjoy@eureka3:7003/eureka
    register-with-eureka: false

spring:
  application:
    name:  springcloud-zuul-gateway

【springcloud-zuul-gateway】 创建启动类

package cn.enjoy;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.web.bind.annotation.RequestMapping;

@SpringBootApplication
@EnableZuulProxy
public class ZuulApp {
    public static void main(String[] args) {
        SpringApplication.run(ZuulApp.class,args);
    }
}

启动: 在这里插入图片描述

发现启动报错,其实这是因为zuul目前对springboot2.1.2支持并不好,这也是zuul最近一直被人诟病的地方。

【springcloud】修改父工程pom文件,降低springboot版本为2.0.7.RELEASE

<dependency> <!-- SpringCloud离不开SpringBoot,所以必须要配置此依赖包 -->
    <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-dependencies</artifactId>
        <version>2.0.7.RELEASE</version>
        <type>pom</type>
        <scope>import</scope>
    </dependency>

重新启动ZuulApp

正常访问用户服务:http://localhost:8090/users/get/1
使用zuul代理访问用户服务:http://localhost:9501/springcloud-provider-users/users/get/1

zuul路由配置

前面以及简单的使用了zuul,但你会发现访问地址还必须知道程序的名称,如果不知道这个名称是无法访问的,但如果让用户知道了这名称,那么使用zuul就是去它的实际意义的,我们可以通过名称直接调用

既然是使用代理,那么代理的功能就是不能让用户看到真实的操作,屏蔽真实的调用地址,这个时候就需要自己增加zuul的路由规则配置了。
【springcloud-zuul-gateway】修改application.yml配置文件,增加路由配置

zuul:
  routes:
    springcloud-provider-users:  /users-proxy/**

这个时候就可以通过/users-proxy 来访问springcloud-provider-users服务
http://localhost:9501/users-proxy/users/get/1
但是还会发现,虽然现在以及开启了路由访问的支持,但依然通过应用程序的名称还是能访问
http://localhost:9501/springcloud-provider-users/users/get/1

【springcloud-zuul-gateway】修改application.yml文件,忽略掉用户服务的名称

zuul:
  routes:
    springcloud-provider-users:  /users-proxy/**
  ignored-services:
    springcloud-provider-users

做完后,就可以进行代理的安全使用,但真实情况下,一般会有很多微服务,如果完全按照上面的配置方式会非常的麻烦,所有最加到的做法是可以采用一个通配符“*”的模式来统一完成。
【springcloud-zuul-gateway】修改application.yml文件

zuul:
  routes:
    springcloud-provider-users:  /users-proxy/**
  ignored-services:
    "*"

除开上面这一种访问模式以外,在zuul中还有另外一种配置方式
【springcloud-zuul-gateway】修改application.yml文件

zuul:
  routes:
    users.path: /users-proxy/**
    users.serviceId: springcloud-provider-users
  ignored-services:
    "*"

其中在配置文件中出现的users其实是一个逻辑名称,这个名称主要作用是将path与serviceId绑定在一起

【springcloud-zuul-gateway】如果说不想通过eureka进行访问,对于zuul来说也是可以实现的,但是在真实的开发环境中,基本不会使用

zuul:
  routes:
    users:
      path: /users-proxy/**
      serviceId: springcloud-provider-users
    users2:
      path: /users2-proxy/**
      url: http://localhost:8090/
  ignored-services:
    "*"
访问:http://localhost:9501/users2-proxy/users/get/1

【springcloud-zuul-gateway】 设置公共前缀

zuul:
  routes:
    users:
      path: /users-proxy/**
      serviceId: springcloud-provider-users
    users2:
      path: /users2-proxy/**
      url: http://localhost:8090/
  ignored-services:
    "*"
  prefix: /enjoy-api

一旦设置了公共前缀,所以的访问路径都要在前面加上前缀
http://localhost:9501/enjoy-api/users-proxy/users/get/1
http://localhost:9501/enjoy-api/users2-proxy/users/get/1

zuul过滤访问

其实zuul的功能本质上就是一个代理操作,类似于nginx,但是在真实的使用中,所有的微服务一点都有增加的认证信息,那么就必须在其访问之前追加认证的头部操作,这样的功能需要通过zuul的过去操作完成。
【springcloud-zuul-gateway】 修改application.yml配置,增加产品微服务

zuul:
  routes:
    users:
      path: /users-proxy/**
      serviceId: springcloud-provider-users
    users2:
      path: /users2-proxy/**
      url: http://localhost:8090/
    product:
      path: /product-proxy/**
      serviceId: springcloud-provider-product
  ignored-services:
    "*"
  prefix: /enjoy-api

这样直接访问:http://localhost:9501/enjoy-api/product-proxy/prodcut/get/1
这样是访问不到的

【springcloud-zuul-gateway】追加过滤处理

package cn.enjoy.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import	org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;

import java.nio.charset.Charset;
import java.util.Base64;

public class AuthorizedRequestFilter extends ZuulFilter{
    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }
    @Override
    public int filterOrder() {
        return 0;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        RequestContext currentContext = RequestContext.getCurrentContext() ; // 获取当前请求的上下文
        String auth = "admin:enjoy"; // 认证的原始信息
        byte[] encodedAuth = Base64.getEncoder()
                .encode(auth.getBytes(Charset.forName("US-ASCII"))); // 进行一个加密的处理
        String authHeader = "Basic " + new String(encodedAuth);
        currentContext.addZuulRequestHeader("Authorization", authHeader);
        return null;
    }
}

其中filterType为过滤的类型
在进行Zuul过滤的时候可以设置其过滤执行的位置,那么此时有如下几种类型:

  • pre:在请求发出之前执行过滤,如果要进行访问,肯定在请求前设置头信息
  • route:在进行路由请求的时候被调用;
  • post:在路由之后发送请求信息的时候被调用;
  • error:出现错误之后进行调用

【springcloud-zuul-gateway】建立一个配置程序类

package cn.enjoy.config;
import cn.enjoy.filter.AuthorizedRequestFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ZuulConfig {
    @Bean
    public AuthorizedRequestFilter getAuthorizedRequestFilter() {
        return new AuthorizedRequestFilter() ;
    }
}

这个时候访问:
http://localhost:9501/enjoy-api/product-proxy/prodcut/get/1
http://localhost:9501/enjoy-api/users-proxy/users/get/1

这两个服务都能正常访问了。

zuul安全访问

作为所有接口的统一门面,zuul也是可以进行加密访问的
【springcloud-zuul-gateway】修改pom文件,增加安全访问模块

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

【springcloud-zuul-gateway】修改application.yml配置文件,增加用户配置

spring:
  application:
    name:  springcloud-zuul-gateway
  security:
    user:
      name: admin
      password: enjoy

再访问http://localhost:9501/enjoy-api/users-proxy/users/get/1
这个时候就需要输入用户名密码了

Feign访问zuul

前面学习feign的时候确实已经知道,他其实是去eureka中获取服务地址的,如果想使用feign来访问zuul,首先就应该让zuul注册到eureka中
【springcloud-zuul-gateway】 修改application.yml文件

eureka:
  client: # 客户端进行Eureka注册的配置
    service-url:
      defaultZone: http://admin:enjoy@eureka1:7001/eureka,http://admin:enjoy@eureka2:7002/eureka,http://admin:enjoy@eureka3:7003/eureka
  instance:
    instance-id: microcloud-zuul-gateway
    prefer-ip-address: true
    lease-renewal-interval-in-seconds: 2 # 设置心跳的时间间隔(默认是30秒)
    lease-expiration-duration-in-seconds: 5 # 如果现在超过了5秒的间隔(默认是90秒)

在这里插入图片描述

【springcloud-service】现在所有的服务要通过zuul的代理进行访问,新增接口

package cn.enjoy.service;
import cn.enjoy.feign.FeignClientConfig;
import cn.enjoy.service.fallback.IProductClientServiceFallbackFactory;
import cn.enjoy.service.fallback.IZUUlClientServiceallbackFactory;
import cn.enjoy.vo.Product;
import cn.enjoy.vo.Users;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.List;

@FeignClient(name = "MICROCLOUD-ZUUL-GATEWAY",configuration = FeignClientConfig.class,
        fallbackFactory = IZUUlClientServiceallbackFactory.class)
public interface IZUUlClientService {

    @RequestMapping("/enjoy-api/product-proxy/prodcut/get/{id}")
    public Product getProduct(@PathVariable("id")long id);

    @RequestMapping("/enjoy-api/product-proxy/prodcut/list")
    public List<Product> listProduct() ;

    @RequestMapping("/enjoy-api/product-proxy/prodcut/add")
    public boolean addPorduct(Product product) ;

    @RequestMapping("/enjoy-api/users-proxy/users/get/{name}")
    public Users getUsers(@PathVariable("name")String name);
}

新增IZUUlClientServiceallbackFactory,在Zuul由于出现网络问题失去联系后进行容错处理

package cn.enjoy.service.fallback;
import cn.enjoy.service.IProductClientService;
import cn.enjoy.service.IZUUlClientService;
import cn.enjoy.vo.Product;
import cn.enjoy.vo.Users;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
public class IZUUlClientServiceallbackFactory implements FallbackFactory<IZUUlClientService> {
    @Override
    public IZUUlClientService create(Throwable throwable) {
        return  new IZUUlClientService() {
            @Override
            public Product getProduct(long id) {
                Product product = new Product();
                product.setProductId(999999L);
                product.setProductName("feign-zuulName");
                product.setProductDesc("feign-zuulDesc");
                return  product;
            }

            @Override
            public List<Product> listProduct() {
                return null;
            }

            @Override
            public boolean addPorduct(Product product) {
                return false;
            }

            @Override
            public Users getUsers(String name) {
                Users user = new Users();
                user.setSex("F");
                user.setAge(17);
                user.setName("zuul-fllback:"+name);
                return user;
            }
        };
    }
}

【springcloud-consumer-hystrix】 修改ConsumerProductController,增加一个新的方法,访问接口

package cn.enjoy.controller;
import cn.enjoy.service.IProductClientService;
import cn.enjoy.service.IZUUlClientService;
import cn.enjoy.vo.Product;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/consumer")
public class ConsumerProductController {

    @Resource
    private IProductClientService iProductClientService;

    @Resource
    private IZUUlClientService izuUlClientService;


    @RequestMapping("/product/get")
    public Object getProduct(long id) {
        return  iProductClientService.getProduct(id);
    }

    @RequestMapping("/product/list")
    public  Object listProduct() {
        return iProductClientService.listProduct();
    }

    @RequestMapping("/product/add")
    public Object addPorduct(Product product) {
        return  iProductClientService.addPorduct(product);
    }

    @RequestMapping("/product/getProductAndUser")
    public Object getProductAndUser(long id) {
        Map<String,Object> result = new HashMap();
        result.put("product",izuUlClientService.getProduct(id));
        result.put("user",izuUlClientService.getUsers(id+""));
        return  result;
    }
}

依次启动eureka,user服务,product服务,zuul服务,customerhystrix服务
在地址栏输入:
http://localhost/consumer/product/getProductAndUser?id=1

关闭zuul服务
http://localhost/consumer/product/getProductAndUser?id=1

发现服务降级已经开启

zuul熔断

zuul是一个代理服务,但如果被代理的服务突然断了,这个时候zuul上面会有出错信息,例如,停止product服务

访问:http://localhost:9501/enjoy-api/product-proxy/prodcut/get/1
在这里插入图片描述
现在服务的调用方已经做了处理,不会出现这样的错误信息,但一般来说,对于zuul本身代理方,也应该进行zuul的降级处理

修改【springcloud-zuul-gateway】建立fallback回退处理类

package cn.enjoy.fallback;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

@Component
public class ProviderFallback implements   FallbackProvider  {
    @Override
    public String getRoute() {
        return "*";
    }

    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
        return new ClientHttpResponse() {

            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders headers = new HttpHeaders() ;
                headers.set("Content-Type", "text/html; charset=UTF-8");
                return headers;
            }

            @Override
            public InputStream getBody() throws IOException {
                // 响应体
                return new ByteArrayInputStream("产品微服务不可用,请稍后再试。".getBytes());
            }

            @Override
            public HttpStatus getStatusCode() throws IOException {
                return HttpStatus.BAD_REQUEST;
            }

            @Override
            public int getRawStatusCode() throws IOException {
                return HttpStatus.BAD_REQUEST.value();
            }

            @Override
            public String getStatusText() throws IOException {
                return HttpStatus.BAD_REQUEST.getReasonPhrase();
            }

            @Override
            public void close() {

            }
        };
    }
}

访问:http://localhost:9501/enjoy-api/product-proxy/prodcut/get/1

getRoute:方法可以返回服务的ID,比如‘springcloud-provider-product’,如果需要匹配全部适应 “*”

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值