1、简介
springcloud 是微服务开发组件集合。
微服务:mirco-service
小服务,一般情况把整个项目划分多个小模块,针对每个小模块建立独立的工程,有独立的数据库,能够提供独立的服务
微服务开发过程遇到最基本的问题:
1、服务发现与注册问题 【Eureka、 Consul、 Nacos】
2、服务调用问题 【Ribbon 、 Feign】
3、服务集中配置问题 【SpringCloud config 结合 git】
4、服务保护问题 【Hystrix】
5、服务的网关问题 【Zuul SpringCloud gateway】
初识springcloud
1、springcloud : 整合市场所有知名企业微服务框架,汇聚在一起!定位集合。
2、版本问题:SpringCloud底层需要依赖springboot构建因此,注意本次:Greenwich ---> 要求springboot版本 2.1.X
3、注意:一般微服务开发:
1、基于apache dubbo
2、基于springcloud
3、基于k8s (技术难度大,运维复杂,绑定,国内目前不太流行)
4、dubbo和springcloud 区别:
性能:dubbo优于springcloud
底层通信协议:(dubbo RPC 、 springcloud HTTP)
dubbo是基于更为底层通信协议,封包和拆包的效率高
springcloud基于应用层通信协议,封包和拆包效率低
功能:springcloud功能更加完善
2、服务注册中心组件
组件: 能够单独完成某一项功能的一个软件或者框架。
两项最基本功能:
服务注册
服务发现
1.1、eureka组件
基本架构: C/S 架构 (client 和 server) Java中很多c/s 依赖方式。
1.1.1 创建一个服务提供方工程 (一个微服务)
提供根据商品id查询商品功能 --- 接口:
①接口Java程序员的眼中interface关键字,
②前端或者测试眼中接口 http://localhost:8080/goods/1
第一步:引入依赖
<dependencies>
<!--spring boot web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--spring cloud client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
第二步: 编写配置文件 application.yml
server:
port: 8000
eureka:
instance:
hostname: localhost #主机名
prefer-ip-address: true #使用IP注册到eureka
ip-address: 127.0.0.1 #设置当前的实例IP
instance-id: ${eureka.instance.ip-address}:${spring.application.name}:${server.port} #eureka控制台中显示的实例ID
lease-renewal-interval-in-seconds: 3 # 每隔3秒发一次心跳包,默认30秒
lease-expiration-duration-in-seconds: 9 # 如果9秒内没有发心跳包,就会被服务器干掉,默认90秒
client:
service-url:
defaultZone: http://localhost:8761/eureka #eureka服务端地址,将来客户端使用该地址和eureka进行通信
spring:
application:
name: eureka-provider #设置当前应用的名称:
# 1.将来会在eureka管理平台的Application中显示
# 2.将来需要使用该名称来获取路径
第三步: 编写相关逻辑代码
//引导类
package com.itheima.provider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient //开启eureka客户端
public class ProviderApp {
public static void main(String[] args) {
SpringApplication.run(ProviderApp.class,args);
}
}
//实体类
public class Goods {
private int id;
private String title;//商品标题
private double price;//商品价格
private int count;//商品库存
public Goods() {
}
public Goods(int id, String title, double price, int count) {
this.id = id;
this.title = title;
this.price = price;
this.count = count;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
}
//dao层
package com.itheima.provider.dao;
import com.itheima.provider.domain.Goods;
import org.springframework.stereotype.Repository;
@Repository
public class GoodsDao {
public Goods fingById(int id){
return new Goods(1,"华为手机",399,10000);
}
}
//service层
package com.itheima.provider.service;
import com.itheima.provider.dao.GoodsDao;
import com.itheima.provider.domain.Goods;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class GoodsService {
@Autowired
private GoodsDao goodsDao;
public Goods findOne(int id){
return goodsDao.fingById(id);
}
}
//controller层
package com.itheima.provider.contrller;
import com.itheima.provider.domain.Goods;
import com.itheima.provider.service.GoodsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/goods")
public class GoodsController {
@Autowired
private GoodsService goodsService;
@GetMapping("/findOne/{id}")
public Goods findOne(@PathVariable("id") int id){
Goods goods = goodsService.findOne(id);
return goods;
}
}
1.1.2 创建一个消费者工程(一个微服务),只需要调用 生产者工程 提供好的这个接口!
第一步:导入依赖同生产者
第二步:编写配置文件
server:
port: 9000
eureka:
instance:
hostname: localhost #主机名
client:
service-url:
defaultZone: http://localhost:8761/eureka #eureka服务端地址,将来客户端使用该地址和eureka进行通信
spring:
application:
name: eureka-consume
//引导类
package com.itheima.consumer;
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;
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient # 开启
public class ConsumerApp {
public static void main(String[] args) {
SpringApplication.run(ConsumerApp.class,args);
}
}
第三步:实体类与生产者一致
第四步: 编写 RestTemplate 配置类
服务调用 : RestTemplate 底部封装HTTP
package com.itheima.consumer.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
第五步: 编写控制层
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/goods/{id}")
public Goods findGoodsById(@PathVariable("id") int id){
//根据服务名称获取服务路径集合
List<ServiceInstance> instances = discoveryClient.getInstances("EUREKA-PROVIDER");
//对路径做作健壮性判断
if (instances == null || instances.size() ==0){
return null;
}
//因为当下只有一个,所以索引为0 直接获取路径
ServiceInstance instance = instances.get(0);
URI uri = instance.getUri();
//远程调用Goods服务中的 findOne() 接口
/**
* 1. 定义Bean restTemplate
* 2. 注入Bean
* 3. 使用方法
*/
String url = uri+"/goods/findOne/"+id;
Goods goods = restTemplate.getForObject(url, Goods.class);
return goods;
}
}
1.1.3 创建eureka服务端:
第一步: 导入依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
第二步: 配置文件 application.yam
server:
port: 8761
eureka:
instance:
hostname: localhost #主机名
client:
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka #eureka服务端地址,将来客户端使用该地址和eureka进行通信
register-with-eureka: false #默认为true,是否将自己的地址注册到eureka上 集群时需要 provider client也需要
fetch-registry: false #是否需要从eureka中抓取路径, consumer client 需要
server:
enable-self-preservation: false # 关闭自我保护机制
eviction-interval-timer-in-ms: 3000 # 检查服务的时间间隔位3秒,默认60秒
第三步: 引导类
package com.itheima;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class EurekaApp {
public static void main(String[] args) {
SpringApplication.run(EurekaApp.class,args);
}
}
eureka配置分为4个部分:
1. dashboard : eureka的web控制台配置
2. server: eureka 的服务端配置
3. client: eureka的客户端配置
4. instance: eureka的实例配置
server中自我保护机制,默认就是开启的 (了解)
eureka:
instance:
lease-renewal-interval-in-seconds: 3 # 每隔3秒发一次心跳包,默认30秒
lease-expiration-duration-in-seconds: 9 # 如果9秒内没有发心跳包,就会被服务器干掉,默认90秒
server:
enable-self-preservation: false # 关闭自我保护机制
eviction-interval-timer-in-ms: 3000 # 检查服务的时间间隔位3秒,默认60秒
1.1.4搭建server服务集群:
①在 C:\Windows\System32\drivers\etc 中有一个hosts文件,
需要分别绑定集群中成员的主机名与IP: 127.0.0.1 eureka-server1
②下面以其中一个配置文件为例: application.yam
server:
port: 8761
eureka:
instance:
hostname: eureka-server1 #主机名
client:
service-url:
defaultZone: http://eureka-server2:8762/eureka #集群这里是相互搭建
register-with-eureka: true #默认为true,是否将自己的地址注册到eureka上
fetch-registry: true #是否需要从eureka中抓取路径
spring:
application:
name: eureka-server-ha #这里同一集群内的每个name最好一致
高可用
1.准备两个Eureka Server
2.分别进行配置,相互注册
3.Eureka Client分别注册到这两个Eureka Server中
1.2、consul组件
- Consul是由HashiCorp基于Go语言开发的, 支持多数据中心, 分布式高可用的服务发布和注册服务软件。
- 用于实现分布式系统的服务发现与配置。
- 使用起来也较为简单。具有天然可移植性(支持Linux、windows和MacOSX) ; 安装包仅包含一个可执行文件,方便部署。
官网地址: https://www.consul.io
开启方式:
1. 直接解压到E盘的software文件夹,按住shift+右键,选择 在此处打开Powersholl窗口
2. 输入开发模式命令: .\consul agent -dev (.\consul可以不加尾缀.exe,也可以加上。-dev开发者模式不会持久化任何数据)
3. 管理控制台地址: http://localhost:8500/ui/dc1/services
1.搭建Provider和Consumer服务。
2.使用RestTemplate完成远程调用。
3.将Provider服务注册到Consul中。
4.Consumer服务通过从Consul中抓取Provider地址完成远程调用
1.2.1 服务提供方
需要依赖 pom.xml
<dependencies>
<!--consul 客户端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</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>
<scope>test</scope>
</dependency>
<!--服务监控用-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
配置文件 application.yml
server:
port: 8000
spring:
cloud:
consul:
host: localhost # consul 服务端的 ip
port: 8500 # consul 服务端的端口 默认8500
discovery:
service-name: ${spring.application.name} # 当前应用注册到consul的名称
prefer-ip-address: true # 注册ip
application:
name: consul-provider # 应用名称
1.2.2 服务消费方
这里注意下开启 @EnableDiscoveryClient : 可以获取服务信息
@SpringBootApplication
@EnableDiscoveryClient
public class ConsulConsumerApp {
public static void main(String[] args) {
SpringApplication.run(ConsulConsumerApp.class,args);
}
}
1.3、nacos组件
- Nacos(Dynamic Naming and Configuration Service) 是阿里巴巴2018年7月开源的项目。
- 它专注于服务发现和配置管理领域致力于帮助您发现、配置和管理微服务。Na cos支持几乎所有主流类型的“服务”的发现、配置和管理。
- 一句话概括就是Nacos=SpringCloud注册中心+SpringCloud配置中心。
官网:https://nacos.io/
下载地址:https://github.com/alibaba/nacos/releases
本地起来后访问网址: http://localhost:8848/nacos
用户名: nacos 密码: nacos
开启nacos服务: E:\software\nacos\bin startup.cmd
1.3.1 需要依赖
<dependencies>
<!--nacos-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>0.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>1.1.0</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-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies><dependencies>
<!--nacos-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>0.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>1.1.0</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-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
配置文件
server:
port: 8000
spring:
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 # 配置nacos 服务端地址
application:
name: nacos-provider # 服务名称
3、Ribbon客户端负载均衡
Ribbon是Netflix提供的一个基于HTTP和TCP的 客户端
负载均衡工具。
Ribbon主要有两个功能:1.简化远程调用 2.负载均衡
服务端负载均衡
- 负载均衡算法在服务端
- 由负载均衡器维护服务地址列表
客户端负载均衡
负载均衡算法在客户端
客户端维护服务地址列表
@Configuration
public class RestTemplateConfig{
@LoadBalance
@Bean
public RestTemplate restTemplate() { return new RestTemplate() ;}
}
/**
*使用Ribbon简化rest Template调用
*1.在声明rest TempLate的Bean时候, 添加一个注解:@LoadBalanced
*2.在使用rest TempLate发起请求时, 需要定义url时,host:port可以替换为, 服务提供方的应用名称
*@param id
*@return
*/
@Get Mapping(“/goods 2/{id} “)
public Goods findGoodsById2(@PathVariable(“id") int id) {
String url=“http://EUREKA-PROVIDER/goods/findOne/“+id;
//3.调用方法
Goods goods = restTemplate.getForObject(url, Goods.class) ;
return goods;
}
服务调用:
① RestTemplate 发送请求
② DiscoveryClient 从eureka中获取服务ip和端口
③ Ribbon 负载均衡 按照规则选取一个来调用
Ribbon负责均衡策略:
随机:RandomRule
轮询:RoundRobinRule
最小并发:BestAvailableRule
过滤:AvailabilityFilteringRule
响应时间:WeightedResponseTimeRule
轮询重试:RetryRule
性能可用性:ZoneAvoidanceRule
配置方式:①客户端代码添加规则 ②配置增加规则
代码方式:
package com.i theima.consumer.config;
import com.netflix.loadbalancer.I Rule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyRule{
@Bean
public IRule rule() {
return new RandomRule();
}
}
@EnableDiscoveryclient //激活DiscoveryClient
@EnableEurekaClient
@SpringBootApplication
/*
配置Ribbon的负载均衡策略 name
*name:设置服务提供方的 应用名称
*configuration:设置负载均衡 Bean
*/
@RibbonClient(name=“EUREKA-PROVIDER", configuration=My Rule.class)
public class ConsumerApp {
public static void main(String[]args) {
Spring Application.run(ConsumerApp.class, args);
}
}
配置方式(在客户端配置)
#配置的方式设置Ribbon的负载均衡策略
EUREKA-PROVIDER: #设置的服务提供方的应用名称
ribbon:
NFloadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #策略类
4、Feign (粪) 简化 服务调用 【项目中会使用】
Feign是一个声明式的REST客户端, 它用了基于接口的注解方式, 很方便实现客户端配置。
Feign最初由Netflix公司提供, 但不支持SpringMVC注解, 后由SpringCloud对其封装, 支持了SpringMVC注解,让使用者更易于接受。
1.依赖 --- 消费端添加
<!--feign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2.定义一个接口
接口上添加注解 @FeignClient,设置value属性为 服务提供者的 应用名称
里面的方法返回值and参数,需要与生产者的方法保持一致
映射路径需要在原方法的基础上添加 /goods
@FeignClient(value = "feign-provider")
public interface GoodsFeignClient {
@GetMapping("/goods/findOne/{id}")
public Goods findGoodsById(@PathVariable("id")int id);
}
3.添加注解
@EnableEurekaClient
@SpringBootApplication
@EnableFeignClients //开启Feign的功能
public class ConsumerApp {
public static void main(String[] args) {
SpringApplication.run(ConsumerApp.class,args);
}
}
4.超时间设置:
在消费者配置文件中配置(feign底层使用的ribbon 我们一般设置ribbon超时时间即可)
# 设置Ribbon的超时时间
ribbon:
ConnectTimeout: 1000 # 连接超时时间 默认1s
ReadTimeout: 1000 # 逻辑处理的超时时间 默认1s
5.feign 的日志问题:
在消费者配置文件中配置
# 设置当前的日志级别 debug,feign只支持记录debug级别的日志
logging:
level:
com.itheima: debug
5、Hystrix 服务熔断器(保险丝)
豪猪(身上带刺的猪,刺保护自己)
A--->B--->C 服务级联调用,过程中很容产生服务雪崩效应,我们Hystrix组件解决问这个问题。
Hystrix是Netflix开源的一个延迟和容错库, 用于隔离访问远程服务、第三方库,防止出现级联失败(雪崩)
雪崩:一个服务失败,导致整条链路的服务都失败的情形
Hystrix主要功能
隔离
降级
熔断
限流
1.隔离(默认配置)
1.线程池隔离
2.信号量隔离
2.降级
当服务发生异常或调用超时,返回默认数据
触发降级的场景: ①异常 ②超时
2.1 服务提供方降级
1.在服务提供方, 引入hystrix依赖
2.定义降级方法
3.使用@HystrixCommand注解配置降级方法
4.在启动类上开启Hystrix功能:@EnableCircuitBreaker
① 引入hystrix依赖
<!-- hystrix -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
②注解: 引导类 @EnableCircuitBreaker , controller 方法上要加入@HystrixCommand(fallbackmethod=“降级方法名称”)
@EnableEurekaClient //该注解 在新版本中可以省略
@SpringBootApplication
@EnableCircuitBreaker //开启熔断器hystrix
public class ProviderApp {
public static void main(String[] args) {
SpringApplication.run(ProviderApp.class,args);
}
}
③编写降级方法 --- 这里的方法是生产者controller层提供服务的方法上
@GetMapping("/findOne/{id}")
@HystrixCommand(fallbackMethod = "findOne_fallback", commandProperties = {//指代触发降级后运行的方法
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds", value="3000")//超时
})
public Goods findOne(@PathVariable("id") int id){
Goods goods = goodsService.findOne(id);
//当前线程睡2秒
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
goods.setTitle(goods.getTitle() + ":" + port);//将端口号,设置到了 商品标题上
return goods;
}
/**
* 定义降级方法
* 1. 方法的参数和返回值需与原方法保持一致
*/
public Goods findOne_fallback(@PathVariable("id") int id){
Goods goods = new Goods();
goods.setTitle("降级了...");
return goods;
}
2.2 服务消费方降级
1.feign组件已经集成了hystrix组件。
2.定义feign调用接口实现类, 复写方法, 即降级方法
3.在@FeignClient注解中使用fallback属性设置降级处理类。
4.配置开启feign.hystrix.enabled=true
①配置文件中开熔断
#开启feign对hystrix的支持
feign:
hystrix:
enabled: true
#开启feign对hystrix的支持
feign:
httpclient:
enabled: true
② 写一个降级的类 a.实现feign客户端接口(自己定义远程调用的接口) b.加入springIOc容器
package com.itheima.consumer.feign;
import com.itheima.consumer.domain.Goods;
import org.springframework.stereotype.Component;
/**
* Feign客户端降级处理
* 1. 定义类 实现Feign接口
* 2. 使用@Component注解,将改类加入springmvc
* 3.
*/
@Component
public class GoodsFeignClientFallback implements GoodsFeignClient {
@Override
public Goods findGoodsById(int id) {
Goods goods = new Goods();
goods.setTitle("又降级了...");
return goods;
}
}
2.3 服务提供方 和消费方同时都提供 降级处理的时候,谁生效?
服务提供方的生效
3.熔断
Hystrix熔断机制, 用于监控微服务调用情况, 当失败的情况达到预定的阈值(5秒失败20次),会打开断路器,拒绝所有请求,直到服务恢复正常为止。
circuitBreaker.sleepWindowInMiliseconds:监控时间默认5000毫秒
circuitBreaker.requestVolumeThreshold:失败次数默认20次
circuitBreaker.errorThresholdPercentage:失败率默认50%
只要我们引入依赖默认的配置不需要你去处理!
feign 底层 对 hystrix 进行了封装
<!-- hystrix 如果有了feign就不需要了 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<!--feign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
#开启feign对hystrix的支持
feign:
httpclient:
enabled: true
熔断器的状态:
关闭: 正常提供服务
打开: 不能正常提供服务
半开: 有部分服务请求能够使用 (水龙头半开状态)
面试问题:
熔断器 :状态:3种状态 关闭状态 、半开状态、打开状态 默认是关闭状态,关闭----》打开状态 (条件:当我们的服务降级次数在5秒内超过20次时候)打开---》半开状态 (条件:五秒之以后我会放一部分请求过来)----》关闭状态 (半开状态放过来的请求成功)
熔断器监控
Hystrix提供了Hystrix-dashboard功能, 用于时监控微服务运行状态。
但是Hystrix-dashboard只能监控一个微服务。
Netfix还提供了Turbine, 进行聚合监控。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-turbine</artifactId>
</dependency>
spring:
application.name: hystrix-monitor
server:
port: 8769
turbine:
combine-host-port: true
# 配置需要被监控的服务名称列表
app-config: hystrix-provider, hystrix-consumer
cluster-name-expression: "'default'"
aggregator:
cluster-config: default
# instanceUrlSuffix: /actuator/hystrix.stream
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
创建启动类
@SpringBootApplication
@EnableEurekaclient
@EnableTurbine //开启Turbine很聚合监控功能
@EnableHystrixDashboard //开启Hystrix仪表盘监控功能
public class HystrixMonitorApp{
public static void main(String[] args) {
SpringApplication.run(HystrixMonitorApp.class, args) ;
}
}
修改被监控模块,需要分别修改hystrix-provider和hystrix-consumer模块:
1、导入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
@Bean
public ServletRegistrationBean getServlet(){
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addurlMappings("/actuator/hystrix.stream/);
registrationBean.setName(“HystrixMetricsStreamServlet");
return registrationBean;
}
启动服务后查看监控仪表盘
http://localhost:8769/hystrix/ 浏览器访问
http://localhost:8769/turbine.stream 仪表盘首页填入
4.限流
4.1 rabbitMQ
4.2 nginx
4.3 gateway
6、SpringCloudGateway 网关 (城门)【项目中会使用】
网关 : 提供统一API路由访问的入口
网关就是系统的入口,封装了应用程序的内部结构,为客户端提供统一服务,一些与业务本身功能无关的公共逻辑可以在这里实现,诸如认证、鉴权、监控、缓存、负载均衡、流量管控、路由转发等
在目前的网关解决方案里, 有Nginx+Lua、Netflix Zuul、SpringCloud Gateway等等
操作步骤:
1.搭建网关模块
2.引入依赖: starter-gateway
<!--引入gateway 网关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
3.编写启动类
@SpringBootApplication
// 启用EurekaClient
@EnableEurekaClient
public class EurekaApp {
public static void main(String[] args) {
SpringApplication.run(EurekaApp.class,args);
}
}
4.编写配置文件
server:
port: 80
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka
spring:
application:
name: api-gateway-server
cloud:
# 网关配置
gateway:
# 路由配置:转发规则
routes: #集合。
# id: 唯一标识。默认是一个UUID
# uri: 转发路径
# predicates: 条件,用于请求网关路径的匹配规则
# filters:配置局部过滤器的
- id: gateway-provider
# 静态路由
# uri: http://localhost:8001/
# 动态路由 lb : load baldance 负载均衡
uri: lb://GATEWAY-PROVIDER
predicates:
- Path=/goods/**
filters: #局部过滤器
- AddRequestParameter=username,zhangsan
- id: gateway-consumer
# uri: http://localhost:9000
uri: lb://GATEWAY-CONSUMER
predicates:
- Path=/order/**
# 微服务名称配置----这里是将请求路径前加项目名称,一般不用
discovery:
locator:
enabled: true # 设置为true 请求路径前可以添加微服务名称
lower-case-service-id: true # 允许为小写
7、Gateway 网关中的过滤器
内置过滤器工厂: https://www.jianshu.com/p/58267466251e
局部过滤器
以添加请求参数为列:
1.在网关模块的yam配置文件上添加配置
cloud:
# 网关配置
gateway:
# 路由配置:转发规则
routes: #集合。
# filters:配置局部过滤器的
- id: gateway-provider
# 动态路由
uri: lb://GATEWAY-PROVIDER
predicates:
- Path=/goods/**
filters: #局部过滤器,这里只仅仅给goods路径下的请求添加参数
- AddRequestParameter=username,zhangsan
- id: gateway-consumer
# uri: http://localhost:9000
uri: lb://GATEWAY-CONSUMER
predicates:
- Path=/order/**
# 微服务名称配置
2.给服务提供者的方法上添加 username 参数
@GetMapping("/findOne/{id}")
@HystrixCommand(fallbackMethod = "findOne_fallback",commandProperties = {
//设置Hystrix的超时时间,默认1s
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "3000"),
//监控时间 默认5000 毫秒
@HystrixProperty(name="circuitBreaker.sleepWindowInMilliseconds",value = "5000"),
//失败次数。默认20次
@HystrixProperty(name="circuitBreaker.requestVolumeThreshold",value = "20"),
//失败率 默认50%
@HystrixProperty(name="circuitBreaker.errorThresholdPercentage",value = "50")
})
public Goods findOne(@PathVariable("id") int id,String username){
System.out.println(username);
//如果id == 1 ,则出现异常,id != 1 则正常访问
if(id == 1){
//1.造个异常
int i = 3/0;
}
/*try {
//2. 休眠2秒
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
Goods goods = goodsService.findOne(id);
goods.setTitle(goods.getTitle() + ":" + port);//将端口号,设置到了 商品标题上
return goods;
}
/**
* 定义降级方法:
* 1. 方法的返回值需要和原方法一样
* 2. 方法的参数需要和原方法一样
*/
public Goods findOne_fallback(int id,String username){
Goods goods = new Goods();
goods.setTitle("降级了~~~");
return goods;
}
全局过滤器
自定义全局过滤器步骤:
1.定义类实现 GlobalFilter 和 Ordered 接口
2.复写方法
3.完成逻辑处理
public class MyFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("自定义全局过滤器执行了......");
return chain.filter(exchange);//放行当前过滤器,执行下个过滤器
}
/**
* 过滤器排序
* 数值越小越先执行
* @return
*/
public int getOrder() {
return 0;
}
}
8、微服务配置中心---Config
优点:
- 集中管理配置文件
- 不同环境不同配置,动态化配置更新
- 配置信息改变时,不需要重启即可更新配置信息到服务
config-server模块:
1.使用gitee创建远程仓库,上传配置文件
2.搭建config server 模块
@SpringBootApplication
@EnableConfigServer // 启用config server功能
public class ConfigServerApp {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApp.class,args);
}
}
3.导入config-server依赖
<!-- config-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
4.编写配置,设置gitee远程仓库地址
server:
port: 9527
spring:
application:
name: config-server
# spring cloud config
cloud:
config:
server:
# git 的 远程仓库地址
git:
uri: https://gitee.com/itheima_cch/itheima-configs.git
label: master # 分支配置
5.测试访问远程配置文件
config-client模块:
1.导入 starter-config 依赖
<!--config client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
2.配置 config server 地址,读取配置文件名称等信息
bootstrap.yml --- 优先级更高,一般用来系统化的配置
# 配置config-server地址
# 配置获得配置文件的名称等信息
spring:
cloud:
config:
# 配置config-server地址
uri: http://localhost:9527
# 配置获得配置文件的名称等信息
name: config # 文件名
profile: dev # profile指定, config-dev.yml
label: master # 分支
3.获取配置值
Config客户端刷新
1.在config客户端引入actuator 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
2.获取配置信息类上,添加 @RefreshScope 注解
@RestController
@RequestMapping("/goods")
@RefreshScope //开启刷新功能
public class GoodsController {
@Autowired
private GoodsService goodsService;
@Value("${server.port}")
private int port;
@Value("${itheima}")
private String itheima;
/**
* 降级:
* 1. 出现异常
* 2. 服务调用超时
* * 默认1s超时
*
* @HystrixCommand(fallbackMethod = "findOne_fallback")
* fallbackMethod:指定降级后调用的方法名称
*/
@GetMapping("/findOne/{id}")
@HystrixCommand(fallbackMethod = "findOne_fallback",commandProperties = {
//设置Hystrix的超时时间,默认1s
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "3000"),
//监控时间 默认5000 毫秒
@HystrixProperty(name="circuitBreaker.sleepWindowInMilliseconds",value = "5000"),
//失败次数。默认20次
@HystrixProperty(name="circuitBreaker.requestVolumeThreshold",value = "20"),
//失败率 默认50%
@HystrixProperty(name="circuitBreaker.errorThresholdPercentage",value = "50")
})
public Goods findOne(@PathVariable("id") int id){
//如果id == 1 ,则出现异常,id != 1 则正常访问
if(id == 1){
//1.造个异常
int i = 3/0;
}
/*try {
//2. 休眠2秒
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
Goods goods = goodsService.findOne(id);
goods.setTitle(goods.getTitle() + ":" + port+":"+itheima);//将端口号,设置到了 商品标题上
return goods;
}
/**
* 定义降级方法:
* 1. 方法的返回值需要和原方法一样
* 2. 方法的参数需要和原方法一样
*/
public Goods findOne_fallback(int id){
Goods goods = new Goods();
goods.setTitle("降级了~~~");
return goods;
}
}
3.添加配置
management:
endpoints:
web:
exposure:
include: '*'
4.使用curl工具在小黑窗口发送post请求
curl -X POST http://localhost:8001/actuator/refresh
config---集成eureka
在config-server模块配置eureka客户端依赖
<!-- eureka-client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
添加配置信息
# 将自己注册到eureka中
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka
9、Bus 消息总线
1.分别在config-server和config-client中引入 bus依赖: bus-amqp
2.分别在config-server和config-client中配置RabbitMQ
3.在config-server中设置暴露监控断点: bus-refresh
4.启动测试
不需要编写代码,只需要做配置:
https://cloud.spring.io/spring-cloud-bus/2.2.x/reference/html/appendix.html
1.分别再 config-server 和 config-client 中引入bus依赖: bus-amqp
<!--导入bus依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
<!-- 暴露断点 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
2.分别在 config-server 和 config-client 中配置 RabbitMQ
3.在config-server 中设置暴露监控断点: bus-refresh
# 配置Rabbitmq,将配置模块注册到eureka中
rabbitmq:
host: localhost
post: 5672
username: guest
password: guest
virtual-host: /
# 暴露bus的刷新断点
management:
endpoints:
web:
exposure:
include: 'bus-refresh'
4.测试
还是要通知1次config_server,config_server通过bus消息总线通知各个微服务配置更新
在小黑窗口中输入: curl -X POST http://localhost:9527/actuator/bus-refresh
10、Stream消息驱动
- Spring Cloud Stream是一个构建消息驱动微服务应用的框架。
- Stream解决了开发人员无感知的使用消息中间件的问题, 因为Stream对消息中间件的进一步封装, 可以做到代码层面对中间件的无感知,甚至于动态的切换中间件,使得微服务开发的高度解耦,服务可以关注更多自己的业务流程。
- Spring Cloud Stream目前支持两种消息中间件Rabbit MQ和Kafka
- Spring Cloud Stream构建的应用程序与消息中间件之间是通过绑定器Binder相关联的。绑定器对于应用程序而言起到了隔离作用,它使得不同消息中间件的实现细节对应用程序来说是透明的。
- binding是我们通过配置把应用和spring cloud stream的binder绑定在一起
- output:发送消息Channel, 内置Source接口
- input:接收消息Channel, 内置Sink接口
1.创建消息生产者模块
1.1引入依赖 starter-stream-rabbit
<!-- stream -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
1.2编写配置,定义binder和bingings
server:
port: 8000
spring:
cloud:
stream:
# 定义绑定器,绑定到哪个消息中间件上
binders:
itheima_binder: # 自定义的绑定器名称
type: rabbit # 绑定器类型
environment: # 指定mq的环境
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
virtual-host: /
bindings:
output: # channel名称
binder: itheima_binder #指定使用哪一个binder
destination: itheima_exchange # 消息目的地
1.3.定义消息发送业务类.添加 @EnableBindings(Source.class),注入MessageChannel output , 完成消息发送
@Component
@EnableBinding(Source.class)
public class MessageProducer {
@Autowired
private MessageChannel output;
public void send(){
String msessage = "hello stream~~~";
//发送消息
output.send(MessageBuilder.withPayload(msessage).build());
System.out.println("消息发送成功~~~");
}
}
@RestController
public class ProducerController {
@Autowired
private MessageProducer producer;
@RequestMapping("/send")
public String sendMsg(){
producer.send();
return "success";
}
}
4.编写启动类,测试
2.创建消息消费者模块
2.1创建消息消费者模块,引入依赖 starter-stream-rabbit
同生产者
2.2编写配置,定义binder和bingings
server:
port: 9000
spring:
cloud:
stream:
# 定义绑定器,绑定到哪个消息中间件上
binders:
itheima_binder: # 自定义的绑定器名称
type: rabbit # 绑定器类型
environment: # 指定mq的环境
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
virtual-host: /
bindings:
input: # channel名称 <-----------------------------------这里特别注意channel名称
binder: itheima_binder #指定使用哪一个binder
destination: itheima_exchange # 消息目的地
2.3定义消息接收业务类,添加 @EnableBinding(Sink.class) ,使用 @StreamListener(Sink.INPUT) ,完成消息接收
/**
* 消息接收类
*/
@EnableBinding({Sink.class})
@Component
public class MessageListener {
@StreamListener(Sink.INPUT)
public void receive(Message message){
System.out.println(message);
System.out.println(message.getPayload());
}
}
2.4编写启动类测试
11、Sleuth+Zipkin链路追踪
Spring Cloud Sleuth 其实是一个工具,它在整个分布式系统中能跟踪一个用户请求的过程,捕获这些跟踪数
据,就能构建微服务的整个调用链的视图,这是调试和监控微服务的关键工具。
• 耗时分析
• 可视化错误
• 链路优化
Zipkin 是 Twitter 的一个开源项目,它致力于收集服务的定时数据,以解决微服务架构中的延迟问题,包括数据的收集、存储、查找和展现。
使用步骤:
安装启动zipkin。 java –jar zipkin.jar
访问zipkin web界面。 http://localhost:9411/
-
在服务提供方和消费方分别引入 sleuth 和 zipkin 依赖
<!-- sleuth-zipkin --> <!--<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-sleuth</artifactId> </dependency>--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zipkin</artifactId> </dependency>
-
分别配置服务提供方和消费方。
server: port: 8001 eureka: client: service-url: defaultZone: http://localhost:8761/eureka spring: application: name: feign-provider zipkin: base-url: http://localhost:9411/ # 设置zipkin的服务端路径 sleuth: sampler: probability: 1 # 采集率 默认 0.1 百分之十。
启动,测试