irpas技术客

Spring Cloud & Alibaba 微服务详细笔记_渣渣苏

未知 3690

文章目录 SpringCloud一、微服务概述1.1、什么是微服务?1.2、为什么是微服务?1.3、架构演变1.4、微服务的解决方案 二、什么是SpringCloud2.1、官方定义2.2、核心架构及其组件 三、环境搭建3.1、版本命名3.2、版本选择3.3、基本环境搭建 四、服务注册中心4.1、什么服务注册中心4.2、常用的注册中心4.2.1、Eureka开发Eureka Server开发Eureka ClientEureka自我保护机制Eureka 已经停止更新Eureka集群搭建小结 4.2.2、Consul安装consulspringcloud集成consulconsul 关闭健康监控检查consul 开启健康监控检查 4.2.3、不同服务中心区别 五、服务间通信方式基于RestTemplate的服务调用RestTemplate 服务调用 基于Ribbon的服务调用1、Ribbon 服务调用2、Ribbon负载均衡策略3、修改服务的默认负载均衡策略4、Ribbon停止维护 六、OpenFeign组件6.1、OpenFeign 组件1、openFeign 服务调用2、调用服务并传参GET方式调用服务传递参数POST方式调用服务传递参数 3、OpenFeign超时处理4.OpenFeign调用详细日志展示 七、Hystrix组件使用7.1、服务雪崩7.2、服务熔断7.3、服务降级7.4、降级和熔断对比7.5、服务熔断的实现7.6、服务降级的实现7.7、Hystrix Dashboard(仪表盘)7.8、Hystrix停止维护 八、Gateway组件8.1、什么是服务网关8.2、zuul 1.x 2.x(netflix 组件)8.3、Gateway (spring)1、开发网关动态路由2、网关路径的解析规则3、配置路由服务负载均衡4、常用路由predicate(断言,验证)5、常用的Filter以及自定义filter 九、Config组件9.1、什么是Config9.2、Config Server 开发9.3、Config Client 开发9.4、手动配置刷新 十、Bus组件10.1、什么是Bus (AMQP RibbitMQ、Kafka)10.2、实现配置刷新原理10.3、搭建RabbitMQ服务10.4、实现自动配置刷新10.5、指定服务刷新配置10.6、集成Webhook实现自动刷新 十一、小结 Spring Cloud Alibaba一、简介二、环境搭建三、Nacos3.1、什么是Nacos3.2、安装nacos3.3、开发服务注册到nacos3.4、服务间通信3.5、使用nacos作为统一配置中心3.5.1、从nacos获取配置3.5.2、实现自动配置刷新3.5.3、DateId3.5.4、命名空间3.5.5、配置分组 3.6、nacos持久化3.7、nacos集群搭建 四、sentinel 流量卫兵4.1、什么是sentinel4.2、sentinel dashboard的安装普通安装Docker方式安装 4.3、sentinel 实时监控服务4.4、sentinel 流量控制QPS限流线程数限流流控模式流控效果 4.5、熔断降级降级策略 4.6、SentinelResource注解 总结

SpringCloud 一、微服务概述

集群:cluster 同一种软件服务的多个服务节点共同为系统提供服务过程 称之为该软件服务集群

分布式:distribute 不同软件集群共同为一个系统提供服务 这个称之为分布式系统

1.1、什么是微服务?

微服务(Microservice Architecture)是近几年流行的一种架构思想,关于它的概念很难一言以蔽之。

究竟什么是微服务呢?我们在此应用 ThoughtWorks 公司的首席科学家 Martin Fowler 于2014年提出的一段话:

官网:https://martinfowler.com/articles/microservices.html

汉化:https://·/liuning8023/p/4493156.html

In short, the microservice architectural(架构) style is an approach to developing a single application as a suite(系列) of small services, each running in its own process(进程) and communicating with lightweight mechanisms, often an HTTP resource API. These services are built around business(业务) capabilities(单元) and independently(独立) deployable(部署) by fully automated deployment machinery. There is a bare(基于) minimum of centralized(分布式) management(管理) of these services, which may be written in different programming languages and use different data storage technologies.

简言之,微服务体系结构风格是一种将单个应用程序开发为一套小型服务的方法,每个服务都在自己的流程中运行,并与轻量级机制(通常是HTTP资源API)通信。这些服务是围绕业务能力构建的,可通过完全自动化的部署机制独立部署。对这些服务的集中管理是最低限度的,这些服务可以用不同的编程语言编写,并使用不同的数据存储技术。

官方定义: 微服务就是由一系列围绕自己业务开发的微小服务构成,他们独立部署运行在自己的进程里,基于分布式的管理

通俗定义: 微服务是一种架构,这种架构是将单个的整体应用程序分割成更小的项目关联的独立的服务。一个服务通常实现一组独立的特性或功能,包含自己的业务逻辑和适配器。各个微服务之间的关联通暴露api来实现。这些独立的微服务不需要部署在同一个虚拟机,同一各系统和同一个应用服务器中。

1.2、为什么是微服务?

单体应用

# 1.优点 - 单一架构模式在项目初期很小的时候开发方便,测试方便,部署方便,运行良好。 # 2.缺点 - 应用随着时间的推进,加入的功能越来越多,最终会变得巨大,一个项目中很有可能数百万行的代码,互相之间繁琐的jar包。 - 久而久之,开发效率低,代码维护困难 - 还有一个如果想整体应用采用新的技术,新的框架或者语言,那是不可能的。 - 任意模块的漏洞或者错误都会影响这个应用,降低系统的可靠性

微服务架构应用

# 1.优点 - 将服务拆分成多个单一职责的小的服务,进行单独部署,服务之间通过网络进行通信 - 每个服务应该有自己单独的管理团队,高度自治 - 服务各自有自己单独的职责,服务之间松耦合,避免因一个模块的问题导致服务崩溃 # 2.缺点 - 开发人员要处理分布式系统的复杂性 - 多服务运维难度,随着服务的增加,运维的压力也在增大 - 服务治理 和 服务监控 关键 1.3、架构演变 # 1.架构的演变过程 - 单一应用架构 ===> 垂直应用架构 ===> 分布式服务架构 ===> 流动计算架构 ===> 微服务架构 ===> 未知

dubbo官网:https://dubbo.apache.org/zh/

# 1.单一应用架构 All in One Application - 起初当网站流量很小时,将所有功能都写在一个应用里面,对整个应用进行部署,以减少部署节点和成本。对于这个架构简化增删改查的工作量的数据访问框架(ORM)是关键 # 2.垂直应用架构 Vertical Application - 当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,提升效率的方法之一是将应用拆成互不相干的几个应用,以提升效率。此时,用于加速前端页面开发的Web框架(MVC)是关键。 # 3.分布式服务架构 Distributed Service - 当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。此时,用于提高业务复用及整合的分布式服务框架(RPC)是关键。 # 4.流动计算架构 Elastic Computing - 当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。此时,用于提高机器利用率的资源调度和治理中心(SOA)是关键。 orm : Object Relational Mapping 对象关系映射 MVC : Model-View-Controller (模型-视图-控制器) RPC : Remote Procedure Call 远程过程调用 SOA : Service-Oriented Architecture 面向服务架构

好的架构并不是设计出来的,一定是进化来的!!!

1.4、微服务的解决方案

1、阿里系 (springboot + dubbo + zookeeper)

- 初出茅庐:2011年末,阿里巴巴在GitHub上开源了基于Java的分布式服务治理框架Dubbo,之后它成为了国内该类开源项目的佼佼者,许多开发者对其表示青睐。同时,先后有不少公司在实践中基于Dubbo进行分布式系统架构,目前在GitHub上,它的fork、star数均已破万。Dubbo致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案,使得应用可通过高性能RPC实现服务的输出、输入功能和Spring框架无缝集成。Dubbo包含远程通讯、集群容错和自动发现三个核心部分。 - 停止维护:从2012年10月23日Dubbo 2.5.3发布后,在Dubbo开源将满一周年之际,阿里基本停止了对Dubbo的主要升级。只在之后的2013年和2014年更新过2次对Dubbo 2.4的维护版本,然后停止了所有维护工作。Dubbo对Srping的支持也停留在了Spring 2.5.6版本上。 - 死而复生:多年漫长的等待,随着微服务的火热兴起,在国内外开发者对阿里不再升级维护Dubbo的吐槽声中,阿里终于开始重新对Dubbo的升级和维护工作。在2017年9月7日,阿里发布了Dubbo的2.5.4版本,距离上一个版本2.5.3发布已经接近快5年时间了。在随后的几个月中,阿里Dubbo开发团队以差不多每月一版本的速度开始快速升级迭代,修补了Dubbo老版本多年来存在的诸多bug,并对Spring等组件的支持进行了全面升级。 - 2018年1月8日,Dubbo创始人之一梁飞在Dubbo交流群里透露了Dubbo 3.0正在动工的消息。Dubbo 3.0内核与Dubbo 2.0完全不同,但兼容Dubbo 2.0。Dubbo 3.0将以Streaming为内核,不再是Dubbo 时代的RPC,但是RPC会在Dubbo 3.0中变成远程Streaming对接的一种可选形态。从Dubbo新版本的路线规划上可以看出,新版本的Dubbo在原有服务治理的功能基础上,将全面拥抱微服务解决方案。 - 结论:当前由于RPC协议、注册中心元数据不匹配等问题,在面临微服务基础框架选型时Dubbo与Spring Cloud是只能二选一,这也是为什么大家总是拿Dubbo和Spring Cloud做对比的原因之一。Dubbo之后会积极寻求适配到Spring Cloud生态,比如作为Spring Cloud的二进制通信方案来发挥Dubbo的性能优势,或者Dubbo通过模块化以及对http的支持适配到Spring Cloud。

2、springcloud

- Spring Cloud NetFlix 基于美国Netflix公司开源的组件进行封装,提供了微服务一栈式的解决方案。 - Spring Cloud alibaba 在Spring cloud netflix基础上封装了阿里巴巴的微服务解决方案。 - Spring Cloud Spring 目前spring官方趋势正在逐渐吸收Netflix组件的精华,并在此基础进行二次封装优化,打造spring专有的解决方案 二、什么是SpringCloud 2.1、官方定义 官方网址: https://spring.io/projects/spring-cloud

Spring Cloud provides tools for developers to quickly build some of the common patterns in distributed systems (e.g. configuration management,service discovery, circuit breakers, intelligent routing, micro-proxy, control bus). Coordination of distributed systems leads to boiler plate patterns, and using Spring Cloud developers can quickly stand up services and applications that implement those patterns.They will work well in any distributed environment, including the developer’s own laptop, bare metal data centres, and managed platforms such as Cloud Foundry.

**Spring Cloud为开发人员提供了在分布式系统中快速构建一些通用模式的工具(例如配置管理、服务发现、断路器、智能路由、微代理、控制总线)。**分布式系统的协调导致了锅炉板模式,使用springcloud开发人员可以快速地建立实现这些模式的服务和应用程序。它们在任何分布式环境中都能很好地工作,包括开发人员自己的笔记本电脑、裸机数据中心和云铸造等托管平台。

2.2、核心架构及其组件

通俗理解:

