Spring Cloud与Docker微服务架构实战简单学习笔记(四)

  • 1. 新建Zuul微服务网关
  • 1.1 Zuul简介
  • 1.2 添加依赖:spring-cloud-starter-netflix-zuul
  • 1.3 配置项
  • 1.4 启动类添加注解: @EnableZuulProxy
  • 1.5 测试
  • 2. Zuul的容错,hystrix.stream(待补)
  • 3. Zuul的路由端点
  • 3.1 SpringBoot2.0需要开启routes端点
  • 3.2 查看路由端点:http://localhost:5031/actuator/routes
  • 3.3 路由配置详情
  • 4. Zuul的安全与Header(待补)
  • 5. 使用Zuul上传文件(待补)
  • 6. Zuul的过滤器
  • 6.1 编写Zuul过滤器
  • 6.1.1 自定义一个pre类型的filter
  • 6.1.2 修改启动类
  • 6.1.3 测试
  • 6.2 禁用过滤器
  • 6.3 Zuul默认整合了Hystrix
  • 6.4 Zuul的容错与回退
  • 6.4.1 为Zuul添加回退
  • 6.5 Zuul的高可用
  • 6.5.1 客户端注册到Eureka Server上
  • 6.5.2 Zuul客户端未注册到Eureka Server上
  • 使用Nginx的例子没有写 (待补)
  • 7. 使用Spring Cloud Config统一管理微服务配置
  • 7.1 创建SpringCloud Config Server
  • 7.1.1 引入依赖包:spring-cloud-config-server
  • 7.1.2 添加配置
  • 7.1.3 添加注解 @EnableConfigServer
  • 7.1.4 测试数据
  • 7.2 创建SpringCloud Config Client
  • 7.2.1 引入依赖包:spring-cloud-config-client
  • 7.2.2 添加配置
  • 7.2.3 新建controller
  • 7.2.4 测试数据
  • 7.3 Config Server的GIT仓库配置详解
  • 7.3.1 占位符支持
  • 7.3.2 匹配模式(待补)
  • 7.3.3 搜索目录
  • 7.3.4 启动时加载配置文件(待补)


1. 新建Zuul微服务网关

未使用网关的微服务请求:
缺点:

  1. 客户端会多次请求不同的微服务,增加了客户端的复杂性。
  2. 存在跨域请求,在一定场景下处理相对复杂。
  3. 认证复杂,每个服务都需要独立认证。
  4. 难以重构,随着项目的迭代,可能需要重新划分微服务。
  5. 某些微服务可能使用了防火墙/浏览器不友好的协议,直接访问会有一定的困难。

    使用网关后的请求架构:
    优点:
    1、 易于监控。可在微服务网关收集监控数据并将其推送到外部系统进行分析。
    2、 易于认证。可在微服务网关上进行认证,然后再将请求转发到后端的微服务,而无需在每个微服务中进行认证。
    3、 减少了客户端与各个微服务之间的交互次数。

1.1 Zuul简介

Zuul的核心是一系列的过滤器,这些过滤器可以完成以下功能:
1、身份认证与安全:识别每个资源的验证要求,并拒绝那些与要求不符的请求。
2、审查与监控:在边缘位置追踪有意义的数据和统计结果,从而带来精确的生产视图。
3、动态路由:动态的将请求路由到不同的后端集群。
4、压力测试:逐渐增加指向集群的流量,以了解性能。
5、负载分配:为每一种负载类型分配对应容量,并弃用超出限定值的请求。
6、静态响应处理:在边缘位置直接建立部分响应,从而避免其转发到内部集群。
7、多区域弹性:跨越AWS Region进行请求路由,皆在实现ELB使用的多样化,以及让系统的边缘更贴近系统的使用者。

1.2 添加依赖:spring-cloud-starter-netflix-zuul

新建一个工程:microservice-simple-gateway-zuul

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

		<!-- 添加Eureka Client依赖 -->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
			<version>2.1.0.RELEASE</version>
		</dependency>

		<!-- 添加zuul依赖 -->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
			<version>2.1.2.RELEASE</version>
		</dependency>

