概述
- Spring Cloud是一个微服务框架的规范,而微服务是一种架构模式,将一个完整的项目根据不同的业务模块拆分成多个小的项目,即,服务模块,每个服务模块独立的运行在一个tomcat容器中,每个服务模块甚至可以有自己独立的数据库。
六大组件
服务注册与发现
eureka
- eureka客户端心跳机制:
在默认情况下,eureka的客户端默认是每30秒给eureka发送一次心跳,90秒没发送,eureka就认为该客户端宕机了。 - eureka自我保护机制
当Eureka Server节点在短时间内丢失过多客户端(微服务)时,那么这个节点就会进入自我保护模式。一旦进入该模式,Eureka Server就会保护服务注册表中的信息,不会注销任何微服务。当网络故障恢复后,该Eureka Server节点会自动退出自我保护模式。
eureka:
server:
enable-self-preservation: false
eureka集群配置
每台eureka节点都注册到其他的eureka注册中心上,使得eureka节点之间可以互相拉取数据,保持数据同步。当一台eureka挂掉之后,客户端还可以继续使用其他的eureka,保证了eureka的高可用。 以三个eureka服务作集群为例:
C:\WINDOWS\system32\drivers\etc
该路径下的hosts文件配置如下:
# 文件尾部
# localhost name resolution is handled within DNS itself.
# 127.0.0.1 localhost
# ::1 localhost
127.0.0.1 activate.navicat.com
127.0.0.1 localhost1
127.0.0.1 localhost2
127.0.0.1 localhost3
spring:
application:
name: eureka
server:
port: 7001
servlet:
context-path: /
eureka:
instance:
hostname: localhost1
server:
enable-self-preservation: false
client:
registerWithEureka: false
fetchRegistry: true
serviceUrl:
defaultZone: http://localhost2:6001/eureka/, http://localhost3:5001/eureka/
spring:
application:
name: eureka2
server:
port: 6001
servlet:
context-path: /
eureka:
instance:
hostname: localhost2
server:
enable-self-preservation: false
client:
registerWithEureka: false
fetchRegistry: true
serviceUrl:
defaultZone: http://localhost1:7001/eureka/, http://localhost3:5001/eureka/
spring:
application:
name: eureka3
server:
port: 5001
servlet:
context-path: /
eureka:
instance:
hostname: localhost3
server:
enable-self-preservation: false
client:
registerWithEureka: false
fetchRegistry: true
serviceUrl:
defaultZone: http://localhost2:6001/eureka/, http://localhost1:7001/eureka/
spring:
application:
name: mocro-8001
server:
port: 8001
servlet:
context-path: /
eureka:
client:
serviceUrl:
defaultZone: http://localhost2:6001/eureka/
- Eureka Server微服务启动类上加@EnableEurekaServer
- Eureka Client微服务启动类上加@EnableEurekaClient
- 效果:
配置文件里Client实例只注册到了eureka2,但其他两个Server都同步了该实例。
nacos
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
spring:
cloud:
nacos:
discovery:
server-addr: localhost:8848
服务调用
RestTemplate
- 依赖导入
引用spring-boot-starter-web即可 - RestTemplate定义了多个与REST资源交互的方法,其中的大多数都对应于HTTP的方法。涉及到get 、post、put、delete 等请求类型,具体有:
delete() 在特定的URL上对资源执行HTTP DELETE操作
exchange() 在URL上执行特定的HTTP方法,返回包含对象的ResponseEntity,这个对象是从响应体中映射得到的
execute() 在URL上执行特定的HTTP方法,返回一个从响应体映射得到的对象
getForEntity() 发送一个HTTP GET请求,返回的ResponseEntity包含了响应体所映射成的对象
getForObject() 发送一个HTTP GET请求,返回的请求体将映射为一个对象
postForEntity() POST 数据到一个URL,返回包含一个对象的ResponseEntity,这个对象是从响应体中映射得到的
postForObject() POST 数据到一个URL,返回根据响应体匹配形成的对象
headForHeaders() 发送HTTP HEAD请求,返回包含特定资源URL的HTTP头
optionsForAllow() 发送HTTP OPTIONS请求,返回对特定URL的Allow头信息
postForLocation() POST 数据到一个URL,返回新创建资源的URL
put() PUT 资源到特定的URL
上述方法详细用法
Feign
- Feign 采用的是基于接口的注解
- Feign 整合了ribbon,具有负载均衡的能力
整合了Hystrix,具有熔断的能力 - 调用端和被调用端都要导依赖,设置注解
- 依赖引入
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
@SpringCloudApplication
@EnableFeignClients
public class Provider1Application {
public static void main(String[] args) {
SpringApplication.run(Provider1Application.class, args);
}
}
@FeignClient("provider1")
public interface Provider1Feign {
@RequestMapping("proInfo")
public Map<String, Object> proInfo();
}
- 如果客户端要传值,则一定要使用注解@RequestParam(“必须起名”)、@PathVariable等。
- 直接注入使用
@Autowired
Provider1Feign provider1Feign;
@RequestMapping("recInfo1")
@ResponseBody
public Map<String, Object> recInfo1(){
Map<String, Object> map = provider1Feign.proInfo();
return map;
}
- 开启内置的hystrix
application.yml 配置:
feign:
hystrix:
enabled: true
feign:
circuitbreaker:
enabled: true
feign:
hystrix:
enabled: true
client:
config:
default:
connectTimeout: 3000
readTimeout: 32000
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 10000
- 创建Feign接口实现类,重写远程调用的接口方法,实现方法即为hystrix的fallback方法。
@Component
public class Provider1FeignImpl implements Provider1Feign {
@Value("${server.port}")
private int port;
@Override
public Map<String, Object> proInfo() {
Map<String, Object> map = new HashMap<>();
map.put("msg", "fallbackMethod 降级啦,provider端口:" + port);
map.put("status", false);
return map;
}
}
- Feign接口处也要在注解的fallback属性中指定该实现类
@FeignClient(value = "provider1", fallback = Provider1FeignImpl.class)
public interface Provider1Feign {
@RequestMapping("proInfo")
public Map<String, Object> proInfo();
}
负载均衡
- 当调用的服务是一个集群时,选择哪一个实例来调用,使用什么策略来达到最优解,这就是负载均衡要做的事。
Ribbon
-
依赖引入 ribbon 基本上不需要单独引用,因为绝大多数的注册中心(eureka,consul等)都已集成了ribbon。 -
Ribbon作为一个负载均衡组件, 里面有一个个的负载均衡策略, 而这些策略的公共接口是IRule。 -
通过往Spring容器中注入一个IRule接口的实现类, 可以改变Ribbon默认的负载均衡策略(默认是轮询)。 -
ribbon相关配置 -
application.yml中相关配置
ribbon:
MaxAutoRetries: 2
MaxAutoRetriesNextServer: 3
OkToRetryOnAllOperations: false
ConnectTimeout: 5000
ReadTimeout: 6000
- application文件配置方式
mocro-8001:
ribbon:
NFLoadBalancerRuleClassName: com.hand.config.balance.IPFirstLB
- 注解方式
- 注意:该配置类不能被启动类扫描到,否则会成为全局配置,@SpringBootApplication包含了@ComponentScan,所以配置类最好放在启动类的上一级,或者在启动类中排除配置类。
自定义一个配置类:
@Configuration
public class RibbonConfig {
@Bean
public IRule iRule(){
return new RandomRule();
}
}
添加@RibbonClient注解,指定配置类和要调用的微服务名
@RibbonClient(name = "mocro-8001",configuration = RibbonConfig.class)
@SpringBootApplication
@ComponentScan(excludeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {RibbonConfig.class})})
public class Micro9001Application {
public static void main(String[] args) {
SpringApplication.run(Micro9001Application.class, args);
}
@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
public class IPFirstLB extends AbstractLoadBalancerRule {
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {
}
@Override
public Server choose(Object key) {
ILoadBalancer loadBalancer = this.getLoadBalancer();
List<Server> reachableServers = loadBalancer.getReachableServers();
List<Server> ipEqualServers = new ArrayList<>();
for(Server s : reachableServers){
if(s instanceof DiscoveryEnabledServer){
String serverIP = ((DiscoveryEnabledServer) s).getInstanceInfo().getIPAddr();
try {
String clientIP = InetAddress.getLocalHost().getHostAddress();
if(serverIP.equals(clientIP)){
ipEqualServers.add(s);
}
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
}
if(ipEqualServers.isEmpty()){
int index = ThreadLocalRandom.current().nextInt(reachableServers.size());
return reachableServers.get(index);
}else {
int index = ThreadLocalRandom.current().nextInt(ipEqualServers.size());
return ipEqualServers.get(index);
}
}
}
- RestTemplate中ribbon实现原理
LoadBalancerInterceptor会去拦截RestTemplate的请求,然后从Eureka中获取服务id与端口号,随后利用负载均衡算法得到真实的服务地址信息,替换服务id - ribbon 饥饿加载
- 问题:服务消费方调用服务提供方接口的时候,第一次请求经常会超时,而之后的调用就没有问题了。
- 问题原因:Ribbon进行客户端负载均衡的Client并不是在服务启动的时候就初始化好的,而是在调用的时候才会去创建相应的Client,所以第一次调用的耗时不仅仅包含发送HTTP请求的时间,还包含了创建RibbonClient的时间,这样一来如果创建时间速度较慢,同时设置的超时时间又比较短的话,可能就熔断了。
- 解决方法:开启饥饿加载模式提前加载好客户端
- 实现方法:
application 文件配置
ribbon.eager-load.enabled=true
ribbon.eager-load.clients=hello-service, user-service
-
CAP定理
- CAP 定理是分布式系统的基础,也是分布式系统的 3 个指标:
- Consistency(一致性)
- Availability(可用性)
- Partition tolerance(分区容错性)
-
负载均衡与高可用
- 负载均衡(LB,Load Balance),是一种技术解决方案。用来在多个资源(一般是服务器)中分配负载,达到最优化资源使用,避免过载。
- 高可用(High Availability),简称 HA,是系统一种特征或者指标,通常是指,提供一定性能上的服务运行时间高于平均正常时间段。
- 一般通过负载均衡,冗余同一个服务实例的方式,解决分布式系统的大流量、高并发和高可用的问题。
- 只要存在调用,就需要考虑负载均衡这个因素。
- 负载均衡核心关键:在于是否分配均匀。
服务熔断降级
- 服务熔断
- 当扇出链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回“错误”的响应信息。当检测到该节点微服务调用响应正常后恢复调用链路。
- Hystrix会监控微服务间调用的状况,当失败的调用到一定阈值,缺省是5秒内20次调用失败就会启动熔断机制。熔断机制的注解是@HystrixCommand。
- 服务降级
- 服务降级的处理是在客户端完成的,与服务端没有关系。
- 所谓降级,就是一般是从整体符合考虑,就是当某个服务熔断之后,服务器将不再被调用,此刻客户端可以自己准备一个本地的fallback回调,返回一个缺省值,这样做,虽然服务水平下降,但好过直接挂掉。
- 熔断与降级的区别
- 触发原因不一样,服务熔断由链路上某个服务引起的,服务降级是从整体的负载考虑
- 管理目标层次不一样,服务熔断是一个框架层次的处理,服务降级是业务层次的处理
实现方式不一样,服务熔断一般是自我熔断恢复,服务降级相当于人工控制 - 触发原因不同 服务熔断一般是某个服务(下游服务)故障引起,而服务降级一般是从整体负荷考虑
- 服务熔断是服务降级的一种特殊情况
Hystrix
Hystrix是一个用于处理分布式系统的延迟和容错的开源库。
- 断路器
“断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(FallBack)。 - Hystrix功能包括:服务降级,服务熔断,服务限流,几近实时监控。
- 依赖导入
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
@SpringBootApplication
@EnableCircuitBreaker
public class Micro9001Application {
public static void main(String[] args) {
SpringApplication.run(Micro9001Application.class, args);
}
}
@RequestMapping("hytest")
@HystrixCommand(
commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000")
}
,fallbackMethod = "fallbackMethod"
)
@ResponseBody
public Map<String, Object> hystrixTest(){
Map<String, Object> map = new HashMap<>();
map.put("msg", "service端口:" + port);
map.put("status", true);
try {
Thread.currentThread().sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return map;
}
public Map<String, Object> fallbackMethod(){
Map<String, Object> map = new HashMap<>();
map.put("msg", "fallbackMethod 降级啦,service端口:" + port);
map.put("status", false);
return map;
}
java.util.concurrent.TimeoutException: null
at com.netflix.hystrix.AbstractCommand.handleTimeoutViaFallback(AbstractCommand.java:997) ~[hystrix-core-1.5.18.jar:1.5.18]
- 降级 fallback 方法执行结果
Sentinel
- Sentinel 与 Hystrix对比:
- 依赖导入
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
spring:
application:
name: receiver1
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
dashboard: localhost:8858
management:
endpoints:
web:
exposure:
include: "*"
@Service
@Slf4j
public class SentinelTestServiceImpl implements SentinelTestService {
@Value("${server.port}")
private int port;
@Override
@SentinelResource(value = "hello", blockHandler = "exceptionHandler", fallback = "helloFallback")
public Map<String, Object> sentinelTest() {
Map<String, Object> map = new HashMap<>();
map.put("msg", "provider端口:" + port);
map.put("status", true);
return map;
}
public String helloFallback() {
log.error("helloFallback:{}",port);
return String.format("Halooooo %d", port);
}
public String exceptionHandler(BlockException ex) {
log.error("exceptionHandler:{}",port);
ex.printStackTrace();
return "Oops, error occurred at " + port;
}
}
服务网关
zuul
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
spring:
application:
name: zuul-gateway
server:
port: 10001
servlet:
context-path: /
eureka:
client:
serviceUrl:
defaultZone: http://localhost2:2001/eureka/
zuul:
prefix: /zuul_head
ignored-services: provider-6
routes:
p5Route:
serviceId: provider-5
path: /p5/**
p6Route:
serviceId: provider-6
path: /p6/**
host:
connect-timeout-millis: 10000
socket-timeout-millis: 60000
provider-5:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
服务追踪
配置中心
Spring Cloud Config
- Spring Cloud Config分为服务端和客户端两部分
- 服务端也称为分布式配置中心,它是一个独立的微服务,用来连接配置服务器并为客户端提供获取配置信息。
- 客户端则是通过指定的配置中心来管理相关的配置内容,并在启动的时候从配置中心获取和加载配置信息,不再需要再在每个微服务上编写配置文件。
- 配置服务器默认采用git来存储配置信息,这样就有助于对环境配置进行版本管理。
- 当配置发生变动时,服务不需要重启即可感知到配置的变化并应用新的配置。
- 配置信息以REST接口的形式暴露。
Config Server
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
spring:
application:
name: config-server
cloud:
config:
server:
git:
uri: https://gitee.com/my_username/config-server.git
search-paths:
- /
username: my_username
password: my_passwory
label: master
@SpringBootApplication
@EnableEurekaClient
@EnableConfigServer
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
- /{label}/{application}-{profiles}.yml (label 即 branch)
- /{application}-{profiles}.yml (默认根据yml中的label)
- /{application}/{profiles}/{label}
Config Client
- 配置文件
- applicaiton.yml 是用户级的资源配置项
- bootstrap.yml 是系统级的
- bootstrap.yml比application.yml先加载
- bootstrap.yml优先级高于application.yml
- 依赖导入
客户端
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
Nacos
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
spring:
profiles:
active: dev
application:
name: receiver1
cloud:
nacos:
discovery:
server-addr: localhost:8848
config:
server-addr: localhost:8848
file-extension: yml
|