注册中心Eureka
Eureka-Server:就是服务注册中心(可以是一个集群),对外暴露自己的地址。
提供者:启动后向Eureka注册自己信息(地址,服务名称等),并且定期进行服务续约
消费者:服务调用方,在使用的时候去拉取一次,之后会定期去Eureka拉取服务列表,然后使用负载均衡算法选出一个服务进行调用。
心跳(续约):提供者和消费者定期通过http方式向Eureka刷新获取(默认30秒)
如果消费者之前已经调用过注册中心获取过提供者的地址信息,那么注册中心宕机后,在提供者地址不变的前提下,不影响服务直接调用
引入SpringCloud为eureka提供的starter依赖:
XML<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency>
编写一个启动类
给eureka-server服务编写一个启动类,一定要添加一个@EnableEurekaServer注解,开启eureka的注册中心功能:
java@SpringBootApplication @EnableEurekaServer //开启eureka的注册中心功能 public class EurekaApplication { public static void main(String[] args) { SpringApplication.run(EurekaApplication.class, args); } }
编写一个application.yml文件,内容如下:
javascriptserver: port: 10086 spring: application: name: eureka-server eureka: client: service-url: defaultZone: http://127.0.0.1:10086/eureka register-with-eureka: false # 自己不注册到eureka中 fetch-registry: false #自己不从eureka中拉取别的服务
启动微服务,然后在浏览器访问:http://127.0.0.1:10086
将微服务注册到注册中心->引入依赖->编写配置文件,名字要指定
javascriptspring: application: name: userservice eureka: client: service-url: defaultZone: http://127.0.0.1:10086/eureka
Ribbon负载均衡
java@SpringBootApplication public class OrderApplication { public static void main(String[] args) { SpringApplication.run(OrderApplication.class, args); } //注册RestTemplate,第三方的类,交给IOC容器管理 @Bean @LoadBalanced//自动负载均衡 public RestTemplate restTemplate() { return new RestTemplate(); } }
@LoadBalanced
在远程调用的RestTemplate,上添加了@LoadBalanced注解,即可实现负载均衡功能
原理:SpringCloud底层其实是利用了一个名为Ribbon的组件,来实现负载均衡功能的。
SpringCloudRibbon的底层采用了一个拦截器,拦截了RestTemplate发出的请求,对地址做了修改。用一幅图来总结一下:
基本流程如下:
拦截我们的RestTemplate请求http://userservice/user/1
RibbonLoadBalancerClient会从请求url中获取服务名称,也就是user-service
DynamicServerListLoadBalancer根据user-service到eureka拉取服务列表
eureka返回列表,localhost:8081、localhost:8082
IRule利用内置负载均衡规则,从列表中选择一个,例如localhost:8081
RibbonLoadBalancerClient修改请求地址,用localhost:8081替代userservice,得到http://localhost:8081/user/1,发起真实请求
Hystrix 熔断器
雪崩问题
微服务中,服务间调用关系错综复杂,一个请求,可能需要调用多个微服务接口才能实现,会形成非常复杂的调用链路:Hystix解决雪崩问题的手段主要是服务降级和线程隔离;
服务降级的演示
第一步:导依赖
XML<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
第二步:在引导类上开启Hystrix
java@EnableCircuitBreaker//开启断路器 @SpringBootApplication public class OrderApplication { public static void main(String[] args) { SpringApplication.run(OrderApplication.class, args); } //注册RestTemplate,第三方的类,交给IOC容器管理 @Bean @LoadBalanced//自动负载均衡 public RestTemplate restTemplate() { return new RestTemplate(); } }
第三步:开发一个方法作为降级的处理方式要求:方法的参数和返回结果类型要和原方法findById应该一致
javapublic String queryOrderByIdFallback(Long orderId) { return "服务器太忙,请稍后重试........"; }
第四步:修改原有的方法,在方法上添加注解
全局降级
上面的写法中每个方法都需要一个降级方法,很麻烦,我们可以使用一个全局的降级方案
java@Service @DefaultProperties(defaultFallback="allMethodFallBack") //在类上添加一个注解 @DefaultProperties(defaultFallback="allMethodFallBack") public class OrderService { @HystrixCommand//指向降级方法 public String queryOrderById() { return "我出错了"; } // 全局降级方法 public String allMethodFallBack() { return "服务器太太太太太太太忙,请稍后重试....."; } }
超时设置
Hystix的默认超时时长为1秒,如果这个微服务响应时间超过1秒,就会报错,降级处理,但是有的方法本身需要执行的时间就长,自定义超时时间
javascripthystrix: command: default: execution: isolation: thread: timeoutInMilliseconds: 3000 # 超时时间3秒
熔断原理
熔断功能:默认5秒内发起20次请求,失败率超过50% 进入熔断状态 5秒内的其他时间都会直接进入降级方法
熔断器有3种状态:open close half-open
状态机有3个状态:
Closed:关闭状态(断路器关闭),所有请求都正常访问。
Open:打开状态(断路器打开),所有请求都会被降级。Hystix会对请求情况计数,当一定时间内失败请求百分比达到阈值,则触发熔断,断路器会完全关闭。默认失败比例的阈值是50%,请求次数最少不低于20次。
Half Open:半开状态,open状态不是永久的,打开后会进入休眠时间(默认是5S)。随后断路器会自动进入半开状态。此时会释放1次请求通过,若这个请求是健康的,则会关闭断路器,否则继续保持打开,再次进行5秒休眠计时。
Feign:封装了RestTemple 远程调用
导入依赖
XML<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
创建interface接口
java@FeignClient("userservice") public interface UserClient { @GetMapping("/user/{id}") //这里的返回结果和 url地址一定要和提供方保持一致 User findById(@PathVariable("id") Long id); }
首先这是一个接口,Feign会通过动态代理,帮我们生成实现类。这点跟mybatis的mapper很像
@FeignClient
,声明这是一个Feign客户端,同时通过value
属性指定服务名称接口中的定义方法,完全采用SpringMVC的注解,Feign会根据注解帮我们生成URL,并访问获取结果
开启Feign功能
java@EnableFeignClients//开启Feign public class OrderApplication { public static void main(String[] args) { SpringApplication.run(OrderApplication.class, args); } }
负载均衡
Feign中本身已经集成了Ribbon依赖和自动配置:
Feign默认也有对Hystix的集成:
javascriptfeign: hystrix: enabled: true # 开启Feign的熔断功能
- 首先,我们要定义一个类,是在刚才编写的UserFeignClient,作为fallback的处理类
javaimport cn.itcast.order.pojo.User; import org.springframework.stereotype.Component; @Component public class UserClientFallback implements UserFeignClient { @Override public User findById(Long id) { User user = new User(); user.setId(0L); user.setUsername("用户查询出现异常!"); return user; } }
- 然后在UserFeignClient中,指定刚才编写的实现类,就实现了降级方法
java@FeignClient(value = "userservice", fallback = UserClientFallback.class) public interface UserFeignClient { @GetMapping("/user/{id}") User findById(@PathVariable("id") Long id); }
Gateway网关
网关的核心功能是:路由和过滤(鉴权)
- 搭建网关
XML<dependencies> <!--引入gateway 网关--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> </dependencies>
- 编写启动类
javaimport org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ApiGatewayApplication { public static void main(String[] args) { SpringApplication.run(ApiGatewayApplication.class,args); } }
- 编写配置
javascriptserver: port: 10010 spring: application: name: api-gateway cloud: # 网关配置 gateway: # 路由配置:转发规则 routes: #集合。 # id: 唯一标识。默认是一个UUID # uri: 转发路径 # predicates: 条件,用于请求网关路径的匹配规则 - id: userservice uri: http://localhost:8081/ predicates: - Path=/user/**
application.yml 中的uri是写死的,就是Gateway-静态路由
Gateway-动态路由
XML
<dependency>
<--添加注册中心Eureka-->
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
修改配置文件
javascript
server:
port: 10010
spring:
application:
name: api-gateway
cloud:
# 网关配置
gateway:
# 路由配置:转发规则
routes: #集合。
# id: 唯一标识。默认是一个UUID
# uri: 转发路径
# predicates: 条件,用于请求网关路径的匹配规则
- id: user-service
uri: lb://userservice # lb负载均衡
predicates:
- Path=/user/**
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
Gateway-过滤器
GatewayFilter :局部过滤器,针对单个路由 GlobalFilter :全局过滤器,针对所有路由
全局过滤器
GlobalFilter 全局过滤器,不需要在配置文件中配置,系统初始化时加载,并作用在每个路由上。
Spring Cloud Gateway 核心的功能也是通过内置的全局过滤器来完成。
自定义全局过滤器步骤:
定义类实现 GlobalFilter 和 Ordered接口
复写方法
完成逻辑处理
java/** * 自定义全局过滤器 */ @Component public class MyGlobalFilter implements GlobalFilter, Ordered { //需求 :请求参数中带有token 内容,就放行,否则提示未授权 @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 获取请求对象 ServerHttpRequest request = exchange.getRequest(); // 获取参数 MultiValueMap<String, String> valueMap = request.getQueryParams(); List<String> valList = valueMap.get("token"); if(CollectionUtils.isEmpty(valList)){//异常情况,没有token参数 // 获取响应对象 ServerHttpResponse response = exchange.getResponse(); // 返回响应码 response.setStatusCode(HttpStatus.FORBIDDEN);//401 // 完成响应 return response.setComplete(); } return chain.filter(exchange);//放行 } @Override public int getOrder() { return 0; } }