1.3 配置项

需要注册到Eureka上。

server:
  port: 5031

spring:
  application:
    name: microservice-simple-gateway-zuul
    
eureka:
  client:
    serviceUrl:
      defaultZone: http://hht:123456@localhost:8761/eureka/,http://hht:123456@localhost:8762/eureka/
    instance:
      prefer-ip-address: true

1.4 启动类添加注解: @EnableZuulProxy

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

1.5 测试

原来直接访问movie:http://localhost:8032/movie/user/feign/1 现在可以访问网关:http://localhost:5031/microservice-simple-consumer-movie/movie/user/feign/1

2. Zuul的容错,hystrix.stream(待补)

3. Zuul的路由端点

当@EnableZuulProxy与Spring Boot Actuator配合使用时,Zuul会暴露一个路由管理端点“routes”。借助这个端点,可以方便、直观的查看以及管理Zuul的路由。

由于spring-cloud-starter-netflix-zuul已经包含了spring-boot-starter-actuator,所以Zuul已经具备路由管理的能力。

3.1 SpringBoot2.0需要开启routes端点

这个时候直接查看路由端点:http://localhost:5031/actuator/routes , 会报错。

重点注意:

  • 因为Spring Boot2.x中,默认只开放了info、health两个端点,剩余的需要自己通过配置management.endpoints.web.exposure.include属性来加载(有include自然就有exclude,不做详细概述了)

添加配置,开启routes端点:

management:
  endpoints:
    web:
      exposure:
        include: '*'

3.2 查看路由端点:http://localhost:5031/actuator/routes

docker SpringCloud DevOps的区别 springcloud与docker区别_ide

3.3 路由配置详情

