简单的springcloud(采用的版本为:Finchley.RELEASE springboot版本为:2.0.3.RELEASE):
1.创建注册中心Eureka-server
1.1:需要的pom坐标:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
1.2:在启动类上加注解:
@EnableEurekaServer :标识是一个注册中心
1.3:添加配置文件 application.yml/application.properties
server:
port: 8761 #注册中心端口号
eureka:
instance:
hostname: localhost
client:
#声明自己是一个服务
registerWithEureka: false
fetchRegistry: false
serviceUrl: #注册中心地址
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
注:注册中心完成
2.创建客户端(消费者,一个简单的客户端):
2.1:需要的pom坐标:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2.2:启动类添加注解:
@EnableEurekaClient (注:这个注解可加可不加不影响) 声明这是一个客户端(消费着)
2.3:修改配置文件:
server:
port: 8771 #客户端的端口号
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/ #指定注册中心的地址
spring:
application:
name: product-client #客户端的名称,必选项,注册中心需要用到,注册中心使用的就是此名称
3.客户端间的调用:
3.1:选择feign和ribbon feign中已经集成好了ribbon. 所以选择feign
3.1.1:导入feign对应的pom坐标
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
3.1.2:如果是ribbon的话,对应的pom坐标
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
3.1.2.1:还需要在启动类中注入一个Bean,用于调用其他服务并开启负载均衡策略
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
3.1.2.2:调用方式:
a.注入RestTemplate类。
b.使用RestTemplate调用对应的方法。
注:底层可查看@LoadBalanced。实际使用的是LoadBalancerClient这个类。内部通过这个去注册中心查找到对应的服务的所有节点,然后根据对应的负载均衡策略进行选择节点。然后返回给最外层,也就是选择好某个节点后进行调用,默认的负载均衡策略是轮询策略。可以配置负载均衡策略。配置完成后使用feign同样生效
#自定义负载均衡策略
product-client: #服务名称
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule 随机策略可查看IRule类的子类。
注:ribbon的默认超时时间为: 60秒
设置ribbon的超时和重试设置
ribbon:
ReadTimeout: 3000
ConnectTimeout: 3000
MaxAutoRetries: 1 #同一台实例最大重试次数,不包括首次调用
MaxAutoRetriesNextServer: 1 #重试负载均衡其他的实例最大重试次数,不包括首次调用
OkToRetryOnAllOperations: false #是否所有操作都重试 慎用
根据上面的参数计算重试的次数:MaxAutoRetries+MaxAutoRetriesNextServer+(MaxAutoRetries *MaxAutoRetriesNextServer) 即重试3次 则一共产生4次调用
如果在重试期间,时间超过了hystrix的超时时间,便会立即执行熔断,fallback。所以要根据上面配置的参数计算hystrix的超时时间,使得在重试期间不能达到hystrix的超时时间,不然重试机制就会没有意义
hystrix超时时间的计算: (1 + MaxAutoRetries + MaxAutoRetriesNextServer) * ReadTimeout 即按照以上的配置 hystrix的超时时间应该配置为 (1+1+1)*3=9秒
Hystrix的超时计算规则应该是 Hystrix的超时时间=Ribbon的重试次数(包含首次)*(ribbon.ReadTimeout+ribbon.ConnectTimeout),如果以你的配置为例Hystrix的超时配置应该是=4*(3000+3000)=24000,即24秒。
当ribbon超时后且hystrix没有超时,便会采取重试机制。当OkToRetryOnAllOperations设置为false时,只会对get请求进行重试。如果设置为true,便会对所有的请求进行重试,如果是put或post等写操作,如果服务器接口没做幂等性,会产生不好的结果,所以OkToRetryOnAllOperations慎用。
如果不配置ribbon的重试次数,默认会重试一次
注意:
默认情况下,GET方式请求无论是连接异常还是读取异常,都会进行重试
非GET方式请求,只有连接异常时,才会进行重试
3.1.3:使用feign调用接口(基于接口实现feign);
a.创建一个接口类,在接口类上加注解@FeignClient然后值有name 和 fallback name:是需要调用的服务的名称
fallback:失败的回调(熔断处理,相当于家里的用电,当用电到达某一阈值,电路就会自动跳闸,从而保护整个电路。),失败的一个降级处理。写的是一个类。每一个方法对应一个方法,此方法参数类型和返回值必须和接口的一样。如果使用feign调用的接口失败了,则会进入这个方法,可以在内部进行一些操作并且返回一些虚拟数据,保证后续服务不会崩溃(解决雪崩效应)。
b.使用feign调用接口的时候,如果有参数 基本类型传递使用@RequestParm 如果是对象的话,要使用@RequestBody传递。
注:使用Feign调用接口分两层,ribbon的调用和hystrix的调用,所以ribbon的超时时间和Hystrix的超时时间的结合就是Feign的超时时间。一般情况下都是ribbon的超时时间(<)hystrix的超时时间(因为涉及到ribbon的重试机制因为ribbon的重试机制和Feign的重试机制有冲突,所以源码中默认关闭Feign的重试机制。
hystrix的默认超时时间是一秒,配置了hystrix的超时时间必须还要配置ribbon的超时时间,否则会有问题。
在使用 Ribbon 时,只需要配置 Hystrix 的超时时间就可以生效,不需要额外配置 Ribbon 的超时时间
记录一个异常,就是第一次启动完成后,调用服务的时候总是在第一次出现超时的情况。这是因为懒加载的特性,在第一次用到的时候才去加载一些东西。而加载这些东西需要耗时,可能耗时的时候就已经出发了熔断/降级处理。
解决办法有两种 1.设置超时时间:全局设置超时时间为60秒
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 60000
# 设置ribbon超时时间
ribbon:
ReadTimeout: 20000
ConnectTimeout: 20000
2. 配置立即加载。不过还需要配置所有的客户端服务
ribbon:
eager-load:
enabled: true
clients: distribution, material-product, outer-data #客户端服务
3.2:引入Hystrix(断路器,熔断)
a.引入所需要的pom坐标文件
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
b.启动类开启断路器,加注解//@EnableHystrix
@EnableCircuitBreaker 这两个都可以,上边的注解里包含了下边的注解。标识启用断路器功能。
c.配置文件配置断路器。需要配置表示feign开启hystrix 和 hystrix的超时时间配置。hystrix的默认超时时间时1秒。
feign:
#开启断路器
hystrix:
enabled: true
#修改调用超时时间
client:
config:
default:
connectTimeout: 3000
readTimeout: 2000
#修改hystrix的调用超时时间配置
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 4000
d.使用:
1. 在方法上加注解@HystrixCommand key有fallbackMethod 表示出现异常时要熔断的方法。
ps:访问该接口时报错了或者超时了,会进入fallbackMethod设置的对应的方法内部。进行一些记录操作返回一些给用户的提示等操作。
注:1.feign/ribbon的超时时间一定要大于hystrix的超时时间,因为只要访问到接口,feign/ribbon 和 hystrix 的计时器就会同步启动,去计算。如果没有到达feign/ribbon的超时时间,但是到达了hystrix的超时时间,尽管没有报错,但是还是会进入的hystrix的熔断方法。
2.设置的fallbackMethod的方法的请求参数和返回结果要和@HystrixCommand标记的接口的一致,否则会出问题。
3.3:引入hystrix仪表盘 hystrix-dashboard。一个健康检查。
hystrix dashboard 仪表盘解释:健康检查监控hystrix的各项指标信息,检查接口调用的成功与失败记录等信息,如果失败率超过50%,会开启熔断,后续请求将不会请求接口。
a:引入pom文件:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<!-- 此依赖是打开 Actuator 作用 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
b.启动类加注解:
@EnableHystrixDashboard
c.配置文件增加endpoint,开启所有访问权限,放行了所有端点,(这样貌似不安全,默认 Actuator 只暴露了2个端点,heath 和 info)
management:
endpoints:
web:
exposure:
include: "*"
注:这个是用来暴露 Actuator 的所有端点的,这一点很重要,不配置你的 Hystrix Dashboard 会出现 Unable to connect to Command Metric Stream 的问题
d.访问入口
http://localhost:8781/hystrix
Hystrix Dashboard输入: http://localhost:8781/actuator/hystrix.stream
3.4:引入链路追踪:seluth,其主要作用是做一个日志的埋点,出现错误时可以根据日志的埋点id找到对应的信息/报错位置。Sleuth可以与日志框架Logback、SLF4J轻松地集成,
a.引入pom文件
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
b.配置文件:
spring:
#配置采样百分比,开发环境可以设置为1,表示全部,生产就用默认(比如请求100次我记录百分之多少/多少次。1代表全部,根据实际情况调节)
sleuth:
sampler:
probability: 1
b. 集成可直接使用,但是需要加log否则控制台不会输出日志,本示例使用的是log4j
private final Logger log = LoggerFactory.getLogger(getClass());
log.info("查询商品");
c.输出的格式
2020-03-26 16:32:08.117 INFO [product-client,1371c3a83b9b13a5,6ac4a8cc3fbbfa12,true] 1812 --- [nio-8772-exec-2] t.o.p.controller.ProductController: findProductById
2020-03-26 16:32:08.117 INFO [product-client,1371c3a83b9b13a5,6ac4a8cc3fbbfa12,true] 1812 --- [nio-8772-exec-2] t.o.product.service.ProductServiceImpl: 查询商品
解释:[]中第一个标识服务的名称,第二个标识本次请求的id唯一标识(一个请求分配的ID号,用来标识一条请求链路。),第三个标识一个工作的基本单元,一个请求可以又多个步骤,而第三个标识每一个步骤。第四个表示 是否要将该信息输出到类似Zipkin这样的聚合器进行收集和展示。
3.5:引入zipkin,上边所提到的聚合器,这里用于手机每次请求链路追踪所产生的记录,都在该组件内存在。
a.引入pom文件:
<!--其中已经包含了链路追踪seluth-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
b.配置文件:
spring:
#zipkin服务所在地址
zipkin:
base-url: http://www.oyygke.top:9411/ #该地址表示zipkin所在地址
c.使用:
集成好sleuth和zipkin之后,进行发送请求,然后访问上边配置的zipkin的地址,就会看到请求记录。点击可查看详情--整个请求的链路是怎么走的。
3.6:引入网关zuul
a.引入pom文件
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
b.添加注解,开启网关
@EnableZuulProxy
c.作用
使用网关进行过滤和请求转发的作用,经过网关转发到某服务。
d.配置文件
zuul:
routes:
#相当于oyygke映射到order-client
order-client: /oyygke/order/**
product-client: /oyygke/product/**
#忽略product-client,不经过网关
# ignored-services: product-client
#只使用一种方式进行访问,过滤以client结尾的服务
ignored-patterns: /*-client/**
#处理http请求头为空的问题
sensitive-headers:
ribbon: #添加请求超时设置,如果不配置,默认第一次访问会进入到熔断的超时处理机制。因为可能还没有加载到,第一次加载需要时间,所以需要添加超时时间设置。
ReadTimeout: 6000
ConnectTimeout: 6000
e.使用过滤
i.新建类继承ZuulFilter,实现其中的方法。相当于一个配置类,记得添加@component。还可以进行限流处理,
//令牌桶 每秒产生多少个令牌 这个需要进行压测确定可以有多少个令牌
//令牌需要配置到配置文件中更改 多少个网管就是 总数/网关数量
//guava谷歌的框架 限流
private static final RateLimiter RATE_LIMITER = RateLimiter.create(100);
工具类:HttpStatus.TOO_MANY_REQUESTS.value() //表示请求过多。
示例:过滤token,表示 如果访问该接口没有token则过滤掉直接返回对应的错误提示信息。
@Component
public class LoginFilter extends ZuulFilter {
/**
* 过滤器的类型 前置还是后置
* @return
*/
@Override
public String filterType() {
return PRE_TYPE; //表示请求接口前执行
}
/**
* 过滤器的一个级别 越小越先执行
* @return
*/
@Override
public int filterOrder() {
return 4;
}
/**
* 是否进行过滤
* @return
*/
@Override
public boolean shouldFilter() {
//全局的上下文对象
RequestContext context = RequestContext.getCurrentContext();
HttpServletRequest request = context.getRequest();
//如果请求路径包含order,则需要进行过滤验证
if(request.getRequestURI().contains("/order/")){
return true;
}
return false;
}
/**
* 业务逻辑
* @return
* @throws ZuulException
*/
@Override
public Object run() throws ZuulException {
RequestContext context = RequestContext.getCurrentContext();
HttpServletRequest request = context.getRequest();
String token = request.getHeader("token");
if(StringUtils.isBlank(token)){
token = request.getParameter("token");
}
if(StringUtils.isBlank(token)){
context.setSendZuulResponse(false);
context.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
}
return null;
}
}
3.7:配置中心 config-server/config-client
a.先搭建配置中心服务,config-server 配置中心可以使用 git、码云等
b.product-client-dev.yml 配置中心的文件名采用 服务名-后缀命名。dev/test等
c.pom文件引入:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
d.@EnableConfigServer 表示这是一个配置服务中心
e.配置文件
spring:
application:
name: config-server #服务名称
cloud:
config:
server:
git:
uri: https://gitee.com/xc-rong/spring-cloud.git #码云的项目地址
username: xxx #码云账号
password: xxx #码云密码
timeout: 5
default-label: master #使用哪个分支的
f.修改原有服务为配置中心的客户端 config-client
i.引入pom文件
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
</dependency>
ii.修改原有的application.yml为bootstrap.yml /bootstrap.properties
iii.修改配置文件
spring:
application:
name: order-client #服务名称
cloud:
config:
discovery:
enabled: true #开启通过服务访问config-server的功能
service-id: CONFIG-SERVER #配置中心的服务名
#后缀 一个区分 指定环境
profile: dev
#分支的区分,指定分支
label: master
iiii. 将一些配置文件的内容放到填写的码云/git仓库上。项目启动的时候会从仓库拉去配置文件。
注:到此配置完成,可以启动项目进行查看日志
g.配置消息总线,用于动态拉取配置,(表示 在码云/git仓库更改配置文件之后,动态拉取不需要重启项目,但是线下可以这样,线上的话不建议这样,因为没办法观察是否拉取到了最新的配置文件)
i:引入pom文件
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
ii.配置文件添加mq
spring:
application:
name: product-client
rabbitmq:
host: www.oyygke.top
port: 5672
username: guest
password: guest
ii.添加注解,在需要动态更新配置的地方添加注解 @RefreshScope
iii.至此代码中的内容添加完毕。动态更新的话需要手动的访问一个地址进行拉取最新配置
http://localhost:2009/refresh #表示每个服务的ip+端口号+refresh 进行刷新,每次只刷新一个服务,多个服务需要调用多次,而且该接口只支持post请求,get请求不支持。
注:必须要有这个pom
<!--开启监控功能-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
4.完成以后可能会出现的小问题。
注册中心每个服务的地址可能不是ip+端口号 可能是一串字母+端口号 这样放到线上是有问题的,需要在配置文件中在加下配置,使注册中心的地址是ip+端口号
解决办法:
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
instance-id: ${spring.cloud.client.ip-address}:${server.port} #表示每个服务的ip示自定义id,ip+端口号格式
prefer-ip-address: true #将IP注册到Eureka Server上