【SpringCloud微服务笔记11】服务网关Zuul

目录

一、Zuul概述

二、Zuul快速入门

三、Zuul路由的映射规则配置

3.1 服务路由配置

3.2 服务路由的默认规则

四、Zuul和Hystrix结合实现熔断

五、Zuul原理解析


前言:通过对前面Spring Dloud Netflix下的核心组件有一定的了解,例如Eureka,Ribbon,Feign,Hystrix等,使用这些组件可以搭建简单的微服务架构系统。微服务架构搭建好之后需要向外部系统提供统一的RESTful API服务调用接口,这个时候就要用到服务网关Zuul。

一、Zuul概述

        我们通过前面的内容,已经可以搭建简单的微服务架构系统并实现各个服务之间的调用,但是不同的微服务架构一般会有不同的网络地址,而外部客户端可能需要调用多个服务的接口才能完成一个业务需求。例如一个电商的APP,可能会调用多个微服务架构的接口才能完成一次购票业务流程,但同样也会出现许多问题:

  • 客户端会多次请求不同的微服务架构,是客户端变得复杂。
  • 存在跨域请求,在一定的场景下处理相对复杂。例如重定向或js发起ajax请求时,会因为域名不同,二级域名不同,子域名不同或端口号不同等因素,是处理变得复杂
  • 项目难以重构。随着项目的迭代,可能需要重新划分微服务架构。
  • 某些微服务架构可能会设置防火墙等不友好协议,难以做到直接访问。

        针对上述的问题,可以使用网关解决,服务网关是相当于介于客户端和服务器之间的中间层,所有的外部请求都会经过服务网关进行调度和过滤。服务网关除了要实现请求路由,负载均衡,过滤等功能之外,还要实现更多的功能,例如与服务相关的框架整合,将服务请求熔断等。

        总的来说,Zuul是netflix的一个开源组件,是通过Servlet实现的。Zuul作为SpringCloud的服务网关组件,能够通过与Eureka进行整合,将自身注册到Eureka Server中,与Eureka,Ribbon,hystrix等进行整合,同时从Eureka中获得其他微服务架构实例信息。这样的设计通过把网关和服务管理整合到一起,让Zuul可以获取到服务注册信息,结合Ribbon,Hystrix等更好地实现路由转发,负载均衡等功能。

二、Zuul快速入门

对Zuul的工作机制有所了解后,下面通过一个构建网关的实例来介绍Zuul的功能:

2.1 搭建Eureka Server

        这里我们无需再去进行Springboot项目eureka-server的搭建,直接使用搭建Eureka所用到的Eureka-server

2.2 创建服务提供者

(1)使用SpringInitiazr的方式创建一个服务提供者的项目,命名为demo-provider,添加Test,Eureka client,Web依赖。

(2)引入依赖后,在全局配置问件application.yml中进行相关配置

spring:
  application:
    name: demo-provider
server:
  port: 7006
eureka:
  client:
    service-url:
      defaultZone: http://localhost:7000/eureka
  instance:
    hostname: localhost

(3)创建controller包,并在controller包下创建HystrixController类,类中创建一个hi()方法。

注:controller包要创建在demo-provider目录下,并与启动类同级

package com.example.demoprovider.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Title: HystrixController
 * @Description:
 * @Auther:
 * @Version: 1.0
 * @create 2023/6/12 17:10
 */
@RestController
public class HystrixController {
    @RequestMapping("/hi")
        public String hi(String id){
        return "hi,访问成功!"+id;
    }
}

(4)启动类上添加@EnableEurekaClient注解,开启EurekaClient功能

@EnableEurekaClient 

2.3 创建服务消费者

(1)使用SpringInitiazr的方式创建一个服务提供者的项目,命名为demo-consumer,添加Test,Eureka client,Web依赖。

(2)引入依赖后,在全局配置问件application.yml中进行相关配置

spring:
  application:
    name: demo-consumer
server:
  port: 7864
eureka:
  client:
    service-url:
      defaultZone: http://localhost:7000/eureka