springcloud是一个含概多个子项目的开发工具集,集合了众多的开源框架,他利用了Spring Boot开发的便利性实现了很多功能,如服务注册,服务注册发现,负载均衡等.SpringCloud在整合过程中主要是针对Netflix(耐非)开源组件的封装.SpringCloud的出现真正的简化了分布式架构的开发。NetFlix 是美国的一个在线视频网站,微服务业的翘楚,他是公认的大规模生产级微服务的杰出实践者,NetFlix的开源组件已经在他大规模分布式微服务环境中经过多年的生产实战验证,因此Spring Cloud中很多组件都是基于NetFlix # 1.核心组件说明 - eurekaserver、consul、nacos 服务注册中心组件 - rabbion & openfeign 服务负载均衡 和 服务调用组件 - hystrix & hystrix dashboard 服务断路器 和 服务监控组件 - zuul、gateway 服务网关组件 - config 统一配置中心组件 - bus 消息总线组件 ......

三、环境搭建 3.1、版本命名

Springcloud是一个由众多独立子项目组成的大型综合项目,原则每个子项目上有不同的发布节奏,都维护自己发布版本号。为了更好的管理springcloud的版本,通过一个资源清单BOM(Bill of Materials),为避免与子项目的发布号混淆,所以没有采用版本号的方式,而是通过命名的方式。这些名字是按字母顺序排列的。如伦敦地铁站的名称(“天使”是第一个版本,“布里斯顿”是第二个版本,"卡姆登"是第三个版本)。当单个项目的点发布累积到一个临界量,或者其中一个项目中有一个关键缺陷需要每个人都可以使用时,发布序列将推出名称以“.SRX”结尾的“服务发布”,其中“X”是一个数字。

伦敦地铁站名称 [了解]

Angel、Brixton、Camden、Dalston、Edgware、Finchley、Greenwich、Hoxton 3.2、版本选择

大版本说明

SpringBootSpringCloud关系1.2.xAngel版本(天使)兼容Spring Boot 1.2.x1.3.xBrixton版本(布里克斯顿)兼容Spring Boot 1.3.x,也兼容Spring Boot 1.4.x1.4.xCamden版本(卡姆登)兼容Spring Boot 1.4.x,也兼容Spring Boot 1.5.x1.5.xDalston版本(多尔斯顿)兼容Spring Boot 1.5.x,不兼容Spring Boot 2.0.x1.5.xEdgware版本(埃奇韦尔)兼容Spring Boot 1.5.x,不兼容Spring Boot 2.0.x2.0.xFinchley版本(芬奇利)兼容Spring Boot 2.0.x,不兼容Spring Boot 1.5.x2.1.xGreenwich版本(格林威治)兼容Spring Boot 2.1.x,不兼容1.x2.2.xHoxton兼容Spring Boot 2.2.x

实际开发版本关系

spring-boot-starter-parentspring-cloud-dependencies版本号发布日期版本号发布日期1.5.2.RELEASE2017年3月Dalston.RC12017年未知月1.5.9.RELEASENov,2017Edgware.RELEASENov, 20171.5.16.RELEASEsep, 2018Edgware.SR5Oct,20181.5.20.RELEASEApr, 2019Edgware.SR5Oct, 20182.0.2.RELEASEMay, 2018Finchley.BUILD-SNAPSHOT2018年未知月2.0.6.RELEASEOct, 2018Finchley.SR2Oct, 20182.1.4.RELEASEApr,2019Greenwich.SR1Mar,20192.2.5.RELEASEHoxton.SR6

一般使用最后的这两个

3.3、基本环境搭建

微服务:基于单个应用围绕业务进行拆分,拆分出来每一个服务独立项目 单独部署 运行在自己计算机进程里面 基于分布式服务管理

# 0.说明 - springboot 2.2.5.RELEASE - springcloud Hoxton.SR6 - java8 # 1.创建springboot项目 指定版本为 2.2.5版本 # 2.引入springcloud的版本管理 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://·piler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <java.version>1.8</java.version> <spring-cloud.version>Hoxton.SR6</spring-cloud.version> </properties> <!--全局管理springcloud版本,并不会引入具体依赖--> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> </project> 四、服务注册中心 4.1、什么服务注册中心

所谓服务注册中心就是在整个微服务架构单独抽取一个服务,这个服务不完成项目中任何业务功能,仅仅用来在微服务中记录微服务以及对整个系统微服务进行健康状态检查,以及服务元数据信息存储

# 1.服务注册中心 - 可以对所有的微服务的信息进行存储,如微服务的名称、IP、端口等 - 可以在进行服务调用时通过服务发现查询可用的微服务列表及网络地址进行服务调用 - 可以对所有的微服务进行心跳检测,如发现某实例长时间无法访问,就会从服务注册表移除该实例。 4.2、常用的注册中心

springcloud支持的多种注册中心Eureka、Consul、Zookeeper、以及阿里巴巴推出Nacos。这些注册中心在本质上都是用来管理服务的注册和发现以及服务状态的检查的。

4.2.1、Eureka # 0.简介 - https://github.com/Netflix/eureka/wiki - Eureka是Netflix开发的服务发现框架,本身是一个基于REST的服务。SpringCloud将它集成在其子项目spring-cloud-netflix中, 以实现SpringCloud的服务注册和发现功能。 Eureka包含两个组件:Eureka Server和Eureka Client。 开发Eureka Server

1、创建项目并引入eureka server依赖

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--引入 eureka server--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency>

2、编写配置application.properties

# eureka server 默认端口号 server.port=8761 #指定服务注册中心的地址 eureka.client.service-url.defaultZone=http://localhost:8761/eureka

3、开启Eureka Server,入口类加入注解

@SpringBootApplication @EnableEurekaServer // 开启Eureka Server服务 public class EurekaServerApplication { public static void main(String[] args) { SpringApplication.run(EurekaServerApplication.class, args); } }

4、访问Eureka的服务注册页面 http://localhost:8761

注意:在项目启动成功之后默认在eureka server管理界面中出现UNKNOW 一个未知应用

在微服务架构中服务名称代表服务唯一标识,至关重要,服务名称必须唯一 使用时必须通过如下配置指定服务名称

# 指定服务名称 唯一标识 注意:服务名不能出现下划线,默认服务名不区分大小写,推荐服务名大写 spring.application.name=EUREKASERVER

5、虽然能看到管理界面为什么项目启动控制台报错?

- 出现上述问题原因:eureka组件包含 eurekaserver 和 eurekaclient。 server是一个服务注册中心,用来接受客户端的注册。client的特性会让当前启动的服务把自己作为eureka的客户端进行服务中心的注册,当项目启动时服务注册中心还没有创建好,所以找不到服务的客户端组件就直接报错了,当启动成功服务注册中心创建好了,日后client也能进行注册,就不再报错啦!

6、关闭Eureka自己注册自己

# 不再将自己同时作为客户端进行注册,让当前应用仅仅是服务注册中心 eureka.client.register-with-eureka=false # 关闭eureka client立即注册 eureka.client.fetch-registry=false

7、再次启动,当前应用就是一个单纯Eureka Server,控制器也不再报错

发现注册中心也没有自己了

开发Eureka Client

1、创建项目并引入eureka client依赖

<!--引入eureka client--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>

2.编写配置application.properties

server.port=8989 spring.application.name=EUREKACLIENT # 指定服务注册中心地址 eureka.client.service-url.defaultZone=http://localhost:8761/eureka

3、开启eureka客户端服务

@SpringBootApplication @EnableEurekaClient //让 当前微服务作为一个eureka client进行服务注册 public class EurekaClientApplication { public static void main(String[] args) { SpringApplication.run(EurekaClientApplication.class, args); } }

4、先启动之前的8761的服务注册中心,在启动eureka客户端服务

Eureka自我保护机制

1、自我保护机制

官网地址: https://github.com/Netflix/eureka/wiki/Server-Self-Preservation-Mode

- 默认情况下,如果Eureka Server在一定时间内(默认90秒)没有接收到某个微服务实例的心跳,Eureka Server将会移除该实例。但是当网络分区故障发生时,微服务与Eureka Server之间无法正常通信,而微服务本身是正常运行的,此时不应该移除这个微服务,所以引入了自我保护机制。Eureka Server在运行期间会去统计心跳失败比例在 15 分钟之内是否低于 85%,如果低于 85%,Eureka Server 会将这些实例保护起来,让这些实例不会过期。这种设计的哲学原理就是"宁可信其有不可信其无!"。自我保护模式正是一种针对网络异常波动的安全保护措施,使用自我保护模式能使Eureka集群更加的健壮、稳定的运行。

2、微服务修改减短服务心跳的时间

eureka.instance.lease-expiration-duration-in-seconds=10 #用来修改eureka server默认接受心跳的最大时间 默认是90s eureka.instance.lease-renewal-interval-in-seconds=5 #指定客户端多久向eureka server发送一次心跳 默认是30s

3、在eureka server端关闭自我保护机制

eureka.server.enable-self-preservation=false #关闭自我保护 eureka.server.eviction-interval-timer-in-ms=3000 #超时3s自动清除[默认60*1000 1分钟]

4、尽管如此关闭自我保护机制还是会出现警告

官方并不建议在生产情况下关闭

?

Eureka 已经停止更新

官方停止更新说明

https://github.com/Netflix/eureka/wiki在1.x版本项目还是活跃的,但是在2.x版本中停止维护,出现问题后果自负!!!

Eureka集群搭建

1、创建3个eureka server服务

有两种创建方式

直接创建三个项目

通过idea的Run Configurations创建

2、引入eureka server依赖

3、添加配置文件

node1: sever.port=8761 eureka.client.service-url.defaultZone=http://localhost:8762/eureka,http://localhost:8763/eureka node2: sever.port=8762 eureka.client.service-url.defaultZone=http://localhost:8761/eureka,http://localhost:8763/eureka node3: sever.port=8763 eureka.client.service-url.defaultZone=http://localhost:8761/eureka,http://localhost:8762/eureka

4、依次启动EurekaServer

注意: 若以第二种方式创建,每次启动项目前,都需要修改springcloud_01eureka_server的application.properties,然后再启动

5、启动Eureka Client

先修改配置文件

server.port=8989 spring.application.name=EUREKACLIENT eureka.client.service-url.defaultZone=http://localhost:8761/eureka,http://localhost:8762/eureka,http://localhost:8763/eureka

这样即便某一个Eureka Server挂了也不影响业务

如果想搭建client集群,只需要修改端口号即可,但一定要保证服务名一致

小结 # springcloud netflix eureka - 作用:在微服务架构中充当服务注册中心 - 两个角色:eureka server 服务注册中心、eureka client 微服务 - 不好消息:eureka 1.0 (稳定) 2.0版本停止更新 - 不在推荐使用eureka 服务注册中心 1. 最新版停止更新 2.每次必须手动通过代码形式创建服务注册中心 4.2.2、Consul 官网:https://·/consul/getting-started/install.html解压之后发现consul只有一个脚本文件

3、根据解压缩目录配置环境变量(推荐)

windows:直接在path添加consul的安装目录

这里是linux系统

4、查看consul环境变量是否配置成功,执行命令出现如下信息代表成功

consul -v

5、启动consul服务

consul agent -dev

6、访问consul的web服务端口;http://localhost:8500

consul默认服务端口是8500

# 管理界面介绍 - dc1: 数据中心名称 datacenter 默认为dc1 指定数据中心启动 consul agent -dev -datacenter=aa - services: 当前consul服务注册中心列表 默认:client server同时启动,自己注册自己 会出现一个consul服务 - nodes: 从来查看consul集群结点 - key/value:可以往consul中存储一些数据 - acl: Access Controls 版本控制 springcloud集成consul

