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、 减少了客户端与各个微服务之间的交互次数。
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
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请求的生命周期如下图:
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, 效果如下:
6.5 Zuul的高可用
Zuul的高可用非常关键,因为外部请求到后端微服务的请求都会经过Zuul。因而在生产环境中一般都需要部署高可用的Zuul以避免单点故障。
6.5.1 客户端注册到Eureka Server上
这种情况是指客户端也是一个微服务。不通过前面那种直接调用目标的微服务名,而是直接调用microservice-simple-gataway-zuul 。
这种情况下,Zuul高可用非常简单,只需将多个Zuul节点注册到Eureka Server上,即可。
Zuul高可用架构:
复制一个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高可用框架:
使用Nginx的例子没有写 (待补)
7. 使用Spring Cloud Config统一管理微服务配置
在微服务架构中,微服务的配置管理一般有以下需求:
- 【集中管理微服务】 一个使用微服务架构的应用系统可能会包含成百上千个微服务,因此集中管理配置非常有必要。
- 【不同环境不同配置】 例如:不同环境的数据源不一样
- 【运行期间可动态调整】 例如:可根据各个微服务的负载情况,动态调整数据源连接池大小或熔断阈值,并且在调整配置时不停止微服务。
- 【配置修改后可自动更新】 如配置发生变化,微服务能够自动更新配置。
SpringCloud Config架构图:
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”】
- http://localhost:6031/microservice-simple-provider-user/dev/prod 【URL指明版本“prod”,获取到prod中的配置信息, prod中只有一个文件】
- http://localhost:6031/microservice-simple-provider-user-dev.properties 【默认取master中指定的这个文件内容】
- http://localhost:6031/prod/microservice-simple-provider-user-dev.properties 【指定版本prod】
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
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 搜索目录
7.3.4 启动时加载配置文件(待补)