通过之前几篇Spring Cloud中几个核心组件的介绍,我们已经可以构建一个简略的(不够完善)微服务架构了。比如下图所示: 愿意了解源码的朋友直接求求交流分享技术 一零三八七七四六二六

我们使用Spring Cloud Netflix中的Eureka实现了服务注册中心以及服务注册与发现;而服务间通过Ribbon或Feign实现服务的消费以及均衡负载;通过Spring Cloud Config实现了应用多环境的外部化配置以及版本管理。为了使得服务集群更为健壮,使用Hystrix的融断机制来避免在微服务架构中个别服务出现异常时引起的故障蔓延。

在该架构中,我们的服务集群包含:内部服务Service A和Service B,他们都会注册与订阅服务至Eureka Server,而Open Service是一个对外的服务,通过均衡负载公开至服务调用方。本文我们把焦点聚集在对外服务这块,这样的实现是否合理,或者是否有更好的实现方式呢?

先来说说这样架构需要做的一些事儿以及存在的不足:

首先,破坏了服务无状态特点。为了保证对外服务的安全性,我们需要实现对服务访问的权限控制,而开放服务的权限控制机制将会贯穿并污染整个开放服务的业务逻辑,这会带来的最直接问题是,破坏了服务集群中REST API无状态的特点。从具体开发和测试的角度来说,在工作中除了要考虑实际的业务逻辑之外,还需要额外可续对接口访问的控制处理。 其次,无法直接复用既有接口。当我们需要对一个即有的集群内访问接口,实现外部服务访问时,我们不得不通过在原有接口上增加校验逻辑,或增加一个代理调用来实现权限控制,无法直接复用原有的接口。 面对类似上面的问题,我们要如何解决呢?下面进入本文的正题:服务网关!

为了解决上面这些问题,我们需要将权限控制这样的东西从我们的服务单元中抽离出去,而最适合这些逻辑的地方就是处于对外访问最前端的地方,我们需要一个更强大一些的均衡负载器,它就是本文将来介绍的:服务网关。

服务网关是微服务架构中一个不可或缺的部分。通过服务网关统一向外系统提供REST API的过程中,除了具备服务路由、均衡负载功能之外,它还具备了权限控制等功能。Spring Cloud Netflix中的Zuul就担任了这样的一个角色,为微服务架构提供了前门保护的作用,同时将权限控制这些较重的非业务逻辑内容迁移到服务路由层面,使得服务集群主体能够具备更高的可复用性和可测试性。

下面我们通过实例例子来使用一下Zuul来作为服务的路有功能。

准备工作

在使用Zuul之前,我们先构建一个服务注册中心、以及两个简单的服务,比如:我构建了一个service-A,一个service-B。然后启动eureka-server和这两个服务。通过访问eureka-server,我们可以看到service-A和service-B已经注册到了服务中心。

开始使用Zuul 引入依赖spring-cloud-starter-zuul、spring-cloud-starter-eureka,如果不是通过指定serviceId的方式,eureka依赖不需要,但是为了对服务集群细节的透明性,还是用serviceId来避免直接引用url的方式吧。

<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-zuul</artifactId>
</dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
复制代码

应用主类使用@EnableZuulProxy注解开启Zuul

@EnableZuulProxy
@SpringCloudApplication
public class Application {public static void main(String[] args) {new SpringApplicationBuilder(Application.class).web(true).run(args);}}
复制代码

这里用了@SpringCloudApplication注解,之前没有提过,通过源码我们看到,它整合了@SpringBootApplication、@EnableDiscoveryClient、@EnableCircuitBreaker,主要目的还是简化配置。这几个注解的具体作用这里就不做详细介绍了,之前的文章已经都介绍过。

application.properties中配置Zuul应用的基础信息,如:应用名、服务端口等。

spring.application.name=api-gateway
server.port=5555
复制代码

