Spring Cloud集群中使用Zuul(十七)

在Eureka集群中使用Zuul

在Spring Cloud中集群中使用Zuul网关,那么Zuul也是集群的一部分,所以它也应该是一个Eureka项目,如图所示,我们搭建一个最简单的集群,通过网关来分发浏览器发起的请求。

依旧是从Spring Cloud服务管理框架Eureka简单示例(三)这篇博客底部拿到我们的源码,这三个项目对应我们架构图中底部的三个项目,可以启动三个项目的*App启动类,测试项目是否能够正常使用。

接下来创建eureka-zuul网关项目,作为一个Eureka项目,引入Eureka相关依赖,再加入zuul依赖和httpClient依赖,完整pom.xml的依赖:

<dependencyManagement>
	<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-dependencies</artifactId>
			<version>Dalston.SR5</version>
			<type>pom</type>
			<scope>import</scope>
		</dependency>
	</dependencies>
</dependencyManagement>
<dependencies>
	<dependency>
		<groupId>org.springframework.cloud</groupId>
		<artifactId>spring-cloud-starter-config</artifactId>
	</dependency>
	<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.apache.httpcomponents</groupId>
		<artifactId>httpclient</artifactId>
		<version>4.5.5</version>
	</dependency>
</dependencies>

接下来在src/main/resources目录下新建application.yml文件,配置网关项目的启动端口、服务名称、注册地址以及zuul网关路由规则:

server:
  port: 9090

spring:
  application:
    name: eureka-zuul

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/
      
