SpringCloud五大组件
注意:yml中微服务名使用(-)划线
所有的操作都是在消费方
服务提供方只负责暴露接口
SpringCloud体系都是短连接基于Http基于SpringBoot
1.Eureka注册中心
注解:
服务器:
@EnableEurekaServer // 声明这个应用是一个EurekaServer
客户端:
@EnableDiscoveryClient //声明Eureka客户端 (官方推荐使用它)支持zookeeper注册中心
@EnableEurekaClient //声明Eureka客户端 意义都是一样的
配置:
服务器:
server:
port: 10086 #服务端口号
spring:
application:
name: eureka-server #应用名称
eureka:
client:
service-url:
defaultZone: http://localhost:10086/eureka //看源码请点defaultZone 55行
fetch-registry: false #因为是单台不抓取服务 默认是true
register-with-eureka: false #因为是单台不需要互相注册 开发中是多台Eureka
客户端:每一个zuul或者微服务都要注册到Eureka上
server:
port: 8080 #服务端口号
spring:
application:
name: order-server #应用名称
eureka:
client:
service-url:
defaultZone: http://localhost:10086/eureka #指定注册中心的ip地址
1.2高可用的Eureka Server
Eureka 搭建集群 10086 10087
高可用注册中心:其实就是把EurekaServer自己也作为一个服务
,注册到其它EurekaServer上,这样多个EurekaServer之间就能互相发现对方,从而形成集群
修改自己原来的Eureka配置
server:
port: 10086 # 端口
spring:
application:
name: eureka-server # 应用名称,会在Eureka中显示
eureka:
client:
service-url: # 配置其他Eureka服务的地址,而不是自己,比如10087
defaultZone: http://127.0.0.1:10087/eureka
客户端注册服务到集群:可以注册一个 可以注册几个上 因为Eureka 是互相抓取的
eureka:
client:
service-url: # EurekaServer地址,多个地址以','隔开
defaultZone: http://127.0.0.1:10086/eureka,http://127.0.0.1:10087/eureka
其他配置参数说明:
默认注册时使用的是主机名,如果我们想用ip进行注册,可以在application.yml添加配置:
eureka:
instance:
ip-address: 127.0.0.1 # ip地址
prefer-ip-address: true # 更倾向于使用ip,而不是host名
instance-id: ${eureka.instance.ip-address}:${server.port} # 自定义实例的id
心跳续约:告诉EurekaServer:“我还活着” 30秒续约一次
获取服务列表:
eureka:
client:
registry-fetch-interval-seconds: 30 //每隔30秒拉取一次
剔除服务:我们可以通过下面的配置来关停自我保护:(在eureka注册中心的微服务中配置)心跳续约的比例是否低于了85%
eureka:
server:
enable-self-preservation: false # 关闭自我保护模式(缺省为打开)
总结:
- 服务的注册和发现都是可控制的,可以关闭也可以开启。默认都是开启
- 注册后需要心跳,
心跳周期默认30秒一次,超过90秒没法认为宕机(失效)
- 服务拉取
默认30秒拉取一次
- Eureka每个
60秒会剔除标记为宕机的服务
- Eureka会有自我保护,当心跳失败比例超过阈值(85%),那么开启自我保护,不再剔除服务。
- Eureka高可用就是多台Eureka互相注册在对方上
2.Ribbon负载均衡
至少有两个微服务 实际开发部署在不同的服务器上
注解:谁发请求给谁加(加消费方) 去掉DiscoveryClient不需要服务发现
@LoadBalanced //打开Ribbon负载均衡功能
SpringBoot也帮我们提供了修改负载均衡规则的配置入口:(消费方)
user-service:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
格式是:{服务名称}.ribbon.NFLoadBalancerRuleClassName
,值就是IRule的实现类。
附加:
Ribbon默认是采用懒加载
,即第一次访问时才会去创建负载均衡客户端。往往会出现超时。如果需要采用饥饿加载,即项目启动即创建,可以这样配置:
ribbon:
eager-load:
enabled: true
clients: user-service
Ribbon默认策略是:轮询(!!!)(还有随机 Hash)
Dubbo重试次数是2次+初始的调用,一共三次 (必须两端都使用java开发)
3.Hystrix熔断器
Hystix(封装了线程池)多线程就是玩线程池
对每一个请求弄了一个线程池 spring整合了一个线程池
两个微服务解耦合 MQ中间件解耦合 MQ存消息做队列的堆积 加速持久化 开多线程(跟CPU性能有关)核心线程数 8核 能确定任务快速完成 使用16核
发起请求通过 Hystrix 的线程池来走的,不同服务走不同线程池,实现不同服务调用的隔离,避免服务雪崩问题 线程隔离:每个请求是独立的线程
熔断机制:生活中保险丝,对请求如果超时直接先返回
两大功能:服务降级(优先保证核心服务,而非核心服务不可用或弱可用),熔断(压力过大,不让访问)
Hystix解决雪崩问题的手段主要是服务降级(服务降级,友好的提示),包括:
- 线程隔离
- 服务熔断
触发Hystix服务降级的情况:
- 线程池已满
- 请求超时
使用流程:入口类上注解,消费方controller上注解指定降级方法,通用降级方法(不指定使用一个)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
注解:(加在消费方入口类上)
@EnableCircuitBreaker //开启熔断
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
Spring就提供了一个组合注解:@SpringCloudApplication 一个顶三个
服务降级注解:消费方controller上
假提示
@HystrixCommand(fallbackMethod = "queryByIdFallBack") //写一个临时方法 直接返回
public String queryByIdFallBack(Long id){
log.error("查询用户信息失败,id:{}", id);
return "对不起,网络太拥挤了!";
}
服务降级方法:要求入参、出参必须和原方法一致!!!
方法多 不能每一个都写 :通用降级方法:默认的Fallback
消费方controller添加注解:@DefaultProperties(defaultFallback = “defaultFallBack”) 方法名自定义
public String defaultFallBack(){
return "默认提示:对不起,网络太拥挤了!";
}
//去掉@HystrixCommand 上的方法 不要指定降级方法 使用通用的
默认超时时间:
Hystrix的默认熔断时间是1秒
熔断有三个状态,默认关闭
,当请求20次默认50%失败后打开熔断,5秒之后进入半开状态放行一次请求,如果请求正常熔断状态为关闭,如果继续异常那么状态继续保持熔断
消费方配置
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 2000
hystrix:
command:
default:
circuitBreaker:
requestVolumeThreshold: 10
sleepWindowInMilliseconds: 10000
errorThresholdPercentage: 50
解读:
- requestVolumeThreshold:触发熔断的最小请求次数,默认10
- sleepWindowInMilliseconds:休眠时长,默认是10000毫秒
- errorThresholdPercentage:触发熔断的失败请求最小占比,默认50%
4.Feign(伪装)远程调用
基于 Feign 的动态代理机制,根据注解和选择的机器,拼接请求 URL 地址,发起请求。并且整合了Hystrix&Ribbon 。
消费方注解:
@EnableFeignClients // 开启Feign功能
Feign客户端
这是一个接口,Feign会通过动态代理,帮我们生成实现
@FeignClient,声明这是一个Feign客户端,同时通过
value属性指定服务名称
@FeignClient("user-service") //微服务的名称
public interface UserClient {
/**
* http://user-service/user/{id}
* 伪装的接口“入参”和“出参"和被调用方的服务要求一致
*/
@GetMapping("/user/{id}")
User findById(@PathVariable("id")Long id);
}
Fegin内置的ribbon默认设置了请求超时时长,默认是1000ms,我们可以通过手动配置来修改这个超时时长:
ribbon:
ReadTimeout: 2000 # 读取超时时长
ConnectTimeout: 1000 # 建立链接的超时时长
Ribbon负载均衡:消费方
ribbon:
ReadTimeout: 2000 # 数据通信超时时长
ConnectTimeout: 500 # 连接超时时长
MaxAutoRetries: 0 # 当前服务器的重试次数
MaxAutoRetriesNextServer: 1 # 重试多少次服务
OkToRetryOnAllOperations: false # 是否对所有的请求方式都重试 如果是false代表只对get请求重试
Feign默认整合了Ribbon和Hystrix,但是Hystrix需要手动打开
消费方
feign:
hystrix:
enabled: true # 开启Feign的熔断功能
请求压缩:
Feign 支持对请求和响应进行GZIP压缩,以减少通信过程中的性能损耗
feign:
compression:
request:
enabled: true # 开启请求压缩
response:
enabled: true # 开启响应压缩
feign:
compression:
request:
enabled: true # 开启请求压缩
mime-types: text/html,application/xml,application/json # 设置压缩的数据类型
min-request-size: 2048 # 设置触发压缩的大小下限
日志级别:消费方
logging:
level:
cn.itcast: debug
5.Zuul网关(入口)
场景非常多:
- 请求鉴权:一般放在pre类型,如果发现没有访问权限,直接就拦截了
- 异常处理:一般会在error类型和post类型过滤器中结合来处理。
- 服务调用时长统计:pre和post结合使用。
基于过滤器:4种
preFilter 前置(自定义过滤器)、routingFilter 路由、 postFilter 后置 、 errorFilter 异常
注解:
@EnableZuulProxy // 开启Zuul的网关功能
实际开发自己写路由规则
Zuul就指定了默认的路由规则:默认情况下,一切服务的映射路径就是服务名本身。
zuul的默认路由规则是 zuul服务器地址 + eureka上微服务名称 + 请求路径及参数
#默认的简化路由规则
zuul:
routes:
user-service: /user-service/** #这里是映射路径 **所有请求
#如果没有eureka,直接调用的话,注意下面是说明,不要复制
zuul:
routes:
user-service: #自定义的路由id
path: /user-service/** #映射的访问名称
url: http://127.0.0.1:8081 #通过ip地址端口号可以不用注册eureka
#将符合`path` 规则的一切请求,都代理到 `url`参数指定的地址
#如果有eureka,则可以直接通过微服务名称调用,不用ip地址端口号
zuul:
routes:
user-service: #自定义的路由id
path: /user-service/** #映射的访问名称
serviceId: user-service #指定服务名称(eureka上的名称)
忽略规则(禁用某个路由规则):放开互相抓取
zuul:
ignored-services: #忽略规则
- eureka-server
自定义过滤器:如果用户请求的参数中没有token,直接返回403异常)
编写自定义过滤器类继承ZuulFilter 重写4个方法
(1).类型是前置过滤器
(2).设置order为0
(3).是否有用到过滤器
(4).过滤器逻辑,判断参数中是否有token
@Component //将自定义过滤器放入spring容器
public class MyFilter extends ZuulFilter {
/**
* 过滤器类型:四种类型pre route post error
* @return
*/
@Override
public String filterType() {
//前置过滤器
return FilterConstants.PRE_TYPE;
}
/**
* 过滤器顺序
* @return
*/
@Override
public int filterOrder() {
//过滤器的顺序是从小到大 -1最小
return 0;
}
//过滤器是否开启过滤功能
@Override
public boolean shouldFilter() {
//如果为false代表过滤器没有用到
return true;
}
//过滤器的主逻辑
@Override
public Object run() throws ZuulException {
/**
* 过滤器逻辑
* 如果用户请求参数没有token,则403
*/
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
//从request的请求的参数中获取内容
String token = (String) request.getParameter("token");
//通过工具类进行非空判断
if (StringUtils.isEmpty(token)) {
ctx.setResponseStatusCode(403);
ctx.setSendZuulResponse(false);//禁止转发
}
return null;
}
}
Zuul高可用:
启动多个Zuul服务,自动注册到Eureka,形成集群。如果是服务内部访问,你访问Zuul,自动负载均衡。来对Zuul进行代理。比如:Nginx
Zuul中默认就已经集成了Ribbon负载均衡和Hystix熔断机制。但是都是默认值
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 6000 #熔断时间要能覆盖ribbon的重试时间
ribbon:
ConnectTimeout: 1000
ReadTimeout: 2000
MaxAutoRetries: 0 #每台服务器最多重试次数,但是首次调用不包括在内
MaxAutoRetriesNextServer: 1 #最多重试多少台服务器
OkToRetryOnAllOperations: false # 是否对所有的请求方式都重试
计算重试次数公式:MaxAutoRetries+MaxAutoRetriesNextServer+(MaxAutoRetries *MaxAutoRetriesNextServer) 从而得知最终会重试1次+起始调用1次=2次的时间
Hystix的超时时间,应该比重试的时间加原本时间的总时间要大,应该配 大于(2000+1000)*2 = 6000
(ribbon.ReadTimeout + ribbon.ConnectTimeout) * (MaxAutoRetries+MaxAutoRetriesNextServer+(MaxAutoRetries *MaxAutoRetriesNextServer) +1)
6.SpringCloudConfig + SpringCloudBus
SpringCloudConfig集中配置组件
SpringCloudConfig目的:将yml文件集中管理
分两个角色,一是config server,二是config client。
Config Server是一个可横向扩展、集中式的配置服务器,它用于集中管理应用程序各个环境下的配置,默认使用Git存储配置文件内容,也可以使用SVN存储,或者是本地文件存储。
Config Client是Config Server的客户端,用于操作存储在Config Server中的配置内容。微服务在启动时会请求Config Server获取配置文件的内容,请求到后再启动容器。
配置服务端
将yml文件放入码云中
,创库,公开,勾选第一个(可以直接上传文件),上传到配置yml配置的命名规则 :微服务名称-dev(开发模式).yml
配置中心:config_server 服务端
pom
<!--config配置中心的坐标-->
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
</dependencies>
yml
spring:
application:
name: config-server
cloud:
config:
server:
git:
uri: https://gitee.com/blueboyhi/itcast03.git #将yml文件的所在git路径复制
server:
port: 12000
入口类
@SpringBootApplication
@EnableConfigServer //打开配置中心功能
public class ConfigApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigApplication.class);
}
}
注解:
@EnableConfigServer //打开配置中心功能
客户端:通过gitter中心获取过来的 随便修改一个user_server的yml文件 修改yml文件名为bootstrap.yml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
</dependency>
spring:
cloud:
config:
name: user #配置文件名 user-dev.yml
profile: dev
label: master #git上分支名称
uri: http://127.0.0.1:12000 #config配置服务器地址
SpringCloudBus 消息总线组件
要是修改了git上的yml配置 1.重启微服务可以重新拉取2.使用SpringCloudBus 消息总件 可以不重启刷新配置文件
config_server加入SpringCloudBus 坐标 前提是:Rabbit开启状态
<!--springcloudbus的坐标-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
yml
spring:
rabbitmq:
host: 127.0.0.1
management: #暴露触发消息总线的地址,访问地址后可以刷新yml配置
endpoints:
web:
exposure:
include: bus-refresh #http://127.0.0.1:12000/actuator/bus-refresh
user_server的yml文件 修改yml文件名为bootstrap.yml 只做了拉取相关配置要到git上的yml文件中进行配置
yml
spring:
rabbitmq:
host: 127.0.0.1
pom
<!--springcloudbus的坐标-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
最后重启后 通过postman发送post请求到 http://127.0.0.1:12000/actuator/bus-refresh 地址更新配置 给配置中心发消息 配置中心给Rabbit发消息
bootstrap.yml 和 application.yml区别
bootstrap.yml(bootstrap.properties)用来程序引导时执行,应用于更加早期配置信息读取,如可以使用来配置application.yml中使用到参数等
application.yml(application.properties) 应用程序特有配置信息,可以用来配置后续各个模块中需使用的公共参数等。
bootstrap.yml 先于 application.yml 加载
技术上,bootstrap.yml 是被一个父级的 Spring ApplicationContext 加载的。
这个父级的 Spring ApplicationContext是先加载的,在加载application.yml 的 ApplicationContext之前。
可以通过设置spring.cloud.bootstrap.enabled=false
来禁用bootstrap
。
Spring Boot多环境配置切换
一般在一个项目中,总是会有好多个环境。
比如:开发环境 -> 测试环境 -> 预发布环境 -> 生产环境。
每个环境上的配置文件总是不一样的,甚至开发环境中每个开发者的环境可能也会有一点不同,配置读取可是一个让人有点伤脑筋的问题。
Spring Boot提供了一种优先级配置读取的机制来帮助我们从这种困境中走出来。
常规情况下,我们都知道Spring Boot的配置会从application.yml或.properties
中读取
根据Spring Boot的文档,配置使用的优先级从高到低的顺序,具体如下所示:
1. 命令行参数。
2. 通过 System.getProperties() 获取的 Java 系统参数。
3. 操作系统环境变量。
4. 从 java:comp/env 得到的 JNDI 属性。
5. 通过 RandomValuePropertySource 生成的“random.*”属性。
6. 应用 Jar 文件之外的属性文件(application.properties/yml)。
7. 应用 Jar 文件内部的属性文件(application.properties/yml)。
8. 在应用配置 Java 类(包含“@Configuration”注解的 Java 类)中通过“@PropertySource”注解声明的属性文件。
9. 通过“SpringApplication.setDefaultProperties”声明的默认属性。
这意味着,如果Spring Boot在优先级更高的位置找到了配置,那么它就会无视低级的配置。
方法一、不在配置文件写上配置节定义,而是通过执行时定位不同配置文件来区分。如:
java -jar demo.jar --spring.config.location=/path/test_evn.properties
方法二、在配置文件写上配置节定义,在执行时传递配置节需激活定义名称来区分。
一般情况下我们这样定义环境:dev :开发,test:测试环境,prod:生产环境
在yml 文件中配置的话,写法如下:
spring:
profiles:
active: prod #生产环境
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/test
username: root
password: root
yml)。
7. 应用 Jar 文件内部的属性文件(application.properties/yml)。
8. 在应用配置 Java 类(包含“@Configuration”注解的 Java 类)中通过“@PropertySource”注解声明的属性文件。
9. 通过“SpringApplication.setDefaultProperties”声明的默认属性。
这意味着,如果Spring Boot在优先级更高的位置找到了配置,那么它就会无视低级的配置。
方法一、不在配置文件写上配置节定义,而是通过执行时定位不同配置文件来区分。如:
```shell
java -jar demo.jar --spring.config.location=/path/test_evn.properties
方法二、在配置文件写上配置节定义,在执行时传递配置节需激活定义名称来区分。
一般情况下我们这样定义环境:dev :开发,test:测试环境,prod:生产环境
在yml 文件中配置的话,写法如下:
spring:
profiles:
active: prod #生产环境
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/test
username: root
password: root
启动Jar包的时候:Java -jar xxxxxx.jar spring.profiles.active=prod 也可以这样启动设置配置文件,但是这只是用于开发和测试