1、什么是微服务
2、微服务优缺点
3、微服务技术栈
4、springcloud 微服务解决方案
5、搭建 REST 微服务演示案例
6、Eureka 服务注册与发现
6.1、Eureka 介绍
6.2、Eureka Server 服务注册中心建立
6.3、将微服务注册进 Eureka 服务中心
6.4、主机映射名称修改和主机IP信息提示
6.5、访问 192.168.181.1::8001/info 的内容构建
6.6、Eureka 自我保护机制
6.7、Eureka 的服务发现
6.8、Eureka 集群配置
6.9、Eureka 与 Zookeeper 比较
7、Ribbon 负载均衡
8、Feign 负载均衡
9、Hystrix 断路器
10、豪猪 hystrix Dashboard
11、zuul 路由网关(代理 + 路由 + 过滤)
12、Spring Cloud Config 分布式配置中心
1、什么是微服务 <--返回目录
马丁.福勒微服务架构博文:https://martinfowler.com/articles/microservices.html
译文:https://blog.csdn.net/qq_32252957/article/details/90247418
微服务架构是一种架构模式或者说是一种架构风格,它提倡将单一应用程序划分成一组小的服务,每个服务运行在其独立的进程中,服务之间相互协调、互相配合,为用户提供最终价值。服务之间采用轻量级的通信机制互相沟通(通常是基于 HTTP 的 RESTful API)。每个服务都围绕着具体业务进行构建,并且能够被独立地部署到生产环境、类生产环境等。另外,应尽量避免统一的、集中式的服务管理机制,对具体的一个服务而言,应根据业务上下文,选择合适的语言、工具对其进行构建,可以有一个非常轻量级的集中式管理来协调这些服务,可以使用不同的语言来编写服务,也可以使用不同的数据存储。
从技术维度理解:微服务化的核心就是将传统的一站式应用,根据业务拆分成一个个的服务,彻底地去耦合,每一个微服务提供单个业务功能的服务,一个服务做一件事,从技术角度看就是一种小而独立的处理过程,类似进程概念,能够自行单独启动或销毁,拥有自己独立的数据库。
2、微服务优缺点 <--返回目录
优点:
每个服务足够内聚,足够小,代码容易理解,这样能聚焦一个指定的业务功能;
开发简单、开发效率提高,一个服务可能就是专一只干一件事;
微服务能够被小团队单独开发,团队是2到5人开发人员组成;
微服务是松耦合的,是有功能意义的服务,无论是在开发阶段或部署阶段都是独立的;
微服务能使用不同的语言开发;
易于和第三方集成,微服务允许容易且灵活的方式集成自动部署,通过持续集成工具如jenkins,hudson,bamboo;
微服务易于被开发人员理解,修改和维护,这样小团队能够更关注自己的工作;
微服务允许你利用融合最新技术;
微服务只是业务逻辑的代码,不会和html、css或其他界面组件混合;
每个微服务都有自己的存储能力,可以有自己的数据库,也可以有统一数据库;
缺点:
开发人员要处理分布式系统的复杂性;
运维压力和难度增加;
系统部署依赖;
服务间通信成本;
数据一致性;
系统集成测试;
性能监控;
3、微服务技术栈 <--返回目录
微服务条目 | 落地技术 |
服务开发 | springboot、spring、springmvc |
服务配置与管理 | Netflix公司的Archaius、阿里的Diamond等 |
服务注册与发现 | Eureka、Consul、Zookeeper等 |
服务调用 | Rest、RPC、gRPC |
服务熔断器 | Hystrix、Envoy等 |
负载均衡 | Ribbon、Nginx等 |
服务接口调用 | Feign等 |
消息队列 | Kafka、RabbitMQ、ActiveMQ等 |
服务配置中心管理 | SpringCloudConfig、Chef等 |
服务路由(API网关) | Zuul等 |
服务监控 | Zabbix、Nagios、Metrics、Spectator等 |
全链路追踪 | Zipkin、Brave、Dapper等 |
服务部署 | Docker、OpenStack、K8s等 |
数据流操作开发包 |
Spring Cloud Stream(封装Redis,Rabbit,Kafka等发送接收消息) |
事件消息总线 |
Spring Cloud Bus |
4、springcloud 微服务解决方案 <--返回目录
springcloud: 基于 springboot 提供了一套微服务解决方案,包括服务注册与发现、配置中心、全链路监控、服务网关、负载均衡、熔断器等组件,除了基于 NetFlix 的开源组件做高度抽象封装之外,还有一些选型中立的开源组件。
springcloud 利用 springboot 的开发便利性巧妙地简化了分布式系统基础设施的开发,springcloud 为开发人员提供了快速构建分布式系统的一些工具,包括配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话等。它们都可以使用springboot的开发风格做到一键启动和部署。
springboot 并没有重复制造轮子,它只是将目前各家公司开发的比较成熟的服务框架组合起来,通过 springboot 风格进行再封装,屏蔽掉了复杂的配置和实现原理,最终给开发者留出一套简单易懂、易部署和易维护的分布式系统开发工具包。
5、搭建 REST 微服务演示案例 <--返回目录
microservicecloud 父工程下面有三个子模块,其中 microservicecloud-api 是公共模块,microservicecloud-provider-dept-8001 服务器提供方,microservicecloud-consumer-dept-80 服务器消费者。
provider-dept-8001 提供了访问接口:
@RestController public class DeptController { @Autowired private DeptService service; @PostMapping("/dept") public boolean add(@RequestBody Dept dept) { return service.add(dept); } @GetMapping("/dept/{id}") public Dept get(@PathVariable("id") Long id) { return service.get(id); } @GetMapping("/dept") public List<Dept> list() { return service.list(); } }
consumer-dept-80 通过 http 调用 provider-dept-8001 提供的接口:
@RestController public class DeptController_Consumer { private static final String REST_URL_PREFIX = "http://localhost:8001"; /** * 使用 使用restTemplate访问restful接口。 * (url, requestMap, ResponseBean.class) * 这三个参数分别代表 REST请求地址、请求参数、HTTP响应转换被转换成的对象类型。 */ @Autowired private RestTemplate restTemplate; @PostMapping("/consumer/dept") public boolean add(Dept dept) { return restTemplate.postForObject(REST_URL_PREFIX + "/dept", dept, Boolean.class); } @GetMapping("/consumer/dept/{id}") public Dept get(@PathVariable("id") Long id) { return restTemplate.getForObject(REST_URL_PREFIX + "/dept/" + id, Dept.class); } @SuppressWarnings("unchecked") @GetMapping("/consumer/dept") public List<Dept> list() { return restTemplate.getForObject(REST_URL_PREFIX + "/dept", List.class); } }
访问 http://localhost:8001/dept/1 和 http://localhost/consumer/dept/1 都成功查询数据。演示基础案例搭建完成。
6、Eureka 服务注册与发现 <--返回目录
6.1、Eureka 介绍 <--返回目录
Eureka 是 Netflix 的一个子模块,也是核心模块之一。Eureka 是一个基于 REST 的服务,用于定位服务,以实现云端中间层服务发现和故障转移。服务注册与发现对于微服务架构来说是非常重要的,有了服务发现与注册,只需要使用服务的标识符,就可以访问到访问,而不需要修改服务调用的配置文件了。功能类似 dubbo 的注册中心 Zookeeper。
Netflix 在设计 Eureka 时遵守的是 AP 原则。
Sprig Cloud 封装了 Eureka 模块来实现服务注册与发现。Eureka 采用了 C-S(Client-Server)的设计架构。Eureka Server 作为服务注册功能的服务器,是服务注册中心。而系统中的其他微服务,使用 Eureka 的客户端连接到 Eureka Server 并维持心跳连接。这样系统的维护人员就可以通过 Eureka Server 来监控系统中各个微服务是否正常运行。Spring Cloud 的一些其他模块(比如 Zuul)就可以通过 Eureka Server 来发现系统的其他微服务,并执行相关的逻辑。
Eureka 包含两个组件:Eureka Server 和 Eureka Client。Eureka Server 通过服务注册服务。各个节点启动后,会在 Eureka Server 中进行注册,这样 Eureka Server 中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到。Eureka Client 是一个 Java 客户端,用于简化 Eureka Server 的交互,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向 Eureka Server 发送心跳(默认周期为 30 秒)。如果 Eureka Server 在多个心跳周期(默认 90 秒)没有接收到某个节点的心跳,Eureka Server 将会从服务注册表中把这个服务节点移除。
在本演示案例中,Eureka Server 提供服务注册和发现;Service Provider 服务提供方将自身服务注册到 Eureka,从而使服务消费方能够找到;Service Consumer 服务消费方从 Eureka 获取注册服务列表,从而能够消费服务。
6.2、Eureka Server 服务注册中心建立 <--返回目录
依赖:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka-server</artifactId> </dependency>
application.yml
server: port: 7001 eureka: instance: hostname: localhost # Eureka 服务端的实例名称 client: register-with-eureka: false # false 表示不向注册中心注册自己 fetch-registry: false # false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务 service-url: # 与 Eureka Server 交互的地址查询服务和注册服务都需要依赖这个地址 defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
启动类添加启动 Eureka Server 的注解
// Eureka Server 服务器端启动,接受其他微服务注册 @EnableEurekaServer @SpringBootApplication public class EurekaServer7001_App { public static void main(String[] args) { SpringApplication.run(EurekaServer7001_App.class, args); } }
浏览器问 http://localhost:7001/
此时还没有服务注册进来
6.3、将微服务注册进 Eureka 服务中心 <--返回目录
本案例服务提供方:microservicecloud-provider-dept-8001,注册进Eureka
在 provider-dept-8001 的 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>
在 application.yml 添加
eureka: client: #客户端注册进eureka服务列表内 service-url: defaultZone: http://localhost:7001/eureka
主启动类添加注解 @EnableEurekaClient
@SpringBootApplication @EnableEurekaClient // 本服务启动后会自动注册进eureka服务中 public class MainApplication { public static void main(String[] args) { SpringApplication.run(MainApplication.class, args); } }
测试:先启动 Eureka Server,在启动 Provider 服务。访问 http://localhost:7001/
服务名在 Provider 服务的application.yml 中配置
spring:
application:
name: microservicecloud-dept
6.4、主机映射名称修改和主机IP信息提示 <--返回目录
Provider 服务的application.yml 中配置
eureka: client: #客户端注册进eureka服务列表内 service-url: defaultZone: http://localhost:7001/eureka instance: instance-id: microservicecloud-dept8001 prefer-ip-address: true #访问路径可以显示IP地址
6.5、访问 192.168.181.1::8001/info 的内容构建 <--返回目录
Provider 服务添加依赖
<!-- actuator监控信息完善 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
父工程 pom.xml配置
<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>
Provider 服务的 application.yml 中配置
eureka: client: #客户端注册进eureka服务列表内 service-url: defaultZone: http://localhost:7001/eureka instance: instance-id: microservicecloud-dept8001 prefer-ip-address: true #访问路径可以显示IP地址 info: app.name: xxx-microservicecloud company.name: www.xxx.com build.artifactId: $project.artifactId$ build.version: $project.version$
6.6、Eureka 自我保护机制 <--返回目录
一句话:某时刻某个微服务不可用了,Eureka 不会立刻清理,依旧会对该微服务的信息进行保存。
默认情况下,如果 Eureka Server 在一定时间内没有收到某个微服务实例的心跳,Eureka Server 将会注销该实例(默认 90 秒)。但是当网络分区故障发生时,微服务与 Eureka Server 之间无法正常通信,以上行为可能变得非常危险--因为微服务本身实际是健康的,此时不应该注销这个微服务。Eureka 通过“自我保护模式”来解决这个问题。当 Eureka Server 节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。一旦进入该模式,Eureka Server 就会保护服务注册表中的信息,不再删除服务注册表中的数据(也就是不会注销任何微服务)。当网络故障恢复后,该 Eureka Server 节点会自动退出自我保护模式。
在自我保护模式中,Eureka Server 会保护服务注册表中的信息,不再注销任何服务实例。当它收到的心跳数重新恢复到阈值以上时,该 Eureka Server 节点就会自动退出自我保护模式。它的设计哲学就是宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例。
综上,自我保护模式是一种应对网络异常的安全保护措施。它的架构哲学是宁可同时保留所有微服务(健康的和不健康的微服务都会保留),也不盲目注销任何健康的微服务。使用自我保护模式,可以让 Eureka 集群更加的健壮、稳定。
在 Spring Cloud 中,可以使用 eureka.server.enable-self-preservation=false 禁用自我保护模式。
6.7、Eureka 的服务发现 <--返回目录
对于注册进 Eureka 里面的微服务,可以通过服务发现来获得该服务的信息。
Provider 服务方,启动类上添加注解 @EnableDiscoveryClient
Provider 添加接口
@Autowired private DiscoveryClient client; @RequestMapping(value = "/dept/discovery", method = RequestMethod.GET) public Object discovery() { List<String> list = client.getServices(); System.out.println("client.getServices: " + list); List<ServiceInstance> srvList = client.getInstances("MICROSERVICECLOUD-DEPT"); for (ServiceInstance element : srvList) { System.out.println(element.getServiceId() + " " + element.getHost() + " " + element.getPort() + " " + element.getUri()); } return this.client; }
Consumer 添加接口
// 测试@EnableDiscoveryClient,消费端可以调用服务发现 @RequestMapping(value = "/consumer/dept/discovery") public Object discovery() { return restTemplate.getForObject(REST_URL_PREFIX + "/dept/discovery", Object.class); }
服务消费方 访问访问发现 http://localhost:8080/consumer/dept/discovery
6.8、Eureka 集群配置 <--返回目录
· 参考 Eureka7001 创建 7002 和 7003
7001 的 application.yml 配置
server: port: 7001 eureka: instance: hostname: eureka7001.com #eureka服务端的实例名称 client: register-with-eureka: false #false表示不向注册中心注册自己。 fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务 service-url: #单机 defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
7002 的 application.yml 配置(7003 类似)
server: port: 7002 eureka: instance: hostname: eureka7002.com #eureka服务端的实例名称 client: register-with-eureka: false #false表示不向注册中心注册自己。 fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务 service-url: #defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7003.com:7003/eureka/
Provider 8001 注册进 Eureka 集群
eureka: client: #客户端注册进eureka服务列表内 service-url: # defaultZone: http://localhost:7001/eureka defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/ instance: instance-id: microservicecloud-dept8001 prefer-ip-address: true #访问路径可以显示IP地址
修改 c:/windows/system32/drivers/etc/hosts, 添加映射
127.0.0.1 eureka7001.com 127.0.0.1 eureka7002.com 127.0.0.1 eureka7003.com
访问 http://eureka7001.com:7001/,http://eureka7002.com:7002/
6.9、Eureka 与 Zookeeper 比较 <--返回目录
CAP:C: Consistency 强一致性;A:Availability 可用性; P: Partition tolerance 分区容错性
CAP:CAP理论的核心是一个分布式系统不可能同时很好的满足一致性、可用性和分区容错性这三个需求,只能同时较好的满足两个,因此根据 CAP 原理分为满足 CA 原则、满足 CP 原则和满足 AP 原则三大类;
CA:单点集群,满足一致性、可用性的系统,通常在可扩展性上不强大;
CP:满足一致性,分区容忍性的系统,通常性能不是特别高;
AP:满足可用性,分区容忍性的系统,通常可能对一致性要求低一些。
而由于当前的网络硬件肯定会出现延迟丢包等问题,所有分区容忍性是我们必须要实现的。所以我们只能在一致性和可用性之间进行权衡。
Zookeeper 保证的是 CP,Eureka 保证的是 AP。
当向注册中心查询服务列表时,我们可以容忍注册中心返回的是几分以前的注册信息,但不能接受服务直接 down 掉不可用。也即是说,服务注册功能对可用性要求高于一致性。但是 Zookeeper 会出现这样一个情况,当 master 节点因为网络故障与其他节点失去联系时,剩余节点会重新进行 leader 选举。问题在于,选举 leader 的时间太长,30~120 s,且选举期间整个 Zookeeper 集群都是不可用的,这就导致在选举期间注册服务瘫痪。在云部署的环境下,因网络原因使得 Zookeeper 集群失去 master 节点是较大概率会发生的事,虽然服务能够最终恢复,但是漫长的选举时间导致的注册长期不可用是不能容忍的。
Eureka 保证 AP:
Eureka 看明白了这一点,因此在设计时就优先保证可用性。Eureka 各个节点都是平等的,几个节点挂掉不会影响正常节点的工作,剩余的节点依然可以提供注册和查询服务。而 Eureka 的客户端在向某个 Eureka 注册或是发现连接失败,则会自动切换到其他节点,只要有一台 Eureka 还在,就能保证注册服务可用(保证可用性),只不过查到的信息可能不是最新的(不保证强一致性)。除此之外,Eureka还有一种自我保护机制,如果 15 分钟内超过 85% 的节点都没有正常的心跳,那么 Eureka 就会认为客户端与注册中心出现了网络故障,此时会出现以下几种情况:
1)Eureka 不再从注册列表中移除因为长时间没收到心跳而应该过期的服务;
2)Eureka 仍然能够接受新服务的注册和查询请求,但是不会被同步到其他节点上(及保证当前节点依然可用)
---
7、Ribbon 负载均衡 <--返回目录
7.1、Ribbon 介绍
Ribbon 是什么:Spring Cloud Ribbon 是基于 Netflix Ribbon 实现的一套客户端负载均衡的工具。简单的说,Ribbon 是 Netflix 发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将 Netflix 的中间层服务连接在一起。Ribbon 客户端组件提供一系列完善的配置项如连接超时、重试等。简单的说就是在配置文件中列出 Load Balancer(LB)后面所有的机器,Ribbon 会自动帮你基于某种规则(如简单轮询、随机连接等)去连接这些机器。我们也很容易使用 Ribbon 实现自定义的负载均衡算法。
集中式负载均衡:在服务的消费方和提供方之间使用独立的LB设施(可以是硬件如 F5,也可以是软件如nginx),由该设施负责把访问请求通过某种策略转发至服务的提供方;
进程内负载均衡:将 LB 逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器。Ribbon 就属于进程内 LB,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址。
7.2、Ribbon 配置初步:将原来通过 host:port 访问改为通过微服务名称访问
修改 consumer-dept-80 工程,添加依赖
<!-- 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>
修改 application.yml,追加 Eureka 的服务注册地址(因为需要从注册中心获知有服务地址)
eureka: client: register-with-eureka: false service-url: defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
创建 RestTemplate 时 @LoadBalanced 声明使用负载均衡,获得 Rest 时加入 Ribbon 的配置
@Bean @LoadBalanced // Spring Cloud Ribbon是基于 Netflix Ribbon实现的一套客户端 负载均衡的工具。 public RestTemplate getRestTemplate() { return new RestTemplate(); }
在主启动类 DeptConsumer80_App 上添加 @EnableEurekaClient 注解。
将restTemplate 连接 host:port 的地址 改成使用 微服务名称:
@RestController public class DeptController_Consumer { //private static final String REST_URL_PREFIX = "http://localhost:8001"; private static final String REST_URL_PREFIX = "http://MICROSERVICECLOUD-DEPT"; @Autowired private RestTemplate restTemplate; @PostMapping("/consumer/dept") public boolean add(Dept dept) { return restTemplate.postForObject(REST_URL_PREFIX + "/dept", dept, Boolean.class); } }
测试:启动 Eureka 集群,再启动 provider-dept-8001 注册进 Eureka,然后启动消费方 consumer-dept-80, 访问 http://localhost:8080/consumer/dept。
7.3、Ribbon 负载均衡配置及测试
复制 服务提供方 provider-dept-8001 两份 8002, 8003;新建 8002 和 8003 自己的数据库;
create database cloudtest1; use cloudtest1; CREATE TABLE `dept` ( `deptno` bigint(20) NOT NULL AUTO_INCREMENT, `dname` varchar(60) DEFAULT NULL, `db_source` varchar(60) DEFAULT NULL, PRIMARY KEY (`deptno`) ) ENGINE=innodb DEFAULT CHARSET=utf8; insert into dept (dname,db_source) values('开发部',DATABASE()); insert into dept (dname,db_source) values('人事部',DATABASE()); insert into dept (dname,db_source) values('财务部',DATABASE()); insert into dept (dname,db_source) values('市场部',DATABASE()); insert into dept (dname,db_source) values('运维部',DATABASE()); =============================================================== create database cloudtest2; use cloudtest2; CREATE TABLE `dept` ( `deptno` bigint(20) NOT NULL AUTO_INCREMENT, `dname` varchar(60) DEFAULT NULL, `db_source` varchar(60) DEFAULT NULL, PRIMARY KEY (`deptno`) ) ENGINE=innodb DEFAULT CHARSET=utf8; insert into dept (dname,db_source) values('开发部',DATABASE()); insert into dept (dname,db_source) values('人事部',DATABASE()); insert into dept (dname,db_source) values('财务部',DATABASE()); insert into dept (dname,db_source) values('市场部',DATABASE()); insert into dept (dname,db_source) values('运维部',DATABASE()); =============================================================== create database cloudtest3; use cloudtest3; CREATE TABLE `dept` ( `deptno` bigint(20) NOT NULL AUTO_INCREMENT, `dname` varchar(60) DEFAULT NULL, `db_source` varchar(60) DEFAULT NULL, PRIMARY KEY (`deptno`) ) ENGINE=innodb DEFAULT CHARSET=utf8; insert into dept (dname,db_source) values('开发部',DATABASE()); insert into dept (dname,db_source) values('人事部',DATABASE()); insert into dept (dname,db_source) values('财务部',DATABASE()); insert into dept (dname,db_source) values('市场部',DATABASE()); insert into dept (dname,db_source) values('运维部',DATABASE());
8002 和 8003 的 application.yml 要修改三个地方:server.port 改成自己的端口;连接的数据库改成自己的数据库; 实例 id 修改(如果实例 id 相同,后面启动的覆盖前面的);
访问 Eureka dashboard,可以看到有三个实例:
连续访问 http://localhost:8081/consumer/dept,可以看到会采用轮询逐个访问 上面的三个服务实例。
7.4、Ribbon 核心组件 IRule
IRule: 根据特定算法从服务列表中选取一个要访问的服务。
1) RoundRobinRule: 轮询
2) RandomRule: 随机
3) AvailabilityFilteringRule: 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,还有并发的连接数量超过阈值的服务,然后对剩余的服务列表按照轮询策略进行访问
4) WeightedResponseTimeRule: 根据平均响应时间计算所有访问的权重,响应时间越快服务权重越大被选中的概率越高。刚启动时如果统计信息不足,则使用轮询策略,等统计信息足够,会切换到该策略
5) RetryRule: 先按照轮询策略获取服务,如果获取服务失败则在指定时间内会进行重试,获取可用的服务
6) BestAvailableRule: 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
7) ZoneAvoidanceRule: 默认规则,复合判断 Server 所在区域的性能和 Server 的可用性选择服务器
microservicecloud-consumer-dept-80 使用:
microservicecloud-consumer@Bean public IRule myRule() { // return new RoundRobinRule(); return new RandomRule();//达到的目的,用我们重新选择的随机算法替代默认的轮询。 //return new RetryRule(); }-dept-80
7.5、自定义Ribbo的负载均衡策略
8、Feign 负载均衡 <--返回目录
Feign 是一个声明式 Web Service 客户端。使用 Feign 能让编写 Web Service 客户端更加简单,它的使用方法是定义一个接口,然后在上面添加注解,同时也支持 JAX-RS 标准的注解。Feign 也支持可拔插式的编码器和解码器。Spring Cloud 对 Feign 进行了封装,使其支持了 SpringMVC 标准注解和 HttpMessageConverters。Feign 可以与 Eureka 和 Ribbon 组合使用以支持负载均衡。
前面使用 Eureka + Ribbon 使得我们可以通过微服务名称来调用服务提供方的 REST 接口。集成 Feign 后,使得我们可以通过 接口 + 注解来调用服务提供方的 REST 接口。
Feign 旨在使编写 Java Http 客户端变得更容易。前面在使用 Ribbon + RestTemplate 时,利用 RestTemplate 对 http 请求的封装处理,形成了一套模板化的调用方法。但在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以通常都会针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用。所以,Feign 在此基础上做了进一步封装,由它来帮助我们定义和实现依赖服务接口的定义。在Feign 的实现下,我们只需创建一个接口并使用注解的方式来配置它,即可完成对服务提供方的接口绑定,简化了使用 Ribbon 时,自动封装服务调用客户端的开发量。
通过微服务名称来调用服务提供方的 REST 接口:
@RestController public class DeptController_Consumer { private static final String REST_URL_PREFIX = "http://MICROSERVICECLOUD-DEPT"; @Autowired private RestTemplate restTemplate; @PostMapping("/consumer/dept") public boolean add(Dept dept) { return restTemplate.postForObject(REST_URL_PREFIX + "/dept", dept, Boolean.class); } }
通过 接口 + 注解来调用服务提供方的 REST 接口
// 绑定接口 @FeignClient("MICROSERVICECLOUD-DEPT") public interface DeptClientService { @PostMapping("/dept") public boolean add(@RequestBody Dept dept); @GetMapping("/dept/{id}") public Dept get(@PathVariable("id") Long id); @GetMapping("/dept") public List<Dept> list(); } // 消费方调用接口 @RestController public class DeptController_Consumer { @Autowired private DeptClientService deptClientService; @PostMapping("/consumer/dept") public boolean add(Dept dept) { return deptClientService.add(dept); } @GetMapping("/consumer/dept/{id}") public Dept get(@PathVariable("id") Long id) { return deptClientService.get(id); }
@GetMapping("/consumer/dept") public List<Dept> list() { return deptClientService.list(); } }
9、Hystrix 断路器 <--返回目录
分布式系统面临的问题:
服务雪崩:多个微服务之间调用的时候,假设微服务 A 调用微服务 B 和 C,微服务 B 和 C 又调用其他的微服务,这就是所谓的“扇出”。如果扇出的链路上某个微服务的调用响应实际过长或者不可用,对微服务 A 的调用就会占用越来越多的系统资源,进而引起系统崩溃,所谓的“雪崩效应”。
对于高流量的应用来说,单一的后端依赖可能会导致所有服务器的所有资源都在几秒钟内饱和。比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,备份队列、线程和其他系统资源紧张,导致整个系统发生更多的级联故障。这些都表示需要对故障和延迟进行隔离和管理,以便单个依赖关系的失败,不能取消整个应用程序或系统。
Hystrix 是什么:
Hystrix 是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等,Hystrix 能够包装在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
“断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间等待或者抛出调用方法无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。
Hystrix 能干什么:服务降级、服务熔断、服务限流、接近实时的监控。。。
9.1、服务熔断
熔断机制是应对雪崩效应的一种微服务链路保护机制。当扇出链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回“错误”的响应信息。当检测到该节点微服务调用响应正常后恢复调用链路。在 Spring Cloud 框架里熔断机制通过 Hystrix 实现。Hystrix 会监控微服务调用的状况,当失败的调用到一定阈值,缺省是 5 秒 内 20 次调用失败就会启动熔断机制。熔断机制的注解是 @HystrixCommand。
创建一个项目:带熔断机制的 Provider 服务提供方 microservicecloud-provider-dept-hystrix-8001。在主启动类上添加注解 @EnableCircuitBreaker。
一旦调用服务方法失败并抛出了错误提示信息后,会自动调用 @HystrixCommand 标注好的fallbackMethod 调用类中指定方法
@GetMapping("/dept/{id}") // 一旦调用服务方法失败并抛出了错误信息后,会自动调用@HystrixCommand标注好的fallbackMethod调用类中的指定方法 @HystrixCommand(fallbackMethod = "processHystrix_Get") public Dept get(@PathVariable("id") Long id) { Dept dept = this.service.get(id); if (null == dept) { throw new RuntimeException("该ID:" + id + "没有没有对应的信息"); } return dept; } public Dept processHystrix_Get(@PathVariable("id") Long id) { Dept dept = new Dept(); dept.setDeptno(id); dept.setDname("该id:"+ id + " 没有对应的信息。"); dept.setDb_source("no this database in MySQL"); return dept; }
启动 Eureka7001 和 provider-dept-hystrix-8001:http://eureka7001.com:7001/
访问 provider-dept-hystrix-8001:http://localhost:8001/dept/10
9.2、服务降级
简单来说,就是整体资源快不够了,忍痛将某些服务先先关掉,待渡过难关,再开启回来。
服务降级处理是在客户端实现完成的,与服务端没有关系。
修改 microservicecloud-api 工程,根据已经有的 DeptClient 接口新建一个实现了 FallbackFactory 接口的类 DeptClientServiceFallbackFactory,并在DeptServcieClient接口上注解@FeignClient 添加 fallbackFactory 属性。
@FeignClient(value = "MICROSERVICECLOUD-DEPT", fallbackFactory = DeptClientServiceFallbackFactory.class) public interface DeptClientService { @GetMapping("/dept/{id}") public Dept get(@PathVariable("id") Long id); // 其他方法省略 } @Component // 不要忘记添加 public class DeptClientServiceFallbackFactory implements FallbackFactory<DeptClientService> { @Override public DeptClientService create(Throwable throwable) { return new DeptClientService() { @Override public Dept get(Long id) { Dept dept = new Dept(); dept.setDeptno(id); dept.setDname("该ID:" + id + "没有没有对应的信息,Consumer客户端提供的降级信息,此刻服务Provider已经关闭"); dept.setDb_source("no this database in MySQL"); return dept; } // 其他方法省略 }; } }
microservicecloud-consumer-dept-feign 的 application.yml 添加:
feign: hystrix: enabled: true
测试:启动 Eureka7001、 provider-dept-hystrix-8001 和 consumer-dept-80,访问 http://localhost/consumer/dept/10, 返回
此时,关闭 provider-dept-hystrix-8001,模拟服务熔断,看客户端如何处理??访问 http://localhost/consumer/dept/10
microservicecloud-consumer-dept-feign 的 application.yml 忘记添加feign.hystrix.enabled=true, 报 500 连接超时
9.3、服务熔断和服务降级的总结
服务熔断:一般是某个服务故障或异常引起,类似现实世界中的“保险丝”,当某个异常条件被触发,直接熔断整个服务,而不是一直等到此服务超时。
服务降级:所谓降级,一般是从整体负荷考虑。就是当某个服务熔断之后,服务器将不再被调用,此时客户端可以自己准备一个本地的fallback 回调,返回一个缺省值。这样做,虽然服务水平下降,但好歹可用,比直接挂掉要强。
Hystrix 会监控微服务调用的状况,当失败的调用到一定阈值,缺省是 5 秒 内 20 次调用失败就会启动熔断机制。服务提供方服务熔断后,客户端使用自己的 fallback 回调,返回一个缺省值。
10、豪猪 hystrix Dashboard <--返回目录
除了隔离依赖服务的调用以外,Hystrix 还提供了准实时的调用监控(Hystrix Dashboard),Hystrix 会持续地记录所有通过Hystrix 发起的请求的执行信息,并以统计报表和图形的形式展示给用户,包括每秒执行多少请求多少成功,多少失败等。
Nexflix 通过 hystrix-metrics-event-stream 项目实现了对以上指标的监控。Spring Cloud 也提供了 Hystrix Dashboard 的整合,对监控内容转化成可视化界面。
依赖:
<!-- 自己定义的api --> <dependency> <groupId>com.oy</groupId> <artifactId>microservicecloud-api</artifactId> <version>${project.version}</version> </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> <!-- 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> <!-- feign相关 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency> <!-- hystrix和 hystrix-dashboard相关 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId> </dependency>
application.yml
server:
port: 9001
主启动类添加注解 @EnableHystrixDashboard
@SpringBootApplication @EnableHystrixDashboard public class DeptConsumer_DashBoard_App { public static void main(String[] args) { SpringApplication.run(DeptConsumer_DashBoard_App.class, args); } }
所有 Provider 微服务提供类(8001/8002/8003)都需要监控依赖配置
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
测试监控:启动 Eureka 集群,启动 provider-dept-hystrix-8001,启动 consumer-hystrix-dashboard,
1) 首先访问 http://localhost:8001/dept, 访问成功;
2)再访问 http://localhost:8001/hystrix.stream,即通过 Hystrix-Dashboard 监控 8001 程序;
测试时发现,ping 的结果返回空。原因是 provider-dept-hystrix-8001 的 "GET /dept" 这个接口没有添加服务熔断机制。该为访问 http://localhost:8001/dept/1,然后访问 http://localhost:8001/hystrix.stream 就正常了。
可视化监控(7 色 1 圈 1 线):
测试 访问接口异常 http://localhost:8001/dept/10
11、zuul 路由网关(代理 + 路由 + 过滤) <--返回目录
zuul 包含了对请求的路由和过滤两个最主要的功能:其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问接口统一入口;而过滤器功能负责对请求的处理过程进行干预,是实现请求校验、服务聚合等功能的基础。 zuul 和 Eureka 进行整合,将 zuul 自身注册为 Eureka 服务治理下的应用,同时从 Eureka 中获得其他微服务的消息,也即以后的访问微服务都是通过 zuul 跳转后获得。
11.1、Zuul路由基本配置
新建子模块 microservicecloud-zuul-gateway-9527
依赖:
<dependency> <groupId>com.oy</groupId> <artifactId>microservicecloud-api</artifactId> <version>${project.version}</version> </dependency> <!-- zuul路由网关 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zuul</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <!-- actuator监控 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!-- hystrix容错 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</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-jetty</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> <!-- 热部署插件 --> <dependency> <groupId>org.springframework</groupId> <artifactId>springloaded</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency>
application.yml
server: port: 9527 spring: application: name: microservicecloud-zuul-gateway eureka: client: service-url: defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7003.com:7003/eureka instance: instance-id: gateway-9527.com prefer-ip-address: true info: app.name: atguigu-microcloud company.name: www.atguigu.com build.artifactId: $project.artifactId$ build.version: $project.version$
修改hosts
127.0.0.1 myzuul.com
主启动类添加注解 @EnableZuulProxy
测试:启动 Eureka 集群,启动 provider-dept-8001 ,启动zuul-gateway-9527 进行测试
此时,Eureka 注册了两个服务:
不用路由访问:http://localhost:8001/dept/1
启动路由访问:http://myzuul.com:9527/microservicecloud-dept/dept/2
11.2、Zuul路由访问映射规则
ignored-services: "*" 将不能直接通过实例 id 访问微服务,也即不能访问 http://myzuul.com:9527/microservicecloud-dept/dept/2
routers 规则:将实例 id 映射成指定路径
prefix: 配置前缀
zuul: #ignored-services: microservicecloud-dept prefix: /oy ignored-services: "*" routes: mydept.serviceId: microservicecloud-dept mydept.path: /mydept/**
最后访问路径:http://myzuul.com:9527/oy/mydept/dept/2。其中,/oy 前缀,/mydept 项目名映射,/dept/2 资源路径
12、Spring Cloud Config 分布式配置中心 <--返回目录
Spring Cloud Config 是什么: 为微服务架构中的微服务提供集中化的外部配置支持,配置服务器为各个不同微服务应用的所有环境提供了一个中心化的外部配置。Spring Cloud Config 分为服务端和客户端两部分。服务端也称为分布式配置中心,它是一个独立的微服务应用,用来连接配置服务器并为客户端提供获取配置信息,加密解密信息等访问接口。客户端则是通过指定的配置中心来管理应用资源,以及与业务相关的配置内容,并在启动的时候从配置中心获取和加载配置信息。配置服务器默认采用 git 来存储配置信息,这样有助于对环境配置进行版本管理,并且可以通过 git 客户端工具来方便的管理和访问配置内容。
Spring Cloud Config 能干啥:集中管理配置文件;不同环境不同配置,动态化的配置更新,分环境部署比如 dev/test/prod/beta/release;运行期间动态调整配置,不再需要在每个服务部署的机器上编写配置文件,服务会向配置中心统一拉取配置,当配置发生变动时,服务不需要重启即可感知到配置的变化并应用新的配置。将配置信息以 REST 接口的形式暴露。
12.1、Config 服务端与 github 通信
github 新建一个仓库,克隆到本地 git clone git@github.com:wenbinouyang/microservicecloud-config.git 或 git clone https://github.com/wenbinouyang/microservicecloud-config.git
新建子模块 config 服务端 microservicecloud-config-3344
依赖:
<!-- 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> <!-- 图形化监控 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!-- 熔断 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency> <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> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jetty</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> <!-- 热部署插件 --> <dependency> <groupId>org.springframework</groupId> <artifactId>springloaded</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency>
application.yml
server: port: 3344 spring: application: name: microservicecloud-config cloud: config: server: git: uri: git@github.com:wenbinouyang/microservicecloud-config.git #GitHub上面的git仓库名字
主类
@SpringBootApplication @EnableConfigServer public class Config_3344_StartSpringCloudApp { public static void main(String[] args) { SpringApplication.run(Config_3344_StartSpringCloudApp.class, args); } }
访问 http://localhost:3344/applicaton-dev.yml, 或 http://localhost:3344/applicaton/dev/master, 或 http://localhost:3344/master/applicaton-dev.yml
12.2、Config 客户端通过 Config 服务端获得 Github 上的配置
新建子模块 microservicecloud-config-client-3355
依赖:
<!-- SpringCloud Config客户端 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency> <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> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jetty</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>springloaded</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency>
application.yml
spring:
application:
name: microservicecloud-config-client
bootstrap.yml
spring: cloud: config: name: application #需要从github上读取的资源名称,注意没有yml后缀名 profile: test #本次访问的配置项 label: master uri: http://config-3344.com:3344 #本微服务启动后先去找3344号服务,通过SpringCloudConfig获取GitHub的服务地址
测试接口
@RestController public class ConfigClientRest { @Value("${spring.application.name}") private String applicationName; @RequestMapping("/config") public String getConfig() { String str = "applicationName: " + applicationName; System.out.println("******str: " + str); return "applicationName: " + applicationName; } }
启动 3344 和 3355,
因为没有配置端口,加载 github 仓库的配置文件中也没有配端口(加载的是上一小节的 application.yml), 所以使用 8080 端口访问
12.3、Config配置演示与策略切换
---
1)本文参考尚硅谷 SpringCloud 视频(周阳2018年4月)整理完成,代码提交到 github, 地址 https://github.com/wenbinouyang/microservice-demo-2018
2)hystrix.stream一直显示ping: 不显示内容
3) 使用eclipse部署springcloud config从GitHub上获取配置内容出现错误:Auth fail
---