(3)创建config包,在config包下新建配置类RextConfig,并添加@Configuration注解。在类中创建一个Bean实例方法restTemplate(),并在方法上添加@LoadBalanced注解,使RestTemplate实例对象处理请求时拥有客户端负载均衡的能力,具体如下:

注:config包要创建在demo-consumer目录下,并与启动类同级

package com.example.democonsumer.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

/**
 * @Title: RestConfig
 * @Description:
 * @Auther:
 * @Version: 1.0
 * @create 2023/6/12 17:18
 */
@Configuration
public class RestConfig {
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
    
}

(4)创建service包,在service包下创建LocalItemService类,并添加@Service注解,在类中注入RestTemplate对象,并添加一个hi()方法,在hi()中对eureka-provider服务提供者进行调用,具体如下:

注:service包要创建在demo-consumer目录下,并与启动类同级

package com.example.democonsumer.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.client.RestTemplateRequestCustomizer;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.client.RestTemplate;

/**
 * @Title: LocalItemService
 * @Description:
 * @Auther:
 * @Version: 1.0
 * @create 2023/6/12 17:20
 */
@Service
public class LocalItemService {
    @Autowired
    RestTemplate restTemplate;
    public String hi(@RequestParam(value = "id")String id){
        return restTemplate.getForObject(
                "http://eureka-provider/hi?id="+id,String.class);
    }
}

(5)创建controller包,在controller包下新建LocalItemController类,并添加Controller注解。在类中注入LocalItemService实例化对象,创建一个hi()方法

注:controller包要创建在demo-consumer目录下,并与启动类同级

package com.example.democonsumer.controller;

import com.example.democonsumer.service.LocalItemService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Title: LocalItemController
 * @Description:
 * @Auther:
 * @Version: 1.0
 * @create 2023/6/12 17:26
 */
@RestController
public class LocalItemController {
    @Autowired
    LocalItemService localItemService;
    @GetMapping("/hi")
    public String hi(String id){
        return localItemService.hi(id);
    }
}

(6)启动类中添加上@EnableEurekaClient注解,开启Eureka Client功能

@EnableEurekaClient

2.4 创建网关服务

(1)使用SpringInitiazr的方式创建一个服务提供者的项目,命名为demo-consumer,添加Test,Eureka client,Web依赖。

 在这里我们手动添加Zuul依赖:

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
<version>2.2.10.RELEASE</version>
</dependency>

(2)在项目gateway-zuul的启动类GatewayZuulApplication中添加@EnableZuulProxy注解,开启服务网关Zuul功能

        

package com.example.demogatewayzuul;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

@EnableZuulProxy
@SpringBootApplication
public class DemoGatewayZuulApplication {

	public static void main(String[] args) {
		SpringApplication.run(DemoGatewayZuulApplication.class, args);
	}

}

(3)在项目gateway-zuul的全局配置文件中配置Zuul的相关信息。

server:
  port: 8835
spring:
  application:
    name: demo-gateway-zuul
  eureka:
    client: 
      sevice-url:
        defaultZone: http://localhost:7000/eureka/