1、创建项目并引入consul客户端依赖

<!--引入consul依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> </dependency> discovery 服务注册&发现

2、编写properties配置

server.port=8889 spring.application.name=consulclient8889 #注册consul服务的主机 spring.cloud.consul.host=localhost #注册consul服务的端口号 spring.cloud.consul.port=8500 #指定注册的服务名称 默认就是应用名 spring.cloud.consul.discovery.service-name=${spring.application.name}

3、编写启动类

@SpringBootApplication @EnableDiscoveryClient // 通用服务注册中心客户端注解 代表 consul、zookeeper、nacos client public class ConsulClientApplication { public static void main(String[] args) { SpringApplication.run(ConsulClientApplication.class, args); } }

4、启动

5、直接启动consul client 出现如下问题

- 原因:consul server 检测所有客户端心跳, 但是发送心跳时client必须给与响应才能使该服务正常使用 在现有客户端中我们并没有引入健康检查依赖,所有导致健康检查始终不会通过,导致服务不能使用 consul 关闭健康监控检查 #关闭consul服务的健康检查[不推荐] spring.cloud.consul.discovery.register-health-check=false

consul 开启健康监控检查

默认情况consul监控健康是开启的,但是必须依赖健康监控依赖才能正确监控健康状态,所以直接启动会显示错误,引入健康监控依赖之后服务正常

<!-- 这个包是用做健康度监控的--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>

4.2.3、不同服务中心区别 # 1.CAP定理 - CAP定理:CAP定理又称CAP原则,指的是在一个分布式系统中,一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)。CAP 原则指的是,这三个要素最多只能同时实现两点,不可能三者兼顾。 `一致性(C):在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本) `可用性(A):在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。(对数据更新具备高可用性) `分区容忍性(P),就是高可用性,一个节点崩了,并不影响其它的节点(100个节点,挂了几个,不影响服务,越多机器越好) # 2.Eureka特点 - Eureka中没有使用任何的数据强一致性算法保证不同集群间的Server的数据一致,仅通过数据拷贝的方式争取注册中心数据的最终一致性,虽然放弃数据强一致性但是换来了Server的可用性,降低了注册的代价,提高了集群运行的健壮性。 # 3.Consul特点 - 基于Raft算法,Consul提供强一致性的注册中心服务,但是由于Leader节点承担了所有的处理工作,势必加大了注册和发现的代价,降低了服务的可用性。通过Gossip协议,Consul可以很好地监控Consul集群的运行,同时可以方便通知各类事件,如Leader选择发生、Server地址变更等。 # 4.zookeeper特点 - 基于Zab协议,Zookeeper可以用于构建具备数据强一致性的服务注册与发现中心,而与此相对地牺牲了服务的可用性和提高了注册需要的时间。

五、服务间通信方式

如何解决微服务的服务间通信问题?

Http rest 方式 使用http协议进行数据传输 json (springcloud 使用HTTP协议传输数据)RPC 方式 远程过程调用 二进制

总结:在springcloud中服务间调用方式主要是使用 http restful方式进行服务间调用

如何在java代码中发起HTTP方式请求?

网络编程

spring框架提供了HttpClient对象 RestTemplate 发起一个http请求 基于RestTemplate的服务调用

说明:

spring框架提供的RestTemplate类可用于在应用中调用rest服务,它简化了与http服务的通信方式,统一了RESTful的标准,封装了http链接, 我们只需要传入url及返回值类型即可。相较于之前常用的HttpClient,RestTemplate是一种更优雅的调用RESTful服务的方式。 RestTemplate 服务调用

1、创建两个服务并注册到consul注册中心中

- users 代表用户服务 端口为 9999 - orders 代表商品服务 端口为 9998 `注意:这里服务仅仅用来测试,没有实际业务意义

2、引入依赖

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>

3、添加配置文件

# users服务 server.port=8888 spring.application.name=USERS spring.cloud.consul.host=localhost spring.cloud.consul.port=8500 # orders服务 server.port=9999 spring.application.name=ORDERS spring.cloud.consul.host=localhost spring.cloud.consul.port=8500

4、在订单服务中提供服务方法

@RestController @RequestMapping("order") public class OrderController { private static final Logger log = LoggerFactory.getLogger(OrderController.class); @GetMapping() public String demo() { log.info("order demo...."); return "order"; } }

5、在用户服务中使用restTemplate进行调用

@RestController @RequestMapping("user") public class UserController { private static final Logger log = LoggerFactory.getLogger(UserController.class); @GetMapping public String invokeDemo() { log.info("user demo...."); RestTemplate restTemplate = new RestTemplate(); String orderResult = restTemplate.getForObject("http://localhost:9999/order", String.class); log.info("调用订单服务成功:{}", orderResult); return "调用订单服务成功, 结果为:" + orderResult; } }

6、启动服务

7、测试服务调用

浏览器访问用户服务 http://localhost:8888/user

总结

rest Template是直接基于服务地址调用没有在服务注册中心获取服务,也没有办法完成服务的负载均衡,如果需要实现服务的负载均衡需要自己书写服务负载均衡策略。

发现问题:

调用服务的路径主机和服务端口直接写死 url中,无法实现服务集群请求,负载均衡调用服务的请求路径写死在代码中,日后提供服务的服务路径发生变化时,不利于后续维护工作

解决方案:

自定义负载均衡解决策略

public String randomHost() { List<String> hosts = new ArrayList<>(); hosts.add("localhost:9999"); hosts.add("localhost:9990"); int i = new Random().nextInt(hosts.size() - 1); return hosts.get(i); }

问题:1. 无法实现服务健康检查 2. 负载均衡策略过于单一

使用springcloud提供组件 ribbon 解决 负载均衡调用 推荐

基于Ribbon的服务调用 官方网址:https://github.com/Netflix/ribbonSpring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现。通过Spring Cloud的封装,可以让我们轻松地将面向服务的REST模版请求自动转换成客户端负载均衡的服务调用。

Ribbon负载均衡原理

根据调用服务的服务id去服务注册中心获取对应服务id的服务列表,并将服务列表拉取本地进行缓存,然后在本地通过默认的轮询的负载均衡策略在现有列表中选择一个可用节点提供服务客户端负载均衡

1、Ribbon 服务调用 # 1.项目中引入依赖 - 说明: 1.如果使用的是eureka client 和 consul client,无须引入依赖,因为在eureka,consul中默认集成了ribbon组件 2.如果使用的client中没有ribbon依赖需要显式引入如下依赖 <!--引入ribbon依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> </dependency> 查看consul client中依赖的ribbon

使用restTemplate + ribbon进行服务调用 使用discovery client 进行客户端调用使用loadBalanceClient 进行客户端调用使用@loadBalanced 进行客户端调用 @GetMapping public String invokeDemo() { log.info("user demo...."); // 1. 使用discovery Client形式调用 List<ServiceInstance> serviceInstances = discoveryClient.getInstances("ORDERS"); serviceInstances.forEach(serviceInstance -> { log.info("服务主机:{} 服务端口:{} 服务地址:{}", serviceInstance.getHost(), serviceInstance.getPort(), serviceInstance.getUri()); }); String result = new RestTemplate().getForObject(serviceInstances.get(0).getUri() + "/order", String.class); return "ok" + result; } @GetMapping public String invokeDemo() { log.info("user demo...."); // 2. 使用LoadBalancerClient进行服务调用 ServiceInstance serviceInstance = loadBalancerClient.choose("ORDERS"); // 默认轮询 log.info("服务主机:{} 服务端口:{} 服务地址:{}", serviceInstance.getHost(), serviceInstance.getPort(), serviceInstance.getUri()); String result = new RestTemplate().getForObject(serviceInstance.getUri() + "/order", String.class); return "ok" + result; } // 3.使用@loadBalanced @Configuration public class BeansConfig { @Bean @LoadBalanced public RestTemplate getRestTemplate() { return new RestTemplate(); } } @Autowired private RestTemplate restTemplate; @GetMapping public String invokeDemo() { log.info("user demo...."); // 使用@LoadBalanced 注解 String result = restTemplate.getForObject("http:" + "ORDERS" + "/order", String.class); return "ok" + result; }

我们发现:及时使用Ribbon实现了负载均衡策略,但是服务路径仍然写死在代码中,不利于维护

2、Ribbon负载均衡策略

# 1.ribbon负载均衡算法 - RoundRobinRule 轮询策略 按顺序循环选择 Server - RandomRule 随机策略 随机选择 Server - AvailabilityFilteringRule 可用过滤策略 `会先过滤由于多次访问故障而处于断路器跳闸状态的服务,还有并发的连接数量超过阈值的服务,然后对剩余的服务列表按照轮询策略进行访问 - WeightedResponseTimeRule 响应时间加权策略 `根据平均响应的时间计算所有服务的权重,响应时间越快服务权重越大被选中的概率越高,刚启动时如果统计信息不足,则使用 RoundRobinRule策略,等统计信息足够会切换到 - RetryRule 重试策略 `先按照RoundRobinRule的策略获取服务,如果获取失败则在制定时间内进行重试,获取可用的服务。 - BestAviableRule 最低并发策略 `会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务

3、修改服务的默认负载均衡策略 # 1.修改服务默认随机策略 - 服务id.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule `下面的products为服务的唯一标识 products.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule 4、Ribbon停止维护

官方停止维护说明

https://github.com/Netflix/ribbon

六、OpenFeign组件 思考: 使用RestTemplate+ribbon已经可以完成对端的调用,为什么还要使用feign? String restTemplateForObject = restTemplate.getForObject("http://服务名/url?参数" + name, String.class);

存在问题:

1.每次调用服务都需要写这些代码,存在大量的代码冗余2.服务地址如果修改,维护成本增高3.使用时不够灵活 6.1、OpenFeign 组件

说明:

https://cloud.spring.io/spring-cloud-openfeign/reference/html/Feign是一个声明式的伪Http客户端,它使得写Http客户端变得更简单。使用Feign,只需要创建一个接口并注解。它具有可插拔的注解特性(可以使用springmvc的注解),可使用Feign 注解和JAX-RS注解。Feign支持可插拔的编码器和解码器。Feign默认集成了Ribbon,默认实现了负载均衡的效果并且springcloud为feign添加了springmvc注解的支持。 1、openFeign 服务调用

1、服务调用方法引入依赖OpenFeign依赖

<!--Open Feign依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>

2、入口类加入注解开启OpenFeign支持

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

3、创建一个客户端调用接口

@FeignClient("PRODUCT") // 指定调用服务调用名称 @RequestMapping("/product") public interface ProductClient { @GetMapping() String product(); }

4、使用feignClient客户端对象调用服务

@RestController @RequestMapping("/category") public class CategoryController { private static final Logger log = LoggerFactory.getLogger(CategoryController.class); @Autowired private ProductClient productClient; @GetMapping public String category() { log.info("category service...."); String product = productClient.product(); return "category ok!!!" + product; } } 2、调用服务并传参

服务与服务之间,不仅仅是调用,往往在调用过程中还伴随着参数传递

GET方式调用服务传递参数

参数传递有两种方式

