SpringCloud学习笔记

学习代码同步Git仓库MrMao14/cloud2020: SpringCloud学习笔记代码 (github.com)

!!!旧仓库迁移后图片有所丢失,会逐渐补全

一、建立父类模块

  1. 父类包中对pom.xml依赖统一管理jar包和modules
  2. 微服务组件引入依赖后无需设置对应版本号
  3. 通用响应实体类建立api-commons统一管理,其他微服务引入即可
  4. 各个微服务之间可以通过RestTemplate进行通讯传输调用

二、服务注册及发现

(1)EureKa

  • 服务注册

EureKa Server作为服务注册功能的服务器,它是服务注册中心,系统中的其他微服务通过Eurka客户端连接到EureKa Server并维持心跳链接。维护人员就可以通过EuraKa Server进行监控

  • application.yml配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
server:
port: 7001

eureka:
instance:
# eureka服务端实例名称
hostname: localhost
client:
# false表示不向注册中心注册自己
register-with-eureka: false
# false表示自己端就是注册中心,不需要去检索服务
fetch-registry: false
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
  • 注册中心开启

启动类添加@EnableEurekaServer注解,代表我就是服务注册中心

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
  • 服务入住

在需要管理的服务类中添加@EnableEurekaClient注解

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

application.yml增加额外配置

1
2
3
4
5
6
7
8
eureka:
client:
# 表示将自己注册进EurekaServer
register-with-eureka: true
# 是否从EurekaServer抓取已有的注册信息,单点无所谓,集群必须为true配置ribbon负载均衡
fetch-registry: true
service-url:
defaultZone: http://localhost:7001/eureka
  • 这时候已经拥有了多个注册中心,那么服务端也需要多个服务提供者同时提供服务

可以复制一个服务提供者,修改其端口号,其余配置一律相同,此时他们拥有同样的服务名称

1
2
3
spring:
application:
name: cloud-payment-service

在eureka集群中就能查看到2个在启动的服务注册中心,以及一个客户端,2个启动的服务端

但是这时候,作为服务的调用者80用户端,依旧是写死的端口号及服务地址,不能进行动态的调用2个服务,需要进行配置

原来的地址配置如下所示

1
public static final String PAYMENT_URL = "http://localhost:8001";

修改后改为从服务名称调用,建议复制

1
public static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE";

80端口通过Eureka查找对外的微服务,此时还无法识别,需要开启负载均衡,添加注解@LoadBalanced赋予RestTemplate负载均衡

此时80端口的config配置如下

1
2
3
4
5
6
7
8
9
10
@Configuration
public class ApplicationContextConfig {

@Bean
@LoadBalanced
public RestTemplate getRestTemple(){
return new RestTemplate();
}

}
  • 去除主机名称,显示服务ip

增加如下yml配置后,显示设定的服务名称

1
2
3
4
eureka :
instance :
instance-id: payment8001 # 设定服务名称
prefer-ip-address: true # 访问路径显示ip地址

  • 服务发现Discovery

对于注册进eureka里的微服务,可以通过服务发现来获取该服务的信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import org.springframework.cloud.client.discovery.DiscoveryClient;
@Resource
private DiscoveryClient discoveryClient;
@GetMapping(value = "/payment/discovery")
public Object discovery() {
//获取服务清单列表
List<String> services = discoveryClient.getServices();
for (String element : services) {
log.info("****element:" + element);
}
//获取服务实例
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
for (ServiceInstance instance : instances) {
log.info(instance.getServiceId() + "\t" + instance.getHost() + "\t" + instance.getUri());
}
return this.discoveryClient;
}

主启动类需要增加注解@EnableDiscoveryClient

启动后访问路径查看信息

  • Eureka自我保护机制

如果某时刻某一个微服务不可用了,Eureka不会立刻清理,依旧会保存该微服务

  • Eureka已经停止更新,但是依然可以继续使用,后续推荐使用Zookeeper代替Eureka

(2)Zookeeper

Zookeeper和Eureka使用区别不大,主要在于配置文件的修改及pom的引入

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
</dependency>

同时 启动类中也不需要加入Eureka相关的注解

application.yml配置文件中加入Zookeeper远程地址即可

关于Zookeeper在远程服务器的安装,推荐使用Docker容器进行,简单快捷,默认端口2181,注意开启端口和关闭防火墙

注意:Zookeeper没有自带图形化界面

1
2
3
4
5
6
7
8
9
10
server:
port: 80
# 服务别名
spring:
application:
name: cloud-consumer-order
# zookeeper配置
cloud:
zookeeper:
connect-string: mrmao.life:2181

(3)Consul

Consul是一套开源的分布式服务发现和配置管理系统,由HashiCorp公司使用Go语言开发

可以去官网下载win版本或者docker安装docker pull consul简单粗暴(docker真好用),默认使用端口8500

启动后直接访问localhost:8500即可,如下图所示