zuul:
  routes:
    users:
      path: /users/**
      serviceId: eureka-consumer

路由规则配置了凡是访问以users作为前缀的请求都会转发到eureka-consumer项目进行处理。

接下来在src/main/java目录下创建com.init.springCloud包,并创建启动类ZuulApp.class,开启zuul服务代理:

package com.init.springCloud;

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

@SpringBootApplication
@EnableZuulProxy
public class ZuulApp {

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

启动eureka-zuul项目,访问:http://localhost:9090/users/router,可以看到浏览器返回了来自eureka-consumer的服务调用,相当于是访问了:http://localhost:8081/router

这样我们就完成了在一个最简单的eureka集群中使用zuul网关的效果,可以看到,我们全程是没有去对原来的项目进行改动的,只是在zuul的配置中设置了简单的规则之后,就实现了路由。

底层请求转发的方式:httpClient与okhttp

微服务分布式架构的出现,让http请求成为RPC(Remote Procedure Call,远程过程调用)的首选方式,虽然java平台本身提供了HttpURLConnection来作为http通讯,但是其本身api有限,功能不够强大,所以才有了一系列的http请求工具库出现,这里主要Zuul支持的三种http请求方式:

httpClient:由Apache提供,来作为标准开源http请求客户端,提供了丰富的api,以及强大的功能库,已经是java平台中默认的http请求客户端。在我们的网关中默认就是用了httpClient。

okhttp:由square公司开发,主要目标是高效,专注于提供网络连接效率。它能实现同一ip和端口的请求重用一个socket,这种方式能大大降低网络连接的时间,和每次请求都建立socket,再断开socket的方式相比,降低了服务器服务器的压力。提供对http(http/2)、https以及SPDY的支持。

RestClient:过去zuul使用的分布式rest风格客户端,它基于Ribbon,不过现在已经废弃了。

切换到okhttp或者RestClient,只需要分别设置ribbon.okhttp.enabled=true或者设置ribbon.restclient.enabled=true。

ribbon:
  httpclient:
    enabled: false
  okhttp:
    enabled: true
#  restclient:
#    enabled: true

另外,如果引用的是okhttp,还需要加入依赖:

<dependency>
	<groupId>com.squareup.okhttp3</groupId>
	<artifactId>okhttp</artifactId>
</dependency>

运行结果是一致的,我这里就不贴出具体结果了。

路由配置

我们先回顾zuul的生命周期:

http请求首先进入“pre”过滤器,实现身份认证、调试信息等;之后进入“routing”过滤器,将我们的请求转发到微服务中心,并获取响应;接着进入“post”过滤器,对响应的内容进行修饰;最后把响应返回给请求方。

1.简单路由:SimpleHostRoutingFilter

在“routing”过滤器阶段,zuul默认使用SImpleHostRoutingFilter将我们的请求封装后转发到源服务,源服务返回响应,再把结果给“post”过滤器处理,可以查看它的核心部分源码:

@Override
	public Object run() {
		//省略部分代码

		String uri = this.helper.buildZuulRequestURI(request);
		this.helper.addIgnoredHeaders();

		try {
			CloseableHttpResponse response = forward(this.httpClient, verb, uri, request,
					headers, params, requestEntity);
			setResponse(response);
		}
		catch (Exception ex) {
			throw new ZuulRuntimeException(ex);
		}
		return null;
	}

SImpleHostRoutingFilter主要处理不走Eureka的proxy,底层使用httpclient来转发请求。配置如下:

zuul:
  routes:
    baidu:
      url: https://www.baidu.com
    simple:
      path: /simple/**
      url: http://www.163.com
简单路由提供一个连接池的属性,请求如果过多,会被放到连接池中依次执行,保证请求的效率。在使用简单路由的时候,我们可以通过设置zuul.host.maxTotalConnections(默认200)和zuul.host.maxPerRouteConnections(默认20),来调整目标主机最大连接数和每个主机的初始连接数。
注意:简单路由在配置path的时候是以“http”或“https”开头的。

2.跳转路由:SendForwordFilter

直接使用RequestDispatcher的forward方法将地址进行跳转,这个时候,虽然我们请求的是网关,但是产生了响应之后,并不会响应到网关,而是我们给出的跳转地址。

我们在eureka-zuul项目下创建MyController,提供一个外部服务:

package com.init.springCloud;

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

@RestController
public class MyController {

	@RequestMapping(value = "/sayHi/{name}", method = RequestMethod.GET)
	public String sendForword(@PathVariable String name){
		return "Hi,"+name;
	}
	
}

浏览器访问:http://localhost:9090/sayHi/spirit,可以看到服务正常提供:

接着配置一个路由规则,让zuul使用sendForwardFilter来完成转发:

zuul:
  routes:
    myForword:
      path: /mf/**
      url: forward:/sayHi

配置好以后重启项目,访问:http://localhost:9090/mf/spirit,可以看到转发已经完成:

注意:跳转路由的path后面会跟着一个“forward:”,之后才是接的服务

3.Ribbon路由:RibbonRoutingFilter

如果写配置的时候用的是ServiceId,则用这个routing过滤器,这个过滤器可以用Ribbon 做负载均衡,用hystrix做熔断。在我们之前测试的时候已经这样配置过了:

zuul:
  routes:
    users:
      path: /users/**
      serviceId: eureka-consumer

我们也可以将这个serviceId替换zuul规则的名称,只提供一个path,这样最终达到的效果是一样的:

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

注意:除了简单路由和跳转路由的path配置方式,其余的格式都会被当成是ribbon路由来处理,也就是当成一个serviceId。

如果一个请求是在我们的路由规则中不存在的,那么我们可以为它配置一个“遗留”请求的规则,如同我们在java中使用switch时设置的default一样,下面给出了整个路由规则的配置状况:

zuul:
  routes:
    users:
      path: /users/**
      serviceId: eureka-consumer
    baidu:
      url: https://www.baidu.com
    simple:
      path: /simple/**
      url: http://www.163.com
    myForword:
      path: /mf/**
      url: forward:/sayHi
    legacy:
      path: /**
      url: https://www.toutiao.com

我们这里就把所有其他的请求都导向到了头条的新闻页,访问:http://localhost:9090看看结果:

自定义路由规则

1.PatternServiceRouteMapper

在eureka-zuul项目下新建MyFilterConf类,通过PatternServiceRouteMapper编写正则表达式,匹配我们的路由规则:

package com.init.springCloud;

import org.springframework.cloud.netflix.zuul.filters.discovery.PatternServiceRouteMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyFilterConf {

	//配置访问"module/**"的项目,都转发到"eureka-module/**"进行处理
	@Bean
	public PatternServiceRouteMapper patternServiceRouteMapper(){
		return new PatternServiceRouteMapper(
				"(eureka)-(?<module>.+)", "${module}/**");
	}
	
}

重启项目,访问:http://localhost:9090/consumer/router,浏览器也是正常返回了结果:

自定义规则是很简单的,麻烦的地方在于正则表达式的编写。

2.zuul.ignoredServices

忽略那些不匹配的服务,譬如:

 zuul:
  ignoredServices: '*'
  routes:
    users: /myusers/**

在这个例子中,所有的服务都将被忽略,除了users。

3.zuul.ignoredPatterns

如果想要加大忽略配置规则的粒度,除了使用忽略服务ID的方式,还可以用具体的规则来匹配需要忽略的服务:

 zuul:
  ignoredPatterns: /**/admin/**
  routes:
    users: /myusers/**

这意味着所有的调用,如“/myusers/101”将被转发到“users”服务的“/101”。但是包括“/admin/”的调用将不会解决。

敏感头部信息设置

在相同的系统中共享服务之间是可以的,但是你可能不希望敏感的头部向外部服务器泄漏。我们可以指定一个被忽略的头部列表作为routing配置的一部分。cookie是一个特殊的角色,因为它在浏览器中有明确的语义,而且应该总是被视为敏感的。

如果调用网关的服务中只有一个设置了cookie,那么你就可以让它们从后端流到服务提供者(对应我们的consumer)。除非你在代理中设置了cookie,并且所有的后台服务都是同一个系统的一部分,那么简单地共享它们是很自然的(例如,使用Spring会话将它们连接到某个共享状态)。除此之外,任何由调用网关的服务设置的cookie可能对服务提供者都不是很有用,所以建议至少要设置"Cookie"和"Set-Cookie"。

 zuul:
  routes:
    users:
      path: /myusers/**
      sensitiveHeaders: Cookie,Set-Cookie,Authorization
      serviceId: eureka-consumer

譬如上面的示例,是每个规则下设置的默认敏感头部信息,包括了Cookie,Set-Cookie和Authorization,我们不用再去指定。除非你想让某些被禁止的项目被发送,譬如全部都不限制,可以将敏感头置为空:

 zuul:
  routes:
    users:
      path: /myusers/**
      sensitiveHeaders:
      serviceId: eureka-consumer

忽略请求头设置

除了每个路由规则上面的敏感头部信息设置,我们还可以在网关与外部服务交互的时候,用一个全局的设置zuul.ignoredHeaders,去除那些我们不想要的http头部信息(包括请求和响应的)。在默认情况下,zuul是不会去除这些信息的。如果Spring Security不在类路径上的话,它们就会被初始化为一组众所周知的“安全”头部信息(例如,涉及缓存),这是由Spring Security指定的。在这种情况下,假设请求网关的服务也会添加头部信息,我们又要得到这些代理头部信息,就可以设置zuul.ignoreSecurityHeaders为false,同时保留Spring Security的安全头部信息和代理的头部信息。当然,我们也可以不设置这个值,仅仅获取来自代理的头部信息。

路由端点

Actuator提供了一个可以查看路由规则的端点/routes,我们引入Actuator依赖:

<dependency>  
    <groupId>org.springframework.boot</groupId>  
    <artifactId>spring-boot-starter-actuator</artifactId>  
    <version>1.5.4.RELEASE</version>  
</dependency> 

再把安全验证关闭,让我们可以访问到这个端点:

management:
  security:
    enabled: false

这里,遗留请求的路由规则会影响到我们访问这个端点,先注释掉这个路由规则:

#    legacy:
#      path: /**
#      url: https://www.toutiao.com

之后,重启项目,访问:http://localhost:9090/routes,我们便能看到zuul网关的路由规则了:

源码点击这里

最后,大家有什么不懂的或者其他需要交流的内容,也可以进入我的QQ讨论群一起讨论:654331206

Spring Cloud系列:

Spring Cloud介绍与环境搭建(一)

Spring Boot的简单使用(二)

Spring Cloud服务管理框架Eureka简单示例(三)

Spring Cloud服务管理框架Eureka项目集群(四)

Spring Cloud之Eureka客户端健康检测(五)

Netflix之第一个Ribbon程序(六)

Ribbon负载均衡器详细介绍(七)

Spring Cloud中使用Ribbon(八)

具有负载均衡功能的RestTemplate底层原理(九)

OpenFeign之第一个Feign程序(十)

OpenFeign之feign使用简介(十一)

Spring Cloud中使用Feign(十二)

Netflix之第一个Hystrix程序(十三)

Netflix之Hystrix详细分析(十四)

Spring Cloud中使用Hystrix(十五)

Netflix之第一个Zuul程序(十六)

Spring Cloud集群中使用Zuul(十七)

Netflix之Zuul的进阶应用(十八)

消息驱动之背景概述(十九)

消息中间件之RabbitMQ入门讲解(二十)

消息中间件之Kafka入门讲解(二十一)

Spring Cloud整合RabbitMQ或Kafka消息驱动(二十二)

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值