目录
说在前面
服务注册与发现
服务网关及熔断
配置中心
消息中心, 服务链路追踪
小言
说在前面
本文偏小白, 大佬慎入, 若有错误或者质疑, 欢迎留言提问, 谢谢, 祝大家新年快乐.
spring cloud
Spring Cloud 是将分布式系统中一系列基础框架 / 工具进行整合的框架. 其中包含: 服务注册与发现, 服务网关, 熔断器, 配置中心, 消息中心, 服务链路追踪等等 . 这也是一个服务化架构的最小组成元素, 有了这些基本的组成要素, 就可以实现一个最简单的服务架构.
Spring Cloud 并没有重复造轮子, Spring Cloud 只是依赖于 Spring Boot 屏蔽掉了各个框架复杂的配置. 所有的组件就相当于 Spring Cloud 的插件, 开发人员可以根据自己的需要自由结合使用.
有 Spring Boot 这个利器在, 所有组件都可以轻松引入, 便捷开发. 也一定程度上降低了各组件的学习成本, 调试成本, 让开发人员可以轻松上手.
服务注册与发现
从最简单, 最核心的问题出发, 假设服务 A 要调用服务 B, 会有什么问题?
服务在哪?(服务治理问题) 怎么调用?(服务调用问题)
这两个是最核心的问题, 也是任何微服务框架首要解决的两个问题.
为了解决第一个问题 Spring Cloud 提供了 Eureka,Zookeeper,Cloud Foundry,Consul 等服务治理框架的集成. 它们的工作模式是将所有的微服务注册到一个 Server 上, 然后通过心跳进行服务健康监测. 这样服务 A 调用 B 时可以从注册中心拿到可用的服务 B 的地址, 端口进行调用.
我公司就是使用的是 Eureka
Eureka 由两个组件组成: Eureka 服务器和 Eureka 客户端.
先找到 Eureka 服务器, 也就是我们项目的注册中心
EurekaApplication.java
- @EnableEurekaServer
- @SpringBootApplication
- public class EurekaApplication {
- public static void main(String[] args) {
- SpringApplication.run(EurekaserverApplication.class, args);
- }
- }
注解 @EnableEurekaServer 表示该 Spring Boot 应用是一个注册中心. Eureka Server 提供服务注册服务, 各个节点启动后, 会在 Eureka Server 中进行注册, 这样 EurekaServer 中的服务注册表中将会存储所有可用服务节点的信息, 服务节点的信息可以在界面中直观的看到.
配置文件 Bootstrap.YAML
- server:
- port: 8700
- eureka:
- client:
- registerWithEureka: false
- fetchRegistry: false
- serviceUrl:
- defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
- server:
- enableSelfPreservation: false
eureka.client.registerWithEureka: false 和 fetchRegistry: false 来表明自己是一个 eureka server.
enableSelfPreservation: false 表示在此 eureka 服务器中关闭自我保护模式, 所谓自我保护模式是指, 出现网络分区, eureka 在短时间内丢失过多客户端时, 会进入自我保护模式, 即一个服务长时间没有发送心跳, eureka 也不会将其删除. 默认为 true
以上是注册中心 EurekaApplication.java, 而所有单独开发的 SB 项目, 就是 client
而我在查看每个项目的配置文件时, 发现并没有找到 eureka.client.serviceUrl.defaultZone:... 这样的配置
于是猜想可能在启动脚本中添加此参数, 果不其然, 找到服务器上的启动脚本:
-Deureka.client.serviceUrl.defaultZone=http://**.**.**.***:8700/eureka
这就是其作为 eureka 客户端的证据 (真的很想吐槽为什么要写在启动脚本中)
第二个服务调用有人可能认为就是一个简单的 HTTP 或者 RPC 调用, 不是什么问题. 但是在分布式的场景下, 服务调用需要考虑的因素会更多. 比如一个服务有多个实例, 此时请求进来了交给谁处理, 请求的负载怎么平衡到各个实例, 都是比较棘手的问题. Spring Cloud 提供了两种服务调用的方式: 一种是 Ribbon + restTemplate, 另一种是 Feign.
其中 Ribbon 是基于 HTTP 和 TCP 客户端的负载均衡器, restTemplate 是 Spring 提供的 Restful 远程调用的模板, 两者结合就可以达到远程调用的负载均衡.
而 Feign 是一个更加声明式的 HTTP 客户端, 开发者可以像调用本地方法一样调用它, 完全感觉不到是远程调用, 结合 Ribbon 也可以做负载均衡.
服务网关及熔断
微服务中的服务很多, 直接暴露给用户一是不安全, 二是对用户不友好. 因此在微服务和面向服务的架构中, 通常会有一个路由网关的角色, 来负责路由转发和过滤. 对应到 Spring Cloud 中有 Zuul 和 Gateway 两个组件可用.
据查看我公司只使用 zuul 做路由转发用
Zuul 是 Netflix 开源的服务网关 / API 网关, 提供动态路由, 监控, 弹性, 安全性等功能.
ZuulApplication.java
- @SpringBootApplication
- @EnableDiscoveryClient
- @EnableZuulProxy
- @RestController
- public class ZuulApplication {
- public static void main(String[] args) {
- SpringApplication.run(ZuulApplication.class, args);
- }
- /**
- * 跨域许可设置
- */
- @Bean
- public CorsFilter corsFilter() {
- final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource= new UrlBasedCorsConfigurationSource();
- final CorsConfiguration corsConfig = new CorsConfiguration();
- corsConfig.setAllowCredentials(true);
- corsConfig.addAllowedOrigin("*");
- corsConfig.addAllowedHeader("*");
- corsConfig.addAllowedMethod("OPTIONS");
- corsConfig.addAllowedMethod("HEAD");
- corsConfig.addAllowedMethod("GET");
- corsConfig.addAllowedMethod("PUT");
- corsConfig.addAllowedMethod("POST");
- corsConfig.addAllowedMethod("DELETE");
- corsConfig.addAllowedMethod("PATCH");
- urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfig);
- return new CorsFilter(urlBasedCorsConfigurationSource);
- }
- @RequestMapping(value = "/")
- public String heartbeat() {
- return "";
- }
- }
在 zuul 服务下添加一个 corsFilter 实现跨域
Bootstrap.YAML
- server:
- port: 8000
- spring:
- application:
- name: zuul
- cloud:
- config:
- discovery:
- enabled: true
- serviceId: CONFIG
- eureka:
- instance:
- preferIpAddress: true
- leaseRenewalIntervalInSeconds: 1
- leaseExpirationDurationInSeconds: 2
- nonSecurePort: ${server.port}
- client:
- serviceUrl:
- defaultZone: http://${eureka.host}:${eureka.port}/eureka/
application.YAML
- spring:
- profiles:
- active: eureka
- logging:
- config: classpath:logback-error.xml
- server:
- tomcat:
- max-threads: 100
- hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 60000
- ribbon:
- ConnectTimeout: 3000
- ReadTimeout: 60000
- hystrix:
- command:
- default:
- execution:
- timeout:
- enabled: false
- threadpool:
- default:
- coreSize: 50
- maxQueueSize: 100
- queueSizeRejectionThreshold: 100
- javamelody:
- login_name: viroyal
- login_pwd: viroyal2017
- ---
- spring:
- profiles: eureka
- zuul:
- routes:
- account:
- path: /account/**
- serviceId: USERMANAGER
- stripPrefix: false
- about:
- path: /about/**
- serviceId: ABOUT
- stripPrefix: false
- App:
- path: /App/**
- serviceId: CAMPUSCMS
- stripPrefix: false
- res:
- path: /res/**
- serviceId: CMS
- stripPrefix: false
- device:
- path: /device/**
- serviceId: CAMPUS-DEVICE
- stripPrefix: false
hystrix 就是熔断器
熔断器的原理很简单, 如同电力过载保护器. 它可以实现快速失败, 如果它在一段时间内侦测到许多类似的错误, 会强迫其以后的多个调用快速失败, 不再访问远程服务器, 从而防止应用程序不断地尝试执行可能会失败的操作, 使得应用程序继续执行而不用等待修正错误, 或者浪费 CPU 时间去等到长时间的超时产生. 熔断器也可以使应用程序能够诊断错误是否已经修正, 如果已经修正, 应用程序会再次尝试调用操作.
hystrix 默认是打开的, 所以此配置中未进行过多设置, 只设置了 hystrix.threadpool.default.threadPool 的相关属性, 这里说一下 queueSizeRejectionThreshold:100, 是为队列设置拒绝阈值, 当线程池队列到达 100 时则开启熔断器.
具体的 hystrix 所欲配置讲解, 请参考: https://www.cnblogs.com/li3807/p/7501427.html
记住: 熔断器就是保护服务高可用的最后一道防线.
关于超时, 由于 zuul 本身就集合了 hystrix 和 ribbon,zuul 中配置超时时间, 据官方的介绍, 分两种情况:
用 serviceId 进行路由时, 使用 ribbon.ReadTimeout 和 ribbon.SocketTimeout 设置
用指定 url 进行路由时, 使用 zuul.host.connect-timeout-millis 和 zuul.host.socket-timeout-millis 设置
此配置文件是用 serviceId 进行路由的 (根据请求 path, 分发到对应的 serviceId 上), 所以未设置 zuul 超时, 且 hystrix.XX.timeout.enabled 设置成了 false, 关掉了 hystrix 的超时设置 (实际上, 如果同时配置了 Ribbon 和 Hystrix 的超时时间, 则以最小的为准). 所以, 此项目只开启了 ribbon 的超时设置. 像此例中的 ribbon 的超时是作用全局的.
直接使用 serviceId 作为前缀, 可以区分不同客户端下的配置, 类似如下:
- USERMANAGER:
- ribbon:
- ConnectTimeout: 5000
- ReadTimeout: 5000
spring Cloud 超时总结参考: http://www.itmuch.com/spring-cloud-sum/spring-cloud-timeout/
关于 zuul.routes.XXX.stripPrefix 的设置, 当 zuul.routes.XXX.path=/API/** 时, 作用如下例子:
当 stripPrefix=true 的时候 (http://127.0.0.1:8181/api/user/list -> http://192.168.1.100:8080/user/list)
当 stripPrefix=false 的时候 (http://127.0.0.1:8181/api/user/list -> http://192.168.1.100:8080/api/user/list)
路由网关接收了所有的用户请求, 有着很高的负载, 因此它通常是一个集群. 用户的请求会先经过一层负载均衡被发到路由网关.
配置中心
在微服务应用中, 服务数量巨多, 而每个服务不同环境都有着不同的配置, 为了方便服务配置文件统一管理, 实时更新, 所以需要分布式配置中心组件. 需要注意的是此处的配置与注册中心注册的配置信息是两个概念, 此处的配置是服务本身的一些配置信息.
ConfigApplication.java
- @SpringBootApplication
- @EnableConfigServer
- @EnableEurekaClient
- public class ConfigApplication {
- public static void main(String[] args) {
- SpringApplication.run(ConfigApplication.class, args);
- }
- }
本身也是 EurekaClient, 使用 @EnableConfigServer 成为配置中心, 提供配置服务
bootstraop.YAML
- #comment can't have chinese
- #using outer.NET interface is convenient for local webapp start and debug
- server:
- port: 8888
- spring:
- application:
- name: config
- profiles:
- active: native
- cloud:
- config:
- server:
- native:
- searchLocations:
- file:/var/configs
- inetutils:
- ignoredInterfaces:
- - eth0
- eureka:
- instance:
- preferIpAddress: true
- leaseRenewalIntervalInSeconds: 1
- leaseExpirationDurationInSeconds: 2
- nonSecurePort: ${server.port}
- client:
- serviceUrl:
- defaultZone: http://${eureka.host}:${eureka.port}/eureka/
这里 spring.appliction.name 为 config
我看了一下其他服务的 Bootstrap.YAML 中有两项配置
- sprong.cloud.config.discovery.enabled: true
- sprong.cloud.config.discovery.serviceId: CONFIG
第一个作用是: 使用注册中心找寻 config-server 的地址
第二个作用是: 配置中心在注册中心的 applicationName
通过找到配置中心 发现 配置文件存储在 / var/configs 路径下, 且读取文件名和本服务 serviceId 相同的文件中的配置
消息中心, 服务链路追踪
据我查看, 我公司并未实现消息中心和服务链路追踪, 这里我就总结一下他们的作用吧
消息中心
在微服务架构的系统中, 我们通常会使用轻量级的消息代理来构建一个共用的消息主题让系统中所有微服务实例都能连接上来, 由于该主题中产生的消息会被所有实例监听和消费, 所以我们称它为消息总线. 在总线上的各个实例都可以方便地广播一些需要让其他连接在该主题上的实例都知道的消息, 例如配置信息的变更或者其他一些管理操作等.
由于消息总线在微服务架构系统的广泛使用, 所以它同配置中心一样, 几乎是微服务架构中的必备组件. spring cloud 作为微服务架构综合性的解决方案, 对此自然也有自己的实现, 这就是 spring cloud bus. 通过 spring cloud bus, 可以非常容易的搭建起消息总线, 同时实现了一些消息总线中的常用功能, 比如配合 spring cloud config 实现微服务应用配置信息的动态更新等.
服务链路追踪
微服务架构是一个分布式架构, 它按业务划分服务单元, 一个分布式系统往往有很多个服务单元. 由于服务单元数量众多, 业务的复杂性, 如果出现了错误和异常, 很难去定位. 主要体现在, 一个请求可能需要调用很多个服务, 而内部服务的调用复杂性, 决定了问题难以定位. 所以微服务架构中, 必须实现分布式链路追踪, 去跟进一个请求到底有哪些服务参与, 参与的顺序又是怎样的, 从而达到每个请求的步骤清晰可见, 出了问题, 很快定位.
在微服务系统中, 一个来自用户的请求, 请求先达到前端 A(如前端界面), 然后通过远程调用, 达到系统的中间件 B,C(如负载均衡, 网关等), 最后达到后端服务 D,E, 后端经过一系列的业务逻辑计算最后将数据返回给用户. 对于这样一个请求, 经历了这么多个服务, 怎么样将它的请求过程的数据记录下来呢? 这就需要用到服务链路追踪.
我公司业务体系不大, 所以就为完成此项集成吧.
小言
以上来看, 可以说这是最精简的 spring cloud 分布式系统的构成了, 不谈具体技术实现如何, 大体的架构还是有的, 也让我对 spring cloud 有了广义上实质的了解.
来源: http://www.bubuko.com/infodetail-2915925.html