application.yml配置如下所示

1
2
3
4
5
6
7
8
9
  # consul配置
cloud:
consul:
host: mrmao.life
port: 8500
discovery:
service-name: ${spring.application.name}
# heartbeat:
# enabled: true

这里直接启动会显示小红叉 问题不大 因为心跳机制没打开,去掉上方的注释即可

(4)三个注册中心异同点

  • C:Consistency(一致性)
  • A:Availability(可用性)
  • P:Partition tolerance(分区容错性)

CAP理论关注粒度是数据,而不是整体系统设计的策略

三、服务注册

(1)Ribbon

SpringCloud Ribbon是基于Netflix Ribbon实现的一套 客户端 负载均衡的工具

可以进行自定义负载均衡算法(轮询,随机等)

Ribbon是本地负载均衡,Nginx是服务端负载均衡,所有请求交由Nginx实现转发请求

Eureka会自动引入Ribbon进行负载均衡 @LoadBalanced 注解一键开启

下图为Ribbon负载均衡规则

那么如何进行替换负载均衡规则?

首先,自定义配置类不能放在 @ComponentScan 所扫描包及子包下,否则配置会被所有Ribbon客户端所共享,不能到达特殊定制化的要求

目录结构如图所示,将启动类包和规则配置类区分放置

MySelfRule.java

1
2
3
4
5
6
7
8
9
@Configuration
public class MySelfRule {

@Bean
public IRule myRule(){
return new RandomRule();//定义为随机
}

}

随后在主启动类上加上注解 @RibbonClient 注意:这里名称(name)需要保持一致,不然无效

1
@RibbonClient(name = "CLOUD-PAYMENT-SERVICE", configuration = MySelfRule.class)

负载均衡算法:rest接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标,每次服务重启后rest接口计数从1开始

手写一个轮询算法(需要了解CAS和自旋锁知识)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Component
public class MyLB implements LoadBalancer {

private AtomicInteger atomicInteger = new AtomicInteger(0);

public final int getAndIncrement() {
int current;
int next;
do {
current = this.atomicInteger.get();
next = current >= 2147483647 ? 0 : current + 1;
} while (!this.atomicInteger.compareAndSet(current,next));
System.out.println("--------next:"+next);
return next;
}

@Override
public ServiceInstance instances(List<ServiceInstance> serviceInstances) {
int index = getAndIncrement() % serviceInstances.size();
return serviceInstances.get(index);
}
}

(2)OpenFeign

Feign是一个声明式的WebService客户端,能让编写Web Service更简单,只需要创建一个接口并添加注解

Feign使用在消费侧

  • 在主启动类添加注解 @EnableFeignClients
  • 新建接口并注解 @FeignClient(value = "CLOUD-PAYMENT-SERVICE") 指定哪个微服务,并写出相同的方法(复制即可)
  • 在controller层调用Service层方法,就不需要每次都写RestTemplate,并且自带负载均衡

下方一图流

在微服务的调用过程中,肯定会出现服务超时的问题,默认的1秒满足不了要求就需要手动更改

配置文件增加如下设置即可,即改为5秒

1
2
3
4
5
6
7
8
feign:
client:
config:
default:
# 建立连接后从服务器读取到可用资源所用时间
connectTimeout: 5000
# 建立连接所用时间,网络正常情况下,两端连接所用时间
readTimeOut: 5000

Feign提供了日志打印功能,可以对HTTP请求的细节进行监控

日志级别

使用方法:首先建立一个配置类

1
2
3
4
5
6
@Configuration
public class FeignConfig {
Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;
}
}

并在配置文件中设定日志类的运行

1
2
3
logging:
level:
com.mrmao.springcloud.service.PaymentFeignService: debug

四、服务降级

(1)Hystrix

  • 服务雪崩:

多个微服务调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其他的微服务,这就是所谓的“扇出”,如果扇出的链路上某个微服务的调用响应时间过长或不可用,对于微服务A的调用就会占用越来越多的资源,最终导致系统崩溃。

Hystrix是一个用于处理分布式系统的系统和容错的开源库,Hystrix能保证一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,提高分布式系统的弹性。

断路器相当于当某个服务单元发生故障之后,通过断路器的故障监控,向调用方法返回一个符合预期的,可处理的备选响应方案(FallBack),而不是长时间的等待或抛出调用方法无法处理的异常。

  • 服务降级(FallBack):

在系统出现问题时,能够返回一个有好的提示信息

哪些情况会触发降级:程序运行异常,超时,服务熔断触发降级,线程池/信号量打满

  • 服务熔断(Break)

类比保险丝达到最大服务访问量之后,直接拒绝访问拉闸,然后调用服务降级的方法返回友好提示

  • 服务限流(FlowLimit)

秒杀高并发等操作,严谨一窝蜂的过来拥挤,让大家排队,一秒n个,有序进行