路径传参 -------在参数前声明@PathVariable("")queryString -------在参数前声明@RequestParam("") // 定义一个接受零散类型参数接口 路径传参 @GetMapping("/test/{name}/{age}") public String test1(@PathVariable("name") String name, @PathVariable("age") Integer age) { log.info("name:{} age:{}", name, age); return "test ok , 当前服务端口为:" + port; } @GetMapping("/test/{name}/{age}") String test1(@PathVariable("name") String name, @PathVariable("age") Integer age); @GetMapping public String category() { log.info("category service...."); String result = productClient.test("小陈", 23); return "category ok!!!" + result; } // 定义一个接受零散类型参数接口 queryString @GetMapping("/test1") public String test1(String name, Integer age) { log.info("name:{} age:{}", name, age); return "test ok , 当前服务端口为:" + port; } @GetMapping("/test1") String test1(@RequestParam("name") String name, @RequestParam("age") Integer age); @GetMapping public String category() { log.info("category service...."); String result = productClient.test1("小陈", 23); return "category ok!!!" + result; } POST方式调用服务传递参数

post方式一般用于对象的传递 application/json方式

// 先创建一个实体 public class Product { private Integer id; private String name; private Double price; private Date bir; public Product(Integer id, String name, Double price, Date bir) { this.id = id; this.name = name; this.price = price; this.bir = bir; } } @PostMapping("/test2") public String test2(@RequestBody Product product) { log.info("product:{}", product); return "test2 ok,当前服务端口为:" + port; } @PostMapping("/test2") String test2(@RequestBody Product product); @PostMapping("/test2") public String test2(@RequestBody Product product) { log.info("product:{}", product); return "test2 ok,当前服务端口为:" + port; }

接收数组类型参数

// 定义一个接口接收数组类型参数 @GetMapping("/test3") public String test3(String[] ids) { for (String id : ids) { log.info("id:{}", id); } return "test3 ok,当前服务端口为:" + port; } @GetMapping("/test3") String test3(@RequestParam("ids") String[] ids); @GetMapping public String category() { log.info("category service...."); String result = productClient.test3(new String[]{"21", "222", "555"}); return "category ok!!!" + result; }

接收集合类型参数

// 定义一个接口接收集合类型参数 // springmvc 不能直接接受集合类型参数, 如果想要接收集合类型参数必须放入对象中,使用对象的方式接收才行 @GetMapping("/test4") public String test4(ArrayList<String> ids) { ids.forEach(id -> log.info("id:{}", id)); return "test3 ok,当前服务端口为:" + port; } @GetMapping("/test4") String test4(@RequestParam("ids") String[] ids); @GetMapping public String category() { log.info("category service...."); String result = productClient.test4(new String[]{"21", "222", "555"}); return "category ok!!!" + result; }

返回对象类型

@GetMapping("/test5/{id}") public Product getProduct(@PathVariable("id") Integer id) { log.info("id:{}", id); return new Product(id, "连衣裙", 23.23, new Date()); } @GetMapping("/test5/{id}") Product getProduct(@PathVariable("id") Integer id); @GetMapping public Product category() { log.info("category service...."); Product product = productClient.getProduct(21); log.info("product:{}", product); return product; }

返回list类型

// 声明调用商品服务根据类别id查询一组商品信息 @GetMapping("/products") public List<Product> findByCategoryId(Integer categoryId) { List<Product> list = new ArrayList<>(); list.add(new Product(1, "sss", 20.20, new Date())); list.add(new Product(2, "xx", 20.21, new Date())); list.add(new Product(3, "xxxx", 20.22, new Date())); log.info("id:{}", categoryId); return list; } @GetMapping("/products") List<Product> findByCategoryId(@RequestParam("categoryId") Integer categoryId); @GetMapping public List<Product> category() { log.info("category service...."); List<Product> products = productClient.findByCategoryId(1); products.forEach(product -> log.info("product:{}", product)); return products; } 3、OpenFeign超时处理

超时说明

默认情况下,openFiegn在进行服务调用时,要求服务提供方处理业务逻辑时间必须在1S内返回,如果超过1S没有返回则OpenFeign会直接报错,不会等待服务执行,但是往往在处理复杂业务逻辑是可能会超过1S,因此需要修改OpenFeign的默认服务调用超时时间。

调用超时会出现如下错误:

1、模拟超时

服务提供方加入线程等待阻塞

2、进行客户端调用

3、修改OpenFeign默认超时时间

在调用方配置

feign.client.config.PRODUCT.connectTimeout=5000 #配置指定服务连接超时时间 feign.client.config.PRODUCT.readTimeout=5000 #配置指定服务等待超时时间 #feign.client.config.default.connectTimeout=5000 #配置所有服务连接超时 #feign.client.config.default.readTimeout=5000 #配置所有服务等待超时 4.OpenFeign调用详细日志展示

说明

- 往往在服务调用时我们需要详细展示feign的日志,默认feign在调用是并不是最详细日志输出,因此在调试程序时应该开启feign的详细日志展示。feign对日志的处理非常灵活可为每个feign客户端指定日志记录策略,每个客户端都会创建一个logger默认情况下logger的名称是feign的全限定名需要注意的是,feign日志的打印只会DEBUG级别做出响应。 - 我们可以为feign客户端配置各自的logger.lever对象,告诉feign记录那些日志logger.lever有以下的几种值 `NONE 不记录任何日志 `BASIC 仅仅记录请求方法,url,响应状态代码及执行时间 `HEADERS 记录Basic级别的基础上,记录请求和响应的header `FULL 记录请求和响应的header,body和元数据

1、开启日志展示

feign.client.config.PRODUCTS.loggerLevel=full #开启指定服务日志展示 #feign.client.config.default.loggerLevel=full #全局开启服务日志展示 logging.level.com.baizhi.feignclients=debug #指定feign调用客户端对象所在包,必须是debug级别

2、测试服务调用查看日志

七、Hystrix组件使用

官网:https://github.com/Netflix/Hystrix

In a distributed environment, inevitably some of the many service dependencies will fail. Hystrix is a library that helps you control the interactions between these distributed services by adding latency tolerance and fault tolerance logic. Hystrix does this by isolating points of access between the services, stopping cascading failures across them, and providing fallback options, all of which improve your system’s overall resiliency.

说明

译: 在分布式环境中,许多服务依赖项不可避免地会失败。Hystrix是一个库,它通过添加延迟容忍和容错逻辑来帮助您控制这些分布式服务之间的交互。Hystrix通过隔离服务之间的访问点、停止它们之间的级联故障以及提供后备选项来实现这一点,所有这些都可以提高系统的整体弹性。通俗定义: Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统中,许多依赖不可避免的会调用失败,超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障(服务雪崩现象),提高分布式系统的弹性。

作用

hystrix 用来保护微服务系统 实现 服务降级 服务熔断

服务雪崩

服务降级

服务熔断

7.1、服务雪崩

服务雪崩

在微服务之间进行服务调用,由于某一个服务故障,导致级联服务故障的现象,称为雪崩效应。雪崩效应描述的是提供方不可用,导致消费方不可用并将不可用逐渐放大的过程。

图解雪崩效应

如存在如下调用链路:

而此时,Service A的流量波动很大,流量经常会突然性增加!那么在这种情况下,就算Service A能扛得住请求,Service B和Service C未必能扛得住这突发的请求。此时,如果Service C因为抗不住请求,变得不可用。那么Service B的请求也会阻塞,慢慢耗尽Service B的线程资源,Service B就会变得不可用。紧接着,Service A也会不可用,这一过程如下图所示

根本原因: 在调用链路中链路中某一服务因为执行业务时间过长,或者是大规模出现异常导致自身服务不可用,并把这用不可用放大的情况

7.2、服务熔断 “熔断器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器(hystrix)的故障监控,某个异常条件被触发,直接熔断整个服务。向调用方法返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方法无法处理的异常,就保证了服务调用方的线程不会被长时间占用,避免故障在分布式系统中蔓延,乃至雪崩。如果目标服务情况好转则恢复调用。服务熔断是解决服务雪崩的重要手段。

类似于保险丝

7.3、服务降级

服务降级说明

服务压力剧增的时候根据当前的业务情况及流量对一些服务和页面有策略的降级,以此缓解服务器的压力,以保证核心任务的进行。同时保证部分甚至大部分任务客户能得到正确的响应。也就是当前的请求处理不了了或者出错了,给一个默认的返回。服务降级:关闭微服务系统中某些边缘服务,保证系统核心服务正常运行

7.4、降级和熔断对比 # 1.共同点 - 目的很一致,都是从可用性可靠性着想,为防止系统的整体缓慢甚至崩溃,采用的技术手段; - 最终表现类似,对于两者来说,最终让用户体验到的是某些功能暂时不可达或不可用; - 粒度一般都是服务级别,当然,业界也有不少更细粒度的做法,比如做到数据持久层(允许查询,不允许增删改); - 自治性要求很高,熔断模式一般都是服务基于策略的自动触发,降级虽说可人工干预,但在微服务架构下,完全靠人显然不可能,开关预置、配置中心都是必要手段;sentinel # 2.异同点 - 触发原因不太一样,服务熔断一般是某个服务(下游服务)故障引起,而服务降级一般是从整体负荷考虑; - 管理目标的层次不太一样,熔断其实是一个框架级的处理,每个微服务都需要(无层级之分),而降级一般需要对业务有层级之分(比如降级一般是从最外围服务边缘服务开始) # 3.总结 - 熔断必会触发降级,所以熔断也是降级一种,区别在于熔断是对调用链路的保护,而降级是对系统过载的一种保护处理 7.5、服务熔断的实现

1、项目中引入hystrix依赖

<!--引入hystrix--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>

2、开启断路器

@SpringBootApplication @EnableCircuitBreaker //用来开启断路器 public class Products9998Application { public static void main(String[] args) { SpringApplication.run(Products9998Application.class, args); } }

3、使用HystrixCommand注解实现断路

//服务熔断 @GetMapping("/product/break") @HystrixCommand(fallbackMethod = "testBreakFall" ) public String testBreak(int id){ log.info("接收的商品id为: "+ id); if(id<=0){ throw new RuntimeException("数据不合法!!!"); } return "当前接收商品id: "+id; } public String testBreakFall(int id){ return "当前数据不合法: "+id; }

从上面演示过程中会发现如果触发一定条件断路器会自动打开,过了一点时间正常之后又会关闭。

那么断路器打开条件是什么呢?

5、断路器打开条件

官网: https://cloud.spring.io/spring-cloud-netflix/2.2.x/reference/html/#circuit-breaker-spring-cloud-circuit-breaker-with-hystrix

- 较低级别服务中的服务故障可能会导致一直到用户的级联故障。当对特定服务的调用超过circuitBreaker.requestVolumeThreshold(默认:20 个请求)并且失败百分比大于circuitBreaker.errorThresholdPercentage(默认:>50%)定义的滚动窗口metrics.rollingStats.timeInMilliseconds(默认:10 秒)时,电路打开并且不进行调用。在出现错误和开路的情况下,开发人员可以提供回退。 # 原文翻译之后,总结打开关闭的条件: - 1、 当满足一定的阀值的时候(默认10秒内超过20个请求次数) - 2、 当失败率达到一定的时候(默认10秒内超过50%的请求失败) - 3、 到达以上阀值,断路器将会开启 - 4、 当开启的时候,所有请求都不会进行转发 - 5、 一段时间之后(默认是5秒),这个时候断路器是半开状态,会让其中一个请求进行转发。如果成功,断路器会关闭,若失败,继续开启。重复4和5。 # 面试重点问题: 断路器流程

