1. 核心知识
网关、服务发现注册、配置中心、链路追踪、负载均衡、熔断
1. 网关:路由转发+过滤器
比如:
/api/v1/product/ 商品服务
/api/v1/order/ 订单服务
/api/v1/user/ 用户服务
根据不同的接口地址,分发到对应的服务
2. 注册服务与发现:调用和被调用方的信息维护
3. 配置中心:管理配置,动态更新 application.properties
4. 链路追踪:分析调用链路耗时
比如:下单->查询商品服务获取商品价格->查询用户信息->保存数据库
5. 负载均衡:分发负载
6. 熔断:保护自己和被调用方
2. CAP定理
指的是在一个分布式系统中,Consistency(一致性)、Availability(可用性)、Partitiontolerance(分区容错性)三者不可同时兼得 CAP理论就是说在分布式存储系统中,最多只能实现其中的两点,而由于当前网络硬件肯定会出现延迟丢包等问题,所以分区容错性是必须实现的,因此只能在一致性和可用性之间进行平衡。
- 一致性(C):在分布式系统中的所有数据备份,在同一时刻 是否是同样的值。即所有的节点在同一时间的数据完全一致,越多节点,数据同步越耗时
- 可用性(A):负载过大后,集群整体是否还能响应客户端的读写请求。即,服务一直可用,而且是正常的响应时间。
- 分区容错性(P):分区容错性,就是搞可用性,一个节点崩了,并不影响其它节点。比如100个节点,挂了几个,不影响服务,越多机器越好。
- CA满足情况下P不能满足的原因:数据提同步需要时间,也要正常的时间内响应,那么机器数量就要少,所以P不能满足。比如数据同步复制需要100ms,响应时间为2秒,则机器数量就不能太多
- CP满足的情况下,A不满足的原因:数据同步需要时间,机器数量也多,但是同步数据需要时间,所以不能在正常的时间内响应,所以A不满足。比如,1000台机器,每台机器的同步复制时间为100ms,那么久不能在1s的时间内进行响应。
- AP满足的情况下,C不满足的原因:机器数量多,正常的时间内响应,那么数据就不能及时同步到其它节点,所以C不满足。比如:1000台机器,响应时间为1s,同步一个节点时间为100ms,那么,如果需要在1s内响应,则数据无法及时同步到其他节点。
3. 注册中心
注册中心就是服务管理,核心是有个服务注册表,通过心跳机制动态维护 服务提供者provider:启动的时候向注册中心上报自己的网络信息 服务消费者consumer:启动的时候向注册中心上报自己的网络信息,同时拉取provider的相关网络信息。 使用注册中心是因为微服务应用和机器越来越多,调用方需要知道接口的网络地址,如果单靠配置文件的方式去控制网络地址,对于动态新增机器和维护带来很大的问题
4. 注册中心的选择
Zookeeper: CP设计,保证了一致性,集群搭建的时候,某个节点失败,则会进行选举行的leader,或者半数以上节点不可用,则无法提供服务,因此可用性没法满足 Eureka: AP原则,无主从节点,一个节点挂了,自动切换到其它可用节点就可以使用,去中心化 所以,如果要保持一致性,则选择zookeeper,如金融行业,如果要去可用性,则Eureka,如电商系统
5. Eureka
Eureka2.0开始闭源
1.引入依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
2.启动类加入注解:@EnableEurekaServer` 3.配置文件加入如下配置,声明自己是服务端
server: port: 8761 eureka: instance: hostname: localhost client: #声明自己是个服务器 registerWithEureka: false fetchRegistry: false serviceUrl: defaultZone: htpp://${eureka.instance.hostname}:${server.port}/eureka/
4.服务方引入如下依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
并在服务方的配置文件中加入如下配置:
#注册到服务中心
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
#声明自己的服务名
spring:
application:
name: product-service
6. 服务消费者ribbon和feign
- Ribbon
1.在服务方加入依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
2.在启动类或配置类中加入如下代码:
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
3.在服务层调用服务
@Autowired
private RestTemplate restTemplate
使用restTemplate.getForObject()
等方法进行服务接口调用,例如:
restTemplate.getForObject("http://product-service/api/v1/product/find?id=" + productId, Map.class)
4.启动类新增的bean中,
@LoadBalanced
的原理1.首先从注册中心获取provider的列表 2.通过一定的策略选择其中一个节点 3.再返回给restTemplate调用
5.Ribbon默认使用轮循的负载均衡策略,如果要修改策略则需要加入如下配置:
#自定义负载均衡策略
product-service:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
其它策略可以查看com.netflix.loadbalancer.IRule
的子类,
策略选择
1、如果每个机器配置一样,则建议不修改策略 (推荐) 2、如果部分机器配置强,则可以改为 WeightedResponseTimeRule
- fegin
- 加入依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
启动类增加
@EnableFeignClients
增加一个接口 并加入注解
@FeignClient(name="服务名")
,例如:@FeignClient(name="product-service")
直接注入该接口,用相关服务接口
7. 服务降级熔断
指系统负载过高,突发流量或者网络等各种异常情况,常用的解决方案
1. 熔断:
类似保险丝,熔断服务是为了防止整个系统崩溃,保护自己服务和下游服务,比如:
下单服务->商品服务->用户服务(出现异常->熔断)
2. 降级:
抛弃一些非核心的数据和接口,比如:
行李箱,只带核心的物品,抛弃非核心的,等有条件的时候再去携带这些物品
3. 熔断和降级相互交集
相同点:
1) 从可用性和可靠性出发,为了防止系统崩溃
2) 最终让用户体验到的是服务暂时不可用
不同点:熔断服务一般是下游服务故障导致的,而服务降级一般是从整体系统负荷考虑,由调用方控制
8. Hystrix断路器
1.加入hystrix依赖
<!--hystrix断路器-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
2.启动类加入
@EnableCircuitBreaker
注解 3.在api方法上加入注解`@HystrixCommand,降级的方法一定要和api的一致,例如:
@RequestMapping("save")
@HystrixCommand(fallbackMethod = "saveOrderFail")
public JsonResult save(@RequestParam("user_id") Long userId,@RequestParam("product_id") Long productId){
ProductOrder order = productOrderService.save(userId, productId);
return JsonResult.success(order);
}
private JsonResult saveOrderFail(Long userId,Long productId){
return JsonResult.fail(-1,null,"当前访问人数过多,请稍后尝试");
}
4.fegin结合Hystrix,需要在
@FeignClient
注解的方法中,加入fallback
属性,fallback调用的类,需要实现fegin所在类的接口,并标记为@Component
,例如:
@FeignClient(name = "product-service",fallback = ProductClientFailBack.class)
public interface ProductClient {}
@Component
public class ProductClientFailBack implements ProductClient {
@Override
public String fingById(Long id) {
System.out.println("fegin 调用product-service fingById 异常");
return null;
}
}
由于2.0.3的fegin默认关闭了hystrix,所以需要在配置中开启:
feign:
#2.0.3的fegin默认是关闭hystrix
hystrix:
enabled: true
9. zuul网关
1.引入zuul依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
2.启动类加入注解
@EnableZuulProxy
3.配置需要加入网关过滤的服务,服务的代替地址不能一样,否则后者会覆盖前者,产生前者服务接口404,由于routes将部分请求头过滤掉了,如果需要往下游服务传递请求头,需要加入sensitive-headers:
配置:
#名称如果一样会覆盖前者
zuul:
routes:
order-service: /apigateway/order/**
product-service: /apigateway/product/**
#处理请求头为空的问题
sensitive-headers:
#统一入口为上面的配置,其他入口忽略
#ignored-patterns: /*-service/**
#忽略整个服务,对外提供接口
#ignored-services: product-service