zuul:
  routes:
    demo-consumer:
      path: /eureka-consumer/**

(4)依次启动项目demo-server,demo-provider,demo-consumer,gateway-zuul,启动成功后访问浏览器http://localhost7000,会显示Eureka注册的服务项目:

此时,如果访问eureka-consumer,就需要在访问的地址中加上在application文件中配置的Zuul路由前缀/eureka-consumer即可。

三、Zuul路由的映射规则配置

3.1 服务路由配置

        在第二部分已经讲解了服务路由的配置。Zuul通过与Eureka的整合,实现了对服务实例的自动化维护,即服务路由功能。在使用服务路由配置的时候,无需通过serviceId指定具体服务实例地址,只需要通过zuul.routes.<路由名>.path与zuul.routes.<路由名>.serviceId的方式成对配置即可

具体如下:

zuul:

        routes:

                demo-consumer:

                        path: /demo-consumer/**

                        serviceId: demo-consumer

        上述的作用是将符合/eureka-consumer/**规则的请求路径转发到名为eureka-consumer的服务实例上 ,其中routes可以指定为任意的路由名称,这里指定的demo-consumer

        对于面向服务的路由配置,除了使用path与serviceId映射的配置方式外,还有一种更间接的配置方式,即zuul.routes.<serviceId>=<path>。其中<serviceId>用来指定路由的具体服务名,<path>用来配置匹配的请求映射地址,例如:

zuul:

        routes:

                demo-consumer: /eureka-consumer/**

3.2 服务路由的默认规则

默认情况下,Zuul会自动为Eueka服务注册中心的所有服务创建映射关系,进行路由,这会使得一些不希望对外开放的服务也可能被外部访问。当使用服务名称作为前缀路径时,实际上会匹配类似如下列对应的默认路由配置

Zuul:

        Routes:

                demo-consumer

                path: /eureka-consumer/**

                serviceId:Eureka-consumer

如果不想使用默认的路由规则,就可以在配置文件中加入以下内容,即可关闭所有默认的路由配置规则,对应如下:

zuul: 

        ignore-services: '*' 

使用上述方式关闭默认的路由配置,此时需要在配置文件中逐个为需要路由的服务添加映射规则。当然可以使用path与serviceId组合的配置方式,也可以使用更简介的zuul.routes.<serviceId>=<path>配置方式。只有在配置文件中出现的映射规则才会被创建路由规则,而从Eureka中获取的其它服务,Zuul将不再会为它们创建路由规则。

四、Zuul和Hystrix结合实现熔断

Zuul和Hystrix结合使用实现熔断功能时,需要完成FallbackProvider接口。该接口提供了两个方法:

  1. getRoute()方法:用于指定提供回退功能的服务。
  2. fallbackResponse()方法:用于执行回退操作的具体逻辑

我们以一个实例来说明,帮助大家更好的理解:

  1. 通过创建一个FallbackProvider的实现类,在类中指明错误回调内容
  2. 启动项目,浏览器访问http://localhost:8835/eureka-consumer/hi?id=12,浏览器会显示MyFallbackProvider类中执行回退逻辑的方法中返回的字符串


@Component
public class MyGatewayFallback implements FallbackProvider {

/*
设置要对哪些服务进行错误回调,return 具体的服务名称,
也可以return "*" 获得return null ==>对所有服务进行错误回调
*/
@Override
public String getRoute() {
return "hello-spring-cloud-web-admin-ribbon";
}

/*
具体的回调内容
*/
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
return new ClientHttpResponse() {
/*
返回一个状态码
*/
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.OK;
}

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

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

@Override
public void close() {

}

@Override
public InputStream getBody() throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
Map<String,Object> map = new HashMap<String,Object>();
map.put("status",200);
map.put("message","无法连接,请检查您的网络");
return new ByteArrayInputStream(objectMapper.writeValueAsBytes(map));

}

@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
// 和 getBody 中的内容编码一致
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
return headers;
}
};
}
}

五、Zuul原理解析

zuul中整个请求的过程是这样的 :

  1. 首先将请求给zuulservlet处理,zuulservlet中有一个zuulRunner对象,该对象中初始化了RequestContext:作为存储整个请求的一些数据,并被所有的zuulfilter共享。
  2. zuulRunner中还有 FilterProcessor,FilterProcessor作为执行所有的zuulfilter的管理器。
  3. FilterProcessor从filterloader 中获取zuulfilter,而zuulfilter是被filterFileManager所加载,并支持groovy热加载,采用了轮询的方式热加载。
  4. 有了这些filter之后,zuulservelet首先执行的Pre类型的过滤器,再执行route类型的过滤器,最后执行的是post 类型的过滤器,如果在执行这些过滤器有错误的时候则会执行error类型的过滤器。执行完这些过滤器,最终将请求的结果返回给客户端。

其次我们要分析原理,还是从它的源码入手,这里推荐一位大佬的文章,个人感觉不错:Zuul原理https://blog.csdn.net/weixin_33795743/article/details/88758881?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522168619262816800211549781%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=168619262816800211549781&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-2-88758881-null-null.142%5Ev88%5Einsert_down38v5,239%5Ev2%5Einsert_chatgpt&utm_term=zuul%E5%8E%9F%E7%90%86%E8%A7%A3%E6%9E%90&spm=1018.2226.3001.4187 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值