导学
课程结构
微服务引入
单体架构:将业务的所有功能集中在一个项目中开发,打成一个包部署。
分布式架构:根据业务功能对系统进行拆分,每个业务模块作为独立项目开发,称为一个服务。
分布式架构的要考虑的问题:
•服务拆分粒度如何?
•服务集群地址如何维护?
•服务之间如何实现远程调用?
•服务健康状态如何感知?
解决方案:微服务技术
Dubbo | SpringCloud | SpringCloudAlibaba | |
---|---|---|---|
注册中心 | zookeeper、Redis | Eureka、Consul | Nacos、Eureka |
服务远程调用 | Dubbo协议 | Feign(http协议) | Dubbo、Feign |
配置中心 | 无 | SpringCloudConfig | SpringCloudConfig、Nacos |
服务网关 | 无 | SpringCloudGateway、Zuul | SpringCloudGateway、Zuul |
服务监控和保护 | dubbo-admin,功能弱 | Hystix | Sentinel |
SpringCloud版本问题:
服务拆分注意事项
1.单一职责:不同微服务,不要重复开发相同业务
2.数据独立:不要访问其它微服务的数据库
3.面向服务:将自己的业务暴露为接口,供其它微服务调用
如何执行远程调用?
Order order = orderMapper.findById(orderId);
//2.利用RestTemplate发起HTTP请求,查询用户
//2.1url
String url="http://localhost:8081/user/" + order.getUserId();
//2.2发送HTTP请求,实现远程调用
User user = restTemplate.getForObject(url, User.class);
//3.封装User到order
order.setUser(user);
提供者与消费者
服务提供者:一次业务中,被其它微服务调用的服务。(提供接口给其它微服务)
服务消费者:一次业务中,调用其它微服务的服务。(调用其它微服务提供的接口)
Eureka注册中心
启动类编写:
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class,args);
}
}
注册E-server
注册E-client
Eureka控制中心不显示实例,错误产生原因:
模拟多实例部署:
--server.port=8090
在order-service完成服务拉取
负载均衡实现
原理:
内置负载均衡规则类 | 规则描述 |
---|---|
RoundRobinRule | 简单轮询服务列表来选择服务器。它是Ribbon默认的负载均衡规则。 |
AvailabilityFilteringRule | 对以下两种服务器进行忽略: (1)在默认情况下,这台服务器如果3次连接失败,这台服务器就会被设置为“短路”状态。短路状态将持续30秒,如果再次连接失败,短路的持续时间就会几何级地增加。 (2)并发数过高的服务器。如果一个服务器的并发连接数过高,配置了AvailabilityFilteringRule规则的客户端也会将其忽略。并发连接数的上限,可以由客户端的..ActiveConnectionsLimit属性进行配置。 |
WeightedResponseTimeRule | 为每一个服务器赋予一个权重值。服务器响应时间越长,这个服务器的权重就越小。这个规则会随机选择服务器,这个权重值会影响服务器的选择。 |
ZoneAvoidanceRule | 以区域可用的服务器为基础进行服务器的选择。使用Zone对服务器进行分类,这个Zone可以理解为一个机房、一个机架等。而后再对Zone内的多个服务做轮询。 |
BestAvailableRule | 忽略那些短路的服务器,并选择并发数较低的服务器。 |
RandomRule | 随机选择一个可用的服务器。 |
RetryRule | 重试机制的选择逻辑 |
自定义负载均衡策略
前者针对全局,后者针对方法。
Ribbon默认是采用懒加载,即第一次访问时才会去创建LoadBalanceClient,请求时间会很长。
而饥饿加载则会在项目启动时创建,降低第一次访问的耗时,通过下面配置开启饥饿加载:
Nacos注册中心
安装后启动:
startup.cmd -m standalone
默认账号密码:nacos
服务集群
Nacos服务分级存储模型
服务调用尽可能选择本地集群的服务,跨集群调用延迟较高
本地集群不可访问时,再去访问其它集群
NacosRule负载均衡策略
①优先选择同集群服务实例列表
②本地集群找不到提供者,才去其它集群寻找,并且会报警告
③确定了可用实例列表后,再采用随机负载均衡挑选实例
根据权重负载均衡
服务器设备性能有差异,部分实例所在机器性能较好,另一些较差,我们希望性能好的机器承担更多的用户请求
①Nacos控制台可以设置实例的权重值,0~1之间
②同集群内的多个实例,权重越高被访问的频率越高
③权重设置为0则完全不会被访问
环境隔离 - namespace
此时访问order-service,因为namespace不同,会导致找不到userservice,控制台会报错
nacos注册中心细节分析(与Eureka不同之处)
服务提供者的健康检测
临时实例:
1.Nacos与eureka的共同点
①都支持服务注册和服务拉取
②都支持服务提供者心跳方式做健康检测
2.Nacos与Eureka的区别
①Nacos支持服务端主动检测提供者状态:临时实例采用心跳模式,非临时实例采用主动检测模式
②临时实例心跳不正常会被剔除,非临时实例则不会被剔除
③Nacos支持服务列表变更的消息推送模式,服务列表更新更及时
④Nacos集群默认采用AP方式,当集群中存在非临时实例时,采用CP模式;Eureka采用AP方式
Nacos配置管理
统一配置管理
其中,配置内容项只用填写需要热更新(有热更新需求)的配置。
1.引入Nacos的配置管理客户端依赖:
2.添加bootstrap.yml,引导文件,读取优先级高于application.yml
测试案例:
配置自动刷新
方式一:通过@Value注解注入,结合@RefreshScope来刷新
方式二:通过@ConfigurationProperties注入,自动刷新(推荐)
实现热更新:
多环境配置共享
微服务启动时会从nacos读取多个配置文件:
[spring.application.name]-[spring.profiles.active].yaml,例如:userservice-dev.yaml
[spring.application.name].yaml,例如:userservice.yaml
无论profile如何变化,[spring.application.name].yaml这个文件一定会加载,因此多环境共享配置可以写入这个文件
优先级:
Nacos集群搭建
集群搭建步骤:
①搭建MySQL集群并初始化数据库表
②下载解压nacos
③修改集群配置(节点信息)、数据库配置
④分别启动多个nacos节点(不用-m配置)
⑤nginx反向代理
搭建成功:
注意:需要注释掉热更新配置
(不对劲就重启下浏览器?)
http客户端Feign
restTemplate存在问题
•代码可读性差,编程体验不统一
•参数复杂URL难以维护
Feign
Feign是一个声明式的http客户端
1.引入依赖:
2.在order-service的启动类添加注解开启Feign的功能:
3.编写Feign客户端(注意包放在启动类同级下):
基于SpringMVC的注解来声明远程调用的信息
4.用Feign客户端代替RestTemplate
自定义Feign的配置
类型 | 作用 | 说明 |
---|---|---|
feign.Logger.Level | 修改日志级别 | 包含四种不同的级别:NONE、BASIC、HEADERS、FULL |
feign.codec.Decoder | 响应结果的解析器 | http远程调用的结果做解析,例如解析json字符串为java对象 |
feign.codec.Encoder | 请求参数编码 | 将请求参数编码,便于通过http请求发送 |
feign. Contract | 支持的注解格式 | 默认是SpringMVC的注解 |
feign. Retryer | 失败重试机制 | 请求失败的重试机制,默认是没有,不过会使用Ribbon的重试 |
方式一:配置文件方式
方式二:java代码方式,需要先声明一个Bean:
①而后如果是全局配置,则把它放到@EnableFeignClients这个注解中:
②如果是局部配置,则把它放到@FeignClient这个注解中:
Feign的性能优化
Feign底层的客户端实现:
•URLConnection:默认实现,不支持连接池
•Apache HttpClient :支持连接池
•OKHttp:支持连接池
因此优化Feign的性能主要包括:
①使用连接池代替默认的URLConnection
②日志级别,最好用basic或none
Feign添加HttpClient的支持:
引入依赖:
配置连接池:
Feign的最佳实践
方式一(继承):给消费者的FeignClient和提供者的controller定义统一的父接口作为标准。
服务紧耦合
父接口参数列表中的映射不会被继承
方式二(抽取):将FeignClient抽取为独立模块,并且把接口有关的POJO、默认的Feign配置都放到这个模块中,提供给所有消费者使用
实现最佳实践方式二的步骤如下:
1.首先创建一个module,命名为feign-api,然后引入feign的starter依赖
2.将order-service中编写的UserClient、User、DefaultFeignConfiguration都复制到feign-api项目中
3.在order-service中引入feign-api的依赖
4.修改order-service中的所有与上述三个组件有关的import部分,改成导入feign-api中的包
5.重启测试
当定义的FeignClient不在SpringBootApplication的扫描包范围时,这些FeignClient无法使用。有两种方式解决:
方式一:指定FeignClient所在包
方式二:指定FeignClient字节码
统一网关Gateway
网关功能:
身份认证和权限校验
服务路由、负载均衡
请求限流
Zuul是基于Servlet的实现,属于阻塞式编程。而SpringCloudGateway则是基于Spring5中提供的WebFlux,属于响应式编程的实现,具备更好的性能。
创建新的module,引入SpringCloudGateway的依赖和nacos的服务发现依赖:
编写路由配置及nacos地址
断言:规则判断布尔表达式
uri: lb://orderservice
路由断言工厂Route Predicate Factory
断言规则交给此工厂处理
名称 | 说明 | 示例 |
---|---|---|
After | 是某个时间点后的请求 | - After=2037-01-20T17:42:47.789-07:00[America/Denver] |
Before | 是某个时间点之前的请求 | - Before=2031-04-13T15:14:47.433+08:00[Asia/Shanghai] |
Between | 是某两个时间点之前的请求 | - Between=2037-01-20T17:42:47.789-07:00[America/Denver], 2037-01-21T17:42:47.789-07:00[America/Denver] |
Cookie | 请求必须包含某些cookie | - Cookie=chocolate, ch.p |
Header | 请求必须包含某些header | - Header=X-Request-Id, \d+ |
Host | 请求必须是访问某个host(域名) | - Host=.somehost.org,.anotherhost.org |
Method | 请求方式必须是指定方式 | - Method=GET,POST |
Path | 请求路径必须符合指定规则 | - Path=/red/{segment},/blue/** |
Query | 请求参数必须包含指定参数 | - Query=name, Jack或者- Query=name |
RemoteAddr | 请求者的ip必须是指定范围 | - RemoteAddr=192.168.1.1/24 |
Weight | 权重处理 |
(了解,用到即查)
路由过滤器 GatewayFilter
例子
默认过滤器
default-filters: # 默认过滤器,会对所有的路由请求都生效
- AddRequestHeader=Truth, Itcast is freaking awesome! # 添加请求头
自定义过滤器
全局过滤器 GlobalFilter
1.实现GlobalFilter接口,做好用户友好的返回HTTP状态码设置
2.添加@Order注解(数字越小过滤器优先级越高)或实现Ordered接口
过滤器执行顺序
每一个过滤器都必须指定一个int类型的order值,order****值越小,优先级越高,执行顺序越靠前。
GlobalFilter通过实现Ordered接口,或者添加@Order注解来指定order值,由我们自己指定
路由过滤器和defaultFilter的order由Spring指定,默认是按照声明顺序从1递增。
当过滤器的order值一样时,会按照 defaultFilter > 路由过滤器 > GlobalFilter的顺序执行。
跨域问题处理
跨域:域名不一致就是跨域,主要包括:
域名不同: www.taobao.com 和 www.taobao.org 和 www.jd.com 和 miaosha.jd.com
域名相同,端口不同:localhost:8080和localhost:8081
跨域问题:浏览器禁止请求的发起者与服务端发生跨域ajax请求,请求被浏览器拦截的问题
解决方案:CORS
Docker
问题产生:
大型项目组件较多,运行环境也较为复杂,部署时会碰到一些问题:
•依赖关系复杂,容易出现兼容性问题
•开发、测试、生产环境有差异
Docker如何解决依赖的兼容问题的?
•将应用的Libs(函数库)、Deps(依赖)、配置与应用一起打包
•将每个应用放到一个隔离容器去运行,避免互相干扰
Docker如何解决开发、测试、生产环境有差异的问题
•Docker镜像中包含完整运行环境,包括系统函数库,仅依赖系统的Linux内核,因此可以在任意Linux操作系统上运行
Docker与虚拟机
Docker架构
镜像(Image):Docker将应用程序及其所需的依赖、函数库、环境、配置等文件打包在一起,称为镜像。
容器(Container):镜像中的应用程序运行后形成的进程就是容器,只是Docker会给容器做隔离,对外不可见。容器内data为镜像中data的复制,不会对镜像造成污染。
Docker是一个CS架构的程序,由两部分组成:
服务端(server):Docker守护进程,负责处理Docker指令,管理镜像、容器等
客户端(client):通过命令或RestAPI向Docker服务端发送指令。可以在本地或远程向服务端发送指令
CENTOS7联网:
Docker基本操作
启动Docker
systemctl start docker
镜像相关命令
镜像名称一般分两部分组成:[repository]:[tag]。 在没有指定tag时,默认是latest,代表最新版本的镜像
镜像操作命令
docker XXX --help
容器相关命令
创建运行一个Nginx容器
去docker hub查看Nginx的容器运行命令
命令解读:
docker run :创建并运行一个容器
--name : 给容器起一个名字,比如叫做mn
-p :将宿主机端口与容器端口映射,冒号左侧是宿主机端口,右侧是容器端口
-d:后台运行容器
nginx:镜像名称,例如nginx
注意:切换到ROOT用户才能启动
进入容器
(进入失败请更新Linux内核:yum update -y)
命令解读:
docker exec :进入容器内部,执行一个命令
-it : 给当前进入的容器创建一个标准输入、输出终端,允许我们与容器交互
mn :要进入的容器的名称
bash:进入容器后执行的命令,bash是一个linux终端交互命令
修改index.html的内容
退出容器
exit
删除容器
•docker rm
•不能删除运行中的容器,除非添加 -f 参数
数据卷
一个虚拟目录,指向宿主机文件系统中的某个目录。
将容器与数据分离,解耦合,方便操作容器内数据,保证数据安全
数据卷操作:
•docker volume create
•docker volume ls
•docker volume inspect
•docker volume rm
•docker volume prune
挂载数据卷
挂载成功
•如果容器运行时volume不存在,会自动被创建出来
将宿主机目录直接挂载到容器
•-v [宿主机目录]:[容器内目录]
•-v [宿主机文件]:[容器内文件]
docker run \
--name musql \
-e MYSQL_ROOT_PASSWORD=123 \
-p 3306:3306 \
-v /tmp/mysql/conf/hmy.cnf:/etc/mysql/conf.d/hmy.cnf \
-v /tmp/mysql/data:/var/lib/mtsql \
-d \
mysql:5.7.25 \
链接成功
数据卷挂载的方式对比
1.docker run的命令中通过 -v 参数挂载文件或目录到容器中:
①-v volume名称:容器内目录
②-v 宿主机文件:容器内文件
③-v 宿主机目录:容器内目录
2.数据卷挂载与目录直接挂载的
①数据卷挂载耦合度低,由docker来管理目录,但是目录较深,不好找
②目录挂载耦合度高,需要我们自己管理目录,不过目录容易寻找查看
Dockerfile自定义镜像
镜像是将应用程序及其需要的系统函数库、环境、配置、依赖打包而成。
Dockerfile
包含一个个的指令(Instruction),用指令来说明要执行什么操作来构建镜像。每一个指令都会形成一层Layer。
指令 | 说明 | 示例 |
---|---|---|
FROM | 指定基础镜像 | FROM centos:6 |
ENV | 设置环境变量,可在后面指令使用 | ENV key value |
COPY | 拷贝本地文件到镜像的指定目录 | COPY ./mysql-5.7.rpm /tmp |
RUN | 执行Linux的shell命令,一般是安装过程的命令 | RUN yum install gcc |
EXPOSE | 指定容器运行时监听的端口,是给镜像使用者看的 | EXPOSE 8080 |
ENTRYPOINT | 镜像中应用的启动命令,容器运行时调用 | ENTRYPOINT java -jar xx.jar |
# 指定基础镜像
FROM ubuntu:16.04
# 配置环境变量,JDK的安装目录
ENV JAVA_DIR=/usr/local
# 拷贝jdk和java项目的包
COPY ./jdk8.tar.gz $JAVA_DIR/
COPY ./docker-demo.jar /tmp/app.jar
# 安装JDK
RUN cd $JAVA_DIR \
&& tar -xf ./jdk8.tar.gz \
&& mv ./jdk1.8.0_144 ./java8
# 配置环境变量
ENV JAVA_HOME=$JAVA_DIR/java8
ENV PATH=$PATH:$JAVA_HOME/bin
# 暴露端口
EXPOSE 8090
# 入口,java项目的启动命令
ENTRYPOINT java -jar /tmp/app.jar
注意:一定要在对应目录下执行build,否则找不到文件
java:8-alpine镜像
部署通用环境,减少重复环境部署。
DockerCompose
Docker Compose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器!
Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。
配置DockerCompose
打包名称
Docker镜像仓库
镜像仓库( Docker Registry )有公共的和私有的两种形式:
公共仓库:例如Docker官方的 Docker Hub,国内也有一些云服务商提供类似于 Docker Hub 的公开服务,比如 网易云镜像服务、DaoCloud 镜像服务、阿里云镜像服务等。
除了使用公开仓库外,用户还可以在本地搭建私有 Docker Registry。企业自己的镜像最好是采用私有Docker Registry来实现。
在私有镜像仓库推送或拉取镜像
服务异步通讯
简介
异步调用方案
事件驱动模式
异步通信的优点:
耦合度低
吞吐量提升
故障隔离
流量削峰
异步通信的缺点:
依赖于Broker的可靠性、安全性、吞吐能力
架构复杂了,业务没有明显的流程线,不好追踪管理
RabbitMQ
MQ (MessageQueue),中文是消息队列,字面来看就是存放消息的队列。也就是事件驱动架构中的Broker。
RabbitMQ是基于Erlang语言开发的开源消息通信中间件,官网地址:https://www.rabbitmq.com/
Linux扩容: VMware 虚拟机根目录磁盘空间补足——给 /dev/sda3扩容 - 掘金 (juejin.cn)
•channel:操作MQ的工具
•exchange:路由消息到队列中
•queue:缓存消息
•virtual host:虚拟主机,是对queue、exchange等资源的逻辑分组
常见消息模型
基本消息队列的消息发送流程:
1.建立connection
2.创建channel
3.利用channel声明队列
4.利用channel向队列发送消息
基本消息队列的消息接收流程:
1.建立connection
2.创建channel(避免队列不存在)
3.利用channel声明队列
4.定义consumer的消费行为handleDelivery()
5.利用channel将消费者与队列绑定
SpringAMQP
Advanced Message Queuing Protocol,是用于在应用程序之间传递业务消息的开放标准。该协议与语言和平台无关,更符合微服务中独立性的要求。
Features
- Listener container for asynchronous processing of inbound messages
RabbitTemplate
for sending and receiving messagesRabbitAdmin
for automatically declaring queues, exchanges and bindings
案例:在publisher中编写测试方法,向simple.queue发送消息
SpringAMQP如何发送消息?
•引入amqp的starter依赖
•配置RabbitMQ地址
•利用RabbitTemplate的convertAndSend方法
SpringAMQP如何接收消息?
•引入amqp的starter依赖
•配置RabbitMQ地址
•定义类,添加@Component注解
•类中声明方法,添加@RabbitListener注解,方法参数就时消息
注意:消息一旦消费就会从队列删除,RabbitMQ没有消息回溯功能
Work Queue 工作队列
消费预取(造成花费时间超过预期)限制
发布( Publish )、订阅( Subscribe )
发布订阅模式与之前案例的区别就是允许将同一消息发送给多个消费者。实现方式是加入了exchange(交换机)。
常见exchange类型包括:
Fanout:广播
Direct:路由
Topic:话题
注意:exchange负责消息路由,而不是存储,路由失败则消息丢失
Fanout Exchange
Fanout Exchange 会将接收到的消息广播到每一个跟其绑定的queue
DirectExchange
Direct Exchange 会将接收到的消息根据规则路由到指定的Queue,因此称为路由模式(routes)。
多、同key*(模拟Fanout)*
1.利用@RabbitListener声明Exchange、Queue、RoutingKey
2.在consumer服务中,编写两个消费者方法,分别监听direct.queue1和direct.queue2
3.在publisher中编写测试方法,向itcast. direct发送消息
TopicExchange
TopicExchange与DirectExchange类似,区别在于routingKey必须是多个单词的列表,并且以 . 分割。
Queue与Exchange指定BindingKey时可以使用通配符:
#:代指0个或多个单词
*:代指一个单词
消息转换器
Spring的对消息对象的处理是由org.springframework.amqp.support.converter.MessageConverter来处理的。而默认实现是SimpleMessageConverter,基于JDK的ObjectOutputStream完成序列化。
如果要修改只需要定义一个MessageConverter 类型的Bean即可。推荐用JSON方式序列化,步骤如下:
•在publisher服务引入依赖
•在publisher服务声明MessageConverter:
•注意发送方与接收方必须使用相同的MessageConverter
分布式搜索引擎(elasticsearch基础)
elasticsearch是一款非常强大的开源搜索引擎,可以帮助我们从海量数据中快速找到需要的内容
elasticsearch结合kibana、Logstash、Beats,也就是elastic stack(ELK)。被广泛应用在日志数据分析、实时监控等领域。
初识elasticsearch
正向索引和倒排索引
Mysql采用的就是正向索引
elasticsearch采用倒排索引:
•文档(document):每条数据就是一个文档
•词条(term):文档按照语义分成的词语
elasticsearch是面向文档存储的,可以是数据库中的一条商品数据,一个订单信息。
文档数据会被序列化为json格式后存储在elasticsearch中。
•索引(index):相同类型的文档的集合
•映射(mapping):索引中文档的字段约束信息,类似表的结构约束
DSL是elasticsearch提供的JSON风格的请求语句,用来操作elasticsearch,实现CRUD
安装
分词器
ik分词器包含两种模式:
•ik_smart:最少切分,粗粒度
•ik_max_word:最细切分,细粒度
拓展词库
索引库操作
mapping属性
mapping是对索引库中文档的约束,常见的mapping属性包括:
•type:字段数据类型,常见的简单类型有:
•字符串:text(可分词的文本)、keyword(精确值,例如:品牌、国家、ip地址)
•数值:long、integer、short、byte、double、float、
•布尔:boolean
•日期:date
•对象:object
•index:是否创建索引,默认为true
•analyzer:使用哪种分词器
•properties:该字段的子字段
创建索引库
查看、删除索引库
修改索引库
索引库和mapping一旦创建无法修改,但是可以添加新的字段,语法如下:
文档操作
添加文档
查看、删除文档
修改
方式一:全量修改,会删除旧文档,添加新文档
方式二:增量修改,修改指定字段值
RestClient操作索引库
ES官方提供了各种不同语言的客户端,用来操作ES。这些客户端的本质就是组装DSL语句,通过http请求发送给ES。
聚合搜索
初始化JavaRestClient
因为SpringBoot默认的ES版本是7.6.2,所以我们需要覆盖默认的ES版本:
初始化RestHighLevelClient:
创建索引库:
利用JavaRestClient批量导入数据到ES
DSL查询文档
Elasticsearch提供了基于JSON的DSL(Domain Specific Language)来定义查询。常见的查询类型包括:
全文检索查询
match查询:全文检索查询的一种,会对用户输入内容分词,然后去倒排索引库检索,语法:
multi_match:与match查询类似,只不过允许同时查询多个字段,语法:
参与查询字段越多,查询性能越差
精确查询
不会对搜索条件分词
term查询:
range查询:
地理查询
geo_bounding_box:查询geo_point值落在某个矩形范围的所有文档
geo_distance:查询到指定中心点小于某个距离值的所有文档
复合查询
•fuction score:算分函数查询,可以控制文档相关性算分,控制文档排名。例如百度竞价
算法演变
Function Score Query
Boolean Query
布尔查询是一个或多个查询子句的组合。子查询的组合方式有:
•must:必须匹配每个子查询,类似“与”
•should:选择性匹配子查询,类似“或”
•must_not:必须不匹配,不参与算分,类似“非”
•filter:必须匹配,不参与算分
搜索结果处理
排序
elasticsearch支持对搜索结果排序,默认是根据相关度算分(_score)来排序。可以排序字段类型有:keyword类型、数值类型、地理坐标类型、日期类型等。
自定义规则后,score字段为空。
分页
ES是分布式的,所以会面临深度分页问题。例如按price排序后,获取from = 990,size =10的数据:
1.首先在每个数据分片上都排序并查询前1000条文档。
2.然后将所有节点的结果聚合,在内存中重新排序选出前1000条文档
3.最后从这1000条中,选取从990开始的10条文档
ES设定结果集查询的上限是10000
高亮
数据聚合
聚合(aggregations)可以实现对文档数据的统计、分析、运算。聚合常见的有三类:
桶(Bucket)聚合:用来对文档做分组
•TermAggregation:按照文档字段值分组
可以限定要聚合的文档范围,只要添加query条件即可:
•Date Histogram:按照日期阶梯分组,例如一周为一组,或者一月为一组
度量(Metric)聚合:用以计算一些值,比如:最大值、最小值、平均值等
•Avg:求平均值
•Max:求最大值
•Min:求最小值
•Stats:同时求max、min、avg、sum等
管道(pipeline)聚合:其它聚合的结果为基础做聚合
RestAPI实现聚合
自动补全
自定义分词器
elasticsearch中分词器(analyzer)的组成包含三部分:
character filters:在tokenizer之前对文本进行处理。例如删除字符、替换字符
tokenizer:将文本按照一定的规则切割成词条(term)。例如keyword,就是不分词;还有ik_smart
tokenizer filter:将tokenizer输出的词条做进一步处理。例如大小写转换、同义词处理、拼音处理等
问题:
因此字段在创建倒排索引时应该用my_analyzer分词器;字段在搜索时应该使用ik_smart分词器;
completion suggester查询
对于文档中字段的类型有一些约束:
•参与补全查询的字段必须是completion类型。
•字段的内容一般是用来补全的多个词条形成的数组。
数据同步
elasticsearch与mysql之间的数据同步
elasticsearch集群
单机的elasticsearch做数据存储,必然面临两个问题:海量数据存储问题、单点故障问题。
•海量数据存储问题:将索引库从逻辑上拆分为N个分片(shard),存储到多个节点
•单点故障问题:将分片数据在不同节点备份(replica)
ES集群的节点角色
节点类型 | 配置参数 | 默认值 | 节点职责 |
---|---|---|---|
master eligible | node.master | true | 备选主节点:主节点可以管理和记录集群状态、决定分片在哪个节点、处理创建和删除索引库的请求 |
data | node.data | true | 数据节点:存储数据、搜索、聚合、CRUD |
ingest | node.ingest | true | 数据存储之前的预处理 |
coordinating | 上面3个参数都为false则为coordinating节点 | 无 | 路由请求到其它节点 合并其它节点处理的结果,返回给用户 |
ES集群的脑裂
为了避免脑裂,需要要求选票超过**(eligible节点数量+1)/2**才能当选为主,因此eligible节点数量最好是奇数。
ES集群的分布式存储
elasticsearch会通过hash算法来计算文档应该存储到哪个分片:
•_routing默认是文档的id
•算法与分片数量有关,因此索引库一旦创建,分片数量不能修改!
ES集群的分布式查询
elasticsearch的查询分成两个阶段:
•scatter phase:分散阶段,coordinating node会把请求分发到每一个分片
•gather phase:聚集阶段,coordinating node汇总data node的搜索结果,并处理为最终结果集返回给用户
ES集群的故障转移
集群的master节点会监控集群中的节点状态,如果发现有节点宕机,会立即将宕机节点的分片数据迁移到其它节点,确保数据安全,这个叫做故障转移。
微服务保护
Sentinel入门
雪崩问题
微服务调用链路中的某个服务故障,引起整个链路中的所有微服务都不可用,这就是雪崩
解决雪崩问题的常见方式有四种:
•超时处理:设定超时时间,请求超过一定时间没有响应就返回错误信息,不会无休止等待
•舱壁模式:限定每个业务能使用的线程数,避免耗尽整个tomcat的资源,因此也叫线程隔离。
•熔断降级:由断路器统计业务执行的异常比例,如果超出阈值则会熔断该业务,拦截访问该业务的一切请求。
•流量控制:限制业务访问的QPS(Query per Second),避免服务因流量的突增而故障。
对比:
Sentinel | Hystrix | |
---|---|---|
隔离策略 | 信号量隔离 | 线程池隔离/信号量隔离 |
熔断降级策略 | 基于慢调用比例或异常比例 | 基于失败比率 |
限流 | 基于 QPS,支持基于调用关系的限流 | 有限的支持 |
流量整形 | 支持慢启动、匀速排队模式 | 不支持 |
控制台 | 开箱即用,可配置规则、查看秒级监控、机器发现等 | 不完善 |
Sentinel安装
配置项 | 默认值 | 说明 |
---|---|---|
server.port | 8080 | 服务端口 |
sentinel.dashboard.auth.username | sentinel | 默认用户名 |
sentinel.dashboard.auth.password | sentinel | 默认密码 |
限流规则
簇点链路
就是项目内的调用链路,链路中被监控的每个接口就是一个资源。默认情况下sentinel会监控SpringMVC的每一个端点(Endpoint),因此SpringMVC的每一个端点(Endpoint)就是调用链路中的一个资源。(Controller中的方法)
流控模式
在添加限流规则时,点击高级选项,可以选择三种流控模式:
•直接:统计当前资源的请求,触发阈值时对当前资源直接限流,也是默认的模式
•关联:统计与当前资源相关的另一个资源,触发阈值时,对当前资源限流
满足下面条件可以使用关联模式:
两个有竞争关系的资源
一个优先级较高,一个优先级较低
•链路:统计从指定链路访问到本资源的请求,触发阈值时,对指定链路限流
Sentinel默认只标记Controller中的方法为资源,如果要标记其它方法,需要利用@SentinelResource注解,示例:
流控效果
流控效果是指请求达到流控阈值时应该采取的措施,包括三种:
•快速失败:达到阈值后,新的请求会被立即拒绝并抛出FlowException异常。是默认的处理方式。
•warm up:预热模式,对超出阈值的请求同样是拒绝并抛出异常。但这种模式阈值会动态变化,从一个较小值逐渐增加到最大阈值。
•排队等待:让所有的请求按照先后次序排队执行,两个请求的间隔不能小于指定时长
Comments NOTHING