SpringCloud详解
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
SpringCloud详解
从all in one ===>模块化开发
微服务架构4个核心问题
- 服务很多客户端该怎么访问 Api
- 这么多服务服务之间如何通信 HttpRPC
- 这么多服务如何治理 服务注册发现Eureka Zookeeper
- 服务挂了怎么办 熔断机制Hystrix
解决方案SpringCloud~ 生态
- Spring Cloud NetFlix
- 一站式解决方案
- api网关zuul组件
- Feign —HttpClient -------Http通信方式同步阻塞
- 服务注册发现Eureka
- 熔断机制Hystrix
- Apache Dubbo Zookeeper
- 半自动需要整合别人的
Api没有找第三方组件或者自己实现
Dubbo
Zookeeper
熔断机制:没有借助Hystrix
- Spring Cloud Alibaba
- 最新的一站式解决方案
一、回顾微服务和微服务架构
1.1、微服务概述
- 什么是微服务
- 微服务是最近几年流行的一种架构思想
- 通常而言微服务架构是一种架构模式或者说是一种架构风格它提倡将单一的应用程序划分成一组小的服务 每个服务运行在其独立的自己的进程内服务之间互相协调互相配置为用户提供最终价值。服务之间采用轻量级的通信机制互相沟通每个服务都围绕着具体的业务进行构建并且能够被独立的部署到生产环境中另外应尽量避免统一的集中式的服务管理机制对具体的一个服务而言应根据业务上下文选择合适的语言、工具对其进行构建可以有一个非常轻量级的集中式管理来协调这些服务可以使用不同的语言来编写服务也可以使用不同的数据存储。
- 通俗来说微服务化的核心就是将传统的一站式应用根据业务拆分成一个一个的服务彻底地去耦合每一个微服务提供单个业务功能的服务一个服务做一件事情从技术角度看就是一种小而独立的处理过程类似进程的概念能够自行单独启动或销毁拥有自己独立的数据库。
1.2、微服务与微服务架构
微服务 强调的是服务的大小它关注的是某一个点是具体解决某一个问题的一个服务应用。强调的是一个个的个体每个个体完成一个具体的任务或者功能
微服务架构 一种新的架构形式Martin Fowler 2014提出
微服务架构是一种架构模式它提倡将单一应用程序划分成一组小的服务服务之间互相协调互相配合为用户提供最终价值。
1.3、微服务优缺点
- 优点
-
单一职责原则
-
每个服务足够内聚足够小代码容易理解这样能聚焦一个指定的业务功能或业务需求
-
开发简单开发效率提高一个服务可能就是专一的只干一件事
-
微服务能够被小团队单独开发
-
微服务是松耦合的是有功能意义的服务无论是在开发阶段或部署阶段都是独立的
-
微服务能使用不同的语言开发
-
易于和第三方集成微服务允许容易且灵活的方式集成自动部署通过持续集成工具如jenkins,Hudson,bamboo
-
微服务易于被一个开发人员理解修改和维护这样小团队能够更关注自己的工作成果无需通过合作才能体现价值
-
微服务允许你利用融合最新技术
-
微服务只是业务逻辑的代码不会和HTMLCSS或其他界面混合
-
每个微服务都有自己的存储能力可以有自己的数据库也可以有统一数据库
- 缺点
- 开发人员要处理分布式系统的复杂性
- 多服务运维难度随着服务的增加运维的压力也在增加
- 系统部署依赖
- 服务间通信成本
- 数据一致性
- 系统集成测试
- 性能监控
1.4、微服务技术栈有哪些
微服务条目 | 落地技术 |
---|---|
服务开发 | SpringBoot,Spring,SpringMvc |
服务配置与管理 | Netfix公司的Archaius、阿里的Diamond等 |
服务注册与发现 | Eureka、Consul、Zookeeper等 |
服务调用 | Rest、RPC、gRPC |
服务熔断器 | Hystrix、Envoydeng |
负载均衡 | Ribbon、Nginx等 |
服务接口调用(客户端调用服务的简化工具) | Feign等 |
消息队列 | Kafka、RabbitMQ、ActiveMQ等 |
服务配置中心管理 | SpringCloudConfig、Chef等 |
服务路由(API网关) | Zuul等 |
服务监控 | Zabbix、Nagios、Metrics、Specatator等 |
全链路追踪 | Zipkin、Brave、Dapper等 |
服务部署 | Docker、OpenStack、Kubernetes等 |
数据流操作开发包 | SpringCloud Stream(封装与RedisRabbitKafka等发送接受消息) |
时间消息总线 | SpringCloud Bus |
1.5、为什么选择springcloud
- 为什么选择springcloud作为微服务架构
- 整体解决方案还有框架成熟度高
- 社区热度高
- 可维护性
- 学习曲线 注解
- 当前各大IT公司用的微服务架构有哪些
- 阿里dubbo +HFS
- 京东JSF
- 新浪Motan
- 当当网 DubboX
二、SpringCloud概述
2.1、SpirngCloud是什么
官网https://spring.io/projects/spring-cloud
SpringCloud 基于SpringBoot提供了一套微服务解决方案包括服务注册与发现配置中心全链路监控服务网关负载均衡熔断器等组件除了基于NetFlix的开源组件做高度抽象封装之外还有一些选型中立的开源组件。
SpringCloud 利用SpringBoot的开发便利性巧妙地简化了分布式系统基础设施的开发SpringCloud为开发人员提供了快速构建分布式系统的一些工具包括配置管理服务发现断路器路由微代理事件总线全局锁决策竞选分布式会话等等 他们都可以用SpringBoot的开发风格做到一键启动和部署。
SpringCloud并没有重复造轮子它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来通过SpringBoot风格再封装屏蔽掉了复杂的配置和实现原理最终给开发者留出了一套简单易懂易部署和易维护的分布式系统开发工具包
SpringCloud 是分布式微服务架构下的一站式解决方案是各个微服务架构落地技术的集合体俗称微服务全家桶
2.2、SpringCloud和SpringBoot关系
- SpringBoot专注于快速方便的开发单个个体微服务。 --jar
- SpringCloud是关注全局的微服务协调整理治理框架它将springboot开发的一个个单体微服务整合并管理起来为各个微服务之间提供配置发现服务发现路由微代理时间总线。全局锁决策竞选分布式会话等等集成服务
- SpringBoot可以离开SpringCloud单独使用开发项目但是SpringCloud离不开SpringBoot属于依赖关系
- SpringBoot专注于快速、方便的开发单个个体微服务SpringCloud关注全局的服务治理框架
2.3、Dubbo 和SpringCloud区别
两者的生态对比
功能 | Dubbo | SpringCloud |
---|---|---|
服务注册中心 | Zookeeper | Eureka(主流、Consul、zookeeper |
服务调用方式 | RPC基于Dubbo协议 | REST API 基于Http协议 |
服务监控 | Dubbo-Monitor | Spring Boot Admin |
熔断器 | 不完善 | Spring Cloud Netflix Hystrix |
服务网关 | 无 | Spring Cloud Netflix Zuul、Gateway |
分布式配置 | 无 | Spring Cloud Config |
服务跟踪 | 无 | Spring Cloud Sleuth+Zipkin(一般) |
数据流 | 无 | Spring Cloud Stream |
批量任务 | 无 | Spring Cloud Task |
信息总线 | 无 | - Spring Cloud Bus |
- 最大区别 SpringCloud抛弃了Dubbo的RPC通信采用的是基于HTTP的REST方式
严格来说这两种方式各有优劣。虽然从一定角度上来说后者牺牲了服务调用的性能但也避免了上面提到的原生RPC带来的问题。而且REST相比RPC更为灵活服务提供方和调用方的依赖只依靠一纸契约不存在代码级别的强依赖这在强调快速演化的微服务环境下显得更加合适。
- 品牌机与组装机的区别 很明显SpringCloud 的功能比DUBBO更加强大涵盖面更广而且作为Spring的拳头项目它也能够与SpringFramework、SpringBoot、SpringData等其他Spring项目完美融合这些对于微服务而言是至关重要的。使用Dubbo构建的微服务架构就像组装电脑各环节我们的选择自由度很高但是最终结果很有可能因为一条内存质量不行就点不亮了总是让人不怎么放心。而SpringCloud就像品牌机保证了机器拥有更高的稳定性。
- 社区支持与更新热度
Dubbo停止了5年左右的更新对于技术发展的新需求需要由开发者自行拓展升级。而SpringCloud社区热度高且有维护和更新
解决的问题域不一样Dubbo的定位是一款RPC框架SpringCloud的目标是微服务架构下的一站式解决方案
2.4、SpringCloud作用
- 官网说的Spring Cloud focuses on providing good out of box experience for typical use cases and extensibility mechanism to cover others.
- Distributed/versioned configuration分布式/版本控制配置
- Service registration and discovery 路由注册与发现
- Routing 路由
- Service-to-service calls 服务到服务的调用
- Load balancing 负载均衡配置
- Circuit Breakers 断路器
- Distributed messaging 分布式消息管理
- …
- 官网下载https://spring.io/projects/spring-cloud#overview
版本号
SpringCloud是一个由众多独立子项目组成的大型综合项目每个子项目有不同的发行节奏都维护着自己的发布版本号。为避免与子项目的发不好混淆所以没有采用版本号的方式而是通过命名的方式
学习网站
- https://www.springcloud.cc/spring-cloud-netflix.html
- https://www.springcloud.cc/spring-cloud-dalston.html中文API文档
- https://springcloud.cc SpringCloud中文网
三、Rest学习环境搭建
3.1、服务提供者
- 最新springboot与springcloud版本适用关系
SpringCloud版本 | SpringBoot版本 |
---|---|
2022.0.0-M2 | Spring Boot >=3.0.0-M2 and ❤️.1.0-M1 |
2022.0.0-M1 | Spring Boot >=3.0.0-M1 and ❤️.0.0-M2 |
2021.0.3 | Spring Boot >=2.6.1 and ❤️.0.0-M1 |
2021.0.0-RC1 | Spring Boot >=2.6.0-RC1 and <2.6.1 |
2021.0.0-M3 | Spring Boot >=2.6.0-M3 and <2.6.0-RC1 |
2021.0.0-M1 | Spring Boot >=2.6.0-M1 and <2.6.0-M3 |
2020.0.5 | Spring Boot >=2.4.0.M1 and <2.6.0-M1 |
Hoxton.SR12 | Spring Boot >=2.2.0.RELEASE and <2.4.0.M1 |
Hoxton.BUILD-SNAPSHOT | Spring Boot >=2.2.0.BUILD-SNAPSHOT |
Hoxton.M2 | Spring Boot >=2.2.0.M4 and <=2.2.0.M5 |
Greenwich.BUILD-SNAPSHO | Spring Boot >=2.1.9.BUILD-SNAPSHOT and <2.2.0.M4 |
Greenwich.SR2 | Spring Boot >=2.1.0.RELEASE and <2.1.9.BUILD-SNAPSHOT |
Greenwich.M1 | Spring Boot >=2.1.0.M3 and <2.1.0.RELEASE |
Finchley.BUILD-SNAPSHOT | Spring Boot >=2.0.999.BUILD-SNAPSHOT and <2.1.0.M3 |
Finchley.SR4 | Spring Boot >=2.0.3.RELEASE and <2.0.999.BUILD-SNAPSHOT |
Finchley.RC2 | Spring Boot >=2.0.2.RELEASE and <2.0.3.RELEASE |
Finchley.RC1 | Spring Boot >=2.0.1.RELEASE and <2.0.2.RELEASE |
Finchley.M9 | Spring Boot >=2.0.0.RELEASE and <=2.0.0.RELEASE |
Finchley.M7 | Spring Boot >=2.0.0.RC2 and <=2.0.0.RC2 |
Finchley.M6 | Spring Boot >=2.0.0.RC1 and <=2.0.0.RC1 |
Finchley.M5 | Spring Boot >=2.0.0.M7 and <=2.0.0.M7 |
Finchley.M4 | Spring Boot >=2.0.0.M6 and <=2.0.0.M6 |
Finchley.M3 | Spring Boot >=2.0.0.M5 and <=2.0.0.M5 |
Finchley.M2 | Spring Boot >=2.0.0.M3 and <2.0.0.M5 |
Edgware.SR5 | 1.5.20.RELEASE |
Edgware.SR5 | 1.5.16.RELEASE |
Edgware.RELEASE | 1.5.9.RELEASE |
Dalston.RC1 | 1.5.2.RELEASE |
- 创建一个maven空项目作为总项目
- 导入父工程依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>springcloud</artifactId>
<version>1.0-SNAPSHOT</version>
<modules>
<module>springcloud-api</module>
<module>springcloud-provider-dept-8001</module>
</modules>
<!-- 打包方式 pom-->
<packaging>pom</packaging>
<!-- 版本号-->
<properties>
<project.buid.sourceEncoding>UTF-8</project.buid.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<junit.version>4.12</junit.version>
<lombok.version>1.16.18</lombok.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- springcloud的依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- springboot-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.1.4.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 数据库-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.3</version>
</dependency>
<!-- SpringBoot 启动器-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<!-- 日志和测试-->
<!-- junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<!-- lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<!-- log4j-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.11</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
- dependencyManagement 管理依赖后面子工程如果需要用到哪个依赖还是需要导入但是版本号不用写因为父工程约束好了版本号。版本号一致。
- 创建模块 springcloud-api用来放实体类
- 导入依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
- 连接数据库写实体类表类映射
package com.qian.springcloud.pojo;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
@Data
@NoArgsConstructor
@Accessors(chain = true) //支持链式写法
public class Dept implements Serializable { //Dept 实体类 要序列化 。类表关系映射
private Long deptno;
private String deptname;
//这个数据是存在哪个数据库的字段~ 微服务一个服务对应一个数据库同一个信息可能存在不同的数据库
private String db_source;
public Dept(String deptname) {
this.deptname = deptname;
}
/*
* Dept dept = new Dept();
* dept.setDeptNo(11).setDeptname("xqh") 支持继续点下去就是链式写法
* */
}
- 创建模块写服务提供者 springcloud-provider-dept-8001
- 导入依赖
<dependencies>
<!-- 首先我们需要拿到实体类所以要配置api module-->
<dependency>
<groupId>org.example</groupId>
<artifactId>springcloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<!-- test-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- jetty-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
<!-- 热部署工具-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
- 配置yml
server:
port: 8001
#Mybatis配置
mybatis:
type-aliases-package: com.qian.springcloud.pojo
mapper-locations: classpath:mybatis/mapper/*.xml
configuration:
cache-enabled: true
#spring配置
spring:
application:
name: springcloud-provider-dept
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: org.gjt.mm.mysql.Driver
url: jdbc:mysql://localhost:3306/db01?useUnicode=true&characterEncoding=utf-8
username: root
password: 123456
- 写mapper层mapper.xmlservice层controller层。以下为代码
package com.qian.springcloud.mapper;
import com.qian.springcloud.pojo.Dept;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import java.util.List;
@Mapper
@Repository
public interface DeptMapper {
int addDept(Dept dept);
Dept queryById(Long id);
List<Dept>queryAll();
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace=绑定一个对应的Dao/Mapper接口-->
<mapper namespace="com.qian.springcloud.mapper.DeptMapper">
<select id="addDept" parameterType="Dept">
insert into dept(deptname,db_source) values (#{deptname},DATABASE())
</select>
<select id="queryById" resultType="Dept">
select * from dept where deptno=#{deptno}
</select>
<select id="queryAll" resultType="Dept">
select * from dept
</select>
</mapper>
package com.qian.springcloud.service;
import com.qian.springcloud.pojo.Dept;
import java.util.List;
public interface DeptService {
int addDept(Dept dept);
Dept queryById(Long id);
List<Dept> queryAll();
}
package com.qian.springcloud.service;
import com.qian.springcloud.mapper.DeptMapper;
import com.qian.springcloud.pojo.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class DeptServiceImpl implements DeptService{
@Autowired
DeptMapper deptMapper;
@Override
public int addDept(Dept dept) {
return deptMapper.addDept(dept);
}
@Override
public Dept queryById(Long id) {
return deptMapper.queryById(id);
}
@Override
public List<Dept> queryAll() {
return deptMapper.queryAll();
}
}
package com.qian.springcloud.controller;
import com.qian.springcloud.pojo.Dept;
import com.qian.springcloud.service.DeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
//提供restful服务
@RestController //返回json字符串不用走视图解析器不走页面
public class DeptController {
@Autowired
DeptService deptService;
@PostMapping("/dept/add")
public String addDept(Dept dept){
deptService.addDept(dept);
return "添加成功";
}
@GetMapping("/dept/get/{id}")
public Dept queryById(@PathVariable("id") Long id){
Dept dept = deptService.queryById(id);
return dept;
}
@RequestMapping("/dept/list")
public List<Dept> queryAll(){
List<Dept> depts = deptService.queryAll();
return depts;
}
}
- 写主应用启动器
package com.qian.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DeptProvider_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProvider_8001.class,args);
}
}
3.2、服务消费者
- 导入依赖
<dependencies>
<!-- 实体类+web-->
<dependency>
<groupId>org.example</groupId>
<artifactId>springcloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
2. RestTemplate 首先要使用它先得注入到spring中于是在ConfigBean中注册上RestTemplate
package com.qian.springcloud.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Controller;
import org.springframework.web.client.RestTemplate;
@Configuration
public class ConfigBean { //在这里配置bean 相当于spring中的 applicationContext.xml
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
使用restTemplate来调用远程服务也就是前面所写的服务提供者
DeptConsumerController.java
package com.qian.springcloud.controller;
import com.qian.springcloud.pojo.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@RestController
public class DeptConsumerController {
//消费者~ 不应该有service层那么需要拿到service怎么办
//RestTemplate 有很多方法供我们直接调用!但是要先注入到spring中--->ConfigBean中管理Bean
//(url,实体map,Class<T> responseType)
@Autowired
private RestTemplate restTemplate;//提供多种便捷访问远程http服务的方法简单的restful服务模板
private static final String REST_URL_PREFIX = "http://localhost:8001";
@RequestMapping("/consumer/dept/get/{id}")
public Dept get(@PathVariable("id")Long id){
//完全解耦
//消费者要拿到提供者的服务通过进入提供者服务请求的urlREST_URL_PREFIX+"/dept/get/"+id
return restTemplate.getForObject(REST_URL_PREFIX+"/dept/get/"+id,Dept.class);
}
@RequestMapping("/consumer/dept/add")
public int addDept(Dept dept){
return restTemplate.postForObject(REST_URL_PREFIX+"/dept/add",dept,int.class);
}
@RequestMapping("/consumer/dept/list")
public List<Dept>list(){
return restTemplate.getForObject(REST_URL_PREFIX+"/dept/list",List.class);
}
//三个参数分别是 url 携带的参数 返回类型
}
启动服务者再启动消费者测试在消费者端口上调用远程服务提供者服务
四、Eureka服务注册与发现
4.1、什么是Eureka
- Netflix 在设计Eureka时遵循的就是AP原则
- Eureka 是Netflix 的一个子模块也是核心模块之一。Eureka是一个基于REST的服务用于定位服务以实现云端中间服务发现和故障转移服务注册与发现对于微服务来说是非常重要的有了服务发现与注册只需要使用服务的标识符就可以访问到服务而不需要修改服务调用的配置文件了功能类似于Dubbo的注册中心比如Zookeeper
4.2、原理
- Eureka的基本架构
- SpringCloud封装了NetFilx公司开发的Eureka模块来实现服务注册和发现对比Dubbo中用的Zookeeper
- Euraka采用了C-S的架构设计EurekaServer 作为服务注册功能的服务器他是服务注册中心
- 而系统中的其他微服务使用Eureka的客户端连接到EurekaServer并维持心跳连接。这样系统的维护人员就可以通过EurekaServer 来监控系统中各个微服务是否正常运行SpringCloud的一些其他模块比如Zuul就可以通过EurekaServer来发现其他微服务并执行相关的逻辑。
- Eureka包含两个组件
- Eureka Server 提供服务注册各个节点启动后会在EurekaServer中进行注册这样Eureka Server 中的服务注册表中将会存储所有可用服务节点的信息服务节点的信息可以在界面中直观的看到
- Eureka Client 是一个java客户端用于简化EurekaServer的交互客户端同时也具备一个内置的使用轮循负载算法的负载均衡器。在应用启动后将会向EurekaServer发送心跳默认周期为30秒。如果EurekaServer在多个心跳周期内没有接收到某个节点的心跳EurekaServer将会从服务注册表中把这个服务节点移除掉默认周期为90秒
- 三大角色
- EurekaServer提供服务的注册与发现 zookeeper
- Service Provider 将自身服务注册到Eureka中从而使消费者方能够找到
- Service Consumer服务消费方从Eureka中获取注册服务列表从而找到消费服务
4.3、搭建注册中心
导入依赖
编写配置文件
开启这个功能 @Enablexxxx
配置类
- 导入依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
- 编写配置文件
server:
port: 7001
#Eureka
eureka:
instance:
hostname: localhost # Eureka服务端的名字
client:
register-with-eureka: false # 表示是否向eureka注册中心注册自己
fetch-registry: false # 如果为false 则表示自己为注册中心
service-url: #监控页面~
defaultZone: https://${eureka.instance.hostname}:${server.port}/eureka/
- 写主启动类开启这个功能
package com.qian.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer //服务端的启动类可以接受别人注册进来
public class EurekaServer_7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaServer_7001.class,args);
}
}
- 运行测试localhost:7001
- 把我们之前写的服务提供者8001注册进去
- 导入依赖
<!-- 加入eureka依赖-->
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<!-- 完善监控信息-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
- 修改配置
#Eureka配置 服务注册到哪里
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/
instance:
instance-id: springcloud-provider-dept8001 #修改的是status修改默认描述信息
#info配置配置监控信息
info:
app.name: xqh-springcloud
company.name: blog.qian.com
- 开启功能
@EnableEurekaClient //把服务启动后自动注册到eureka中
- 测试先运行注册中心7001再运行提供者服务8001可以看到provider已经注册进去了点击status下的连接会进入到自己编写的监控信息
- 自我保护机制
“突然将8001服务关闭在注册中心会报一串红EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY’RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE.此时8001服务信息仍然在注册中心这就是自我保护机制等到8001再次连上时自动退出自我保护机制”
一句话总结某时刻某一个微服务不可以用了eureka不会立刻清理依旧会对该微服务的信息进行保存
- 默认情况下如果EurekaServer在一定时间内没有接收到某个微服务实例的心跳 EurekaServer将会注销该实例默认90秒。但是当网络分区故障发生时微服务与Eureka之间无正常通行以上行为可能变得非常危险–因为微服务本身其实是健康的此时本不应该注销这个服务即注销了健康的服务 。Eureka通过自我保护机制 来解决这个问题—当EurekaServer节点在短时间内丢失过多客户端时可能发生了网络分区故障那么这个节点就会进入自我保护模式。一旦进入该模式EurekaServer就会保护服务注册表中的信息不再删除服务注册表中的数据也就是不会注销任何微服务。当网络故障恢复后该EurekaServer节点会自动退出自我保护模式
- 在自我保护模式中EurekaServer 会保护服务注册表中的信息不再注销任何服务实例。当它收到的心跳数重新恢复到阈值以上时该EurekaServer节点就会自动退出自我保护模式。它的设计哲学就是宁可保留错误的服务注册信息也不盲目注销任何可能健康的服务实例。“好死不如赖活着 ”。
- 综上自我保护模式是一种应对网络异常的安全保护措施。
- 在springcloud中可以使用 eureka.server.enable-self-preservation = false 来关闭自我保护模式不建议关闭
- 扩展功能
@Autowired
private DiscoveryClient client; //获取一些配置的信息得到具体的微服务
...
//注册进来的微服务~获取一些消息
@GetMapping("/dept/discovery")
public Object discovery(){
//获取微服务列表的清单
List<String> services = client.getServices();
System.out.println("discovery=>service:"+services);
//得到一个具体的微服务信息通过具体的微服务id applicationName
List<ServiceInstance> instances = client.getInstances("SPRINGCLOUD-PROVIDER");
for (ServiceInstance instance :instances){
System.out.println(
instance.getHost()+"\t"+
instance.getPort()+"\t"+
instance.getUri()+"\t"+
instance.getServiceId()
);
}
return this.client;
}
开启功能
@EnableDiscoveryClient //服务发现
运行测试。
4.4、集群环境配置
- 再创建两个注册中心springcloud-eureka-7002 springcloud-eureka-7003 ,配置和启动文件都和7001 一样把端口号改为各自的
- 为了方便区分把host文件中修改 127.0.0.1 eureka.7001.com 127.0.0.1 eureka.7002.com 127.0.0.1 eureka.7003.com
- application.yml
server:
port: 7001
#Eureka
eureka:
instance:
hostname: eureka7001.com # Eureka服务端的名字
client:
register-with-eureka: false # 表示是否向eureka注册中心注册自己
fetch-registry: false # 如果为false 则表示自己为注册中心
#单机 defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
#集群关联关联7002
service-url: #监控页面~
defaultZone: http://eureka7002.com/eureka/,http://eureka7003.com/eureka/
将每个注册中心同时关联另外两个
- 把服务提供者注册到这三个注册中心
#Eureka配置 服务注册到哪里
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
instance:
instance-id: springcloud-provider-dept8001 #修改的是status修改默认描述信息
- 运行测试。每个注册中心都和另外两个互相关联
4.6、CAP原则及对比Zookeeper
- 回顾CAP原则
- RDBMS(Mysql、Oracle、sqlServer) ====>ACID 关系数据库
- A:原子性
- C一致性
- I隔离性
- D持久性
- NoSQLredis、mongdb===>CAP
- C强一致性
- A可用性
- P分区容错性
- CAP理论的核心
- CPA的三进二CA/AP/CP 一个分布式系统不可能同时很好的满足一致性可用性和分区容错性这三个需求
- 根据CAP原理将NoSQL数据库分成了满足CA原则满足CP原则和满足AP原则三大类
- CA单点集群满足一致性可用性的系统通常可扩展性较差
- CP满足一致性分区容错性的系统通常性能不是特别高
- AP满足可用性分区容错性的系统通常可能对一致性要求低一些
- 作为服务员注册中心Eureka和Zookeeper好在哪里
当向注册中心查询服务列表时我们可以容忍注册中心返回的是几分钟以前的注册信息但不能接收服务直接down掉不可用也就是说服务注册功能对可用性的要求要高于一致性。
-
Zookeeper保证的是CP降低了可用性满足一致性通常性能不是特别高
-
Eureka保证的是AP满足可用性通常对一致性要求低一些。
因此Eureka可以很好的应对因网络故障导致部分节点失去联系的情况而不会像zookeeper那样使整个注册服务瘫痪
五、Ribbon
5.1、负载均衡及Ribbon
- ribbon是什么
- SpringCloud Ribbon 是基于Netflix Ribbon 实现的一套客户端负载均衡的工具
- 简单的来说Ribbon是Netflix发布的开源项目主要功能是提供客户端的软件负载均衡算法将NetFlix的中间层服务连接在一起Ribbon的客户端组件提供一系列完整的配置项如连接超时、重试等等。简单的说就是在配置文件中列出LoadBalancer简称LB负载均衡后面所有的机器Ribbon会自动的帮助你基于某种规则如简单轮询随机连接等等去连接这些机器。我们也很容易使用Ribbon 实现自定义的负载均衡算法
- ribbon能干嘛
- LB即负载均衡在微服务或分布式集群中经常用的一种应用
- 负载均衡简单地说就是将用户的请求平摊的分配到多个服务上从而达到系统的HA高可用
- 常见的负载均衡软件有NginxLvs等等
- dubbo、SpringCloud 中均给我们提供了负载均衡Springcloud的负载均衡算法可以自定义
- 负载均衡简单分类
- 集中式LB即在服务的消费方和提供方之间使用独立的LB设施如Nginx由该设施负责把访问请求通过某种策略转发至服务的提供方
- 进程式LB将LB逻辑集成到消费方消费方从服务注册中心获知有哪些地址可用然后自己再从这些地址中选出一个合适的服务器。Ribbon就属于进程内LB 它只是一个类库集成于消费方进程消费方通过它来获取到服务提供方的地址
5.2、使用Ribbon实现负载均衡
- 导入依赖在消费者端
<!-- ribbon -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<!-- eureka-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
- 写配置
server:
port: 80
#Eureka配置
eureka:
client:
register-with-eureka: false #不向eureka注册自己消费者不需要注册
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
- 开启功能
@EnableEurekaClient
@SpringBootApplication
- configBean.java
package com.qian.springcloud.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Controller;
import org.springframework.web.client.RestTemplate;
@Configuration
public class ConfigBean { //在这里配置bean 相当于spring中的 applicationContext.xml
//配置负载均衡实现restTemplate
@Bean
@LoadBalanced //ribbon
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
- controller
package com.qian.springcloud.controller;
import com.qian.springcloud.pojo.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@RestController
public class DeptConsumerController {
//消费者~ 不应该有service层那么需要拿到service怎么办
//RestTemplate 有很多方法供我们直接调用!但是要先注入到spring中--->ConfigBean中管理Bean
//(url,实体map,Class<T> responseType)
@Autowired
private RestTemplate restTemplate;//提供多种便捷访问远程http服务的方法简单的restful服务模板
//ribbon 我们这里的地址应该是一个变量通过服务名来访问
//之前是private static final String REST_URL_PREFIX = "http://localhost:8001";直接调用8001现在通过注册中心拿到注册名来调用
private static final String REST_URL_PREFIX = "http://SPRINGCLOUD-PROVIDER-DEPT";//基于微服务的名字访问
@RequestMapping("/consumer/dept/get/{id}")
public Dept get(@PathVariable("id")Long id){
//完全解耦
//消费者要拿到提供者的服务通过进入提供者服务请求的urlREST_URL_PREFIX+"/dept/get/"+id
return restTemplate.getForObject(REST_URL_PREFIX+"/dept/get/"+id,Dept.class);
}
@RequestMapping("/consumer/dept/add")
public int addDept(Dept dept){
return restTemplate.postForObject(REST_URL_PREFIX+"/dept/add",dept,int.class);
}
@RequestMapping("/consumer/dept/list")
public List<Dept>list(){
return restTemplate.getForObject(REST_URL_PREFIX+"/dept/list",List.class);
}
//三个参数分别是 url 携带的参数 返回类型
}
- 再写两个服务提供者来模拟eureka集群获取可用服务列表过程
数据库也要复制一份除database不同以外其他都相同
- 启动一个注册中心springcloud-eureka-7001 三个服务提供者springcloud-provider-dept-8001 ,springcloud-provider-dept-8002 ,springcloud-provider-dept-8003 再启动消费者 springcloud-consumer-80 访问localhost:consumer/dept/list 每一次访问获取到的服务列表都不同这就是eureka集群获取可用服务类表实施的算法。
5.3、自定义负载均衡算法
myRule.java
package com.qian.myrule;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class KuangRule {
@Bean
public IRule myRule(){
return new RandomRule(); //使用随机算法
}
}
在启动类上加注解
package com.qian.springcloud;
import com.qian.myrule.KuangRule;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
//Ribbon和Eureka整合以后客户端可以直接调用不用关心IP地址和端口号
@EnableEurekaClient
@SpringBootApplication
//在微服务启动的时候就能去加载我们自定义Ribbon类
@RibbonClient(name = "SPRINGCLOUD-PROVIDER-DEPT",configuration = KuangRule.class)
public class DeptConsumer_80 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer_80.class,args);
}
}
六、Feign负载均衡
7.1、简介
- feign是声明式的web service 客户端它让微服务之间的调用变得更简单了类似controller调用service。SpringCloud 集成了Ribbon 和Eureka 可在使用Feign 时提供负载均衡的http客户端
只需要创建一个接口然后添加注解即可。
- Feign能干什么
- Feign旨在使编写java http客户端变得更加容易
- 在Feign的实现下我们只需要创建一个接口并使用注解的方式来配置它类似于以前Dao接口上标注mapper注解现在是一个微服务接口上面标注一个Feign注解即可即可完成对服务提供方的接口绑定简化了使用springcloud ribbon 时自动封装服务调用客户端的开发量
- Feign集成了Ribbon
- 利用Ribbon维护MicroServiceCloud-Dept的服务列表信息并且通过轮询实现了客户端的负载均衡而与Ribbon不同的 是通过Feign只需要定义服务绑定接口且以声明式的方法优雅而且简单的实现了服务调用。
7.2、Feign使用步骤
- 在api中写服务接口
DeptClientService.java
package com.qian.springcloud.service;
import com.qian.springcloud.pojo.Dept;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import java.util.List;
@Component
@FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT")
public interface DeptClientService {
@GetMapping("/dept/get/{id}")
public Dept queryById(@PathVariable("id") Long id);
@GetMapping("/dept/list")
public List<Dept> queryAll();
@PostMapping("/dept/add")
public Boolean addDept(Dept dept);
}
- 创建一个新的消费者模块springcloud-consumer-dept-feign.
DeptConsumerController.java
package com.qian.springcloud.controller;
import com.qian.springcloud.pojo.Dept;
import com.qian.springcloud.service.DeptClientService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@RestController
public class DeptConsumerController {
//消费者~ 不应该有service层那么需要拿到service怎么办
//RestTemplate 有很多方法供我们直接调用!但是要先注入到spring中--->ConfigBean中管理Bean
//(url,实体map,Class<T> responseType)
@Autowired
private RestTemplate restTemplate;//提供多种便捷访问远程http服务的方法简单的restful服务模板
@Autowired
private DeptClientService deptClientService = null;
@RequestMapping("/consumer/dept/get/{id}")
public Dept get(@PathVariable("id")Long id){
return this.deptClientService.queryById(id);
}
@RequestMapping("/consumer/dept/add")
public boolean addDept(Dept dept){
return this.deptClientService.addDept(dept);
}
@RequestMapping("/consumer/dept/list")
public List<Dept>list(){
return this.deptClientService.queryAll();
}
//三个参数分别是 url 携带的参数 返回类型
}
使用调用接口的方式
- 主启动类
package com.qian.springcloud;
import com.qian.myrule.KuangRule;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
//Ribbon和Eureka整合以后客户端可以直接调用不用关心IP地址和端口号
@EnableEurekaClient
@SpringBootApplication
//在微服务启动的时候就能去加载我们自定义Ribbon类
@RibbonClient(name = "SPRINGCLOUD-PROVIDER-DEPT",configuration = KuangRule.class)
public class DeptConsumer_80 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer_80.class,args);
}
}
6.3、Feign和Ribbon如何选择
- 根据个人习惯而定如果喜欢REST风格使用Ribbon如果喜欢社区版的面向接口风格使用Feign.
Feign 本质上也是实现了 Ribbon只不过后者是在调用方式上为了满足一些开发者习惯的接口调用习惯
七、Hystrix
7.1、服务熔断
- 什么是Hystrix
Hystrix是一个用于处理分布式系统的延迟和容错的开源库在分布式系统里许多依赖不可避免的会调用失败比如超时异常等Hystrix能够保证在一个依赖出问题的情况下不会导致整体服务失败避免级联故障以提高分布式系统的弹性
“熔断器”本身是一种开关装置当某个服务单元发生故障之后通过断路器的故障监控类似熔断保险丝向调用方法返回一个服务预期的可处理的备选响应FallBack而不是长时间的等待或者抛出调用方法无法处理的异常这样就可以保证了服务调用方的线程不会被长时间、不必要的占用从而避免了故障在分布式系统中的蔓延乃至雪崩。
- 分布式面临的问题
复杂分布式体系结构中的应用程序有数十个依赖关系每个依赖关系在某些时候将不可避免失败
- 服务雪崩
- 多个微服务之间调用的时候假设微服务A调用微服务B和微服务C微服务B和微服务C又调用其他的微服务这就是所谓的“扇出”如果扇出的链路上某个微服务的调用响应时间过长或者不可用对微服务A的调用就会占用越来越多的系统资源进而引起系统崩溃所谓的“雪崩效应”。
- 对于高流量的应用来说单一的后端依赖可能会导致所有服务器上的所有资源都在几十秒内饱和。比失败更糟糕的是这些应用程序还可能导致服务之间的延迟增加备份队列线程和其他系统资源紧张导致整个系统发生更多的级联故障这些都表示需要对故障和延迟进行隔离和管理以达到单个依赖关系的失败而不影响整个应用程序或系统运行。
我们需要“弃车保帅”
- Hystrix能干啥
- 服务降级
- 服务熔断
- 服务限流
- 接近实时的监控
- …
- 使用熔断功能
-
创建新模块springcloud-provider-dept-hystrix-8001 , 导入依赖、配置文件项目结构等
-
导入hystrix依赖
<!-- 导入hystrix依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
- DeptController
package com.qian.springcloud.controller;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.qian.springcloud.pojo.Dept;
import com.qian.springcloud.service.DeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
//提供restful服务
@RestController //返回json字符串不用走视图解析器不走页面
public class DeptController {
@Autowired
private DeptService deptService;
@GetMapping("/dept/get/{id}")
@HystrixCommand(fallbackMethod ="hystrixGet") //有异常就调用下面的备选方案
public Dept get(@PathVariable("id") Long id){
Dept dept = deptService.queryById(id);
if (dept==null){
throw new RuntimeException("id=>"+id+"不存在该用户或者信息无法找到~");
}
return dept;
}
//备选方案
public Dept hystrixGet(@PathVariable("id") Long id){
return new Dept()
.setDeptno(id)
.setDeptname("id=>"+id+"没有对应的信息null")
.setDb_source("no this database in mysql");
}
}
- 主启动类添加注解支持
package com.qian.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
@EnableEurekaClient //把服务启动后自动注册到eureka中
@SpringBootApplication
@EnableDiscoveryClient
//添加对熔断的支持
@EnableHystrix
public class DeptProviderHystrix_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProviderHystrix_8001.class,args);
}
}
- 运行注册中心运行服务提供者、消费者体验熔断
7.2、服务降级
- 什么是服务降级
服务降级是指 当服务器压力剧增的情况下根据实际业务情况及流量对一些服务和页面有策略的不处理或换种简单的方式处理从而释放服务器资源以保证核心业务正常运作或高效运作。说白了就是尽可能的把系统资源让给优先级高的服务。
资源有限而请求是无限的。如果在并发高峰期不做服务降级处理一方面肯定会影响整体服务的性能严重的话可能会导致宕机某些重要的服务不可用。所以一般在高峰期为了保证核心功能服务的可用性都要对某些服务降级处理。比如当双11活动时把交易无关的服务统统降级如查看蚂蚁深林查看历史订单等等。
服务降级主要用于什么场景呢当整个微服务架构整体的负载超出了预设的上限阈值或即将到来的流量预计将会超过预设的阈值时为了保证重要或基本的服务能正常运行可以将一些 不重要 或 不紧急 的服务或任务进行服务的 延迟使用 或 暂停使用。
降级的方式可以根据业务来可以延迟服务比如延迟给用户增加积分只是放到一个缓存中等服务平稳之后再执行 或者在粒度范围内关闭服务比如关闭相关文章的推荐。
-
服务降级需要考虑的问题
- 1那些服务是核心服务哪些服务是非核心服务
- 2那些服务可以支持降级那些服务不能支持降级降级策略是什么
- 3除服务降级之外是否存在更复杂的业务放通场景策略是什么
-
自动降级分类
1超时降级主要配置好超时时间和超时重试次数和机制并使用异步机制探测回复情况
2失败次数降级主要是一些不稳定的api当失败调用次数达到一定阀值自动降级同样要使用异步机制探测回复情况
3故障降级比如要调用的远程服务挂掉了网络故障、DNS故障、http服务返回错误的状态码、rpc服务抛出异常则可以直接降级。降级后的处理方案有默认值比如库存服务挂了返回默认现货、兜底数据比如广告挂了返回提前准备好的一些静态页面、缓存之前暂存的一些缓存数据
4限流降级秒杀或者抢购一些限购商品时此时可能会因为访问量太大而导致系统崩溃此时会使用限流来进行限制访问量当达到限流阀值后续请求会被降级降级后的处理方案可以是排队页面将用户导流到排队页面等一会重试、无货直接告知用户没货了、错误页如活动太火爆了稍后重试。
-
服务熔断和服务降级的区别
- 服务熔断—>服务端某个服务超时或异常引起熔断~类似于保险丝(自我熔断)
- 服务降级—>客户端从整体网站请求负载考虑当某个服务熔断或者关闭之后服务将不再被调用此时在客户端我们可以准备一个 FallBackFactory 返回一个默认的值(缺省值)。会导致整体的服务下降但是好歹能用比直接挂掉强。
- 触发原因不太一样服务熔断一般是某个服务下游服务故障引起而服务降级一般是从整体负荷考虑管理目标的层次不太一样熔断其实是一个框架级的处理每个微服务都需要无层级之分而降级一般需要对业务有层级之分比如降级一般是从最外围服务开始
- 实现方式不太一样服务降级具有代码侵入性(由控制器完成/或自动降级)熔断一般称为自我熔断。
熔断降级限流
限流限制并发的请求访问量超过阈值则拒绝
降级服务分优先级牺牲非核心服务不可用保证核心服务稳定从整体负荷考虑
熔断依赖的下游服务故障触发熔断避免引发本系统崩溃系统自动执行和恢复
7.3、Dashboard 流监控
-
新建springcloud-consumer-hystrix-dashboard模块
-
导入依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<!-- 实体类+web-->
<dependency>
<groupId>org.example</groupId>
<artifactId>springcloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</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-ribbon</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<!-- eureka-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
</dependencies>
- 主启动类
package com.qian.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
@SpringBootApplication
@EnableHystrixDashboard //开启监控
public class DeptConsumerDashboard_9001 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumerDashboard_9001.class,args);
}
}
- 给springcloud-provider-dept-hystrix-8001模块下的主启动类添加如下代码,添加监控
@SpringBootApplication
@EnableEurekaClient //EnableEurekaClient 客户端的启动类在服务启动后自动向注册中心注册服务
public class DeptProvider_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProvider_8001.class,args);
}
//增加一个 Servlet
@Bean
public ServletRegistrationBean hystrixMetricsStreamServlet(){
ServletRegistrationBean registrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet());
//访问该页面就是监控页面
registrationBean.addUrlMappings("/actuator/hystrix.stream");
return registrationBean;
}
}
- 访问http://localhost:9001/hystrix 进入监控页面
八、Zuul路由网关
- 概述
Zuul包含了对请求的路由和过滤两个最主要的功能
其中路由功能负责将外部请求转发到具体的微服务实例上是实现外部访问同一入口的基础而过滤器功能则负责对请求的处理过程进行干预是实现请求校验服务聚合等功能的基础。Zuul和Eureka进行整合将Zuul自身注册为Eureka服务治理下的应用同时从Eureka中获得其他微服务的信息也即以后的访问微服务都是通过Zuul跳转后获得。
注意Zuul服务最终还是会注册金Eureka
提供代理+路由+过滤 三大功能
- Zuul能干啥
- 路由
- 过滤
官方文档https://github.com/Netflix/zuul/
- 入门案例
- 新建springcloud-zuul模块并导入依赖
<dependencies>
<!-- zull-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<!-- 实体类+web-->
<dependency>
<groupId>org.example</groupId>
<artifactId>springcloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</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-ribbon</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<!-- eureka-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
</dependencies>
- 配置文件
server:
port: 9527
spring:
application:
name: springcloud-zuul #微服务名称
# eureka 注册中心配置
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
instance: #实例的id
instance-id: zuul9527.com
prefer-ip-address: true # 显示ip
info:
app.name: haust.springcloud # 项目名称
company.name: 河南科技大学西苑校区 # 公司名称
# zull 路由网关配置
zuul:
# 路由相关配置
# 原来访问路由 eg:http://www.cspStudy.com:9527/springcloud-provider-dept/dept/get/1
# zull路由配置后访问路由 eg:http://www.cspstudy.com:9527/haust/mydept/dept/get/1
routes:
mydept.serviceId: springcloud-provider-dept # eureka注册中心的服务提供方路由名称
mydept.path: /mydept/** # 将eureka注册中心的服务提供方路由名称 改为自定义路由名称
# 不能再使用这个路径访问了* 忽略,隐藏全部的服务名称~
ignored-services: "*"
# 设置公共的前缀
prefix: /haust
- 主启动类
package com.qian.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication_9527 {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication_9527.class,args);
}
}
- 运行测试。
没有经过Zull路由网关配置时服务接口访问的路由可以看出直接用微服务(服务提供方)名称去访问这样不安全不能将微服务名称暴露而加上zuul网关后我们看到微服务名称被替换并隐藏换成了我们自定义的微服务名称mydept同时加上了前缀haust这样就做到了对路由fan访问的加密处理
九、SpringCloud config分布式配置
9.1、概述
- 分布式系统面临的–配置文件问题
微服务意味着要将单体应用中的业务拆分成一个个子服务每个服务的粒度相对较小因此系统中会出现大量的服务由于每个服务都需要必要的配置信息才能运行所以一套集中式的动态的配置管理设施是必不可少的。spring cloud提供了configServer来解决这个问题我们每一个微服务自己带着一个application.yml那上百个的配置文件修改起来令人头疼
- 什么是SpringCloud config分布式配置中心
- spring cloud config 为微服务架构中的微服务提供集中化的外部支持配置服务器为各个不同微服务应用的所有环节提供了一个中心化的外部配置。
- spring cloud config 分为服务端和客户端两部分。
- 服务端也称为 分布式配置中心它是一个独立的微服务应用用来连接配置服务器并为客户端提供获取配置信息加密解密信息等访问接口。
- 客户端则是通过指定的配置中心来管理应用资源以及与业务相关的配置内容并在启动的时候从配置中心获取和加载配置信息。配置服务器默认采用git来存储配置信息这样就有助于对环境配置进行版本管理。并且可用通过git客户端工具来方便的管理和访问配置内容。
- spring cloud config 分布式配置中心能干嘛
- 集中式管理配置文件
- 不同环境不同配置动态化的配置更新分环境部署比如 /dev /test /prod /beta /release
- 运行期间动态调整配置不再需要在每个服务部署的机器上编写配置文件服务会向配置中心统一拉取配置自己的信息
- 当配置发生变动时服务不需要重启即可感知到配置的变化并应用新的配置
- 将配置信息以REST接口的形式暴露
- spring cloud config 分布式配置中心与GitHub整合
由于spring cloud config 默认使用git来存储配置文件 (也有其他方式比如自持SVN 和本地文件)但是最推荐的还是git 而且使用的是 http / https 访问的形式。
git add . 添加文件
git status 查看文件状态
git commit -m “提交信息” 提交
git push origin master push到远程仓库
9.2、Config服务端连接Git配置
-
创建新模块 springcloud-config-server-3344
-
导入依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
</dependencies>
- 编写配置文件
server:
port: 3344
spring:
application:
name: springcloud-config-server
cloud:
config:
server:
git:
url: https://gitee.com/pluto8/git-study.git #注意是https不是ssh
eureka:
client:
register-with-eureka: false
fetch-registry: false
- 主启动类开启
package com.qian.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
@SpringBootApplication
@EnableConfigServer //开启
public class Config_Server_3344 {
public static void main(String[] args) {
SpringApplication.run(Config_Server_3344.class,args);
}
}
- 测试
9.3、Config客户端连接服务端访问远程
- 新建一个springcloud-config-client-3355模块并导入依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
<version>3.1.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
- 写配置
bootstrap.yml
# 系统级别的配置
spring:
cloud:
config:
name: config-client # 需要从git上读取的资源名称不要后缀
profile: dev
label: master
uri: http://localhost:3344
application.yml
# 用户级别的配置
spring:
application:
name: springcloud-config-client
- ConfigClientController.java
package com.qian.springcloud.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ConfigClientController {
@Value("${spring.application.name}")
private String applicationName; //获取微服务名称
@Value("${eureka.client.service-url.defaultZone}")
private String eurekaServer; //获取eureka服务
@Value("${server.port}")
private String port; //获取服务端的端口号
@RequestMapping("/config")
public String getConfig(){
return "applicationName:"+applicationName +
"eurekaServer:"+eurekaServer +
"port:"+port;
}
}
- 主启动类
package com.qian.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ConfigClient_3355 {
public static void main(String[] args) {
SpringApplication.run(ConfigClient_3355.class,args);
}
}
测试
启动服务端Config_server_3344 再启动客户端ConfigClient
访问http://localhost:8201/config/