为什么微服务要使用网关
微服务的架构,属于比较细粒度的拆分,会根据业务拆分成很多个微服务,那么有可能一个用户下单,就需要 先调 用户微服务->商品微服务->支付微服务->订单微服务,这样才能完成一次购买商品的操作。如图。
这样的话,让客户端直接与各个微服务进行通信的话,会存在很多问题:
- 客户端多次请求不同的微服务,增加客户端的复杂程度。
- 存在跨域现象。
- 认证复杂,每个微服务都需要单独认证。
- 可重用性较差。重构困难。有可能要拆分某些微服务,重构起来就很麻烦。
所以,微服务给出了网关的解决方案。如图
网关封装了应用程序的内部结构,客户端只需要跟网关做交互就行,就不需要调用各个微服务的接口了。这样,开发就得到了简化,不仅如此,网关还有以下优点:
- 易于监控。可在微服务网关收集并监控数据然后推送到外部系统进行分析。
- 易于认证。可在微服务网关做一次认证,就不需要在每个微服务进行认证了。
- 减少客户端和微服务交互次数。
Zuul 简介
Zuul是netflix开源的微服务网关,它具有以下功能:
- 身份认证和安全:识别每个资源的验证要求,并拒绝那些与要求不符的请求。
- 审查与监控:在边缘位置追踪有意义的数据和统计结果,从而带来精确的生产视图。
- 动态路由:动态的将请求路由到不同的后端集群。
- 压力测试:逐渐增加指向集群的流量以了解性能。
- 负载分配:为每一种负载类型分配对应容量,并弃用超出限定值的请求。
- 静态响应处理:在边缘位置直接建立部分响应,从而避免及转发到内部集群。
- 多区域弹性:跨越AWS区域进行请求路由,旨在实现ELB使用多样化并保证边缘位置与使用者尽可能接近。
Zuul原理
过滤器就是Zuul业务逻辑的核心所在,这是他的运行流程。
zuul的过滤器
- Inbound Filters:前置过滤器,在请求被路由之前调用。可以实现身份认证、记录调试信息等。
- Endpoint Filter:路由过滤器,这种过滤器将请求直接路由到微服务,用于构建发送到微服务的请求。
- Outbound Filters:路由到微服务之后执行。可以用来收集响应信息,将响应信息返给客户端。
实现自己的过滤器很简单,只要集成ZuulFilter
,实现其中的一些方法即可。
Netty作为高性能异步网络通讯框架,如图zuul2.0使用netty server作为网关监听服务器监听客户端发来的请求,然后把请求转发到前置过滤器(inbound filters)进行处理,处理完毕后在把请求使用netty client代理到具体的后端服务器进行处理,处理完毕后在把结果交给后者过滤器(outbound filters)进行处理,然后把处理结果通过nettyServer写回客户端。
有兴趣的可以看看How-It-Works-2.0,也可以看看各种过滤器。
编写Zuul网关
首先新建一个服务 叫做 gateway-zuul。在pom文件中添加:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
然后启动类上添加注解@EnableZuulProxy
。在配置文件中加入配置。
server:
port: 8008
spring:
application:
name: gateway-zuul
eureka:
client:
service-url:
defaultZone: http://user:123456@server1:8000/eureka/,http://user:123456@server2:8002/eureka/
然后启动服务。
新建两个服务分别是 user 和 goods 服务。(也可以用之前的 eureka-client)两个服务都是普通服务,注册到Eureka Server。 分别提供一个方法。
goods
@RestController
@RequestMapping("/goods")
public class GoodsController {
@RequestMapping("/{id}")
public String getGoodsInfo(@PathVariable Long id){
//进行数据库操作,这里省略
return "this is goods service !! ";
}
}
user
@RestController
@RequestMapping("/getUser")
public class UserController {
@RequestMapping("/{id}")
public String getUserInfo(@PathVariable Long id){
//进行数据库操作,这里省略
return "this is user service !! ";
}
}
打开 Eureka Server 的管理界面。
可以看到,两个提供和一个网关都注册上了。
然后直接浏览器请求zuul。http://localhost:8008/eureka-client-user/getUser/1
。可以看到结果。
再访问 http://localhost:8008/eureka-client-goods/goods/1
。可以看到。
到这里就说明zuul网关功能已经实现了。zuul会默认集成Ribbon做负载均衡以及Hystrix做监控。
zuul的路由端点
zuul默认还集成了actuator,在配置文件中加入
# spring boot 升为 2.0 后,为了安全,默认 Actuator 只暴露2个端点,heath 和 info
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: ALWAYS
重启服务。访问http://localhost:8008/actuator/routes
。可以看到
路由配置详解
zuul的路由配置提供更加细粒度、更加精确的控制。比如,只想要代理某一些微服务的请求。
1. 自定义指定微服务的访问路径
配置zuul.routes.serviceId = 指定路径即可。例如:
zuul:
routes:
eureka-client-goods: /goods/**
这样表示 eureka-client-goods
的微服务就被映射到/goods/**
路径。也就是说可以不用输入eureka-client-goods
,用goods
代替了。可以先查看一下映射关系。
访问一下http://localhost:8008/goods/goods/1
即可。
2. 忽略指定微服务
可以使用zuul.ignored-services:
来指定。多个微服务可以使用,
隔开。例如:
zuul:
ignored-services: eureka-client-goods
如果配置了1里面的类似别名一样的映射,那个别名还是可以访问到的。
3. 同时指定Path 和 URL
这样可以将路径映射到具体的url。例如:
zuul:
routes:
user-route:
url: http://localhost:8080/
path:/user/**
但是这样配置会破坏zuul 的 Ribbon 和 Hystrix特性。
4.同时指定Path 和 URL,并且不破坏zuul 的 Ribbon 和 Hystrix特性
zuul:
routes:
user-route:
path:/user/**
service-id: eureka-client-user
ribbon:
eureka:
enabled: false
eureka-client-user:
ribbon:
listOfServers: localhost:8005,localhost:8006
5.路由前缀
给访问路由加个前缀,这前缀是服务提供者的映射路径。
zuul:
routes:
eureka-client-goods: /goods/**
prefix: /goods
strip-prefix: false
这样配置的话。访问http://localhost:8008/goods/eureka-client-goods/1
就相当于eureke-client-goods/goods/1
。感觉没啥用。。
6.忽略某些路径
可以使用ignored-patterns:
来指定忽略某些微服务的敏感路径。