springcloud记录篇5-zuul路由

一。 zuul介绍

   路由是微服务架构的不可或缺的一部分。例如:”/” 可能映射到你应用主页,/api/users映射到用户服务,/api/shop映射到购物服务。Zuul。Zuul是Netflix出品的一个基于JVM路由和服务端的负载均衡器
   当一个UI应用想要代理调用一个或者多个后台服务的时候,Sping cloud创建了一个嵌入的Zuul proxy很方便的开发一个简单的案例。这个功能对于代理前端需要访问的后端服务非常有用,避免了所有后端服务需要关心管理CORS和认证的问题.


zuul路由 虽然方便了前端应用 依然带来其他问题  所有的请求和响应都经过zuul路由 zuul的带宽需要是后台服务所有请求流量的总和  并发量的过大

也可能导致zuul路由的崩溃 同时考虑zuul路由的高可用性


二 。zuul反向代理

1>实现反向代理

    实现反向代理  给多个服务添加路由

    演示环境 代码沿用springcloud记录篇4环境

  eurekaserver    本机ip为 192.168.88.20  主机名 mymaster 端口 8761  这里为了简单就不开启eurekaserver的从服务eurekabackup    
服务提供和服务消费配置      
  eurekapub  本机ip  192.168.88.20 端口8086 应用名称 idserver  为了简单不开启多个发布方 只开启一个  
  eurekaconsumer 本机ip  192.168.88.20 端口8888    

添加一个新项目 名称为zuul 添加zuul的maven依赖 zuul需要发现eurekaserver上的服务 必须包含eureka发现组件  

zuul包含了 web组件 无需额外添加

<?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">
	<modelVersion>4.0.0</modelVersion>

	<groupId>cn.et</groupId>
	<artifactId>zuul</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>zuul</name>
	<description>Demo project for Spring Boot</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.7.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.7</java.version>
		<spring-cloud.version>Dalston.SR4</spring-cloud.version>
	</properties>

	<dependencies>
		<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-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>


</project>
添加 application.yml   默认配置如下

server:
  port: 8890  #zuul端口是8890
spring:
  application:
    name:   zuultest
eureka:
  client:
    serviceUrl:
      defaultZone: http://jiaozi:jiaozi123@mymaster:8761/eureka/,http://jiaozi:jiaozi123@mybackup:8762/eureka/ 

    

添加运行类  ZuulApplication  该类 如果需要启用反向代理 必须添加注解@EnableZuulProxy 该注解是个混合注解 源代码上 被注解了断路器和自动发现

@EnableCircuitBreaker
@EnableDiscoveryClient
主类代码如下:

package cn.et;

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

@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication {

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

依次启动  eurekaserver  eurekapub eurekaconsume,zuul

 默认情况 zuul给所有被发现的服务都提供了默认的路由 路由名称为服务id小写 访问路由的方式是

http://zuul服务器ip:zuul服务端口/zuul/被调用服务id/被调用服务的方法

比如访问eurekaconsume(serviceid是myclient)的getUser方法:

http://localhost:8890/zuul/myclient/getUser?id=1

  访问eurekapub(service是idserver)的getId方法

http://localhost:8890/zuul/idserver/getId

2>使用不同的路径映射

假设需要给 eurekaconsume服务 添加一个路由 所有的资源访问 通过  /ec/ 路由访问

                     eurekapub服务 添加一个路由 所有资源访问 通过 /ep/路由访问

以下两种方式(application.yml配置)都可

方式1:

zuul:
  routes:
    被注册的服务id: /映射路径/**  
比如可以配置eurekaconsume(注册服务id是MYCLIENT)如下

zuul:
  routes:
    MYCLIENT: /ec/**

就可以通过zuul来访问  http://localhost:8890/zuul/ec/所有eurekaconsume提供的服务方法

比如http://localhost:8890/zuul/ec/getUser?id=1 成功访问

方式2:

可以实现更加细粒度的控制  可以给每组映射取一个别名 

 zuul:
  routes:
    别名:
      path: /ep/**
      serviceId: 服务id
比如可以配置eurekapub(注册服务id是IDSERVER)如下
 zuul:
  routes:
    pubMapping:
      path: /ep/**
      serviceId: IDSERVER

通过地址访问 http://localhost:8890/zuul/ep/getId 正常访问

如果需要忽略其他的service路由 只允许配置的 可以通过ignoredServices

zuul:
  ignoredServices: '*'  #忽略所有的服务
  routes:               #只启用以下的服务
    MYCLIENT: /ec/**
如果按照以上配置 默认的通过serviceid的路由都失效了 只能访问 myclient服务 通过 /ec

如果需要使用正则表达式 映射特定规则的服务id 使用特定规则的地址 可以使用 java代码进行定义 添加配置类

在扫描环境下

package cn.et;


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


@Configuration
public class MyConf {
	/**
	 * PatternServiceRouteMapper 构造方法存在两个参数
	 *   参数1 :服务id的正则表达式
	 *   参数2 :映射路径的正则表达式
	 *   
	 * @return
	 */
	@Bean
	public PatternServiceRouteMapper serviceRouteMapper() {
		//(?<prefix>^.+) 中^.+表示任意字符串出现1次以上 通过?<prefix> 定义了一个变量  匹配值 配置到prefix环境中
		//下面正则的意思是 所有  (任意字符1)c(任意字符2 ) 自动被匹配为 任意字符1/任意字符2
		//比如 myclient 如果需要访问  /zuul/my/lient访问
		return new PatternServiceRouteMapper(
				"(?<prefix>^.+)c(?<suffix>.+$)",
				"${prefix}/${suffix}");
	}
}


访问 http://localhost:8890/zuul/my/lient/getUser?id=1 成功

窒息模式和本地跳转(Strangulation Patterns and Local Forwards)

