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网关的路由规则了:


源码点击这里

猜你喜欢

转载自blog.csdn.net/mrspirit/article/details/80483667