6、默认的服务FallBack处理方法

如果为每一个服务方法开发一个降级,对于我们来说,可能会出现大量的代码的冗余,不利于维护,这个时候就需要加入默认服务降级处理方法 @GetMapping("/product/hystrix") @HystrixCommand(fallbackMethod = "testHystrixFallBack") //通过HystrixCommand降级处理 指定处理的方法 public String testHystrix(String name) { log.info("接收名称为: " + name); int n = 1/0; return "服务[" + port + "]响应成功,当前接收名称为:" + name; } //服务降级处理 public String testHystrixFallBack(String name) { return port + "当前服务已经被降级处理!!!,接收名称为: "+name; } 7.6、服务降级的实现 # 服务降级: 站在系统整体负荷角度 实现: 关闭系统中某些边缘服务 保证系统核心服务运行 Emps 核心服务 Depts 边缘服务

1、客户端openfeign + hystrix实现服务降级实现

引入hystrix依赖(注意:默认openfeign底层已经依赖hystrix依赖,无须显式引入)配置文件开启feign支持hystrix在feign客户端调用加入fallback指 定降级处理开发降级处理方法

2、开启openfeign支持服务降级

feign.hystrix.enabled=true #开启openfeign支持降级

3、在openfeign客户端中加如Hystrix

@FeignClient(value = "PRODUCTS",fallback = ProductFallBack.class) public interface ProductClient { @GetMapping("/product/hystrix") String testHystrix(@RequestParam("name") String name); }

4、开发fallback处理类

@Component public class ProductClientFallBack implements ProductClient { @Override public String testHystrix(String name) { return "我是客户端的Hystrix服务实现!!!"; } } 7.7、Hystrix Dashboard(仪表盘)

Hystrix Dashboard的一个主要优点是它收集了关于每个HystrixCommand的一组度量。Hystrix仪表板以高效的方式显示每个断路器的运行状况。

1、项目中引入依赖

<!--引入hystrix dashboard 依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId> </dependency>

2、入口类中开启hystrix dashboard

@SpringBootApplication @EnableDiscoveryClient // 注意:默认只要引入discovery client 依赖 该注解无须显示声明 自动注册 @EnableHystrixDashboard public class HystrixDashboardApplication { public static void main(String[] args) { SpringApplication.run(HystrixDashboardApplication.class, args); } }

3、启动hystrix dashboard应用

http://localhost:9990/hystrix

4、在需要监控的服务入口类中加入监控路径配置

@Bean public ServletRegistrationBean getServlet() { HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet(); ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet); registrationBean.setLoadOnStartup(1); registrationBean.addUrlMappings("/actuator/hystrix.stream"); registrationBean.setName("HystrixMetricsStreamServlet"); return registrationBean; }

5、通过监控界面监控

Unable to connect to Command Metric Stream 可以在

Hystrix Dashboard 模块的配置文件下添加:

hystrix: dashboard: proxy-stream-allow-list: "*"

7.8、Hystrix停止维护

官方地址:https://github.com/Netflix/Hystrix

Hystrix 不再处于积极开发阶段,目前处于维护模式。

Hystrix(版本 1.5.18)足够稳定,可以满足 Netflix 对我们现有应用程序的需求。同时,我们的重点已经转移到对应用程序的实时性能做出反应的更具适应性的实现上,而不是预先配置的设置(例如,通过自适应并发限制)。对于像 Hystrix 这样有意义的情况,我们打算继续将 Hystrix 用于现有应用程序,并利用 resilience4j 等开放和活跃的项目用于新的内部项目。我们开始建议其他人也这样做。

Netflix Hystrix 现已正式进入维护模式,对更大的社区有以下期望:Netflix 将不再积极审查问题、合并拉取请求和发布新版本的 Hystrix。我们已经在每个issue 1891中发布了Hystrix (1.5.18) 的最终版本,以便 Maven Central 中的最新版本与 Netflix 内部使用的最后一个已知稳定版本 (1.5.11) 保持一致。

多年来,Hystrix 为 Netflix 和社区提供了良好的服务,向维护模式的过渡绝不表明 Hystrix 的概念和想法不再有价值。相反,Hystrix 激发了许多伟大的想法和项目。我们感谢 Netflix 以及更大的社区中的每一个人,感谢这些年来对 Hystrix 所做的所有贡献。

该项目的 hystrix-dashboard 组件已被弃用并移至Netflix-Skunkworks/hystrix-dashboard。请参阅那里的自述文件以获取更多详细信息,包括重要的安全注意事项。

小结:

八、Gateway组件 8.1、什么是服务网关

网关统一服务入口,可方便实现对平台众多接口进行管控,对访问服务的身份认证、防报文重放与防数据篡改、功能调用的业务鉴权、响应数据的脱敏、流量与并发控制,甚至基于API调用的计量或者计费等等。

网关 = 路由转发 + 过滤器 路由转发:接收一切外界请求,转发到后端的微服务上去;在服务网关中可以完成一系列的横切功能,例如权限校验、限流以及监控等,这些都可以通过过滤器完成

为什么需要网关?

1.网关可以实现服务的统一管理2.网关可以解决微服务中通用代码的冗余问题(如权限控制,流量监控,限流等)

网关组件在微服务中架构

8.2、zuul 1.x 2.x(netflix 组件)

Zuul is the front door for all requests from devices and web sites to the backend of the Netflix streaming application. As an edge service application, Zuul is built to enable dynamic routing, monitoring, resiliency and security.

# 0.原文翻译 - https://github.com/Netflix/zuul/wiki - zul是从设备和网站到Netflix流媒体应用程序后端的所有请求的前门。作为一个边缘服务应用程序,zul被构建为支持动态路由、监视、弹性和安全性。 # 1.zuul版本说明 - 目前zuul组件已经从1.0更新到2.0,但是作为springcloud官方不再推荐使用zuul2.0,但是依然支持zuul2. # 2.springcloud 官方集成zuul文档 - https://cloud.spring.io/spring-cloud-netflix/2.2.x/reference/html/#netflix-zuul-starter 8.3、Gateway (spring) 官网:https://spring.io/projects/spring-cloud-gateway

This project provides a library for building an API Gateway on top of Spring WebFlux. Spring Cloud Gateway aims to provide a simple, yet effective way to route to APIs and provide cross cutting concerns to them such as: security, monitoring/metrics, and resiliency.

该项目提供了一个库,用于在 Spring WebFlux 之上构建 API 网关。Spring Cloud Gateway 旨在提供一种简单而有效的方式来路由到 API,并为它们提供横切关注点,例如:安全性、监控/指标和弹性。

特性:

基于 Spring Framework 5、Project Reactor 和 Spring Boot 2.0 构建 响应式异步非阻塞IO模型动态路由请求过滤 1、开发网关动态路由 创建项目引入网关依赖 注意:去掉项目中springboot-starter-web,存在冲突 <!--引入gateway网关依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> 编写网关配置 server: port: 7979 spring: application: name: GATEWAY cloud: consul: host: localhost port: 8500 gateway: routes: - id: category_router # 路由对象唯一标识 uri: http://localhost:8786/ predicates: # 断言 用来配置路由规则 - Path=/category - id: product_router # 路由对象唯一标识 uri: http://localhost:8787/ predicates: # 断言 用来配置路由规则 - Path=/list 启动gateway网关项目

在启动日志中发现,gateway为了效率使用webflux进行异步非阻塞模型的实现,因此和原来的web包冲突,去掉原来的web即可

再次启动,测试网关路由转发 - 测试通过网关访问用户服务: http://localhost:7979/category/findOne?productId=21 - 测试通过网关访问商品服务: http://localhost:7979/list/findOne?productId=1

java配置类方式配置路由

@Configuration public class GatewayConfig { @Bean public RouteLocator customRouteLocator(RouteLocatorBuilder builder) { return builder.routes() .route("category_route", r -> r.path("/category").uri("http://localhost:8786")) .route("product_route", r -> r.path("/list").uri("http://localhost:8787")) .build(); } } 2、网关路径的解析规则

访问网关路径

path规则可以写一个,写多个,支持通配符配置

gateway: routes: - id: product_router uri: http://localhost:8787/ predicates: - Path=/list, /product/** gateway提供路由访问规则列表的web界面,但是默认是关闭的,如果想要查看服务路由网关规则可以在配置文件中开启 management: endpoints: web: exposure: include: "*" #开启所有web端点暴露 访问路由管理列表地址 http://localhost:8989/actuator/gateway/routes

3、配置路由服务负载均衡

动态路由以及负载均衡转发配置

spring: application: name: gateway cloud: consul: host: localhost port: 8500 gateway: routes: - id: user_route #uri: http://localhost:9999/ uri: lb://users # lb代表转发后台服务使用负载均衡,users代表服务注册中心上的服务名 predicates: - Path=/user/** - id: product_route #uri: http://localhost:9998/ uri: lb://products # lb(loadbalance)代表负载均衡转发路由 predicates: - Path=/product/** discovery: locator: enabled: true #开启根据服务名动态获取路由 4、常用路由predicate(断言,验证)

网关 gateway= 断言 predicate + 过滤(后置filter)

断言:当请求到达网关时,网关前置处理(满足断言放行请求)

过滤:当请求满足断言的所有条件之后,会向后端你服务转发,在向后端服务转发之前会经过一些过滤

1、Gateway支持多种方式的predicate

- After=2020-07-21T11:33:33.993+08:00[Asia/Shanghai] `指定日期之后的请求进行路由 - Before=2020-07-21T11:33:33.993+08:00[Asia/Shanghai] `指定日期之前的请求进行路由 - Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver] - Cookie=username,chenyn `基于指定cookie的请求进行路由 - Cookie=username,[A-Za-z0-9]+ `基于指定cookie的请求进行正则匹配路由 `curl http://localhost:8989/user/findAll --cookie "username=zhangsna" - Header=X-Request-Id, \d+ `基于请求头中的指定属性的正则匹配路由(这里全是整数) `curl http://localhost:8989/user/findAll -H "X-Request-Id:11" - Method=GET,POST `基于指定的请求方式请求进行路由 官方更多: https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.3.RELEASE/reference/html/#the-cookie-route-predicate-factory

2、使用predicate

spring: application: name: gateway cloud: consul: host: localhost port: 8500 gateway: routes: - id: user_route #uri: http://localhost:9999/ uri: lb://users predicates: - Path=/user/** - After=2020-07-21T11:39:33.993+08:00[Asia/Shanghai] - Cookie=username,[A-Za-z0-9]+ - Header=X-Request-Id, \d+ 5、常用的Filter以及自定义filter 官网: https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.3.RELEASE/reference/html/#gatewayfilter-factories

Route filters allow the modification of the incoming HTTP request or outgoing HTTP response in some manner. Route filters are scoped to a particular route. Spring Cloud Gateway includes many built-in GatewayFilter Factories.

路由筛选器允许以某种方式修改传入的HTTP请求或传出的HTTP响应。路由筛选器的作用域为特定路由。Spring Cloud Gateway包括许多内置网关过滤器工厂。

1、作用:

当我们有很多个服务时,比如下图中的user-service、order-service、product-service等服务,客户端请求各个服务的Api时,每个服务都需要做相同的事情,比如鉴权、限流、日志输出等。

2、使用内置过滤器