常见的迁移旧应用或者旧接口的方式,就是逐步的替换它的实现。 Zuul代理是一种很有用的工具, 因为你可以使用这种方式处理所有客户端到旧接口的请求. 只是重定向了一些请求到新的接口.

实例 

  修改eurekaconsume  添加一个Controller的类

package cn.feign.v;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.netflix.feign.ribbon.FeignLoadBalancer;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.netflix.loadbalancer.Server;

@RestController
public class TestVController {
	
	@RequestMapping("/my/article/details/{arcId}")
	public String getArc(@PathVariable String arcId) {
		return "我的博客id是:"+arcId+"<br/>";
	}
	@RequestMapping("/p/getUser/{id}")
	public String getUserId(@PathVariable String id) {
		return "p_user_"+id;
	}
	@RequestMapping("/p1/getUser/{id}")
	public String getId(@PathVariable String id) {
		return "p1_user_"+id;
	}
	@RequestMapping("/p/getId")
	public String getPUserId() {
		return "p";
	}
	@RequestMapping("/p1/getId")
	public String getP1UserId() {
		return "p1";
	}
}
重启 eurekaconsume

配置 zuul项目

zuul:
  routes:
    first:
      path: /my/**
      url: http://blog.csdn.net/liaomin416100569
    second:
      path: /p/**
      url: forward:/p1
    legacy:
      path: /**
      service-id: myclient

最后一个legacy表示  serviceid是myclient应用所有路径 都可以直接通过  /zuul/路径访问

consume的controller类暴露了以下方法

/my/article/details/{arcId}
/p/getUser/{id}
/p1/getUser/{id}

假设/my 这个路径迁移到了 http://blog.csdn.net/liaomin416100569

当 访问 http://localhost:8890/zuul/my/arcticle/detail/78179404

不在显示之前的内容 会被重定向到  http://blog.csdn.net/liaomin416100569/arcticle/detail/78179404


假设 /p下所有路径都迁移到/p1下 

 http://localhost:8890/zuul/p/getUser/1  应该被转发到同一个应用的 http://localhost:8890/zuul/p1/getUser/1下

以上yml配置可以实现以上两种情况

三 。zuul上传文件

  参考官方文档(http://cloud.spring.io/spring-cloud-static/Dalston.SR4/single/spring-cloud.html#_uploading_files_through_zuul)

四 。zuul过滤器

   Zuul大部分功能都是通过过滤器来实现的。Zuul中定义了四种标准过滤器类型,这些过滤器类型对应于请求的典型生命周期。

(1) PRE:这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
(2) ROUTING:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用Apache HttpClient或Netfilx Ribbon请求微服务。
(3) POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。
(4) ERROR:在其他阶段发生错误时执行该过滤器。


实例演示一个简单的pre过滤器  

   比如 zuul需要定义一个路由  http://localhost:8890/zuul/cli/getUser?id=1

  application.xml中定义了

zuul:  
 routes:  
   test:  
     path: /cli/**   
但是没有定义举例的serviceId  希望当用户访问时 添加参数来决定 具体的serviceId

 http://localhost:8890/zuul/cli/getUser?id=1&serviceId=myclient 

具体实现 

package cn.et;

import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.FORWARD_TO_KEY;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.ROUTE_TYPE;

import javax.servlet.http.HttpServletRequest;

import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
/**
 * 添加注解需要被扫描器扫描到
 * @author jiaozi
 *
 */
@Component
public class MyFilter extends ZuulFilter {
	/**
	 * 被扫描的所有ZuulFilter的子类 首先调用shouldFilter方法 根据他的返回值
	 * 决定 是否执行该过滤器(其实就是调用run方法)
	 * 
	 */
	@Override
	public boolean shouldFilter() {
		//过滤器获取上下文
		RequestContext ctx = RequestContext.getCurrentContext();
		//获取当前浏览器的请求  判断是否有 serviceId这个参数 如果有 返回true
		HttpServletRequest request=ctx.getRequest();
		return request.getParameter("serviceId")!=null;
	}

	@Override
	public Object run() {
		RequestContext ctx = RequestContext.getCurrentContext();
		HttpServletRequest request=ctx.getRequest();
		//如果是forward 获取到forward的url
		String path = (String) ctx.get(FORWARD_TO_KEY);
		//获取路由的
		String seriviceId=(String) ctx.get(FilterConstants.SERVICE_ID_KEY);
		//获取路由最终的url
		String requestUrl=(String) ctx.get(FilterConstants.REQUEST_URI_KEY);
		//手工将参数的serviceId传入过滤器上下文
		ctx.put(FilterConstants.SERVICE_ID_KEY, request.getParameter("serviceId"));
		return null;
	}
	/**
	 * 路由器的类型 包括 pre  route post  error四个类型 定义在FilterConstants接口中
	 */
	@Override
	public String filterType() {
		return FilterConstants.PRE_TYPE;
	}
	/**
	 * 执行的顺序 越小优先级越高
	 */
	@Override
	public int filterOrder() {
		// TODO Auto-generated method stub
		return 0;
	}

}

application.yml定义

zuul:  
 routes:  
   test:  
     path: /cli/**   
 http://localhost:8890/zuul/cli/getUser?id=1&serviceId=myclient  成功

 http://localhost:8890/zuul/cli/getUser?id=1 失败 报错(没有serviceId) 

Thu Oct 12 09:32:36 CST 2017
There was an unexpected error (type=Internal Server Error, status=500).
GENERAL

其他filter可以参考ZuulFilter子类 

如果希望禁用某些filter

zuul.<SimpleClassName>.<filterType>.disable=true
比如 
zuul.SendResponseFilter.post.disable=true



原文地址:https://www.cnblogs.com/liaomin416100569/p/9331185.html