序言
鉴于代码的自行改造和版本升级,随意更换依赖版本可能导致项目报错,因此在正文之前附上版本对应表.
SpringBoot和SpringCloud的版本需要对应,不然会因为jar包版本不兼容导致一系列奇怪的问题。因为官方不会保证SpringBoot和SpringCloud不同版本的兼容性。
大版本对应
SpringCloud版本 | SpringBoot版本 |
---|---|
Hoxton | 2.2.x |
Greenwich | 2.1.x |
Finchley | 2.0.x |
Edgware | 1.5.x |
Dalston | 1.5.x |
参考:https://spring.io/projects/spring-cloud
其他
spring-cloud-dependencies 版本列表可查看:https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-dependencies
spring-boot-starter-parent 版本列表可查看:https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-parent
本文正文中的代码均基于以下版本:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>1.5.9.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
附上尚硅周阳2018Spring Cloud第一期教程:【侵删】
链接:https://pan.baidu.com/s/1jDuYfzmYL2YSJV4RBSoGyg
提取码:zzgs
鉴于目前企业实际情况,自己重做了Spring Boot 2.1.6 + Spring Cloud Greenwich.SR3版本的代码,期间坑真的是很多,大部分都是版本升级导致的依赖名称变更等,感兴趣的朋友可以自己尝试重构,再拿我的代码做参考:
链接:https://pan.baidu.com/s/1sIDEqymSMFjdlIl5XAxWIQ
提取码:zfur
SpringCloud
一、微服务概述
马丁福勒微服务论文: https://martinfowler.com/articles/microservices.html
1.1.什么是微服务
- 微服务化的核心就是将传统的一站式应用,根据业务拆分成一个一个的服务,彻底地去耦合,每一个微服务提供单个业务功能的服务,一个服务做一件事;从技术角度看就是一种小而独立的处理过程,类似进程概念,能够自行单独启动或销毁,并且可以拥有自己独立的数据库。
1.2.微服务与微服务架构
- 微服务是一种架构模式或者一种架构风格,提倡将单一应用程序划分成一组小的服务独立部署,服务之间相互配合、相互协调,每个服务运行于自己的进程中;
- 服务与服务间采用轻量级通讯,如HTTP的RESTful API等;
- 避免统一的、集中式的服务管理机制。
补充:
要弄清楚什么是RESTful API,首先要弄清楚什么是REST。
REST – REpresentational State Transfer,英语的直译就是“表现层状态转移”。如果看这个概念,估计没几个人能明白是什么意思。那下面就让我来用一句人话解释一下什么是RESTful:
URL定位资源,用HTTP动词(GET,POST,PUT,DELETE等)描述操作。
Resource:资源,即数据。
Representational:某种表现形式,比如用JSON,XML,JPEG等;
State Transfer:状态变化。通过HTTP动词实现。
所以RESTful API就是REST风格的API。 那么在什么场景下使用RESTful API呢?在当今的互联网应用的前端展示媒介很丰富。有手机、有平板电脑还有PC以及其他的展示媒介。那么这些前端接收到的用户请求统一由一个后台来处理并返回给不同的前端肯定是最科学和最经济的方式,RESTful API就是一套协议来规范多种形式的前端和同一个后台的交互方式。
1.3.微服务的优缺点
1.3.1.优点
- 每个服务足够内聚,足够小,比较容易聚焦【代码量少耦合低,开发人员能够快速理解逻辑并上手修改代码】;
- 开发简单且效率高,一个服务只做一件事情;
- 开发团队小,一般2-5人足以(当然按实际为准);
- 微服务是松耦合的,无论开发还是部署都可以独立完成;
- 微服务能用不同的语言开发;
- 易于和第三方集成,微服务允许容易且灵活的自动集成部署(持续集成工具有Jenkins,Hudson,bamboo等);
- 微服务易于被开发人员理解,修改和维护,这样可以使小团队更加关注自己的工作成果,而无需一定要通过合作才能体现价值;
- 微服务允许你融合最新的技术;
- 微服务只是业务逻辑的代码,不会和HTML,CSS或其他界面组件混合;
- 每个微服务都可以有自己的存储能力,数据库可自有也可以统一,十分灵活。
1.3.2.缺点
- 开发人员要处理分布式系统的复杂性
- 多服务运维难度,随着服务的增加,运维的压力也会增大
- 系统部署依赖
- 服务间通讯的成本
- 数据的一致性
- 系统集成测试
- 性能监控的难度
1.4.微服务的技术栈
微服务技术栈:多种技术的集合体
我们讨论一个分布式的微服务架构时,需要考虑哪些维度/有哪些落地技术?
微服务条目 | 落地技术 |
---|---|
服务开发 | SpringBoot,Spring,SpringMVC |
服务配置与管理 | Netflix公司的Archaius、阿里的Diamond等 |
服务注册与发现 | Eureka、Consul、Zookeeper等 |
服务调用 | Rest、RPC、gRPC |
服务熔断器 | Hystrix、Envoy等 |
负载均衡 | Ribbon、Nginx等 |
服务接口调用(客户端调用服务的简化工具) | Feign等 |
消息队列 | Kafka、RabbitMQ、ActiveMQ等 |
服务配置中心管理 | Spring Cloud Config、Chef等 |
服务路由(API网关) | Zuul等 |
服务监控 | Zabbix、Nagios、Metrics、Specatator等 |
全链路追踪 | Zipkin、Brave、Dapper等 |
服务部署 | Docker、OpenStack、Kubernetes等 |
数据流操作开发包 | Spring Cloud Stream(封装与Redis,Rabbit,Kafka等发送接收消息) |
事件消息总线 | Spring Cloud Bus |
1.5.为什么选SpringCloud作为微服务架构
1.5.1.选型依据
- 整体解决方案和框架的成熟度
- 社区热度
- 可维护性
- 学习曲线
1.5.2.当前各大IT公司的微服务架构
- 阿里Dubbo/HSF(“好舒服”框架)
- 京东JSF
- 新浪Motan
- 当当DubboX
1.5.3.各微服务的框架对比
功能点/服务框架 | Netflix/SpringCloud | Motan(新浪) | gRPC | Thrift(Facebook) | Dubbo(阿里)/DubboX(当当) |
---|---|---|---|---|---|
功能定位 | 完整的微服务架构 | RPC框架,但整合了ZK或Consul,实现集群环境的基本服务注册/发现 | RPC框架 | RPC框架 | 服务框架 |
支持REST | 是,Ribbon支持多种可插拔的序列化选择 | 否 | 否 | 否 | 否 |
支持RPC | 否 | 是 | 是 | 是 | 是 |
支持多语言 | 是(REST形式) | 否 | 是 | 是 | 否 |
服务注册/发现 | 是(Eureka) Eureka服务注册表,Karyon服务端框架支持服务自注册和健康检查 | 是(zookeeper/consul) | 否 | 否 | 是 |
负载均衡 | 是(服务端zuul+客户端Ribbon) zuul-服务,动态路由 云端负载均衡 Eureka(针对中间层服务器) | 是(客户端) | 否 | 否 | 是(客户端) |
配置服务 | Netflix Archaius SpringCloud Config Server集中配置 | 是(zookeeper提供) | 否 | 否 | 否 |
服务调用链监控 | 是(zuul) Zuul提供边缘服务,API网关 | 否 | 否 | 否 | 否 |
高可用/容错 | 是(服务端Hystrix+客户端Ribbon) | 是(客户端) | 否 | 否 | 是(客户端) |
典型应用案例 | Netflix | Sina | |||
社区活跃度 | 高 | 一般 | 高 | 一般 | 2017年8月才重启 |
学习难度 | 中等 | 一般 | 高 | 一般 | 低 |
文档丰富度 | 高 | 一般 | 一般 | 一般 | 高 |
其他 | Spring Cloud Bus为我们应用程序带来了更多管理端点 | 支持降级 | Netflix内部在开发集成gRPC | IDL定义 | 实践公司比较多 |
1.5.4.补充内容
-
Dubbo和Dubbox
Dubbo源于阿里的淘宝网开源的分布式的服务架构,致力于提供高性能和透明化的RPC远程服务调用方案,是SOA(Service-Oriented Architecture,面向服务的架构)服务化治理方案的核心框架。淘宝网将其开源之后,得到了很多的拓展和支持(比较出名的有:当当网的扩展版本dubbox,京东的扩展版本jd-hydra等),2012年之后Dubbo不再维护,17年8月份阿里重启维护。正是这段时间让SpringCloud异军突起。 梁飞团队被打散之后,内部开始使用HSF。Dubbox是一个开源的RPC(Remote ProcedureCall Protocol)远程调用框架,是由当当对阿里的Dubbo的升级,可以被视为Dubbo的增强版,基本架构没有变动,升级spring2.x到spring3.x,支持RESTful风格的调用调试方式,丰富了序列化的方式,提高了序列化的性能。Dubbox(即Dubbo eXtensions)是当当网Fork基于dubbo2.x的升级版本,兼容原有的dubbo。其中升级了zookeeper和spring版本,并且支持RESTfull风格的远程调用。【2018年3月当当被收购,对于后续社区维护不看好】
以下引自知乎:(如今梁飞已经掌管天猫)
阿里巴巴为什么主推HSF?比Dubbo有哪些优势?
“真实原因是 dubbo 是阿里 b2b 团队编写的框架,hsf 是淘宝团队编写的框架。淘宝在业务复杂度以及规模上当然完爆 b2b,所以内部 pk 必然是 hsf 获胜。”
-
RPC vs REST
服务提供方与调用方接口依赖方式太强:我们为每个微服务定义了各自的service抽象接口,并通过持续集成发布到私有仓库中,调用方应用对微服务提供的抽象接口存在强依赖关系,因此不论开发、测试、集成环境都需要严格的管理版本依赖,才不会出现服务方与调用方的不一致导致应用无法编译成功等一系列问题,以及这也会直接影响本地开发的环境要求,往往一个依赖很多服务的上层应用,每天都要更新很多代码并install之后才能进行后续的开发。若没有严格的版本管理制度或开发一些自动化工具,这样的依赖关系会成为开发团队的一大噩梦。而REST接口相比RPC更为轻量化,服务提供方和调用方的依赖只是依靠一纸契约,不存在代码级别的强依赖,当然REST接口也有痛点,因为接口定义过轻,很容易导致接口文档与实际实现不一致导致服务集成时的问题,但是该问题很好解决,只需要通过每个服务整合Swagger,让每个服务的代码与文档一体化,就能解决。所以在分布式环境下,REST方式的服务依赖要比RPC方式的依赖更为灵活。服务对平台敏感,难以简单复用:通常我们在提供对外服务时,都会以REST的方式提供出去,这样可以实现跨平台的特点,任何一个语言的调用方都可以根据接口定义来实现。那么在Dubbo中我们要提供REST接口时,不得不实现一层代理,用来将RPC接口转换成REST接口进行对外发布。若我们每个服务本身就以REST接口方式存在,当要对外提供服务时,主要在API网关中配置映射关系和权限控制就可实现服务的复用了。
二、Spring Cloud入门概述
- Spring的三大模块:Spring Boot(构建),Spring Cloud(协调),Spring Cloud Data Flow(连接)
2.1.Spring Cloud是什么
- 分布式系统的简化版(官方介绍)
- Spring Cloud基于Spring Boot提供了一整套微服务的解决方案,包括服务注册与发现,配置中心,全链路监控,服务网关,负载均衡,熔断器等组件,除了基于Netflix的开源组件做高度抽象封装之外,还有一些选型中立的开源组件
- Spring Cloud利用Spring Boot的开发便利性巧妙地简化了分布式系统的基础设施开发,Spring Cloud为开发人员提供了快速构建分布式系统的一些工具,包括配置管理、服务发现、断路器、路由、微代理、事件总线,全局所、决策精选、分布式会话等等,他们都可以用Spring Boot的开发风格做到一键启动和部署。
- 一句话概括:Spring Cloud是分布式微服务架构下的一站式解决方案,是各个微服务架构落地技术的集合体,俗称微服务全家桶
2.2.Spring Cloud和Spring Boot的关系
Spring Boot:专注于快速方便的开发单个个体微服务(关注微观)
Spring Cloud:关注全局的微服务协调治理框架,将Spring Boot开发的一个个单体微服务组合并管理起来(关注宏观)
- Spring Boot可以离开Spring Cloud独立使用,但是Spring Cloud不可以离开Spring Boot,属于依赖关系
2.3.Dubbo是怎么到Spring Cloud的?哪些优缺点去技术选型
2.3.1.目前成熟的互联网架构(分布式+服务治理Dubbo)
2.3.2.对比
Dubbo | Spring | |
---|---|---|
服务注册中心 | Zookeeper | Spring Cloud Netfilx Eureka |
服务调用方式 | RPC | REST API |
服务监控 | Dubbo-monitor | Spring Boot Admin |
断路器 | 不完善 | Spring Cloud Netflix Hystrix |
服务网关 | 无 | Spring Cloud Netflix Zuul |
分布式配置 | 无 | Spring Cloud Config |
服务跟踪 | 无 | Spring Cloud Sleuth |
消息总线 | 无 | Spring Cloud Bus |
数据流 | 无 | Spring Cloud Stream |
批量任务 | 无 | Spring Cloud Task |
最大区别:
- Spring Cloud抛弃了RPC通讯,采用基于HTTP的REST方式。Spring Cloud牺牲了服务调用的性能,但是同时也避免了原生RPC带来的问题。REST比RPC更为灵活,不存在代码级别的强依赖,在强调快速演化的微服务环境下,显然更合适。
- 一句话:Dubbo像组装,Spring Cloud像一体
- 社区的支持与力度:Dubbo曾经停运了5年,虽然重启了,但是对于技术发展的新需求,还是需要开发者自行去拓展,对于中小型公司,显然显得比较费时费力,也不一定有强大的实力去修改源码
2.3.3.总结
- 解决的问题域不一样:Dubbo的定位是一款RPC框架,Spring Cloud的目标是微服务架构下的一站式解决方案
2.4.Spring Cloud的参考资料
Spring Cloud的中文参考资料: https://springcloud.cc/spring-cloud-netflix.html
Spring Cloud的中文API: https://springcloud.cc/spring-cloud-dalston.html
Spring Cloud中国社区: http://springcloud.cn/
Spring Cloud中文网: https://springcloud.cc/
三、构建Spring Cloud工程
概述:Spring Cloud工程由一个父工程和若干个Module组成
应该遵循的条件:约定 > 配置 > 编码
3.1.RestTemplate类
3.1.1.介绍
RestTemplate是Spring提供的用于访问Rest服务的客户端模板工具集,提供了多种远程访问Http的方法
RestTemplate的API : https://docs.spring.io/spring/docs/5.2.2.RELEASE/javadoc-api/
3.1.2.意义
在一些不涉及实现方法的模块中(消费者),只需要调用其他服务暴露出的接口即可满足的需求,使用RestTemplate类中的方法可以发出需要的HTTP请求并得到返回结果。(类似Ajax)
3.1.3.RestTemplate用法
RestTemplate restTemplate = new RestTemplate();
//url:请求地址
//requestMap:请求参数
//type.class:HTTP响应转换成的对象类型
restTemplate.getForObject(url,type.class);
restTemplate.postForObject(url,requestMap,type.class);
3.2.构建父工程
- 创建一个Maven父工程并命名GAV座标
<groupId>com.chengsw</groupId>
<artifactId>microservicecloud</artifactId>
<version>0.0.1-SNAPSHOT</version>
- 打包方式为POM
- 在pom.xml中定义各依赖的版本号(若Module中pom.xml的依赖没有指定版本号,则会根据父工程的版本号加入依赖)
- 加入通用的依赖和插件
3.3.构建Module
- 在父工程下新建Maven的Module,打包方式为jar
- 一般来时GAV中GV随父工程,自己定义A即可
- 在该Module下pom.xml中加入其它需要的依赖
- 正常开发即可
- 完成后先clean一下Maven项目,然后再install提供给其它模块调用
3.3.1.添加其它Module的依赖方法
- 直接引用其GAV即可
<dependencies>
<dependency>
<groupId>com.lzl</groupId>
<artifactId>microservice-api</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
3.3.2.配置该module下的yml
- 微服务需要独立的端口
- 微服务最重要的是取名字!!!!一定要给微服务配置一个名字!这个名字就是这个微服务对外暴露的名字!
- 配置该模块下的其它相关配置(如本例配置了mybatis)
server:
port: 8001
mybatis:
config-location: classpath:mybatis/mybatis.cfg.xml
type-aliases-package: com.XXX.entity
mapper-locations:
- classpath:mybatis/mapper/**/*.xml
spring:
application:
name: microservicecloud-dept #为这个服务取名,非常重要!!!!!
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: org.gjt.mm.mysql.Driver
url: jdbc:mysql://localhost:3306/cloudDB01
username: root
password: 123456
dbcp2:
min-idle: 5 #最小连接数
initial-size: 5 #初始化连接数
max-total: 10 #最大连接数
max-wait-millis: 200 #等待连接最长的超时时间
3.3.3.编写主启动类
- 记得主启动类放在根包下,com.xxx.xxx
package com.XXX;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Provider8001_APP {
public static void main(String[] args) {
SpringApplication.run(Provider8001_APP.class,args);
}
}
SpringCloud添加组件的基本套路
- 新增这个组件的maven座标GAV
- 在启动类上面标注启动该组件(一般来说是@EnableXXXXX)
- 编写业务逻辑代码
四、Eureka服务注册与发现
4.1.Eureka介绍及原理
4.1.1.理解
Eureka就像一个物业管理公司,其他微服务就像小区的住户,每个住户入住时都要向物业管理公司注册,并定时向物业公司交管理费
4.1.2.介绍
- Eureka是一个基于REST的服务,用于定位服务,以实现云端中间层服务发现和故障转移。
- Eureka主管服务注册与发现,在微服务中,以后了这两者,只需要使用服务的标识符(就是那个在每个服务的yml文件中取得服务名称),就可以访问到服务,不需要修改服务调用的配置文件
- Eureka遵循AP原则(高可用,分区容错性),因为使用了自我保护机制所以保证了高可用
4.1.3.原理
- Eureka使用的是C-S结构(客户端-服务端)
- 两大组件:Eureka Server(提供注册服务)、 Eureka Client(JAVA客户端,负责发送心跳)
- 系统中的其他微服务使用Eureka客户端连接到Eureka服务端维持心跳连接(即注册)。SpringCloud的其他模块可以通过Eureka Server 来发现系统中的微服务并加以调用
4.2.Eureka服务注册中心构建
4.2.1.加入服务端依赖
<dependency>
<!--Eureka-Server服务端-->
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
4.2.2.配置yml
- 理解:物业公司肯定不向自己注册自己,并肯定知道自己在哪,不用参加检索
server:
port: 7001
eureka:
instance:
hostname: localhost
client:
register-with-eureka: false #false表示不向注册中心注册自己
fetch-registry: false #false表示自己就是注册中心,职责是维护实例,不参加检索
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #设置eureka server的交互地址,即对外暴露的地址
4.2.3.添加启动类
- 注意:要在类前加@EnableEurekaServer标注
package com.XXX;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class Eureka7001_APP {
public static void main(String[] args) {
SpringApplication.run(Eureka7001_APP.class,args);
}
}
4.2.4.验证是否构建成功
先启动EurekaServer主程序,访问该服务地址即可
4.3.向Eureka注册中心注册微服务
4.3.1.增加依赖
在要注册的微服务的pom.xml文件中增加依赖
<!--将微服务provider侧注册进Eureka-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
4.3.2.修改yml
- 在application.yml中增加以内容,将客户端注册到服务列表内
- 理解:小区用户要找到物业管理处的地址进行注册
# 将微服务客户端注册进Eureka服务列表中
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka # 就是Eureka注册中心地址
4.3.3.主启动类增加注解
- 增加@EnableEurekaClient注解
@SpringBootApplication //本服务启动后,会自动注册进Eureka服务中
@EnableEurekaClient
public class Provider8001_APP {
public static void main(String[] args) {
SpringApplication.run(Provider8001_APP.class,args);
}
}
4.4.actuator与微服务注册完善
4.4.1.主机名称与服务名称的修改
- 修改服务名称,在yml中eureka节点下添加如下内容
eureka:
instance:
instance-id: dept8001 #修改别名
prefer-ip-address: true #显示IP地址
4.4.2.info内容的详细信息修改
作用
在查看Eureka时点击进入某个微服务的info时,能给查看者一些必要的信息,可以帮助查看者快速的了解该微服务,开发中十分有意义。
修改方法
- 当前工程添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
- 总的父工程的build节点下添加如下内容
<build>
<finalName>microservicecloud</finalName>
<resources>
<resource>
<!--允许扫描该路径下的资源文件-->
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<delimiters>
<!--指定动态获取以$标志开头结尾的信息-->
<delimit>$</delimit>
</delimiters>
</configuration>
</plugin>
</plugins>
</build>
- 在当前工程 的application.yml文件添加回显信息
info:
author: XXX
build-version: $project.version$
4.5.Eureka的自我保护
4.5.1.介绍
Eureka的自我保护机制主要是为了网络异常时保持高可用设计的,当在Eureka中注册的微服务超过设定是时间内(默认90秒)没有向Eureka服务端发送心跳,该微服务会进入自我保护模式。在自我保护模式中,Eureka会保护服务注册表中的信息,不会注销任何服务实例,直至收到的心跳数恢复至阈值以上,该微服务退出自我保护模式。
4.5.2.理解
好死不如赖活:Eureka的设计哲学是宁可保留错误的服务信息,也不盲目注销可能健康的服务。所以异常的服务不会被注销,而是进入了自我保护模式。
4.5.3.自我保护模式的开关
在Eureka Server模块下的yml文件中添加配置信息即可,true表示打开自我保护模式;false表示关闭自我保护模式(不推荐)
eureka:
server:
enable-self-preservation: false #禁用自我保护
4.6.Eureka的服务发现
4.6.1.介绍
系统中的微服务可以通过Eureka的服务发现去获得在Eureka中注册的服务的信息,这是一个对外暴露的接口。
4.6.2.使用方法(provider中)
- 注入DiscoveryClient 对象(spring包下的),在controller方法中获取
@Autowired
private DiscoveryClient discoveryClient;
@ResponseBody
@GetMapping("/provider/discovery")
public Object discovery(){
List<String> list = discoveryClient.getServices();
System.out.println(list);
List<ServiceInstance> insList = discoveryClient.getInstances("MICROSERVICECLOUD-DEPT");
for (ServiceInstance si:insList) {
System.out.println(si.getHost() +"," + si.getServiceId() +"," +si.getPort() +"," +si.getUri() +"," +si.getMetadata());
}
return this.discoveryClient;
}
- 在主启动类中加入@EnableDiscoveryClient注解
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
public class Provider8001_APP {
public static void main(String[] args) {
SpringApplication.run(Provider8001_APP.class,args);
}
}
4.6.3.使用方法(consumer中)
在controller方法中使用restTemplate对象调用provider中暴露的URL 并获得返回对象即可
private static final String URL_PREFIX = "http://localhost:8001";
@GetMapping("/discovery")
public Object discovery() {
return restTemplate.getForObject(URL_PREFIX+"/provider/discovery",Object.class);
}
4.7.Eureka的集群配置
基本原理
上图是来自eureka的官方架构图,这是基于集群配置的eureka;
- 处于不同节点的eureka通过Replicate进行数据同步
- Application Service为服务提供者
- Application Client为服务消费者
- Make Remote Call完成一次服务调用
服务启动后向Eureka注册,Eureka Server会将注册信息向其他Eureka Server进行同步,当服务消费者要调用服务提供者,则向服务注册中心获取服务提供者地址,然后会将服务提供者地址缓存在本地,下次再调用时,则直接从本地缓存中取,完成一次调用。
当服务注册中心Eureka Server检测到服务提供者因为宕机、网络原因不可用时,则在服务注册中心将服务置为DOWN状态,并把当前服务提供者状态向订阅者发布,订阅过的服务消费者更新本地缓存。
服务提供者在启动后,周期性(默认30秒)向Eureka Server发送心跳,以证明当前服务是可用状态。Eureka Server在一定的时间(默认90秒)未收到客户端的心跳,则认为服务宕机,注销该实例。
4.7.1.集群
集群就是在不同的机器上配置相同的服务来构建要一个大的运算整体
4.7.2.实现集群
- 新建N个Eureka Server模块
- 每个模块的pom.xml中加入与单个Eureka Server相同的依赖
- 每个模块加入主程序(记得加@EnableEurekaServer注解)
- 修改hosts文件(C:\Windows\System32\drivers\etc)
单机版的Eureka可以设置localhost默认是127.0.0.1,但是搭建集群后不可能N台机器都用localhost,N个王富贵怎么区分?因此需要修改hosts文件做映射。
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
127.0.0.1 eureka7003.com
- 修改Eureka Server模块的application.yml文件,加入集群,主要修改两个地方:
- hostname:修改为hosts文件中映射的地址
- service-url下的defaultZone节点:填入集群中另外的server服务端的地址
server:
port: 7001
eureka:
instance:
hostname: eureka7001.com #hostname为hosts文件中映射的地址
client:
register-with-eureka: false #false表示不向注册中心注册自己
fetch-registry: false #false表示自己就是注册中心,职责是维护实例,不参加检索
service-url:
#defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #设置eureka server的交互地址
defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/ #其他两个服务端的地址
- 修改Eureka Client模块的application.yml文件,使其向集群注册服务
- service-url下的defaultZone节点:填入集群中需要向其注册server服务端的地址
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7003.com:7003/eureka
- 访问地址
http://eureka7001.com:7001
http://eureka7002.com:7002
http://eureka7003.com:7003
- 注:defaultZone中/eureka 后缀是必须的,如果删除,Server类不会报错,但是Client注册时会报404错误
4.8.Eureka与Zookeeper对比
4.8.1.CAP设计原则不同
Eureka遵守AP,Zookeeper遵守CP(C:强一致性,A:高可用,P:分区容错性,三者只能选其二,高并发下P必选)
4.8.2.网络波动下两者的处理对比
Zookeeper | Eureka |
---|---|
当网络出现故障时,剩余zk集群会发起投票选举新的leader,但是此过程会持续30~120s,此过程对于高并发来说十分漫长,会导致整个注册服务的瘫痪,这是不可容忍的 | 在15分钟内85%的节点都没有心跳,则注册中心 会认为客户端与之出现了网络故障,则会进入自动保护模式。1.Eureka不会移除没有收到心跳的服务;2.新的服务仍能在服务端注册,但是暂时不会被同步到其他节点上直到网络稳定 |
4.8.3.结论
Eureka可以很好的应对网络故障导致部分节点失去连接的情况,而不会像zookeeper那样导致整个注册服务系统的瘫痪。
4.8.4.补充
RDBMS(MySQL/Oracle/sqlServer) ——> ACID
NOSQL(Redis/mongoDB) ——> CAP
ACID:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)
CAP:一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)
CAP原则又称CAP定理,指的是在一个分布式系统中,一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)。**CAP 原则指的是,这三个要素最多只能同时实现两点,不可能三者兼顾。**由于分区容错性P在是分布式系统中是必须要保证的,因此我们只能在A和C之间进行权衡。
五、Ribbon负载均衡
Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端 负载均衡工具。
Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将Netflix的中间层服务连接在一起。Ribbon客户端组件提供一系列完善的配置项如连接超时、重试等。简单的说,就是在配置文件中列出Load Balancer(简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询、随机连接等)去连接这些机器。我们也很容易使用Ribbon实现自定义的负载均衡算法。
Ribbon源码: https://github.com/Netflix/Ribbon
5.1.负载均衡
-
英文名称:Load Balance,微服务或分布式集群中常用的一种应用
-
简单来说负载均衡就是将用户的请求ping平摊的分配到多个任务上,从而是系统达到HA(高可用)
-
两种负载均衡:
- 集中式LB:偏硬件,服务的消费方和提供方之间使用独立的LB设施,由该设施负责把访问请求以某种策略转发至服务的提供方。
- 进程内LB:偏软件, 将LB逻辑集成到消费方,消费方从服务注册中心指导哪些地址可用,再自己选择一个合适的服务器。
5.2.Ribbon初步配置
- ==Ribbon是客户端负载均衡工具!!!Ribbon是客户端负载均衡工具!!!Ribbon是客户端负载均衡工具!!!==所以应该配置在客户端
- 加入依赖,因为Riboon需要依赖Eureka运行,所以要同时加入Eureka依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
- 对实现类加入@LoadBalanced注解
@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
- 在application.yml文件中配置向注册中心注册,如果是作为消费者模块不提供服务,不应该注册自己
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
register-with-eureka: false #作为消费者不提供服务,不应该注册自己
- 主启动类中加入@EnableEurekaClient注解
@SpringBootApplication
@EnableEurekaClient
public class Consumer80_APP {
public static void main(String[] args) {
SpringApplication.run(Consumer80_APP.class,args);
}
}
- 以上步骤1~4完成后即可在controller中直接通过服务名访问系统中的微服务,服务名作为URI
Ribbon和Eureka整合后Consumer可以直接调用服务而不用再关心地址和端口号
private static final String URL_PREFIX = "http://MICROSERVICECLOUD-DEPT/";
5.3.Ribbon负载均衡实现
架构示意图:
实现方法
目标:构建provider集群后consumer通过负载均衡轮询调用在Eureka中注册的服务
- 构建集群,新开两个provider模块,将原provider的代码部分和pom.xml中依赖照搬到新的provider中
<!--修改对应的端口号-->
<artifactId>microservicecloud-provider-dept-8002</artifactId>
- 将原provider中application.yml文件照搬到新provider,并修改端口号,若新的provider使用自己的数据库,则修改数据库信息(其他配置也一样,如修改别名)
- 集群中服务名称必须一致!!!
spring:
application:
name: microservicecloud-dept #同一集群下必须使用同一服务名!!!!!
- 启动服务,进行测试
总结
Ribbon其实就是一个软负载均衡的客户端组件,可以和其他需要请求的客户端结合使用。
5.4.Ribbon核心组件IRule
IRule:根据特定算法从服务列表中选取一个需要访问的服务
5.4.1.七大方法
IRule是一个接口,七大方法是其自带的落地实现类
- RoundRobinRule:轮询(默认方法)
- RandomRule:随机
- AvailabilityFilteringRule:先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,还有并发的连接数量超过阈值的服务,然后对剩余的服务进行轮询
- WeightedResponseTimeRule:根据平均响应时间计算服务的权重。统计信息不足时会按照轮询,统计信息足够会按照响应的时间选择服务
- RetryRule:正常时按照轮询选择服务,若过程中有服务出现故障,在轮询一定次数后依然故障,则会跳过故障的服务继续轮询。
- BestAvailableRule:先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
- ZoneAvoidanceRule:默认规则,符合判断server所在的区域的性能和server的可用性选择服务
5.4.2.切换规则方法
只需在配置类中配置一个返回具体方法的bean即可
@Bean
public IRule MyRule(){
return new RandomRule();
}
RetryRule测试,在Eureka集群启动完成,provider集群启动完成后,启动consumer服务,ConfigBean已改为RetryRule()
先测试,发现是provider服务轮询机制,然后人为的停掉8002服务,再次测试,结果如下:
5.5.自定义Ribbon负载均衡算法
5.5.1.配置及包位置
- 自定义的Ribbon算法类不能放在主启动类所在的包及子报下(确切来说是不能放在@ComponentScan注解的包及子包下),否则会被全局应用到Ribbon服务中。应该把自定义算法类放在另外新建的包下,且这个类应该是为配置类。(其实与普通切换负载均衡规则类似,只不过是位置不同而已,普通的可以放在主启动类所在的包,自定义的要放在外面的包下)
- 主启动类添加@RibbonClient(name = “微服务名”,configuration = XXX.class)注解指定需要用到负载均衡的微服务名及自定义算法的class对象。
- 在启动类配置@RibbonClient优先级>ConfigBean配置类中加@Bean自定义规则>什么都不写(默认轮询)
例如简单的:
@SpringBootApplication
@EnableEurekaClient
//在启动该微服务的时候就能去加载我们的自定义Ribbon配置类,从而使配置生效
@RibbonClient(name="MICROSERVICECLOUD-DEPT",configuration=MySelfRule.class)
public class DeptConsumer80_App
{
public static void main(String[] args)
{
SpringApplication.run(DeptConsumer80_App.class, args);
}
}
5.5.2.通过修改源代码获得自定义算法
目标:每个服务调用3次后再随机
package com.atguigu.myrule;
import java.util.List;
import java.util.Random;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
public class MyRandomRule extends AbstractLoadBalancerRule {
private int total = 0; // 总共被调用的次数,目前要求每台被调用3次
private int currentIndex = 0; // 当前提供服务的机器号
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
return null;
}
Server server = null;
while (server == null) {
if (Thread.interrupted()) {
return null;
}
List<Server> upList = lb.getReachableServers();
List<Server> allList = lb.getAllServers();
int serverCount = allList.size();
if (serverCount == 0) {
/*
* No servers. End regardless of pass, because subsequent passes only get more
* restrictive.
*/
return null;
}
// int index = rand.nextInt(serverCount);// java.util.Random().nextInt(3);
// server = upList.get(index);
if (currentIndex < upList.size()) {
if (total < 2) {
server = upList.get(currentIndex);
total++;
} else {
server = upList.get(currentIndex);
currentIndex++;
total = 0;
}
} else {
Random random = new Random();
int index = random.nextInt(serverCount);
server = upList.get(index);
}
if (server == null) {
/*
* The only time this should happen is if the server list were somehow trimmed.
* This is a transient condition. Retry after yielding.
*/
Thread.yield();
continue;
}
if (server.isAlive()) {
return (server);
}
// Shouldn't actually happen.. but must be transient or a bug.
server = null;
Thread.yield();
}
return server;
}
@Override
public Server choose(Object key) {
return choose(getLoadBalancer(), key);
}
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
}
}
六、Feign负载均衡
Feign是一个声明式WebService客户端,使用方法时定义一个接口并在上面添加注解即可。Feign支持可拔插式的编码器和解码器。Spring Cloud对Feign进行了封装,使其支持SpringMVC和HttpMessageConverters。Feign可以与Eureka和Ribbon组合使用以支持负载均衡。
Feign源码 : https://github.com/OpenFeign/Feign
6.1.使用案例
- 新建Feign模块,加入依赖(其实跟80消费端差不多,主要是多了Feign依赖)
<dependencies>
<dependency><!-- 自己定义的api -->
<groupId>com.atguigu.springcloud</groupId>
<artifactId>microservicecloud-api</artifactId>
<version>${project.version}</version>
</dependency>
<!--feign依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
<!-- Ribbon相关 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 修改后立即生效,热部署 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>springloaded</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
- 因为Feign开发其实是面向接口编程,所以Feign接口可以放在api模块中供各模块使用,所以要在microservicecloud-api模块中添加Feign依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
- 在api中编写接口,接口上添加@FeignClient注解,并通过value指定作用的微服务名
// microservicecloud-api模块中
@FeignClient(value = "MICROSERVICECLOUD-DEPT")
public interface DeptClientService {
@PostMapping("/dept")
public boolean addDept(Dept dept);
@GetMapping("/dept")
public List<Dept> findAll();
@GetMapping("/dept/{id}")
public Dept findById(@PathVariable("id")Integer id);
}
- 在Feign模块中编写Controller,并注入FeignClient接口,直接调用service接口中的方法即可(因为声明Feign接口时已经指定过微服务,所以访问时会正确地找到微服务)
@RestController
@RequestMapping("/consumer")
public class ConsumerDeptController {
@Autowired
private DeptClientService service;
@PostMapping("/dept")
public boolean addDept(Dept dept){
return service.addDept(dept);
}
@GetMapping("/dept")
public List<Dept> findAll(){
return service.findAll();
}
@GetMapping("/dept/{id}")
public Dept findById(@PathVariable("id")Integer id){
return service.findById(id);
}
}
- 修改Feign模块的主启动类,加入@EnableFeignClients注解和@ComponentScan注解(主要是扫描api中声明的接口)
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients(basePackages = {"com.XXX"})
@ComponentScan("com.XXX")
public class Consumer80Feign_APP {
public static void main(String[] args) {
SpringApplication.run(Consumer80Feign_APP.class,args);
}
}
- 启动后访问,即会按照轮询的方式调用provider集群
6.2.总结
- Feign通过接口方法调用REST服务,在Eureka中查找对应的服务
- Feign集成了Ribbon技术,所以也支持负载均衡(轮询)
七、Hystrix断路器
7.1.分布式系统面临的问题
复杂分布式体系结构中的应用程序有数十个依赖关系,每个依赖关系在某些时候将不可避免地失败。
扇出
多个微服务互相调用的时候,如果A调用B、C,而B、C又继续调用其他微服务,这就是扇出(像一把扇子一样慢慢打开)。
服务雪崩
- 扇出过程中,如果某一个环节的服务出现故障或连接超时,就会导致前面的服务占用越来越多的资源,进而引起系统崩溃,就是“雪崩效应”。
- 对于高流量的应用来说,单一的后端依赖会导致服务器所有的资源都在几秒钟内饱和。比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,备份队列,线程和其他系统资源紧张,导致整个系统发生更多的级联故障。这些都表示需要对故障和延迟进行隔离和管理,单个依赖关系的失败,不能影响整个应用程序或系统。
7.2.Hystrix介绍
Hystrix源码: https://github.com/Netflix/Hystrix
- Hystrix是一个用于处理分布式系统延迟和容错的开源库。分布式系统中,依赖避免不了调用失败,比如超时,异常等。Hystrix能保证在出现问题的时候,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
- Hystrix类似一个“断路器”,本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。
7.2.1.服务熔断
- 熔断机制的注解是@HystrixCommand
- 熔断机制是应对雪崩效应的一种链路保护机制,一般存在于服务端
- 当扇出链路的某个服务出现故障或响应超时,会进行服务降级,进而熔断该节点的服务调用,快速返回“错误”的相应信息。
- Hystrix的熔断存在阈值,缺省是5秒内20次调用失败就会触发
熔断案例
- 构建一个新的provider module:microservicecloud-provider-dept-hystrix-8001(复制8001)
- pom.xml加入hystrix依赖(一定要配合Eureka)
<!-- hystrix -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<!-- 将微服务provider侧注册进eureka -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
- application.yml中配置端口和Eureka信息(必配)和其他框架的配置信息(可选,如mybatis)
- 编写具体业务逻辑:
Controller类中,在需要配置Fallback的方法上加入@HystrixCommand(fallbackMethod = “XXX”)注解,XXX为FallBack方法名,本例中作为测试所以抛出了异常。
@ResponseBody
@GetMapping("/dept/{id}")
@HystrixCommand(fallbackMethod = "nullDeptFallBack")
public Dept findById(@PathVariable("id")Integer id) {
Dept dept = deptService.findById(id);
if (null == dept){
throw new RuntimeException("返回值为空!");
}
return dept;
}
public Dept nullDeptFallBack(@PathVariable("id")Integer id) {
System.out.println(111);
return new Dept().setId(id).setDeptName("nullName").setDbSource("nullDB");
}
- 主启动类中加入@EnableCircuitBreaker注解
@SpringBootApplication
@EnableEurekaClient //本服务启动后会自动注册进eureka服务中
@EnableDiscoveryClient //服务发现
@EnableCircuitBreaker//对hystrixR熔断机制的支持
public class DeptProvider8001_Hystrix_App
{
public static void main(String[] args)
{
SpringApplication.run(DeptProvider8001_Hystrix_App.class, args);
}
}
- 开启服务,测试。
7.2.2.解耦与降级处理
降级
- 当系统整体资源快不够的时候,忍痛将部分服务暂时关闭,带渡过难关后,再重新开启。
- 降级处理时在客户端完成的,与服务端没有关系
- 理解:所谓降级,一般是从整体负荷考虑,当某个服务熔断之后,服务器将不再被调用,此时客户端可以自己准备一个本地的FallBack回调,返回一个缺省值。这样做虽然服务水平下降,但好歹可用,比直接挂掉好。
为什么要解耦
如果按照上面的熔断案例来做的话,Controller下的每个方法,都要给其编写一个FallBack方法,当方法慢慢变多,就会造成代码膨胀,一个是增加编写的工作量,另外一个也会增大维护的难度,代码的耦合度也会高,是十分不合理的,所以要将其解耦。
解耦思路
因为服务端的是通过实现接口访问服务端的,如果在父接口上实现了FallBack方法,通过这样一种方式去维护起来就能实现解耦,也顺便完成了降级的机制。
解耦&降级案例
- 在api模块中新建实现了FallbackFactory接口的类,其中泛型T就是我们需要维护其FallBack的接口方法,并实现其create方法,在create方法中返回实现了T的对象,使用匿名内部类实现T。注意:这个类一定要加@Component注解!!
import com.XXX.entity.Dept;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class DeptClientServiceFallBackFactory implements FallbackFactory<DeptClientService> {
public DeptClientService create(Throwable throwable) {
return new DeptClientService() {
public boolean addDept(Dept dept) {
return false;
}
public List<Dept> findAll() {
return null;
}
public Dept findById(Integer id) {
return new Dept().setId(id).setDeptName("服务器跪了,").setDbSource("迟点来吧");
}
};
}
}
- 修改步骤1中传入的泛型T接口,添加@FeignClient(fallbackFactory = T.class)注解
@FeignClient(value = "MICROSERVICECLOUD-DEPT",fallbackFactory = DeptClientServiceFallBackFactory.class)
public interface DeptClientService {
@PostMapping("/dept")
public boolean addDept(Dept dept);
@GetMapping("/dept")
public List<Dept> findAll();
@GetMapping("/dept/{id}")
public Dept findById(@PathVariable("id")Integer id);
}
- 修改microservicecloud-consumer-dept-feign模块的application.xml文件,开启hystrix
feign:
hystrix:
enabled: true
- 开启服务并测试
7.2.3.HystrixDashboard服务监控
介绍:SpringCloud对Hystrix Dashboard进行了整合,可以对通过Hystrix发起的请求进行准实时统计,并以报表和图形的形式展示给用户(包括每秒执行多少次请求成功和失败等)。
配置案例
- 新建HystrixDashboard模块,pom.xml文件加入HystrixDashboard依赖,其他依赖包括Feign,Ribbon,Eureka(可参考80模块的依赖)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
</dependency>
- application.yml文件中配置端口(如9001)
- provider类中确认要加入actuator依赖(此为监控依赖)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
- 编写主启动类,上标@EnableHystrixDashboard标注
@SpringBootApplication
@EnableHystrixDashboard
public class DashBoard9001_APP {
public static void main(String[] args) {
SpringApplication.run(DashBoard9001_APP.class,args);
}
}
- 启动服务进行测试,访问地址:http://localhost:9001/hystrix,(根据配置端口号),看到刺猬表示已经配置好了
如何监控
- 在dashboard界面中,填入需要监控的服务地址和端口号加上\hystrix.stream和监测间隔即可进入监控。如http://localhost:8001/hystrix.stream
- 在使用Spring Boot 2.0.x + Spring Cloud Finchley.x或者2.1.x + Greenwich.x版本,需要更改路径/actuator/hystrix.stream,并更改代码
【具体可以参考https://www.cnblogs.com/x1mercy/p/9291348.html】
监控分析
- 七色:进入监控界面后会有其中颜色的数字,其含义可以对应右上角相同颜色的单词表示的状态,其值代表该状态下触发的次数
- 一圈:圈的大小代表该服务的流量,圈越大流量越大
通过颜色的变化代表了实例的健康程度,它的健康度从绿色<黄色<橙色<红色递减。
通过大小代表实例的请求流量变化,流量越大该实心圆就越大。所以通过该实心圆的展示,就可以在大量的实例中快速的发现故障实例和高压力实例。 - 一线:代表监控间隔中,服务被访问的频率的折线图
- 通过观察这些就可以在大量的实例中找出故障实例和高压实例进行修复和维护。
八、Zuul路由网关
8.1.概述
-
代理:Zuul提供外部的请求转发到具体的微服务实例中的服务
-
路由:Zuul可以对外部访问实现统一的入口
-
过滤:Zuul可以对外部访问进行干预,如请求校验、服务聚合等
-
Zuul需要配合Eureka使用,需要在Eureka中注册并获得其他微服务的信息。Zuul服务最终还是会注册进Eureka
-
理解:Zuul就像大楼的保安,可以请他找人(代理),找的人在外面叫什么名字(路由),准不准你进楼(过滤)。因为保安属于物业公司,所以保安要在物业公司注册,所获得的信息也来源于物业公司(与Eureka的关系)。
Zuul的源码: http://github.com/Netflix/zuul
8.2.基本配置
- 构建新的Zuul模块并在pom.xml中加入依赖(Zuul和Eureka必须同时加入)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
- 新建application.yml文件并配置(一定要向Eureka注册,因为Zuul本身也是一个微服务)
server:
port: 9527
spring:
application:
name: microservicecloud-zuul #为这个服务取名,非常重要!!!!!
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
instance:
instance-id: zuul.com
prefer-ip-address: true
- 修改hosts文件(非必须,不过能更好看出效果)
127.0.0.0 zuul.com
- 创建主启动类,并加入@EnableZuulProxy注解
@SpringBootApplication
@EnableZuulProxy
public class Zuul9527_APP {
public static void main(String[] args) {
SpringApplication.run(Zuul9527_APP.class,args);
}
}
-
启动测试,访问规则:步骤3中指定映射+端口号+微服务名称+访问路径。
例子:http://zuul.com:9527/microservicecloud-dept/dept
8.3.路由访问映射规则
服务名映射和统一公共前缀
当不想暴露真实的服务名时,可以对服务名进行映射,只需在application.yml中配置即可,具体作用看注释
# 隐藏真实微服务名称
zuul:
# ignored-services: microservicecloud-dept 隐藏单个微服务名称
prefix: /chengsw # 统一访问路径前缀
ignored-services: "*" # 隐藏所有微服务
routes: # 映射:mydept.serviceId映射到mydept.path,不做隐藏,则访问两个地址都能成功,不安全
mydept.serviceId: microservicecloud-dept
mydept.path: /mydept/**
注:因为Zuul是针对外部访问管理的,所以配置了隐藏的服务,在系统中其他模块进行服务名访问时依然可以正常运行的,这点可以通过打开consumer模块进行验证!
因为做了映射之后,新旧地址还是都可以访问,因此可以设置隐藏服务名,用于屏蔽原来的请求路径。
九、Spring Cloud Config 分布式配置中心
分布式系统面临的配置问题:微服务意味着将单体应用拆分成一个个子服务,这些服务都是要相应的配置信息才能运行,随着系统内微服务数量越来越多,配置信息也不断地增多,所以一套集中式的、动态的配置管理设施是必不可少的。
9.1.Spring Cloud Config概述
- Spring Cloud Config是一个提供外部集中式配置管理的设施,配置服务器为各种不同的微服务应用提供了一个中心化的外部配置
- Spring Cloud Config分为客户端和服务端两部分
- 服务端:分布式配置中心,是一个独立的微服务,用来连接并为客户端提供配置信息,加密/解密信息等访问接口
- 客户端:通过指定的配置中心获取配置资源,cloud推荐用git来存储配置信息
- Spring Cloud Config解决的问题:
- 集中管理配置文件
- 不同环境不同配置,动态化的配置更新
- 运行期间动态调整配置,不再需要在每个服务部署的机器上编写配置文件,让服务中心统一为服务拉取配置文件
- 当配置发生变动时,服务不需要重启即可感知配置变化并应用
- 将配置信息以REST接口形式暴露
9.2.Spring Cloud Config服务端与Github通讯
目标:将配置文件部署在github,Config服务端从github获取配置
9.2.1.案例
- 新建ConfigServer模块并配置pom.xml
<!-- springCloud Config -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<!-- 避免Config的Git插件报错:org/eclipse/jgit/api/TransportConfigCallback -->
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit</artifactId>
<version>4.10.0.201712302008-r</version>
</dependency>
- 建立远程仓库,并上传配置文件。yml文件编码UTF-8!如下例
spring:
profiles:
active:
- dev
---
spring:
profiles: dev
application:
name: microservicecloud-config-XXX-dev
---
spring:
profiles: test
application:
name: microservicecloud-config-XXX-test
- 在application.xml文件中配置github地址
server:
port: 3344
spring:
application:
#为这个服务取名,非常重要!!!!!
name: microservicecloud-config
cloud:
config:
server:
git:
# uri填github上仓库地址
uri: https://github.com/XXXX/SpringCloud_Configuration.git
- 编写主启动类,加入@EnableConfigServer注解
@SpringBootApplication
@EnableConfigServer
public class ConfigServer3344_APP {
public static void main(String[] args) {
SpringApplication.run(ConfigServer3344_APP.class,args);
}
}
- 启动服务并尝试访问配置文件,有以下五种访问配置规则
- {application}:配置文件的文件名
- {profile}:读取的环境
- {lable}:分支
/{application}/{profile}[/{lable}]
/{application}-{profile}.yml
/{lable}/{application}-{profile}.yml
/{application}-{profile}.properties
/{lable}/{application}-{profile}.properties
可用例子(返回格式可能不大相同,但返回值相同):
- http://config3344.com:3344/application-test.yml
- http://config3344.com:3344/master/application-dev.yml
- http://config3344.com:3344/application-test.yml/master
不可用例子:
- 没有该环境,返回空值:http://config3344.com:3344/application-test11.yml/master
- 没有配置文件,犯回错误页面:http://config3344.com:3344/lkjliiusdfsddsfl.yml
9.3.bootstrap.yml介绍
- bootstrap.yml比application.yml具有更高的优先级。
- bootstrap.yml是系统级的资源配置项,application.yml是用户级的资源配置项。
- Spring Cloud会创建"BootStrap Context"作为"ApplicationContext"的父上下文。初始化的时候BootStrap Context负责从外部源加载配置属性并解析。这两个上下文共享一个"Environment",BootStrap 具有更高优先级,他们不会被本地配置覆盖。
9.4.客户端的配置与测试
介绍:客户端主要是在加载时通过config server服务端获得github配置仓库的地址,进而通过目标配置文件的文件名获取相应的配置,最后将取得的配置对自身资源进行赋值并提供访问
9.4.1.实现过程
1.创建远程配置yml文件并上传到github上。如下测试案例因为需要进行测试,所以配置了两个profiles方便切换并观察
spring:
profiles:
active:
- dev
---
server:
port: 8201
spring:
profiles: dev
application:
name: microservicecloud-config-client-dev
eureka:
client:
service-url:
defaultZone: http://eureka-dev.com:7001/eureka/
---
server:
port: 8202
spring:
profiles: test
application:
name: microservicecloud-config-client-test
eureka:
client:
service-url:
defaultZone: http://eureka-dev.com:7001/eureka/
- 本地创建config client模块,并配置好pom.xml,以下本组件是必选依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
- 编写bootstrap.yml配置文件,这个步骤比较关键,主要是根据此处的配置信息去寻找config server以获得github仓库地址和配置中的目标配置文件文件名
spring:
cloud:
config:
name: application_config #需要从github上读取的资源名称,注意没有yml后缀名
profile: test #本次访问的配置项
label: master
uri: http://config3344.com:3344 #本微服务启动后先去找3344号服务,通过Spring Cloud Config获取GitHub的服务地址
- application.yml文件在本module中其实是可写可不写的,为了习惯需要,还是给他写了个名字
spring:
application:
name: microservicecloud_config
- 修改host文件增加映射,和3344一样
- 编写主启动类,没什么特别的,最基本的主启动类
- 编写controller,此步骤也比较关键,主要是利用@Value注解赋值,若写错了bootstrap.yml中的配置文件名称而没有获取到配置,启动时这里会抛出异常。@Value中注解的参数即是目标配置文件中的参数值,使用El表达式获取
@RestController
public class RestController {
@Value("${server.port}")
private String port;
@Value("${eureka.client.service-url.defaultZone}")
private String eurekaZone;
@Value("${spring.application.name}")
private String name;
@GetMapping("/config")
@Override
public String toString() {
return "RestController{" +
"port='" + port + '\'' +
", eurekaZone='" + eurekaZone + '\'' +
", name='" + name + '\'' +
'}';
}
}
- 先启动Config server服务端服务,然后再启用本Config客户端服务,根据profiles的值访问对应的端口即可。如本例选择的是test,则访问端口为:http://config3355.com:8202/config。(config3355.com为hosts文件中配置了的映射)
9.5.Spring Cloud的配置实战
介绍:其实前面client的配置案例都是帮助理解这个组件为主,并没有很大的实际意义。。。。。。这节的案例中是配置一个Provider,一个Eureka,他们的配置统一在github上获取,实现统一配置分布式管理和多环境变更,这个才比较有实战意义。
9.5.1.实现过程
- 先写好provider和Eureka的配置yml文件,这两个文件和平常配置没什么不同,因为这里主要是说config,所以就没有配置集群,上传yml到github
Eureka配置文件示例:
spring:
profiles:
active:
- dev
---
spring:
profiles: dev
application:
name: microservicecloud-eureka-client-dev
server:
port: 7001
eureka:
instance:
hostname: eureka7001.com #hostname为hosts文件中映射的地址
client:
register-with-eureka: false #false表示不向注册中心注册自己
fetch-registry: false #false表示自己就是注册中心,职责是维护实例,不参加检索
service-url:
defaultZone: http://eureka7001.com:7001/eureka/ #设置eureka server的交互地址
---
spring:
profiles: test
application:
name: microservicecloud-eureka-client-dev
server:
port: 7001
eureka:
instance:
hostname: eureka7001.com #hostname为hosts文件中映射的地址
client:
register-with-eureka: false #false表示不向注册中心注册自己
fetch-registry: false #false表示自己就是注册中心,职责是维护实例,不参加检索
service-url:
defaultZone: http://eureka7001.com:7001/eureka/ #设置eureka server的交互地址
Provider配置文件示例:
spring:
profiles:
active:
- dev
---
server:
port: 8001
mybatis:
config-location: classpath:mybatis/mybatis.cfg.xml
type-aliases-package: com.XXX.entity
mapper-locations:
- classpath:mybatis/mapper/**/*.xml
spring:
profiles: dev
application:
name: microservicecloud-dept #为这个服务取名,非常重要!!!!!
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: org.gjt.mm.mysql.Driver
url: jdbc:mysql://192.168.88.246:3306/cloudDB01
username: root
password: 123456
dbcp2:
min-idle: 5 #最小连接数
initial-size: 5 #初始化连接数
max-total: 10 #最大连接数
max-wait-millis: 200 #等待连接最长的超时时间
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
instance:
instance-id: dept8001
prefer-ip-address: true
---
server:
port: 8001
mybatis:
config-location: classpath:mybatis/mybatis.cfg.xml
type-aliases-package: com.XXX.entity
mapper-locations:
- classpath:mybatis/mapper/**/*.xml
spring:
profiles: test
application:
name: microservicecloud-dept #为这个服务取名,非常重要!!!!!
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: org.gjt.mm.mysql.Driver
url: jdbc:mysql://192.168.88.246:3306/cloudDB02
username: root
password: 123456
dbcp2:
min-idle: 5 #最小连接数
initial-size: 5 #初始化连接数
max-total: 10 #最大连接数
max-wait-millis: 200 #等待连接最长的超时时间
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
instance:
instance-id: dept8001
prefer-ip-address: true
- 新开eureka和provide的模块并在pom.xml中添加依赖,其他必要依赖和之前的案例一样,但是config的依赖一定要添加上
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
- 两个模块都要编写bootstrap.yml文件,和上面的案例一样
spring:
cloud:
config:
name: application_config #需要从github上读取的资源名称,注意没有yml后缀名
profile: test #本次访问的配置项
label: master
uri: http://config3344.com:3344 #本微服务启动后先去找3344号服务,通过SpringCloudConfig获取GitHub的服务地址
- (可选)两个模块中编写application.yml文件,可以配置一下服务名
spring:
application:
name: microservicecloud_config
- 两个模块的主启动类,Eureka的正常加EurekaServer注解,Provider加EurekaClient注解,不详述
- 编写Provider模块的业务代码
- 启动测试,因为这两个模块都要通过3344ConfigServer为其在github上获取配置,所以要先启动3344模块,然后再一次启动eureka和provider模块,进行测试即可。
十、Spring Cloud内容技术梳理与架构
- 整套开发技术栈以SpringCloud为主,单个微服务模块以SpringMVC+SpringBoot+MyBatis组合进行开发
- 前端层,页面H5+thymeleaf/样式CSS3+Bootstrap/前端框架JQuery+Node/Vue等
- 负载层,前端访问通过Http或Https协议到达服务端的LB,可以是F5等硬件做负载均衡,还可以自行部署LVS+Keepalived等(前期量小可以直接使用Nginx)
- 网关层,请求通过LB后,会到达整个微服务体系的网关层Zuul(Gateway),内嵌Ribbon做客户端负载均衡,Hystrix做熔断降级等
- 服务注册,采用Eureka来做服务治理,Zuul会从Eureka集群获取已发布的微服务访问地址,然后根据配置把请求代理到相应的微服务去
- docker容器,所有的微服务模块都部署在Docker容器里面,而且前后端的服务完全分开,各自独立部署后前端微服务调用后端微服务,后端微服务之间会有相互调用
- 服务调用,微服务模块间调用都采用标准的Http/Https+REST+JSON的方式,调用技术采用Feign+HttpClient+Ribbon+Hystrix
- 统一配置,每个微服务模块会跟Eureka集群、配置中心(Spring Cloud Config)等进行交互
- 第3方框架,每个微服务模块根据实现的需要,通常还需要使用一些第三发框架,比如常见的有:缓存服务(Redis)、图片服务(FastDFS)、搜索服务(ElasticSearch)、安全管理(Shiro)等等
- Mysql数据库,可以按照微服务模块进行拆分,统一访问公共库或者单独自己库,可以单独构建MySQL集群或者分库分表MyCat等。
十一、Spring Cloud微服务架构体系知识扩展
微服务架构体系知识展望,包含但不限于
- SpringCloud Stream:数据流操作开发包
- SpringCloud Turbine:是聚合服务器发送事件流数据的一个工具,用来监控集群下hystrix的metrics情况。
- SpringCloud Task:提供云端计划任务管理、任务调度。
- SpringCloud Sleuth:日志收集工具包实现了一种分布式追踪解决方案,封装了Dapper和log-based追踪以及Zipkin和HTrace操作,
- SpringCloud Security:基于spring security的安全工具包,为应用程序添加安全控制
- 服务部署:Kubernetes , OpenStack
- 全链路追踪:Zipkin,brave
- 服务监控:zabbix
- SpringCloud CLI:基于 Spring Boot CLI,可以让你以命令行方式快速建立云组件。
- 全局控制:选举leader、全局锁、全局唯一id
- 安全鉴权: auth2、 openId connect
- 自动化构建与部署: gitlab + jenkins + docker
- 服务监控和告警(Spring Boot Admin)
- ……