- AddRequestHeader=X-Request-red, blue `用来给路由对象的所有转发请求加入指定请求头信息 - AddRequestParameter=red, blue `增加请求参数的filterr - AddResponseHeader=X-Response-Red, AAA `增加响应头filter - PrefixPath=/emp `增加前缀的filter - StripPrefix=2 `去掉前缀的filter

3、使用自定义filter

/** * 自定义网关全局filter */ @Configuration public class CustomerGlobalFilter implements GlobalFilter, Ordered { private static final Logger log = LoggerFactory.getLogger(CustomerGlobalFilter.class); /** * 类似 javaweb doFilter * * @param exchange * @param chain * @return */ @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { log.info("进入自定义的filter"); if (exchange.getRequest().getQueryParams().get("username") != null) { log.info("用户身份信息合法,放行请求继续执行!!!"); return chain.filter(exchange); } log.info("非法用户,拒绝访问!!!"); return exchange.getResponse().setComplete(); } /** * @return 用来指定filter执行顺序 默认顺序按照自然数字进行排序 -1 在所有filter执行之前执行 */ @Override public int getOrder() { return 0; } } 九、Config组件 9.1、什么是Config https://cloud.spring.io/spring-cloud-static/spring-cloud-config/2.2.3.RELEASE/reference/html/#_spring_cloud_config_server

Config(配置)又称为 统一配置中心,顾名思义,就是将配置统一管理,配置统一管理的好处是在日后大规模集群部署服务应用时相同的服务配置一致,日后再修改配置只需要统一修改全部同步,不需要一个一个服务手动维护。

9.2、Config Server 开发

1、引入依赖

<!--引入统一配置中心--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency>

2、开启统一配置中心服务

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

3、修改配置文件

server.port=7878 spring.application.name=configserver spring.cloud.consul.host=localhost spring.cloud.consul.port=8500 spring.cloud.config.server.git.uri=https://github.com/chenyn-java/configservers.git 指定仓库的url spring.cloud.config.server.git.default-label=master 指定访问的分支 #spring.cloud.config.server.git.username= 私有仓库访问用户名 #spring.cloud.config.server.git.password= 私有仓库访问密码

新建远程git仓库,拉取配置

定位资源的默认策略是克隆一个git仓库(在spring.cloud.config.server.git.uri),并使用它来初始化一个迷你SpringApplication。小应用程序的Environment用于枚举属性源并通过JSON端点发布。

HTTP服务具有以下格式的资源:

/{application}/{profile}[/{label}] /{application}-{profile}.yml /{label}/{application}-{profile}.yml /{application}-{profile}.properties /{label}/{application}-{profile}.properties

其中“应用程序”作为SpringApplication中的spring.config.name注入(即常规的Spring Boot应用程序中通常是“应用程序”),“配置文件”是活动配置文件(或逗号分隔列表的属性),“label”是可选的git标签(默认为“master”)。

9.3、Config Client 开发

1、项目中引入config client依赖

<!--引入config client--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency>

2、编写配置文件

注意: 这里配置文件要用 bootstrap.properties|yml 命名,否则会启动报错

# 报错原因 - 项目中目前使用的是application.properties启动项目,使用这个配置文件在springboot项目启动过程中不会等待远程配置拉取,直接根据配置文件中内容启动,因此当需要注册中心,服务端口等信息时,远程配置还没有拉取到,所以直接报错 # 解决方案 - 应该在项目启动时先等待拉取远程配置,拉取远程配置成功之后再根据远程配置信息启动即可,为了完成上述要求springboot官方提供了一种解决方案,就是在使用统一配置中心时应该将微服务的配置文件名修改为bootstrap.(properties|yml),bootstrap.properties作为配置启动项目时,会优先拉取远程配置,远程配置拉取成功之后根据远程配置启动当前应用。 server.port=8990 spring.application.name=CONDIGCLIENT spring.cloud.consul.host=localhost spring.cloud.consul.port=8500 spring.cloud.config.discovery.enabled=true #开启统一配置中心服务 spring.cloud.config.discovery.service-id=configserver #指定统一配置服务中心的服务唯一标识 spring.cloud.config.label=master #指定从仓库的那个分支拉取配置 spring.cloud.config.name=client #指定拉取配置文件的名称 spring.cloud.config.profile=dev #指定拉取配置文件的环境

3.远程仓库创建配置文件

- client.properties [用来存放公共配置][] spring.application.name=configclient spring.cloud.consul.host=localhost spring.cloud.consul.port=8500 - client-dev.properties [用来存放研发相关配置][注意:这里端口为例,以后不同配置分别存放] server.port=9099 - client-prod.properties [用来存放生产相关配置][] server.port=9098

?

9.4、手动配置刷新

在生产环境中,微服务可能非常多,每次修改完远端配置之后,不可能对所有服务进行重新启动,这个时候需要让修改配置的服务能够刷新远端修改之后的配置,从而不要每次重启服务才能生效,进一步提高微服务系统的维护效率。在springcloud中也为我们提供了手动刷新配置和自动刷新配置两种策略,这里我们先使用手动配置文件刷新。

1、在需要刷新代码的类中加入刷新配置的注解 @RefreshScope

@RestController @RefreshScope public class TestController { @Value("${name}") private String name; @GetMapping("/test/test") public String test(){ log.info("当前加载配置文件信息为:[{}]",name); return name; } }

2、修改完远程git仓库配置后,向每一个微服务发送一个post请求

# 手动调用刷新配置接口 - curl -X POST http://localhost:8990/actuator/refresh

3、必须在微服务配置文件中暴露刷新端点

management.endpoints.web.exposure.include=* #开启所有web端点暴露

再次访问发现配置已经成功刷新

十、Bus组件

官网: https://spring.io/projects/spring-cloud-bus

10.1、什么是Bus (AMQP RibbitMQ、Kafka)

Spring Cloud Bus links nodes of a distributed system with a lightweight message broker. This can then be used to broadcast state changes (e.g. configuration changes) or other management instructions. AMQP and Kafka broker implementations are included with the project. Alternatively, any Spring Cloud Stream binder found on the classpath will work out of the box as a transport.

Spring Cloud Bus通过轻量级消息代理将分布式系统的节点连接起来。然后,这可以用于广播状态更改(例如配置更改)或其他管理指令。AMQP和Kafka broker(中间件)实现包含在项目中。或者,在类路径上找到的任何Spring Cloud Stream绑定器都可以作为一种传输工具使用。

通俗定义: bus称之为springcloud中消息总线,主要用来在微服务系统中实现远端配置更新时通过广播形式通知所有客户端刷新配置信息,避免手动重启服务的工作

10.2、实现配置刷新原理

10.3、搭建RabbitMQ服务

这里采用Docker的安装方式

其他安装方式可参考:https://blog.csdn.net/su2231595742/article/details/120351479

10.4、实现自动配置刷新

1、在所有项目中引入bus依赖

<!--引入bus依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bus-amqp</artifactId> </dependency>

2、配置统一配置中心连接到mq

spring.rabbitmq.host=localhost #连接主机 spring.rabbitmq.port=5672 #连接mq端口 spring.rabbitmq.username=user #连接mq用户名 spring.rabbitmq.password=password #连接mq密码 spring.rabbitmq.virtual-host=/ # 虚拟主机

3、在各微服务远端配置中加入连接mq配置

4、启动统一配置中心服务

正常启动

5、启动客户端服务

- 加入bus组件之后客户端启动报错 - 原因springcloud中默认链接不到远程服务器不会报错,但是在使用bus消息总线时必须开启连接远程服务失败报错

spring.cloud.config.fail-fast=true

6、修改远程配置后在配置中心服务通过执行post接口刷新配置

- curl -X POST http://localhost:8848/actuator/bus-refresh

通过上述配置就实现了配置统一刷新

10.5、指定服务刷新配置

默认情况下使用 curl -X POST http://localhost:7878/actuator/bus-refresh 这种方式刷新配置是全部广播形式,也就是所有的微服务都能接收到刷新配置通知,但有时我们修改的仅仅是某个服务的配置,这个时候对于其他服务的通知是多余的,因此就需要指定服务进行通知

指定服务刷新配置实现

- 指定端口刷新某个具体服务: curl -X POST http://localhost:7878/actuator/bus-refresh/configclient:9090 - 指定服务id刷新服务集群节点: curl -X POST http://localhost:7878/actuator/bus-refresh/configclient [注意:][configclient代表刷新服务的唯一标识] 10.6、集成Webhook实现自动刷新

git仓库提供一种特有机制:这种机制就是一个监听机制 监听就是仓库提交事件 … 触发对应事件执行

1、配置webhooks

github

gitee

2、解决400错误问题

在配置中心服务端加入过滤器进行解决(springcloud中一个坑) @Component public class UrlFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpServletRequest = (HttpServletRequest)request; HttpServletResponse httpServletResponse = (HttpServletResponse)response; String url = new String(httpServletRequest.getRequestURI()); //只过滤/actuator/bus-refresh请求 if (!url.endsWith("/bus-refresh")) { chain.doFilter(request, response); return; } //获取原始的body String body = readAsChars(httpServletRequest); System.out.println("original body: "+ body); //使用HttpServletRequest包装原始请求达到修改post请求中body内容的目的 CustometRequestWrapper requestWrapper = new CustometRequestWrapper(httpServletRequest); chain.doFilter(requestWrapper, response); } @Override public void destroy() { } private class CustometRequestWrapper extends HttpServletRequestWrapper { public CustometRequestWrapper(HttpServletRequest request) { super(request); } @Override public ServletInputStream getInputStream() throws IOException { byte[] bytes = new byte[0]; ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes); return new ServletInputStream() { @Override public boolean isFinished() { return byteArrayInputStream.read() == -1 ? true:false; } @Override public boolean isReady() { return false; } @Override public void setReadListener(ReadListener readListener) { } @Override public int read() throws IOException { return byteArrayInputStream.read(); } }; } } public static String readAsChars(HttpServletRequest request) { BufferedReader br = null; StringBuilder sb = new StringBuilder(""); try { br = request.getReader(); String str; while ((str = br.readLine()) != null) { sb.append(str); } br.close(); } catch (IOException e) { e.printStackTrace(); } finally { if (null != br) { try { br.close(); } catch (IOException e) { e.printStackTrace(); } } } return sb.toString(); } }

十一、小结 # 1. 微服务 - 定义:mircro service 微服务是中服务架构 - 特点:基于单体应用基于业务进行服务拆分,拆分出来每一个服务独立项目,独立运行独立部署,运行在自己计算机进程里面,针对于拆分出来微服务都是基于分布式管理 # 2. 为什么微服务 - 优势:1.拆分每个微服务各司其职 解耦合 2.每个服务高度自治(管理团队) 3.不会因为一个问题导致其他服务不可用 4.新员工面对单一服务,不需要面对整体应用代码复杂度 5.方便在项目中增加新的业务功能 - 缺点:1.开发人员面对微服务部署复杂性 2.开发人员面对分布式系统复杂性(服务间通信 请求负载均衡。。。 高可用) # 3. springcloud 微服务工具集 - 作用:提供了一站式微服务工具,帮助我们解决了分布式系统中各种问题,让开发人员更专注自己业务开发 # 4. springcloud 工具集核心组件 ## 4.1、服务注册中心组件: Eureka、Consul,用来管理微服务架构中每一个服务健康状态以及服务元数据存储 ## 4.2、服务间通信实现 : - 服务间通信方式: RPC 、 Http 协议 (SpringCloud中) a. RestTemplate(HttpClient对象) + Ribbon组件(springcloud) 实现请求负载均衡 b. openfegin(底层默认集成Ribbon) 伪http客户端,实现请求负载均衡,解决restTemplate路径写死,灵活 推荐 ## 4.3、微服务保护组件: Hystrix (防止服务雪崩现象) Hystrix DashBoard 组件 维护状态 - 服务雪崩问题:调动链路中某一个微服务不可用,并导致上游服务不可用,并将这种不可逐渐放大最终导致整个系统服务不可用 这用称之为服务雪崩 - 服务熔断:当服务触发异常,或者执行业务逻辑超时等待触发服务熔断,一旦触发服务熔断之后,当前服务不可用 自动触发 - 服务降级:站在系统整体负荷角度进行系统考虑 一般都是人为干预 服务熔断属于服务降级一种 ## 4.4、微服务网关组件: Zuul1.x Zuul2.x(netflix组件)---> Gateway(Spring 组件) 网关: 路由转发(请求负载均衡) + 过滤器(前置predicate 后置filter) ## 4.5、统一配置中心组件: Config (netflix) 作用: 用来将微服务中所有配置进行远程git仓库统一管理 configServer(用来获取uanduan仓库配置,并存入本地仓库)、configClient(根据configServer 读取自身配置) ## 4.6、消息总线: Bus 作用: 用来通过消息中间件将所有微服务连接到一起,利用广播模型实现配置自动刷新机制 Spring Cloud Alibaba 一、简介

官网:https://spring.io/projects/spring-cloud-alibaba

Spring Cloud Alibaba provides a one-stop solution for distributed application development. It contains all the components required to develop distributed applications, making it easy for you to develop your applications using Spring Cloud.

With Spring Cloud Alibaba, you only need to add some annotations and a small amount of configurations to connect Spring Cloud applications to the distributed solutions of Alibaba, and build a distributed application system with Alibaba middleware.

Spring Cloud Alibaba 提供分布式应用开发的一站式解决方案。它包含开发分布式应用程序所需的所有组件,使您可以轻松使用 Spring Cloud 开发应用程序。

使用 Spring Cloud Alibaba,您只需要添加一些注解和少量配置,即可将 Spring Cloud 应用连接到 Alibaba 的分布式解决方案,并通过 Alibaba 中间件构建分布式应用系统。

微服务实战项目开发

服务注册中心 ===> Nacos服务间通信 ===> Openfeign服务流控与服务降级 ===> Sentinel服务网关 ===> Gateway统一配置中心 ===> Nacos 二、环境搭建

构建项目并引入依赖

<properties> <spring-cloud.version>Hoxton.SR6</spring-cloud.version> <spring.cloud.alibaba.version>2.1.2.RELEASE</spring.cloud.alibaba.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>${spring.cloud.alibaba.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> 三、Nacos 3.1、什么是Nacos

官网:https://nacos.io/zh-cn/index.html

Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。

Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。 Nacos 是构建以“服务”为中心的现代应用架构 (例如微服务范式、云原生范式) 的服务基础设施。

Name Service(服务注册与发现) & Configurations Services(统一配置中心)

总结:Nacos就是微服务架构中服务注册中心以及统一配置中心,用来替换原来的(eureka,consul)以及config组件

3.2、安装nacos

这里选用linux环境,注意安装nacos前,要保证 jdk1.8+、maven3.2.x+ 环境

1.下载nacos

https://github.com/alibaba/nacos/releases

2.解压缩安装包到指定位置

- [root@localhost ~]# tar -zxvf nacos-server-1.3.1.tar.gz - bin 启动nacos服务的脚本目录 - conf nacos的配置文件目录 - target nacos的启动依赖存放目录 - data nacos启动成功后保存数据的目录

3.启动安装服务

- linux/unix/mac启动 打开终端进入nacos的bin目录执行如下命令 ./startup.sh -m standalone

4.访问nacos的web服务管理界面 http://localhost:8848/nacos/

- 用户名 和 密码都是 nacos

3.3、开发服务注册到nacos

1、创建项目并引入依赖

<!--引入nacos client的依赖--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency>

2、配置注册地址

server.port=8789 spring.application.name=NACOSCLIENT spring.cloud.nacos.server-addr=ip:8848 #指定nacos服务总地址 spring.cloud.nacos.discovery.server-addr=${spring.cloud.nacos.server-addr} #指定注册中心地址

3、加入启动服务注册注解

注意:新版本springcloud只要引入依赖后,默认开启服务注册与发现,可以不写 @EnableDiscoveryClient

4、查看nacos的服务列表

3.4、服务间通信

由于springcloud alibaba中没有提供服务间通信的组件,所以还采用 openFeign,方式与之前springcloud中一致,这里不再写

3.5、使用nacos作为统一配置中心 3.5.1、从nacos获取配置

1、创建项目并引入nacons配置中心依赖

<!--引入nacos client 依赖--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!--引入配置中心依赖--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency>

2、添加配置文件 bootstrap.properties

spring.cloud.nacos.server-addr=localhost:8848 # 远程配置中心的地址 spring.cloud.nacos.config.server-addr=${spring.cloud.nacos.server-addr} # 去指定nacos地址读取配置 spring.cloud.nacos.config.group=DEFAULT_GROUP # 读取配置的分组 spring.cloud.nacos.config.file-extension=properties # 指定读取文件后缀 spring.cloud.nacos.config.name=configclient-dev # 指定文件名

配置文件名称必须是bootstrap.properties|yml,否则会启动报错,让项目在启动时先预拉取远程配置

3、在nacos中创建配置

4、编写控制器测试配置读取情况

@RestController public class ConfigClientController { private static final Logger log = LoggerFactory.getLogger(ConfigClientController.class); @Value("${user.name}") private String name; @GetMapping("/demo") public String demo() { log.info("username:{}", name); return name; } } 3.5.2、实现自动配置刷新

默认情况下nacos已经实现了自动配置刷新功能,如果需要刷新配置直接在控制器中加入 @RefreshScope 注解即可

3.5.3、DateId

dateId = ${prefix}-${spring.profile.active}.${file-extension}

dateId = spring.cloud.nacos.config.name + spring.cloud.nacos.config.file-extension

读取远程配置中心的配置文件有两种方式:

${prefix}-${spring.profile.active}.${file-extension} - a. prefix 默认为 spring.application.name 的值,也可以通过配置项 spring.cloud.nacos.config.prefix来配置。 - b. spring.profile.active 即为当前环境对应的 profile,详情可以参考 Spring Boot文档。 注意:当 spring.profile.active 为空时,对应的连接符 - 也将不存在,dataId 的拼接格式变成 ${prefix}.${file-extension} - c. file-exetension 为配置内容的数据格式,可以通过配置项 spring.cloud.nacos.config.file-extension 来配置。目前只支持 properties 和 yaml 类型。 ${name}.${file-extension} - name 指的就是文件名,比如configclient-dev,可以通过配置项spring.cloud.nacos.config.name来设置 - file-exetension 文件后缀,即文件的数据格式 3.5.4、命名空间

https://github.com/alibaba/spring-cloud-alibaba/wiki/Nacos-config

用于进行租户粒度的配置隔离。不同的命名空间下,可以存在相同的 Group 或 Data ID 的配置。Namespace 的常用场景之一是不同环境的配置的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等。

在没有明确指定 ${spring.cloud.nacos.config.namespace} 配置的情况下, 默认使用的是 Nacos 上 Public 这个namespae。如果需要使用自定义的命名空间,可以通过以下配置来实现:

每个命名空间都有一个唯一id,这个id是读取配置时指定空间的唯一标识

spring.cloud.nacos.config.namespace=b3404bc0-d7dc-4855-b519-570ed34b62d7

注意:该配置必须放在 bootstrap.properties 文件中。

3.5.5、配置分组

Nacos 中的一组配置集,是组织配置的维度之一。通过一个有意义的字符串(如 Buy 或 Trade )对配置集进行分组,从而区分 Data ID 相同的配置集。当您在 Nacos 上创建一个配置时,如果未填写配置分组的名称,则配置分组的名称默认采用 DEFAULT_GROUP 。

在没有明确指定 ${spring.cloud.nacos.config.group} 配置的情况下, 默认使用的是 DEFAULT_GROUP 。如果需要自定义自己的 Group,可以通过以下配置来实现:

spring.cloud.nacos.config.group=YinXue

注意:该配置必须放在 bootstrap.properties 文件中。并且在添加配置时 Group 的值一定要和 spring.cloud.nacos.config.group 的配置值一致。

3.6、nacos持久化

在0.7版本之前,在单机模式时nacos使用嵌入式数据库实现数据的存储,不方便观察数据存储的基本情况。0.7版本增加了支持mysql数据源能力,具体的操作步骤:

1.安装数据库,版本要求:5.6.5+2.初始化mysql数据库,数据库初始化文件:nacos-mysql.sql https://github.com/alibaba/nacos/blob/master/config/src/main/resources/META-INF/nacos-db.sq 3.修改conf/application.properties文件,增加支持mysql数据源配置(目前只支持mysql),添加mysql数据源的url、用户名和密码。 spring.datasource.platform=mysql db.num=1 db.url.0=jdbc:mysql://11.162.196.16:3306/nacos_devtest?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true db.user=nacos_devtest db.password=youdontknow

再以单机模式启动nacos,nacos所有写嵌入式数据库的数据都写到了mysql

3.7、nacos集群搭建

官网:https://nacos.io/zh-cn/docs/cluster-mode-quick-start.html

nacos集群架构图

# 1.集群搭建注意事项 - 注意: a.3个或3个以上Nacos节点才能构成集群。 b.要求虚拟机内存分配必须大于2G以上 # 2.集群规划 - node cluster: 192.168.10.138:8845 nacos01 192.168.10.138:8846 nacos02 192.168.10.138:8847 nacos03 192.168.10.138 80 nginx 192.168.10.138 3306 mysql # 3.搭建nacos集群 1).将nacos安装包从新解压缩 2).开启nacos mysql持久化 注意:数据库中不能存在原始数据 3).复制三份 nacos 分别为 nacos01 nacos02 nacos03 3).修改nacos conf目录中cluster.conf文件添加所有集群节点 192.168.10.138:8845 192.168.10.138:8846 192.168.10.138:8847 4).修改nacos 各自端口号 vim nacos01/conf/application.properties 8845 vim nacos02/conf/application.properties 8846 vim nacos03/conf/application.properties 8847 5).分别启动三台机器 ./startup.sh # 4.安装Nginx - 0.安装必要依赖 yum install -y gcc pcre-devel zlib-devel - 1.下载Nginx http://nginx.org/en/download.html - 2.将Nginx上传到linux中,并解压缩 tar -zxvf nginx-1.11.1.tar.gz - 3.查看Nginx安装目录 [root@localhost nginx-1.11.1]# ls auto CHANGES CHANGES.ru conf configure contrib html LICENSE man README src - 4.在Nginx安装目录中执行如下命令:(指定安装位置) ./configure --prefix=/usr/nginx - 5.执行上述命令后,执行如下命令: make && make install - 6.启动 1. 进入sbin目录 ./nginx 查看nginx是否启动成功: ps aux|grep nginx 默认监听端口为80 2. 关闭nginx服务 ./nginx -s stop # 5.配置nginx conf配置文件 a.加入如下配置: upstream nacos-servers { server 192.168.10.138:8845; server 192.168.10.138:8846; server 192.168.10.138:8847; } b.修改 location / { proxy_pass http://nacos-servers/; } # 6.启动nginx进行测试即可 四、sentinel 流量卫兵 4.1、什么是sentinel

As microservices become popular, the stability of service calls is becoming increasingly important. Sentinel takes “flow” as the breakthrough point, and works on multiple fields including flow control, circuit breaking and load protection to protect service reliability.

随着微服务的普及,服务调用的稳定性变得越来越重要。Sentinel 以“流量”为切入点,在流量控制、断路、负载保护等多个领域开展业务,保障业务可靠性。

https://spring-cloud-alibaba-group.github.io/github-pages/hoxton/en-us/index.html#_how_to_use_sentinel

https://github.com/alibaba/Sentinel/wiki

特性:

丰富场景:Sentinel 支持阿里巴巴双十一重点场景,如秒杀(即控制突发流量,使其在系统容量可接受范围内)、消息负载转移、不可靠的下游应用的断路。全面的实时监控: Sentinel 提供实时监控能力。您可以看到您的服务器的监控数据,精确到秒级,甚至可以看到少于 500 个节点的集群的整体运行状态。广泛的开源生态系统: Sentinel 提供开箱即用的模块,可以轻松与其他开源框架/库集成,例如 Spring Cloud、Dubbo 和 gRPC。使用 Sentinel,只需要引入相关依赖并做一些简单的配置即可。Sound SPI Extensions: Sentinel 提供简单易用的声音 SPI 扩展接口。您可以使用 SPI 扩展快速自定义逻辑,例如,您可以定义自己的规则管理,或适应特定的数据源。

sentinel提供了两个服务组件:

一个是 sentinel 用来实现微服务系统中服务熔断、降级等功能。 这点和Hystrix 类似一个是 sentinel dashboard 用来监控微服务系统中流量调用等情况 流控 熔断 降级 配置。 这点和Hystrix dashboard类似 4.2、sentinel dashboard的安装 普通安装

1、下载 https://github.com/alibaba/Sentinel/releases

2、启动

仪表盘是个jar包可以直接通过java命令启动 如: java -jar 方式运行 默认端口为 8858java -jar sentinel-dashboard-1.7.2.jar

3、访问web界面 http://localhost:8858/

用户名&密码: sentinel

注:这种启动方式要一直终端窗口开启

Docker方式安装 # 1. 搜索镜像 docker search sentinel-dashboard # 2. 拉取镜像 docker pull bladex/sentinel-dashboard # 3. 查看端口 docker inspect sentinel-dashboard 发现默认暴露端口为8719、8858 # 4. 启动 docker run --name sentinel -d -p 8858:8858 -p 8719:8719 -d bladex/sentinel-dashboard 4.3、sentinel 实时监控服务

1、创建项目引入依赖

<!--引入nacos client依赖--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!--引入sentinel依赖--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>

2、配置

server.port=8789 spring.application.name=nacosclient spring.cloud.nacos.server-addr=localhost:8848 spring.cloud.nacos.discovery.server-addr=${spring.cloud.nacos.server-addr} spring.cloud.sentinel.enabled=true # 开启sentinel 默认开启 spring.cloud.sentinel.transport.dashboard=localhost:8858 # 连接dashboard spring.cloud.sentinel.transport.port=8719 # 与dashboard通信的端口

3、启动后访问dashboard界面查看服务监控

发现界面什么都没有?默认情况下sentiel为延迟加载,不会在启动之后立即创建服务监控,需要对服务进行调用时才会初始化

注意:dashboard信息必须在指定服务进行资源调用后才能进行初始化

4、开发服务

@RestController public class DemoController { @GetMapping("/demo") public String demo() { return "demo ok!!!"; } }

注意: sentinel必须和项目需要在同一服务器上,否则会导致监控页面空白

解决方案:

sentinel必须和项目需要在同一服务器上在配置里面添加 spring.cloud.sentinel.transport.client-ip=localhost ,修改容器的时区

# 五大规则 # 流控规则:流量控制(flow control) 定义:其原理是监控应用流量的 QPS 或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。 # 降级规则: 熔断降级(Degrade Service) 定义:其原理是监控应用中资源调用请求,达到指定阈值时自动触发熔断降级 # 热点规则: 热点参数(ParamFlow) 热点:何为热点? 热点即经常访问的数据 定义:其原理很多时候我们希望统计某个热点数据中访问频次最高的Top K 数据,并对其访问进行限制。 # 系统规则: SystemFlow 定义:其原理是 Sentinel 系统自适应限流从整体纬度对应用入口流量进行控制 # 授权规则: 黑白名单控制规则 定义:很多时候,我们需要根据调用来源判断该次请求是否允许放行,这时候可以使用 Sentinel 的来源访问控制(黑白名单控制)的功能。 来源访问控制根据资源的请求来源(origin)限制资源是否通过,若配置白名单则只有请求来源位于白名单内时才可通过:若配置黑名单则请求来源位于黑名单时不通过,其余的请求通过。 4.4、sentinel 流量控制

流量控制(flow control),其原理是监控应用流量的 QPS 或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。

QPS:每秒查询率(Queries-per-second)是对一个特定的查询服务器在规定时间内所处理流量多少的衡量标准。 当每秒请求数超过指定阈值之后对当前请求进行限流 并发线程数 当服务器中创建线程数超过指定阈值之后对当前请求进行限流

同一个资源可以创建多条限流规则。FlowSlot 会对该资源的所有限流规则依次遍历,直到有规则触发限流或者所有规则遍历完毕。

一条限流规则主要由下面几个因素组成,我们可以组合这些元素来实现不同的限流效果:

resource:资源名,即限流规则的作用对象count:限流阈值grade: 限流阈值类型(QPS 或并发线程数)limitApp: 流控针对的调用来源,若为 default 则不区分调用来源strategy:调用关系限流策略 ( 直接 关联 链路 )controlBehavior:流量控制效果(直接拒绝、Warm Up、匀速排队)

流量控制主要有两种统计类型,一种是统计并发线程数,另外一种则是统计 QPS

更多细节参见官网:https://github.com/alibaba/Sentinel/wiki/%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6

QPS限流

1、配置QPS流量控制

2、测试

每秒只能最大接收1个请求,超过1个报错

线程数限流

1、配置线程数限流

2、访问测试

这里通过压测工具 JMeter进行测试

流控模式 - 直接:标识流量控制规则到达阈值直接触发流量控制 - 关联: 当两个资源之间具有资源争抢或者依赖关系的时候,这两个资源便具有了关联。比如对数据库同一个字段的读操作和写操作存在争抢,读的速度过高会影响写得速度,写的速度过高会影响读的速度。如果放任读写操作争抢资源,则争抢本身带来的开销会降低整体的吞吐量。可使用关联限流来避免具有关联关系的资源之间过度的争抢,举例来说,read_db 和 write_db 这两个资源分别代表数据库读写,我们可以给 read_db 设置限流规则来达到写优先的目的:设置 strategy 为 RuleConstant.STRATEGY_RELATE 同时设置 refResource 为 write_db。这样当写库操作过于频繁时,读数据的请求会被限流。 - 链路限流: https://github.com/alibaba/Sentinel/wiki/%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6

流控效果

只适用于QPS

直接拒绝:(RuleConstant.CONTROL_BEHAVIOR_DEFAULT)方式是默认的流量控制方式,当QPS超过任意规则的阈值后,新的请求就会被立即拒绝,拒绝方式为抛出FlowException。Warm Up:(RuleConstant.CONTROL_BEHAVIOR_WARM_UP)方式,即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。 更多:https://github.com/alibaba/Sentinel/wiki/%E9%99%90%E6%B5%81—%E5%86%B7%E5%90%AF%E5%8A%A8 排队等待:(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。 只能对请求进行排队等待 更多:https://github.com/alibaba/Sentinel/wiki/%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6-%E5%8C%80%E9%80%9F%E6%8E%92%E9%98%9F%E6%A8%A1%E5%BC%8F 4.5、熔断降级

https://github.com/alibaba/Sentinel/wiki/%E7%86%94%E6%96%AD%E9%99%8D%E7%BA%A7

除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。由于调用关系的复杂性,如果调用链路中的某个资源不稳定,最终会导致请求发生堆积。Sentinel 熔断降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联错误。当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认行为是抛出 DegradeException)。

降级策略 平均响应时间 (DEGRADE_GRADE_RT):当 1s 内持续进入 N 个请求,对应时刻的平均响应时间(秒级)均超过阈值(count,以 ms 为单位),那么在接下的时间窗口(DegradeRule 中的 timeWindow,以 s 为单位)之内,对这个方法的调用都会自动地熔断(抛出 DegradeException)。注意 Sentinel 默认统计的 RT 上限是 4900 ms,超出此阈值的都会算作 4900 ms,若需要变更此上限可以通过启动配置项 -Dcsp.sentinel.statistic.max.rt=xxx 来配置。

异常比例 (DEGRADE_GRADE_EXCEPTION_RATIO):当资源的每秒请求量 >= N(可配置),并且每秒异常总数占通过量的比值超过阈值(DegradeRule 中的 count)之后,资源进入降级状态,即在接下的时间窗口(DegradeRule 中的 timeWindow,以 s 为单位)之内,对这个方法的调用都会自动地返回。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。

异常数 (DEGRADE_GRADE_EXCEPTION_COUNT):当资源近 1 分钟的异常数目超过阈值之后会进行熔断。注意由于统计时间窗口是分钟级别的,若 timeWindow 小于 60s,则结束熔断状态后仍可能再进入熔断状态。

4.6、SentinelResource注解 https://github.com/alibaba/Sentinel/wiki/%E6%B3%A8%E8%A7%A3%E6%94%AF%E6%8C%81

@GetMapping("/sentinel/test1") @SentinelResource(value = "aa",blockHandler = "fallBack",fallback = "fall") public String test1(int id){ log.info("sentinel test1"); if(id<0){ throw new RuntimeException("非法参数!!!"); } return "sentinel test1 :"+id; } //降级异常处理 public String fallBack(int id,BlockException e){ if(e instanceof FlowException){ return "当前服务已被流控! "+e.getClass().getCanonicalName(); } return "当前服务已被降级处理! "+e.getClass().getCanonicalName(); } //异常处理 public String fall(int id){ return "当前服务已不可用!"; } 总结 # 1.springcloud & springcloud alibaba - springcloud 微服务工具集 微服务工具集 - springcloud alibaba 微服务一站式解决方案 # 2.springcloud中组件 五大组件 a.服务注册中心 eureka consul b.服务间通信组件(ribbon) Openfeign c.服务熔断 Hystrix、Hystrix DashBoard d.服务网关 zuul1.x zuul2.x Gateway e.服务配置中心 config + bus 自动配置刷新 # 3.springcloud alibaba组件 a.服务注册中心 服务配置中心 nacos ====> 替换 eureka consul 替换config+bus b.服务熔断、流控 sentinel(sentinel、sentinel dashBoard)====>替换原始 Hystrix(Hystrix、Hystrix DashBoard) # 4.总结:五大组件 a.服务注册中心 nacos b.服务间通信组件 1.RestTemplate+Ribbon 2.OpenFeign组件 c.服务熔断、流控 sentinel d.服务网关 gateway e.服务配置中心 nacos


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #Spring #Cloud #amp #Alibaba #微服务详细笔记