Zuul配置 完成上面的工作后,Zuul已经可以运行了,但是如何让它为我们的微服务集群服务,还需要我们另行配置,下面详细的介绍一些常用配置内容。

服务路由 通过服务路由的功能,我们在对外提供服务的时候,只需要通过暴露Zuul中配置的调用地址就可以让调用方统一的来访问我们的服务,而不需要了解具体提供服务的主机信息了。

在Zuul中提供了两种映射方式:

通过url直接映射,我们可以如下配置:

# routes to url
zuul.routes.api-a-url.path=/api-a-url/**
zuul.routes.api-a-url.url=http://localhost:2222/
复制代码

该配置,定义了,所有到Zuul的中规则为:/api-a-url/**的访问都映射到http://localhost:2222/上,也就是说当我们访问http://localhost:5555/api-a-url/add?a=1&b=2的时候,Zuul会将该请求路由到:http://localhost:2222/add?a=1&b=2上。

其中,配置属性zuul.routes.api-a-url.path中的api-a-url部分为路由的名字,可以任意定义,但是一组映射关系的path和url要相同,下面讲serviceId时候也是如此。

通过url映射的方式对于Zuul来说,并不是特别友好,Zuul需要知道我们所有为服务的地址,才能完成所有的映射配置。而实际上,我们在实现微服务架构时,服务名与服务实例地址的关系在eureka server中已经存在了,所以只需要将Zuul注册到eureka server上去发现其他服务,我们就可以实现对serviceId的映射。例如,我们可以如下配置:

zuul.routes.api-a.path=/api-a/**
zuul.routes.api-a.serviceId=service-Azuul.routes.api-b.path=/api-b/**
zuul.routes.api-b.serviceId=service-Beureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/
复制代码

针对我们在准备工作中实现的两个微服务service-A和service-B,定义了两个路由api-a和api-b来分别映射。另外为了让Zuul能发现service-A和service-B,也加入了eureka的配置。

接下来,我们将eureka-server、service-A、service-B以及这里用Zuul实现的服务网关启动起来,在eureka-server的控制页面中,我们可以看到分别注册了service-A、service-B以及api-gateway

尝试通过服务网关来访问service-A和service-B,根据配置的映射关系,分别访问下面的url

http://localhost:5555/api-a/add?a=1&b=2: 通过serviceId映射访问service-A中的add服务 http://localhost:5555/api-b/add?a=1&b=2: 通过serviceId映射访问service-B中的add服务 http://localhost:5555/api-a-url/add?a=1&b=2: 通过url映射访问service-A中的add服务

推荐使用serviceId的映射方式,除了对Zuul维护上更加友好之外,serviceId映射方式还支持了断路器,对于服务故障的情况下,可以有效的防止故障蔓延到服务网关上而影响整个系统的对外服务

服务过滤 在完成了服务路由之后,我们对外开放服务还需要一些安全措施来保护客户端只能访问它应该访问到的资源。所以我们需要利用Zuul的过滤器来实现我们对外服务的安全控制。

在服务网关中定义过滤器只需要继承ZuulFilter抽象类实现其定义的四个抽象函数就可对请求进行拦截与过滤。

比如下面的例子,定义了一个Zuul过滤器,实现了在请求被路由之前检查请求中是否有accessToken参数,若有就进行路由,若没有就拒绝访问,返回401 Unauthorized错误。

public class AccessFilter extends ZuulFilter  {private static Logger log = LoggerFactory.getLogger(AccessFilter.class);@Overridepublic String filterType() {return "pre";}@Overridepublic int filterOrder() {return 0;}@Overridepublic boolean shouldFilter() {return true;}@Overridepublic Object run() {RequestContext ctx = RequestContext.getCurrentContext();HttpServletRequest request = ctx.getRequest();log.info(String.format("%s request to %s", request.getMethod(), request.getRequestURL().toString()));Object accessToken = request.getParameter("accessToken");if(accessToken == null) {log.warn("access token is empty");ctx.setSendZuulResponse(false);ctx.setResponseStatusCode(401);return null;}log.info("access token ok");return null;}}
复制代码

自定义过滤器的实现,需要继承ZuulFilter,需要重写实现下面四个方法:

filterType:返回一个字符串代表过滤器的类型,在zuul中定义了四种不同生命周期的过滤器类型,具体如下:

pre:可以在请求被路由之前调用

routing:在路由请求时候被调用

post:在routing和error过滤器之后被调用

error:处理请求时发生错误时被调用

filterOrder:通过int值来定义过滤器的执行顺序

shouldFilter:返回一个boolean类型来判断该过滤器是否要执行,所以通过此函数可实现过滤器的开关。在上例中,我们直接返回true,所以该过滤器总是生效。

run:过滤器的具体逻辑。需要注意,这里我们通过ctx.setSendZuulResponse(false)令zuul过滤该请求,不对其进行路由,然后通过ctx.setResponseStatusCode(401)设置了其返回的错误码,当然我们也可以进一步优化我们的返回,比如,通过ctx.setResponseBody(body)对返回body内容进行编辑等。

在实现了自定义过滤器之后,还需要实例化该过滤器才能生效,我们只需要在应用主类中增加如下内容:

@EnableZuulProxy
@SpringCloudApplication
public class Application {public static void main(String[] args) {new SpringApplicationBuilder(Application.class).web(true).run(args);}@Beanpublic AccessFilter accessFilter() {return new AccessFilter();}}
复制代码

启动该服务网关后,访问:

http://localhost:5555/api-a/add?a=1&b=2: 返回401错误 http://localhost:5555/api-a/add?a=1&b=2&accessToken=token: 正确路由到server-A,并返回计算内容

最后,总结一下为什么服务网关是微服务架构的重要部分,是我们必须要去做的原因:

不仅仅实现了路由功能来屏蔽诸多服务细节,更实现了服务级别、均衡负载的路由。 实现了接口权限校验与微服务业务逻辑的解耦。通过服务网关中的过滤器,在各生命周期中去校验请求的内容,将原本在对外服务层做的校验前移,保证了微服务的无状态性,同时降低了微服务的测试难度,让服务本身更集中关注业务逻辑的处理。 实现了断路器,不会因为具体微服务的故障而导致服务网关的阻塞,依然可以对外服务。资料源码来源

转载于:https://juejin.im/post/5c64cefa6fb9a049d74831d3

Spring Cloud构建微服务架构-服务网关相关推荐

  1. Spring Cloud构建微服务架构(五)服务网关

    通过之前几篇Spring Cloud中几个核心组件的介绍,我们已经可以构建一个简略的(不够完善)微服务架构了.比如下图所示: alt 我们使用Spring Cloud Netflix中的Eureka实 ...

  2. Spring Cloud构建微服务架构:服务网关(路由配置)【Dalston版】

    在上一篇<Spring Cloud构建微服务架构:服务网关(基础)>一文中,我们通过使用Spring Cloud Zuul构建了一个基础的API网关服务,同时也演示了Spring Clou ...

  3. Spring Cloud构建微服务架构:分布式服务跟踪(整合zipkin)【Dalston版】

    通过上一篇<分布式服务跟踪(整合logstash)>,我们虽然已经能够利用ELK平台提供的收集.存储.搜索等强大功能,对跟踪信息的管理和使用已经变得非常便利.但是,在ELK平台中的数据分析 ...

  4. Spring Cloud构建微服务架构:分布式服务跟踪(整合logstash)【Dalston版】

    通过之前的<入门示例>,我们已经为两个由SpringCloud构建的微服务项目 trace-1和 trace-2引入了Spring Cloud Sleuth的基础模块 spring-clo ...

  5. Spring Cloud构建微服务架构:分布式服务跟踪(跟踪原理)

    通过上一篇<分布式服务跟踪(入门)>的例子,我们已经通过Spring Cloud Sleuth往微服务应用中添加了实现分布式跟踪具备的基本要素.下面通过本文来详细说说实现分布式服务跟踪的一 ...

  6. Spring Cloud构建微服务架构:服务容错保护(Hystrix断路器)

    断路器 断路器模式源于Martin Fowler的Circuit Breaker一文."断路器"本身是一种开关装置,用于在电路上保护线路过载,当线路中有电器发生短路时," ...

  7. Spring Cloud构建微服务架构(七)消息总线(续:Kafka)

    Spring Cloud Bus除了支持RabbitMQ的自动化配置之外,还支持现在被广泛应用的Kafka.在本文中,我们将搭建一个Kafka的本地环境,并通过它来尝试使用Spring Cloud B ...

  8. Spring Cloud构建微服务架构:分布式配置中心【Dalston版】

    Spring Cloud Config是Spring Cloud团队创建的一个全新项目,用来为分布式系统中的基础设施和微服务应用提供集中化的外部配置支持,它分为服务端与客户端两个部分.其中服务端也称为 ...

  9. Spring Cloud构建微服务架构:服务容错保护(Hystrix服务降级)【Dalston版】

    前言 在微服务架构中,我们将系统拆分成了一个个的服务单元,各单元应用间通过服务注册与订阅的方式互相依赖.由于每个单元都在不同的进程中运行,依赖通过远程调用的方式执行,这样就有可能因为网络原因或是依赖服 ...

  10. Spring Cloud构建微服务架构:服务容错保护(Hystrix依赖隔离)【Dalston版】

    前言 在上一篇<Spring Cloud构建微服务架构:服务容错保护(Hystrix服务降级)>中,我们已经体验了如何使用@HystrixCommand来为一个依赖资源定义服务降级逻辑.实 ...

最新文章

  1. 如何安装系统认证签名过的APK
  2. android原生组件,XUI: 一个简洁而优雅的Android原生UI框架,解放你的双手!
  3. 渲染优化 lock unlock
  4. HTML5 文件操作API
  5. diamond淘宝框架使用
  6. 计算机架构专业排名,全国大学计算机专业排名(转贴)
  7. 华为手机的分类有何区别_“鸿蒙”系统能不能玩安卓游戏?如果能,它跟安卓系统有何区别?...
  8. 编译原理——实验壹——TINY语言的词法分析
  9. pingback协议与traceback协议的区别
  10. asp.net电子影像相册_大连孕妈看过来 | 290元=孕中期四维排畸+孕妇写真+胎宝电子影集...
  11. C++ 数组动态分配
  12. 2010-6-15 Linux 学习笔记
  13. 从零开始学PCR技术(五):试验污染
  14. 慕课 springmvc 起步 maven wbe
  15. jQuery对Class属性的操作
  16. win10删除多余账户_win10删除多余账户,win10
  17. 计算机组成原理——乘法运算(一位乘)
  18. SSM+在线学习系统 毕业设计-附源码131843
  19. [Android通信]基于socket的聊天app(七):好友分组
  20. AI成功破译古老未知语言,人工智能技术开辟历史研究新时代

热门文章

  1. 地平线新征程!官网焕然一新,二代自动驾驶芯片势已蓄,只待发
  2. 浏览器事件循环与node事件循环
  3. data (phantonjs onclick)exploring cleaning
  4. oracle 使用netca报错
  5. Unity 3D:在现有的Android游戏场景中显示AdMob的横幅
  6. Android日志打印类LogUtils,能够定位到类名,方法名以及出现错误的行数并保存日志文件...
  7. 新生赛(2) problem 2 丁磊养猪
  8. 用ATL的W2A和A2W宏转换Unicode与ANSI字符串
  9. JavaScript Table排序
  10. 5G NGC — AMF 接入和移动性管理功能