详情参考:参考:

  • 1、自定义指定微服务的访问路径 (微服务就会被映射到 “ /user/** ”路径。)
zuul:
	routes: 
		microservice-provider-user: /user/**
  • 2、忽略指定微服务 (Zuul忽略user、movie两个微服务,只代理其他微服务。 忽略多个微服务用逗号隔开)
zuul:
	ignored-services: microservice-provider-user,microservice-consumer-movie
  • 3、忽略所有微服务,只路由指定的微服务
zuul:
	ignored-services: '*'     # 使用'*'可忽略所有的微服务
	routes:
		microservice-provider-user: /user/**
  • 4、同时指定微服务的serviceId和对应路径 (本例的效果同示例1)
zuul:
	routes:
		hht-user-route:			# 该配置方式中,hht-user-route只是给路由一个名称,可以任意起名
			service-id: microservice-provider-user
			path: /user/**		# service-id对应的路径
  • 5、同时指定path和URL (这样就可以将 /user/** 映射到 http://localhost:8000/**)

注意:使用这种方式配置的路由不会作为HystrixCommand执行,同时也不能使用Ribbon来负载均衡多个URL。例子6可以解决该问题。

zuul:
	routes:
		hht-user-route:			# 该配置方式中,hht-user-route只是给路由一个名称,可以任意起名
			url: http://localhost:8000/			# 指定的URL
			path: /user/**						# url对应的路径
  • 6、同时指定path和URL,并且不破坏Zuul的Hystrix、Ribbon特性
zuul:
	routes:
		hht-user-route:
			path: /user/**
			service-id: microservice-provider-user
ribbon:
	eureka:
		enabled: false			# 为Ribbon禁用Eureka
microservice-provider-user:
	ribbon:
		listOfServices: localhost:8000,localhost:8001
  • 7、使用正则表达式指定Zuul的路由匹配规则
    借助PatternServiceRouteMapper,实现从微服务到映射路由的正则配置。
    通过这段代码可实现将诸如:microservice-provider-user-v1这个微服务,映射到/v1/microservice-provider-user/** 这个路径。
@Bean
public PatternServiceRouteMapper serviceRouteMapper(){
	// 调用构造函数PatternServiceRouteMapper(String servicePatern, String routePattern)
	// servicePattern: 指定微服务的正则
	// routePattern: 指定路由正则
	return new PatternServiceRouteMapper("(?<name>^.+)-(?<servion>v.+$)", "${version}/${name}");
}
  • 8、路由前缀
    示例1:访问Zuul的 /api/microservice-provider-user/1 的路径,会被转发到 microservice-provider-user的 /api/1
zuul:
	prefix: /api
	strip-prefix: false
	routes:
		microservice-provider-user: /user/**

示例2: 访问Zuul的 /user/1 路径,会被转发到 microservice-provider-user 的 /user/1

zuul:
	routes:
		microservice-provider-user:
			path: /user/**
			strip-prefix: false
  • 9、忽略某些路径 (将microservice-provider-user微服务映射到 /user/**路径,但会忽略所有包含 /admin/的路径)
zuul:
	ignoredPatterns: /**/admin/** 		# 忽略所有包含 /admin 的路径
	routes:
		microservice-provider-user: /user/**

【自己的demo举2个简单的例子:】

zuul:
  routes:
    microservice-simple-consumer-movie: /movie/**
  #排除某些路由
  ignored-services: microservice-simple-provider-user

再查看http://localhost:5031/actuator/routes,发现已经没有了“microservice-simple-provider-user”。

4. Zuul的安全与Header(待补)

5. 使用Zuul上传文件(待补)

对于小文件(1M以内)上传,无需任何处理,即可正常上传。
对于大文件(10M以上)上传,需要为上传路径添加/zuul前缀。也可以使用zuul.servlet-path来自定义前缀。

6. Zuul的过滤器

Zuul中定义了4种标准的过滤器类型,这些过滤器类型对应于请求的典型生命周期。

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

除了默认的过滤器类型,Zuul还允许创建自定义的过滤器类型。 例如:可以定制一种STATIC类型的过滤器,直接在Zuul中生成响应,而不将请求转发到后端的微服务。

Zuul请求的生命周期如下图:

docker SpringCloud DevOps的区别 springcloud与docker区别_ide_02

6.1 编写Zuul过滤器

6.1.1 自定义一个pre类型的filter

要继承ZuulFilter类,并实现以下几个方法:

  • filterType:返回过滤器的类型。有pre、route、post、error几种。
  • filterOrder:返回一个int值来指定过滤器的执行顺序,不同的过滤器允许返回相同的数字。
  • shouldFilter:返回一个boolean值来判断该过滤器是否要执行。true表示执行,false表示不执行。
  • run:过滤器的具体逻辑。本例中让它打印了请求的http方法及请求地址。
public class MyPreRequestLogFilter extends ZuulFilter{
	
	private static final Logger logger = LoggerFactory.getLogger(MyPreRequestLogFilter.class);

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

	@Override
	public Object run() throws ZuulException {
		RequestContext context = RequestContext.getCurrentContext();
		HttpServletRequest request = context.getRequest();
		logger.info(String.format("================ send 【%s】 request to 【%s】 .", request.getMethod(), request.getRequestURL().toString()));
		return null;
	}

	@Override
	public String filterType() {
		return "pre";
	}

	@Override
	public int filterOrder() {
		return 0;
	}

}

6.1.2 修改启动类

@Bean
	public MyPreRequestLogFilter myPreRequestFilter() {
		return new MyPreRequestLogFilter();
	}

6.1.3 测试

访问:http://localhost:5031/microservice-simple-consumer-movie/movie/user/feign/1, 确认打印了日志。

6.2 禁用过滤器

配置文件: zuul.<SimpleClassName>.<filterType>.disable=true

例如:禁用上面的自定义过滤器

#禁用ZuulFilter
  zuul:
  	MyPreRequestLogFilter:
    	pre:
      	disable: true

可以看到过滤器不起作用了。

6.3 Zuul默认整合了Hystrix

可直接访问:http://localhost:5031/hystrix.stream, 查看监控,也可以在仪表盘中直观看图。
由图可知,Zuul的Hystrix监控的粒度是微服务,而不是某个API。同时也说明,所有经过Zuul的请求,都会被Hystrix保护起来。

6.4 Zuul的容错与回退

6.4.1 为Zuul添加回退

想要为Zuul添加回退,需要实现FallbackProvider接口。在实现类中,指定为哪个微服务提供回退,并提供一个FallbackProvider作为回退响应。

package com.itmuch.cloud.gateway.zuul.fallback;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;

import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;


@Component
public class MovieFallbackProvider implements FallbackProvider {

	@Override
	public String getRoute() {
		//表明是为哪个微服务提供回退
		return "microservice-simple-consumer-movie";
	}

	@Override
	public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
		
		return new ClientHttpResponse() {
			
			@Override
			public HttpHeaders getHeaders() {
				// headers设定
				HttpHeaders headers = new HttpHeaders();
				MediaType mt = new MediaType("application", "json", Charset.forName("UTF-8"));
				headers.setContentType(mt);
				return headers;
			}
			
			@Override
			public InputStream getBody() throws IOException {
				// 响应体
				return new ByteArrayInputStream("用户微服务不可用,请稍后再试。".getBytes());
			}
			
			@Override
			public String getStatusText() throws IOException {
				// 状态文本,本例返回的其实就是OK,详见HttpStatus
				return getStatusCode().getReasonPhrase();
			}
			
			@Override
			public HttpStatus getStatusCode() throws IOException {
				// fallback时的状态码
				return HttpStatus.OK;
			}
			
			@Override
			public int getRawStatusCode() throws IOException {
				// 数字类型的状态码,本例返回的其实就是200, 详见HttpStatus
				return this.getStatusCode().value();
			}
			
			@Override
			public void close() {
				// TODO Auto-generated method stub
				
			}
		};
		
	}
}

停掉微服务“microservice-simple-consumer-movie”,再访问http://localhost:5031/microservice-simple-consumer-movie/movie/user/feign/1, 效果如下:

docker SpringCloud DevOps的区别 springcloud与docker区别_ide_03

6.5 Zuul的高可用

Zuul的高可用非常关键,因为外部请求到后端微服务的请求都会经过Zuul。因而在生产环境中一般都需要部署高可用的Zuul以避免单点故障。

6.5.1 客户端注册到Eureka Server上

这种情况是指客户端也是一个微服务。不通过前面那种直接调用目标的微服务名,而是直接调用microservice-simple-gataway-zuul 。

这种情况下,Zuul高可用非常简单,只需将多个Zuul节点注册到Eureka Server上,即可。

Zuul高可用架构:

docker SpringCloud DevOps的区别 springcloud与docker区别_spring_04


复制一个zuul工程:microservice-simple-gataway-zuul-2

本例

  • 把movie作为客户端,它已经注册到Eureka Server上
  • 在movie中新建一个FeignClient,像一般微服务那样调用zuul。
@FeignClient(name = "microservice-simple-gateway-zuul")
public interface ZuulFeignClient {

	@GetMapping(value = "/microservice-simple-provider-user/user/{id}")
	public UserEntity findByIdFeign(@PathVariable("id") int id);
	
}
  • 新增一个接口: /zuul/user/feign/{id}
  • 访问:http://localhost:8032/movie/zuul/user/feign/1 ,可以看到效果,确实是通过负载均衡调Zuul,然后再调到user 。

6.5.2 Zuul客户端未注册到Eureka Server上

现实中,更多的场景是客户端没有注册到Eureka Server上。 比如,客户端是一个手机APP — 不可能让所有的手机终端都注册到Eureka上。
这种情况下,可借助一个额外的负载均衡器来实现Zuul的高可用,例如:Nginx 、 HAProxy 、 F5等。

Zuul高可用框架:

docker SpringCloud DevOps的区别 springcloud与docker区别_spring_05

使用Nginx的例子没有写 (待补)

7. 使用Spring Cloud Config统一管理微服务配置

在微服务架构中,微服务的配置管理一般有以下需求:

  • 【集中管理微服务】 一个使用微服务架构的应用系统可能会包含成百上千个微服务,因此集中管理配置非常有必要。
  • 【不同环境不同配置】 例如:不同环境的数据源不一样
  • 【运行期间可动态调整】 例如:可根据各个微服务的负载情况,动态调整数据源连接池大小或熔断阈值,并且在调整配置时不停止微服务。
  • 【配置修改后可自动更新】 如配置发生变化,微服务能够自动更新配置。

SpringCloud Config架构图:

docker SpringCloud DevOps的区别 springcloud与docker区别_spring_06

7.1 创建SpringCloud Config Server

新建一个工程:microservice-simple-config-server

7.1.1 引入依赖包:spring-cloud-config-server

<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-config-server</artifactId>
			<version>2.1.3.RELEASE</version>
		</dependency>

7.1.2 添加配置

server:
  port: 6031

spring:
  application:
    name: microservice-simple-config-server
  cloud:
    config:
      server:
        git:
          uri: https://github.com/guessagithub/Spring-cloud-config-repo
          username: guessagithub
          password: 待定

7.1.3 添加注解 @EnableConfigServer

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

7.1.4 测试数据

在git仓库Spring-cloud-config-repo中,新建两个版本:

  • master:
    新建文件:microservice-simple-provider-user.properties , 内容为: adress=shenzhen-default
    新建文件:microservice-simple-provider-user-dev.properties , 内容为:adress=shenzhen-dev
  • prod:
    新建文件:microservice-simple-provider-user-dev.properties , 内容为: adress=shenzhen-prod

访问以下url和查看对应结果:

  • http://localhost:6031/microservice-simple-provider-user/dev/ 【默认版本为master, 里面有两个文件,虽然URL中指明了为“dev”,但还是会获取文件“microservice-simple-provider-user.properties”】
  • docker SpringCloud DevOps的区别 springcloud与docker区别_ide_07


  • http://localhost:6031/microservice-simple-provider-user/dev/prod 【URL指明版本“prod”,获取到prod中的配置信息, prod中只有一个文件】
  • docker SpringCloud DevOps的区别 springcloud与docker区别_ide_08


  • http://localhost:6031/microservice-simple-provider-user-dev.properties 【默认取master中指定的这个文件内容】
  • docker SpringCloud DevOps的区别 springcloud与docker区别_微服务_09


  • http://localhost:6031/prod/microservice-simple-provider-user-dev.properties 【指定版本prod】
  • docker SpringCloud DevOps的区别 springcloud与docker区别_SpringCloud_10


7.2 创建SpringCloud Config Client

新建一个工程:microservice-simple-config-client

7.2.1 引入依赖包:spring-cloud-config-client

<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-config-client</artifactId>
			<version>2.1.3.RELEASE</version>
		</dependency>

7.2.2 添加配置

  • application.yml:
server:
  port: 6035
  • bootstrap.yml:
spring:
  application:
          #对应config server所获取的配置文件的{application}
    name: microservice-simple-config-client
  cloud:
    config:
      uri: http://localhost:6031/
      #profile对应config server所获取的配置文件中的{profile}
      profile: dev
                #指定GIT仓库的分支,对应config server所获取的配置文件的{label}
      label: master

7.2.3 新建controller

@RestController
@RequestMapping("/configclient")
public class ConfigClientController  {
	
	@Value("${address}")
	private String address;
	
	@GetMapping("/address")
	public String getAddress() {
		return address;
	}
}

7.2.4 测试数据

在git上新建文件:

  • microservice-simple-config-client.properties
  • microservice-simple-config-client-dev.properties

访问URL:http://localhost:6035/configclient/address

docker SpringCloud DevOps的区别 springcloud与docker区别_微服务_11

7.3 Config Server的GIT仓库配置详解

前文中,使用【spring.cloud.config.server.git.uri】指定了一个Git仓库,事实上,该属性非常灵活。

7.3.1 占位符支持

Config Server的占位符支持 {application}、{profile}、{label} 。
示例:

  • 在仓库“Spring-cloud-config-repo”中,新建一个文件“Spring-cloud-config-repo-dev.properties”
  • 配置文件修改为:
  • 访问URL : http://localhost:6031/Spring-cloud-config-repo/dev

7.3.2 匹配模式(待补)

7.3.3 搜索目录

docker SpringCloud DevOps的区别 springcloud与docker区别_spring_12

7.3.4 启动时加载配置文件(待补)