吃水果毫不费力zz

吃水果毫不费力zz 查看完整档案

上海编辑上海交通大学  |  机械工程 编辑银联数据服务有限公司  |  java开发工程师 编辑填写个人主网站
编辑

啦啦啦

个人动态

吃水果毫不费力zz 发布了文章 · 11月22日

Docker swarm部署spring boot服务

原先部署单体式应用是把服务打成jar包放到服务器,通过shell脚本启停服务。对于微服务来说,这种部署方案十分繁琐。因为服务很多,每个服务都不止一个副本,并且服务部署在不同的机器。如果系统换版,光启停服务都需要一段时间。可以通过jenkins这种CI/CD工具帮助快速部署服务,但是生产环境可能不允许部署这种工具。对于微服务来说,需要动态扩缩容,版本回滚,服务上下线,故障自动转移,传统的部署方案已经不再适用。使用docker swarm可以帮助我们快速搭建微服务

使用docker的部署方案:
整体流程图:
image.png

Boot项目使用dockerfile-maven-plugin插件,会在maven deploy的同时,把代码打到镜像内并推送到仓库中。Dockerfile在本地配置,随代码一同推送至gitlab,jenkins自动化地完成从gitlab拉取代码-构建镜像-推送镜像至harbor-到目标服务器部署服务的操作。服务的部署即docker stack –c docker-compose.yml app。Docker会根据docker-compose.yml的配置从镜像仓库拉取镜像并启用容器。

应用:使用dockerfile-maven-plugin的maven插件和编写dockerfile。代码在gitlab保存,jenkins构建时会使用该插件根据dockerfile的配置将服务打成镜像推送到镜像仓库。

基础环境:

  • Docker环境:只有部署了docker基础环境,才能运行镜像,需指定镜像仓库,安装portainer-agent。
  • 搭建docker-swarm集群:微服务运行的机器环境
  • 创建overlay网络:应用运行的网络环境,网络内的容器可以相互通讯。
  • Harbor:docker镜像仓库,相当于gitlab,docker从harbor拉取镜像部署容器
  • Portainer:监控docker容器信息
  • 编写docker-compose.yml文件:定制微服务,即需要部署哪些服务,服务的镜像来源,服务的部署方式,副本数,健康检查方式等。

生产环境:互联网环境拉取镜像,docker save拉取到本地,拷至生产环境再docker load,docker tag至生产环境的harbor,再用docker-compose部署。如果构建的镜像太大,可以在本地把服务打成jar包,到生产机器构建镜像并推送到harbor。

和手动部署并启用服务相比,即在一台一台机器启动tomcat或java -jar,swarm解决了什么问题:

  • 一键部署。
  • 动态扩容缩容。如果流量大了,可以很快加副本。
  • 故障转移。如果某台机器挂掉,服务会转移到健康的机器
  • 可分配cpu,内存,硬盘空间等资源
  • 判断服务是否启动成功
查看原文

赞 1 收藏 1 评论 0

吃水果毫不费力zz 发布了文章 · 11月5日

alertmanager使用体验

AlertManager用于接收Prometheus发送的告警并对于告警进行一系列的处理后发送给指定的用户。
在prometheus中的角色:
image.png
对于告警,最简单的原则是来一次告警就发一次,这样会有大量重复且不必要的告警,漏掉重要的告警。
场景:

  • 正在发布,有服务的重启,不想在这段时间进行告警
  • 有些告警是由另一个告警引起,只想对最高级的错误进行告警
  • 类型相同的错误归集到一起告警
  • 一个类型的告警不重复发送,隔一段时间
  • 不同的告警发给不同的人

缺点:无法动态配置

告警过程:
Prometheus->PromQL触发阈值->超出持续时间->alertmanager->分组|抑制|静默|route->媒体类型->邮件|钉钉|微信|web_hook等
image.png

特性:

  • 分组:类型相同的告警会被合并为一个通知。避免一次性加收大量通知。
  • 抑制:当警报发出后,停止重复发送由此警报引发的其他警报。可以消除冗余告警。
  • 静默:是一种简单的特定时间静音的机制。例如:服务器要升级维护可以先设置这个时间段告警静默。

目的:避免重复告警而漏掉重要的报警,也避免漏报。

自定义规则文件,由prometheus定时扫描,包括:

  • expr: PromQL
  • for: 1m # 告警持续时间,超过这个时间才会发送给alertmanager
  • labels:自定义标签,发往alertmanager

Prometheus可查看规则的触发状态:inactive(正常),pending(规则触发但还未发往alertmanager),firing(已发往alertmanager,告警持续了for时间)

配置:

  • 全局配置(global):用于定义一些全局的公共参数,如全局的SMTP配置,Slack配置等内容;
  • 模板(templates):用于定义告警通知时的模板,如HTML模板,邮件模板等;
  • 告警路由(route):根据标签匹配,确定当前告警应该如何处理;
  • 接收人(receivers):接收人是一个抽象的概念,它可以是一个邮箱也可以是微信,Slack或者Webhook等,接收人一般配合告警路由使用;
  • 抑制规则(inhibit_rules):合理设置抑制规则可以减少垃圾告警的产生group_by: ['alertname'] # 采用哪个标签来作为分组依据

重要的参数:

  • group_wait: 10s # 分组报警等待时间,聚合相同组的报警
  • group_interval: 10s # 发送组告警间隔时间(组不同,如CPU_DOWN和MEMOPRY_OUT,发送的间隔时间为group_interval)
  • repeat_interval: 1h # 重复告警发送间隔时间(相同组,如CPU_DOWN重复发的化,需要等repeat_interval的时间)

在项目中的使用:

  • Alertmanager通过web_hook方式告警,配置basic auth
  • 提供短信告警接口,并校验basic auth

告警规则在prometheus中设置,可在rules画面中查看:
image.png
这里在rules中通过label配置告警人的联系方式,方便告警接口使用进行告警
alertmanager画面可查看当前有哪些被触发,可对告警组进行静默
image.png
可查看告警方式配置,这里采用web_hook的方式
image.png

也可以自己写http请求发送至alertmanager,帮助我们更合理地告警,不必捆绑prometheus。后续计划使用flink对网关交易进行监控,如果有异常交易,发送http请求至alertmanager,由alertmanager决定是否告警及何时告警。

查看原文

赞 0 收藏 0 评论 0

吃水果毫不费力zz 发布了文章 · 11月2日

apollo在项目的使用

Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性,适用于微服务配置管理场景。

功能:

  • 统一管理不同环境、不同集群的配置,以命名空间namespace为最小粒度进行配置,一个服务引入了这个命名空间,即使用了该命名空间的配置。
  • 配置修改实时生效
  • 版本发布管理
  • 灰度发布
  • 权限管理、发布审核、操作审计
  • 客户端配置信息监控

使用:

  • 服务端的配置:

    • 新建appId,appId可以理解为是一套应用。在appId新建namespace添加配置内容。Namespace可以理解为是配置的集合,原先一个yml文件存放配置,现在可以通过某个环境 ,某个appId下的namespace引入。
  • 客户端的使用:

    • 引入maven包并在启动类加入@EnableApollo即可,通过meta.server,appId和namespace找到所需的配置。可以理解为在apollo的配置就在配置文件里。通过@Value,@ConfigurationProperties引入的变量不受影响,对代码的入侵比较小。
    • 监听配置的变化,不同的namespace
  • 在项目的使用:

    • 把配置信息放入apollo。直接的做法是每个服务使用一个namespace,但是经过梳理发现,有些配置是多个服务共同使用的。
    • 服务的配置进行分类:

      • 通用:Log,eureka,feign调用相关的
      • 某些服务公用:database(openapi monitor) redis(card openapi double) kafka activemq ElasticSearch
      • 各自的服务拥有单独的namespace

经过分类后,如果要修改数据库地址,redis地址,或者新增一个中间件的地址,只用新增namespace,在配置文件引入/修改该namespace即可。
如果想更改某个服务的配置,在相应的namespace下修改,并重启docker服务。可以看到发布历史,有哪些实例在使用。
项目是用docker部署的,原先一部分配置在服务里,一部分配置在docker-compose里,改造后尽可能所有的配置都放在apollo里,apollo的配置放在docker-compose.yml里,docker-compose.yml可以引入公共的配置文件env_file,真正的apollo配置存放文件,包括apollo.meta,app.id,apollo.cacheDir,不用再通过profile.active区分。一些无法通过apollo配置的放在服务的配置或docker的配置中。理论上能当在服务的配置文件里的都能放在apollo里。
每个服务只需一个配置文件,甚至不用配置文件。为了本地开发的方便,在本地放置了sit环境的配置。
通过apollo.meta区分不同的环境,apollo.meta变量通过docker-compose文件的公共配置env_file引入

需要注意的:

  • 配置的备份
  • 假设两个人对同一个服务进行开发,需要修改配置
  • Application里的配置会实时生效,自定义的namespace不会。
  • 开关,限流额度,路径
  • 对于日志级别,database等需要更新bean的,需要写代码

Namespace的类型:

  • 私有:namespace从属于appId,只有配置了该appId才可访问
  • 公有:只要连上了apollo就能使用,属于所有应用,全局唯一
  • 关联:属性是私有的,可以理解为在继承公有属性的基础上,可对公有属性进行修改

其他的功能:

  • 密钥管理,保护配置不被其他应用获取
  • 权限分配:以namespace为粒度分配修改,发布,查看的权限
  • 添加集群
  • 配置覆盖,前面的会覆盖后面的,尽可能不要覆盖,保证配置的唯一性

架构:
image.png
从逻辑上来说比较清晰,将配置的编辑/发布与客户端获取配置分开,用两个服务实现
Portal通过调用admin service接口修改配置
Client通过config service接口获取配置
为了保证高可用,添加了一层eureka,client和portal通过eureka调用接口服务。
为了使不同语言的client可通过http接口即可获取admin service和config service的信息,eureka上搭建Meta service,用来封装服务发现的细节,不用关心背后的注册中心和服务发现组件(用于不同语言的服务注册到eureka上)。

完整架构如下:
image.png
主要模块:

  • Config Service:配置的读取、推送等,服务对象是Apollo客户端
  • Admin Service:配置的修改、发布等,服务对象是Apollo Portal(管理界面)
  • Config Service和Admin Service都是多实例、无状态部署,所以需要将自己注册到Eureka中并保持心跳
  • Meta Server:在Eureka之上搭建,用于封装Eureka的服务发现接口:

    • Client通过域名访问Meta Server获取Config Service服务列表(IP+Port),而后直接通过IP+Port访问服务,同时在Client侧会做load balance、错误重试
    • Portal通过域名访问Meta Server获取Admin Service服务列表(IP+Port),而后直接通过IP+Port访问服务,同时在Portal侧会做load balance、错误重试
  • 为了简化部署,实际上会把Config Service、Eureka和Meta Server三个逻辑角色部署在同一个JVM进程中

服务端设计:
image.png
Admin service发布了配置修改的事件,所有config service收到消息并通知客户端修改配置。
有两个值得去探索的点:

  • Admin service发布事件的方式:可以用中间件redis,activemq实现。这里为了减少外部组件的依赖,通过数据库实现。

image.png
具体的做法是Admin发布一条配置后,这个配置肯定属于某个namespace,在ReleaseMessage表插入一条记录AppId+Cluster+Namespace,config service有一个线程每秒扫描ReleaseMessage表,如果有新的消息记录,就通知客户端更新配置。所以配置如果有更新,一般一秒内就可通知到客户端。

  • Config service通知客户端的方式:客户端发起获取配置的请求,config service用defferResult将请求挂起,如果60秒内没有感兴趣的namespace配置发生变化,返回304,否则立即返回变化后的配置。使用defferResult可以提高请求的并发量

客户端设计:
image.png

  • 客户端和服务端保持了一个长连接,从而能第一时间获得配置更新的推送。(通过Http Long Polling实现)
  • 客户端还会定时从Apollo配置中心服务端拉取应用的最新配置。

    • 这是一个fallback机制,为了防止推送机制失效导致配置不更新
    • 客户端定时拉取会上报本地版本,所以一般情况下,对于定时拉取的操作,服务端都会返回304 - Not Modified
    • 定时频率默认为每5分钟拉取一次,客户端也可以通过在运行时指定System Property: apollo.refreshInterval来覆盖,单位为分钟。
  • 客户端从Apollo配置中心服务端获取到应用的最新配置后,会保存在内存中
  • 客户端会把从服务端获取到的配置在本地文件系统缓存一份

    • 在遇到服务不可用,或网络不通的时候,依然能从本地恢复配置
  • 应用程序可以从Apollo客户端获取最新的配置、订阅配置更新通知
  • 测试环境下多环境部署SIT,QA,UAT,用一套画面管理不同的环境:

image.png

与spring的集成原理
Spring的ApplicationContext会包含一个Environment,在服务启动的时候把参数注入即可。

查看原文

赞 0 收藏 0 评论 0

吃水果毫不费力zz 发布了文章 · 11月2日

prometheus使用简介

Prometheus由go语言编写的基于时序数据库的监控告警系统,已加入CNCF基金会,是CNCF推荐使用的监控告警系统。Prometheus可以看做微服务监控告警的解决方案,通过部署exporter可以监控中间件的状态,微服务的运行状态,通过alertmanager进行告警。自研的中间件可以通过暴露/metrics接口纳入prometheus的监控,通过grafana模板可视化展示。

时序/监控数据的特点是:

  • 增:需要频繁的进行写操作,而且是按时间排序顺序写入
  • 删:不需要随机删除,一般情况下会直接删除一个时间区块的所有数据
  • 改:不需要对写入的数据进行更新
  • 查:需要支持高并发的读操作,读操作是按时间顺序升序或降序读,数据量非常大,缓存不起作用

prometheus特点:

  • 多维度数据模型-由指标键值对标识的时间序列数据组成(底层的数据结构是时序数列,可以对指标添加标签便于筛选)
  • PromQL,一种灵活的查询语言。(在查询的时候组合条件进行筛选)
  • 不依赖分布式存储; 单个服务器节点是自治的。部署方便
  • 以HTTP方式,通过pull模式拉取时间序列数据
  • 支持通过中间网关推送时间序列数据
  • 通过服务发现或者静态配置,来发现目标服务对象
  • 支持多种多样的图表和界面展示

架构:
image.png

组件:

  • Prometheus server:用于抓取和存储时间序列数据
  • client libraries:暴露监控指标的代码库
  • exporter:监控目标的服务
  • push gateway:根据prometheus的告警请求管理告警,可配置邮件,slack,web hook等方式
  • service discovery:发现被监控服务的方式,包括静态文件,k8s,consul等
  • 各种支持工具如可视化工具grafana

简单的工作原理:
客户端安装exporter监控中间件或服务集成依赖包,暴露监控指标,prometheus定期从客户端pull数据,通过grafana或api clinet进行查询。可配置告警规则,prometheus将符合规则的指标发往alertmanager,由alertmanager决定是否告警以及何时告警。

在项目的应用:

  • 对微服务的改造:

    • 通过actuator和prometheus,对外暴露/actuator/prometheus接口,以供prometheus拉取监控数据
    • 不想对外暴露监控接口,通过spring security保护/actuator/**接口,即为每个服务添加filter,prometheus访问时,需要提供basic auth。注意区分servlet和webflux应用
  • 服务发现:

    • 微服务架构中一个服务通常有多个副本,因此prometheus中通过IP:port无法获取每个服务的指标。如果微服务用docker部署,prometheus得到的是一个时刻某一容器的监控指标,无法获取全部容器指标
    • 因此采用eureka-consul进行服务发现,可以从eureka获取注册的所有服务。此时需要将prometheus部署到服务网络,通过内部ip:port访问到所有容器
  • Prometheus server:

    • 监控SIT/QA/UAT环境:

      • 通过一个prometheus监控不同环境,通过标签(job_name)进行区分。不可行。因为prometheus需要加入sit,qa,uat服务的docker网络,一个prometheus无法同时加入多个网络。
      • Push gateway,架构复杂,需要保证pushgateway的高可用;无法主动获取各个服务的状态
      • 最终采取方案:每个环境均搭建prometheus ,grafana共用一个,在grafana通过选取不同的数据源展示不同环境的数据。使用到的数据库和中间件如mysql,redis,zookeeper,kafka,canal使用单独的prometheus进行监控

image.png

prometheus部署
image.png

使用流程

  1. Grafana中下载spring boot,Mysql,Redis等模板
  2. 下载对应exporter,修改配置
  3. Prometheus修改配置,添加exporter监控
  4. Grafana导入模板,配置prometheus中对应的exporter数据源

image.png

监控的目的:

  • 目前我们做的监控比较少,仅有的是健康检查,以及在portainer里docker容器的系统信息,业务指标如应用的qps没有监控。prometheus可以搭建基础的监控服务,需要的监控可以在prometheus集中展示。
  • 大部分时间服务都是正常运行的,不出事的话显得监控没什么意义,一旦出了问题,监控可以看出现故障时刻的监控指标。如交易数过多,内存,cpu使用情况。
  • 事前预警,事后追踪
  • 性能优化

不适用场景:
如果需要100%的准确度,例如按请求计费、出报表,Prometheus不是一个好的选择,它不适合做基础的数据库,因为收集的数据可能不够详细和完整。它在这种情况下,最好使用其他系统如es来收集和分析数据,使用Prometheus进行其余监控。

prometheus数据模型:
从根本上存储的所有数据都是时间序列: 具有时间戳的数据流只属于单个度量指标和该度量指标下的多个标签维度。除了存储时间序列数据外,Prometheus还可以生成临时派生的时间序列作为查询的结果。
每一个时间序列数据由metric度量指标名称和它的标签labels键值对集合唯一确定。这个metric度量指标名称指定监控目标系统的测量特征(如:http_requests_total- 接收http请求的总计数)。 metric度量指标可能包含ASCII字母、数字、下划线和冒号,他必须配正则表达式a-zA-Z_:* 。
labels开启了Prometheus的多维数据模型:对于相同的度量名称,通过不同标签列表的结合, 会形成特定的度量维度实例。(例如:所有包含度量名称为/api/tracks的http请求,打上method=POST的标签,则形成了具体的http请求)。这个查询语言在这些度量和标签列表的基础上进行过滤和聚合。改变任何度量上的任何标签值,则会形成新的时间序列。
image.png

  • counter 是表示单个单调递增计数器的累积度量,其值只能在重启时增加或重置为零。Eg:请求数
  • gauge是一个度量指标,它表示一个既可以递增, 又可以递减的值。Eg:cpu内存占用
  • histogram,对观察结果进行采样(通常是请求持续时间或响应大小等),并将其计入可配置存储桶中。它还提供所有观察值的总和。
  • summary是采样点分位图统计(通常是请求持续时间和响应大小等)。虽然它还提供观察的总数和所有观测值的总和,但它在滑动时间窗口上计算可配置的分位数。

关于alertmanager的使用可参考:
https://segmentfault.com/a/11...

查看原文

赞 0 收藏 0 评论 0

吃水果毫不费力zz 发布了文章 · 10月29日

基于MDC的日志链路监控

gateway链路监控
现状:目前查找一个交易的详情,需要通过portainer进入请求所在的容器查找流水号对应的日志。由于请求很多,同一请求的日志可能不在一起,并且同一请求中有的日志无法添加流水号。
目的:通过流水号对网关日志进行查询,可得到同一请求的所有日志。
原理图:
image.png
方法:
将gateway的同一请求的日志添加流水号:

  • 请求进入时日志添加流水号
  • 响应回来时日志添加流水号,由于请求和响应不是同一个线程处理
  • 发送响应时删除日志流水号
  • 异步操作传递流水号

通过ELK对网关日志进行收集,以gateway-{date}归集一个index,做为基础的服务日志。
Filebeat:换行的日志当作一条发送到kafka
Logstash:解析orderNo当作es的一个字段

查询:
配置后端服务的日志收集地址,在查询交易日志时可通过api查到关联的下游日志
以api-invoke-his做为交易流水库,根据流水号,网关类型和日期到gateway-{date}(昨今明天)查询具体gateway日志,根据apiName查到关联的下游日志index,综合得到整条链路的日志

缺点:

  • 代码侵入性比较强,需要改动四处地方
  • 主动配置的地方较多,需要配置交易的日志收集index,即交易的链路。需要提前知道交易的链路。对比skywalking,引入agent即可打印链路的信息,但是无法打印日志。
  • 如果想监控mysql、es,需要自己开发agent

系统日志监控:
背景:
微服务中系统日志散落在各个机器中,无法集中查看,目前通过elk可收集各个系统的日志在kibana查看。但是日志结构比较松散,出了问题排查困难,需要事先找到机器。此外如果日志量大,难以查看报错日志的上下文。因此需要快速找到一次请求链上的所有日志。因此需要一种快速知道哪里报错以及找到报错上下文日志的方案。

方案简述:
将同一请求/处理逻辑的日志添加相同流水号,方便系统异常时查到关联日志。其中所有服务的异常日志放入同一个es的index,方便直接展示错误日志。如果该日常包含流水号,可去相关服务查询详细日志。如果知道请求经过的服务,可以查询链路的相关日志。错误日志也放入kafka的topic中,便于监控系统对接告警。

目的:

  • 快速找到报错的日志及流水号,便于查看系统启动时是否成功
  • 通过流水号找到统一请求的日志。(需要手动关联同一流水号的多个服务,无法做到api级别的动态关联,如果查找所有index比较耗费性能)

原理图:
image.png
流程:

  • Filebeat监听服务日志,定义logtopic放入kafka的openapi_{logtopic}中
  • Logstash监听openapi_{logtopic}的topic,将数据格式化后输出至三个方向:
  • 全量输出至{logtopic}_{date},即在es中按logtopic和日期为index进行保存
  • 级别为error的日志一方面输出至openapi_error_{date},即以error和日期为index进行保存,另一方面输出至topic为openapi-error的kafka中,供监控告警使用。
  • 这些输出是并行的。
  • Monitor以error_{date}为基础数据源进行查看,如果异常日志包含流水号,则可跳转至{logtopic}-{date}进行查看服务的日志。如果查看相关链路日志,需要联合index进行查询。

需要解决的问题:

  • 流水号在服务内部传递
  • 流水号在服务间传递
  • 流水号在异步线程传递
  • 服务在某个方法中传递

服务日志添加流水号的方式:

  • request filter:web请求到来时,如果此时线程中已经设置过流水号,不用再设置。如果没有设置且header中包含orderNo,则将其作为当前线程的order,否则新生成orderNo并设置。确保filter是请求第一个进入的,避免一些log记录不到流水号。
  • Feign intercepter:服务通过feign调用其他服务时,将当前线程的orderNo放入header中,保证下游服务可以获得orderNo。如果有使用hystrix,需要在hystrix线程池中传递mdc
  • 异步线程池:异步执行时,无法传递流水号,因此需要使用特定的线程池,可以传递流水号
  • 单独执行的方法:redis,mq的监听,执行定时任务,由特定事件执行,而非web请求触发,通过aop在方法执行前设置MDC,完成后清除

改造的地方:

  • 异步执行使用特定线程池
  • 单独执行的方法添加注解
  • Filebeat增加换行的配置,只传输*All的日志
  • Logstash增加orderNo的解析,log_level为error的传入es及kafka中

问题:
如果不是logger.info或logger.error的日志,如果出现异常如NullPointerException,虽然是同一个线程处理,但流水号无法继承。需要有GlobalExceptionAspect捕获全局异常

Async-threadpool:

  • 提供一个可以直接使用的线程池,@Async直接使用。使用方便,不用什么配置
  • 提供获取线程池的静态方法,可以通过方法获取,通过@Bean注入,便于设置线程池参数。场景:有多个参数不同的线程池需求
  • 提供线程池装饰器,自定义的线程池加上装饰器即可添加mdc

Method-aspect:
有些代码的执行并不由web请求触发,而是由定时任务,mq的监听触发,执行某个方法。因此需要对方法进行AOP拦截,执行前添加MDC,执行完后删除。此外注意发生异常时也需要拦截并删除MDC。
异常如果不用logger.error/info记录,则无法打印MDC,自动抛出的异常(如除数为0)虽然在一个线程处理但mdc为空。因此AOP需要拦截异常并用logger.error进行记录。
此外,@PostConstruct执行的方法无法添加MDC。

Feign-interceptor:
如果当前线程包含MDC,则在header中设置,否则不做处理。由于有可能使用到hystrix进行限流熔断,需要对hystrix进行配置

Request-interceptor:
如果当前线程包含MDC,不做处理;如果不包含,查看header是否含有orderNo,如果包含,将orderNo作为流水号设为MDC,否则生成新orderNo作为MDC。

注意的几个点:

  • Filebeat同步的是**All.log日志,避免error出现两次
  • MDC put后再put,会清除
  • Request filter的order; filter在interceptor前,因此需要用filter且优先级最高,保证请求到来第一个进入
  • Gateway无法使用skywalking,因为是nio,请求和响应是不同线程处理,响应无法与请求的mdc保持一致
  • 第三方jar包的类名最好加上服务名称,避免重复
查看原文

赞 0 收藏 0 评论 0

吃水果毫不费力zz 发布了文章 · 10月28日

微服务思考

随着系统的架构和功能越来越复杂,单体应用已经满足不了性能和可维护性的需求,需要将功能拆分,因此产生微服务。通过将不同功能的模块拆分,可以保证高可扩展,高性能,高可用,易测试的特性。由于将服务拆分,会带来一些问题:

  • 节点不可靠,节点之间的通讯不可靠。不像单体应用调用不是成功就是失败,服务间调用会有延迟,超时,丢包,乱序等现象。
  • 分布式事务:由于不同服务使用各自的数据库,acid特性已无法保证,由强一致性到最终一致性

即使系统99.999%的时间是正常提供服务的,大量的节点长时间工作也会出问题。因此做架构设计时需考虑异常出现会怎么办。

微服务需要满足一下几个特性:

  • 计算节点无状态
  • 接口幂等

微服务相关组件:

  • 注册中心:eureka,zookeeper,nacos
  • 配置中心:config,apollo
  • Rpc框架:feign,dubbo
  • 负载均衡:ribbon
  • 限流熔断器:hystrix,sentinel
  • 网关:gateway

可以认为注册中心是一个中心化存储,通过http的形式暴露哪些服务可以被调用。服务也可以通过http注册到配置中心。配置中心保存了各个服务的配置信息。服务从注册中心获取可访问的服务列表,根据一定的负载均衡策略,通过rpc调用下游服务。下游接口由限流熔断器保证安全,防止被击垮。

其他:

  • 监控告警:actuator,prometheus,alertmanager,grafana
  • 任务调度
  • 跑批
  • 鉴权

保证接口高可用:

  • 熔断
  • 降级
  • 限流
  • 幂等

对于分布式存储,典型的如Redis cluster,Zookeeper,Es,Kafka,eureka,需要考虑:

  • 做到高可用,解决数据冗余带来的一致性问题,由CAP到BASE,数据一致性协议
  • 有没有管理节点/主节点
  • 主/从节点挂了如何处理
  • 数据如何分片
  • 是否读写分离
查看原文

赞 0 收藏 0 评论 0

吃水果毫不费力zz 发布了文章 · 10月28日

分布式事务介绍

事务是指要满足acid条件的一系列操作。事务内的操作要么同时成功要么同时失败。单体数据库的事务性由存储引擎保证,在分布式应用中,服务在不同的机器,由于网络、应用不可靠,保证操作的全局事务性十分困难,需要一定的妥协。保证分布式事务实质是保证不同数据库的数据一致性。
分布式场景下,满足绝对ACID原则的刚性事务性能会有较大损耗,因此从强一致性变为基于BASE理论的最终一致性,即数据可以不一致,但最终会达到一致的状态,满足柔性事务。

典型场景:

  • 转账:A账户增加10元,B账户减少10元
  • 商品购买:下单-扣账户金额-扣库存-发货
  • 异常:如果是调用第三方服务,下游接口超时或失败则重试或回滚事务

首先要明确哪些业务需要保证事务性,哪些需要同步保证,哪些需要异步保证。能不用分布式事务尽量不用。

XA协议/二阶段提交
XA 规范是 X/Open 组织定义的分布式事务处理(DTP,Distributed Transaction Processing)标准。描述了全局的事务管理器与局部的资源管理器之间的接口。目的是允许的多个资源(如数据库,应用服务器,消息队列等)在同一事务中访问,这样可以使 ACID 属性跨越应用程序而保持有效。使用两阶段提交(2PC,Two-Phase Commit)来保证所有资源同时提交或回滚任何特定的事务。在上世纪 90 年代初就被提出。目前,几乎所有主流的数据库都对 XA 规范 提供了支持。

流程:

  • 第一阶段(prepare):事务管理器向所有本地资源管理器发起请求,询问是否是 ready 状态,所有参与者都将本事务能否成功的信息反馈发给协调者;
  • 第二阶段 (commit/rollback):事务管理器根据所有本地资源管理器的反馈,通知所有本地资源管理器,步调一致地在所有分支上提交或者回滚。

问题:

  • 数据源可能不支持XA协议,如es,redis
  • 长事务影响性能,同步阻塞资源
  • 事务管理器容易出现单点故障,导致整个系统不可用
  • 如果只有部分资源管理器commit,会使数据不一致

除了基于xa协议的分布式事务,还有基于最终一致性的分布式事务方案,即允许数据有不一致的状态,但最终会达到一致
理想的方案:业务入侵小,需要手写的代码少,性能高

解决方案:
业务是否有入侵:

  • 业务无入侵

    • XA,seata
  • 业务有入侵

    • TCC 基于消息队列/本地消息表 saga

一致性保证:

  • 强一致性

    • XA,需要锁定资源
  • 最终一致性,可能会读到脏数据,遵循base理论,实现柔性事务

    • 事务补偿:TCC,saga
    • 事务回滚:Seata
    • 消息日志:基于消息队列/本地消息表
    • 本质是感知子事务的执行状态,子事务要有回退/重试的逻辑(手写或代理实现)

参考:
https://xiaomi-info.github.io...

最终一致性大致思路:

  • 服务A调用下游服务B,执行业务sql的同时插入调用日志表,调用状态为已创建。这两个sql保证是一个事务,保证有迹可查。然后调用B。调用可为rpc或mq异步调用
  • 下游服务B如果执行成功,调用服务A接口,修改调用记录表,调用状态改为处理成功
  • 通过定时任务扫描调用记录表,状态不为处理成功的进行重发。调用记录表的存在确保消息一定被下游处理,保证数据最终一致。下游可能会处理多次重复的消息,因此要保证接口幂等。

异常情况:

  • 调用下游服务失败,调用状态为已创建
  • 调用成功,但由于网络原因下游未收到,调用状态为已创建
  • 下游处理成功,但未返回,调用状态为已创建

这些异常情况都会被定时任务扫描并进行重试
对于seata,调用日志表为回滚日志表,如果全局事务的任一子事务失败,则全局回滚。

基于本地消息表
image.png

步骤:

  • 当系统 A 被其他系统调用发生数据库表更操作,首先会更新数据库的业务表,其次会往相同数据库的消息表中插入一条数据,两个操作发生在同一个事务中
  • 系统 A 的脚本定期轮询本地消息往 mq 中写入一条消息,如果消息发送失败会进行重试
  • 系统 B 消费 mq 中的消息,并处理业务逻辑。如果本地事务处理失败,会在继续消费 mq 中的消息进行重试,如果业务上的失败,可以通知系统 A 进行回滚操作

本地消息表实现的条件:

  • 消费者与生成者的接口都要支持幂等
  • 生产者需要额外的创建消息表
  • 需要提供补偿逻辑,如果消费者业务失败,需要生产者支持回滚操作

容错机制:

  • 步骤 1 失败时,事务直接回滚
  • 步骤 2、3 写 mq 与消费 mq 失败会进行重试
  • 步骤 3 业务失败系统 B 向系统 A 发起事务回滚操作

此方案的核心是将需要分布式处理的任务通过消息日志的方式来异步执行。消息日志可以存储到本地文本、数据库或消息队列,再通过业务规则自动或人工发起重试。人工重试更多的是应用于支付场景,通过对账系统对事后问题的处理。
本地消息表保证消息一定会被下游服务消费,至少消费一次,因此下游服务的接口要保证幂等。

参考:
https://juejin.im/post/684490...
https://jeremyxu2010.github.io/2020/03/%E5%BE%AE%E6%9C%8D%E5%8A%A1%E4%B8%AD%E7%9A%84%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%96%B9%E6%A1%88/
https://houbb.github.io/2018/...
https://www.pianshen.com/article/36841500709/
https://segmentfault.com/a/11...

基于可靠的消息服务
image.png

  • 第一阶段Prepared消息,会拿到消息的地址。
  • 第二阶段执行本地事务。
  • 第三阶段通过第一阶段拿到的地址去访问消息,并修改状态。消息接受者就能使用这个消息。
  • 如果确认消息失败,在RocketMq Broker中提供了定时扫描没有更新状态的消息,如果有消息没有得到确认,会向消息发送者发送消息,来判断是否提交,在rocketmq中是以listener的形式给发送者,用来处理。

通过rocketmq本身的事务性保证执行业务逻辑和消息发送同时成功或失败,下游数据至少被消费一次

参考:
https://mp.weixin.qq.com/s/Uc...

TCC
TCC是一种两阶段型模式,只有在所有的服务的第一阶段(try)都成功的时候才进行第二阶段确认(Confirm)操作,否则进行补偿(Cancel)操作,而在try阶段是不会进行真正的业务处理的。
TCC模式的具体流程为两个阶段:

  1. Try,业务服务完成所有的业务检查,预留必需的业务资源
  2. 如果Try在所有服务中都成功,那么执行Confirm操作,Confirm操作不做任何的业务检查(因为try中已经做过),只是用Try阶段预留的业务资源进行业务处理;否则进行Cancel操作,Cancel操作释放Try阶段预留的业务资源。

TCC模式跟纯业务补偿模式相比,需要每个服务都需要实现Confirm和Cancel两个接口,因此落地实施上比纯业务补偿模式复杂一些,但好处是数据一致的实时性高,因此其在很多金融、电商场景中大量采用。
在事务补偿方案中,由于上游服务依赖于下游服务的结果,考虑到上下游服务间网络有可能是不稳定的,因此业务接口、补偿接口(Saga模式中)和Try接口、Confirm接口、Cancel接口(TCC模式中)均有可能会被多次调用,因此这些接口在实现时需要考虑幂等性。

参考:
https://www.cnblogs.com/jajia...
https://www.bytesoft.org/

比较三种最终一致性的分布式事务方案,在执行业务sql时都要在同一事务中插入操作/回滚记录,保证相关事务异常时回滚或重试,当全局事务成功后删除记录。

查看原文

赞 0 收藏 0 评论 0

吃水果毫不费力zz 发布了文章 · 10月28日

Redis cluster简介

为了避免redis单点故障,提高redis的读写性能,redis官方提供了一种集群方案redis cluster。它的特点是:

  • 高性能:做了数据分片,可由多个节点提供读写功能
  • 高可靠性:每个主节点都有从节点备份,可做主从转换
  • 高可扩展:可水平扩展,将数据重新分片即可

架构:
image.png
Cluster将根据一致性哈希规则将数据分片,每个master放置一部分数据,slave从master同步数据,当master挂掉,slave切换为master。Master之间通过心跳交换元数据如slot信息,节点健康信息。每个redis均需开通两个端口,一个对外提供服务,另一个+10000,用来集群间通过gossip协议相互通讯。该通讯协议是一种二进制协议,节点间可高效交换数据,占用更少的网络带宽及处理时间。

客户端访问cluster:

  1. 客户端访问集群的某一节点
  2. 节点判断该key的slot是否属于自己

    • 如果属于,返回数据
    • 如果不属于,返回MOVED重定向至所在节点

数据分片:

  • 简单的hash算法

按key计算hash后对节点个数取余,按余数落到对应机器
c = hash(key)/n。当增加或缩减集群节点时,会有大量key对应的节点变化,缓存失效,造成缓存击穿。

  • 一致性hash

将key的hash值映射到一个圆环,落在顺时针最近的节点。当某个节点挂掉后,只需迁移该节点的数据至顺时针下一个节点即可。如果增加节点,将原先属于一个节点的数据分成两部分即可。节点的扩缩容只需迁移一个节点的数据即可,大部分数据不受影响。
c –> hash(key)/n
问题:数据分配不均匀,有可能有的节点数据很多,有的很少。

  • 虚拟hash槽

Cluster采用虚拟hash槽,slot=crc16(key)%16383,每个节点维护一定槽位和槽位对应的键值。虚拟hash槽将圆环分成了更加细小的16383份,用户可自行维护槽位和节点的关系,内存多的节点多分配。使数据的落位更加均匀,避免某一节点的数据过多。节点的扩缩容是槽位的重新分配,只影响槽位对应的数据。
image.png
优点:

  • 解耦数据和节点的关系,降低了扩缩容的难度及影响性
  • 集群维护槽位与数据的关系,无需客户端或中间件代理

Cluster可在不影响对外提供服务的情况下进行扩缩容,槽是cluster维护数据的基本单位,集群的扩缩容是槽位的重新分配,cluster不会自动分配,需要手动维护。
客户端指定节点操作数据
Redis 计算槽时并非只简单的计算键值内容,当键值内容包括大括号时,则只计算括号内的内容。比如key为 user:{10000}:books时,计算哈希值只计算10000
当所有槽位均分配完毕后,集群才可对外提供服务

节点间的通讯:
采用gossip协议
Redis cluster将数据分片存储,但集群元信息通过节点间相互通信,集群内所有节点的元信息是一样的。

  • 分散存储元信息特点:ap优先,保证每个节点都有元信息,数据的更新有延迟,保证最终一致。
  • 集中式存储元信息特点:cp优先,各节点会很快感知元数据的变化。但是读写都在一个地方,存储会有压力。
  • 当节点数很多时,集中式存储可能会有读写的压力,而分散存储会有数据不一致的问题。

Cluster各节点通过+10000端口发送ping信息,其他节点收到后回复pong。交换节点信息,slot信息。

消息种类:

  • Meet:新节点发送meet给集群的某一结点,即加入集群,新节点会与其他节点通讯。Cluster初始化时会meet其他节点
  • Ping:每个节点给其他节点发送ping信息以交换元信息,每次会选择5个最久没有通信的其他节点,每次ping带上自己节点和1/10其他节点的信息,最少3个,最多all-2个。
  • Pong:对ping和meet的响应,包含自己的状态
  • Fail:判断某个节点宕机

Smart客户端 jediscluster
由于cluster会返回moved重定向以及该slot所属的正确的节点信息,需要客户端重新发请求获取数据,因此需要smart客户端。smart客户端功能:

  • 缓存并维护slot和节点对应关系
  • 如果服务端slot更新,处理moved/ask重定向逻辑

客户端查询流程:

  • 客户端根据本地缓存slot信息发往对应节点,如果数据存在直接返回结果
  • 如果返回moved错误,更新本地slot映射信息,发往新的节点

在我们项目的使用:

  • 没有用spring-data-redis,封装了service层,针对常用操作实现了get,set,delete,hasKey,hget,hset,hdelete,lpush,rpop,ttl,publish等方法
  • 实现了分布式锁,分布式计数器,get后立即失效,集群环境下执行lua脚本的命令

    • 分布式锁:如果key在redis中存在,则获取失败返回false,否则设置该key及失效时间,返回成功。Redis操作为单线程,判断key是否存在及设置key应为一个原子操作,通过lua脚本实现。在分布式定时任务中抢锁执行定时任务有用到,定时任务在多个服务中启用,但同时只有抢到锁的那个服务能调用成功,保证服务的冗余及同一时刻只有一个节点执行定时任务。
    • 分布式计数器:和分布式锁类似,如果key在redis中存在,则+1,否则设为1。该过程为原子操作。用于接口限流。
    • get后立即失效:和分布式锁类似,如果key在redis中存在,则删除,返回true,否则返回false。用于用户登录时校验图片验证码,校验后验证码立即失效,提高脚本进攻的难度。
    • lua脚本执行方法。由于在集群环境下,key存储在不同节点,为确保lua脚本是一个原子操作,需要不同的key在同一节点执行。即lua脚本中的每个key前添加同一{xx},cluster分片时会按照xx进行hash分槽。
  • 支持同时连接多个cluster,根据dbkey指定集群。为了将缓存隔离,搭建了多个redis集群,配置文件中通过dbkey区分不同的集群。调用redisService方法时如果不指定dbkey则操作默认cluster,如果传入dbkey参数则操作指定cluster
  • 可按cluster添加统一前缀。由于测试环境分为sit,qa,uat,希望使用同一套cluster,cluster没有单机db的概念,因此在配置cluster时,统一添加前缀sit,qa,uat进行区分,调用redisService方法时自动添加。
  • 支持配置密码。可连接配置和不配置密码的cluster,由配置文件决定
查看原文

赞 0 收藏 0 评论 0

吃水果毫不费力zz 发布了文章 · 10月27日

seata使用体验

Seata 是一站式的分布事务的解决方案,提供四种事务模式:

  • AT 模式
  • TCC 模式
  • Saga 模式
  • XA 模式

Seata将一个分布式事务看作一个全局事务,将其分解为若干个分支事务,而一个分支事务是一个满足 ACID 的本地事务,因此我们可以操作分布式事务像操作本地事务一样。

Seata的三种角色:
image.png

  • TC (Transaction Coordinator) - 事务协调者:维护全局和分支事务的状态,驱动全局事务提交或回滚。
  • TM (Transaction Manager) - 事务管理器:定义全局事务的范围,开始全局事务、提交或回滚全局事务。一般是一个方法,保证方法内的rpc调用对数据库的操作同时成功或失败。
  • RM ( Resource Manager ) - 资源管理器:管理分支事务处理的资源( Resource ),与 TC 交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。可以认为一个数据库实例是一个RM

TC 为单独部署的 Server 服务端,TM 和 RM 为嵌入到应用中的 Client 客户端。

工作流程:
image.png

  1. TM 请求 TC 开启一个全局事务。TC 会生成一个 XID 作为该全局事务的编号XID。XID会在微服务的调用链路中传播,保证将多个微服务的子事务关联在一起。
  2. RM 请求 TC 将本地事务注册为全局事务的分支事务,通过全局事务的 XID 进行关联。

    • 如果rm发现当前线程包含xid,将本地事务注册到tc,关联到全局事务
  3. TM 请求 TC 告诉 XID 对应的全局事务是进行提交还是回滚。

    • 当@GlobalTransaction的方法执行完成后,判断是提交还是回滚
    • 也可调用rollback(xid)方法手动回滚
  4. TC 驱动 RM 们将 XID 对应的自己的本地事务进行提交还是回滚。

机制:
两阶段提交协议的演变:
一阶段:
解析sql,生成回滚sql,将业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。提交前向TC注册分支,提交后将本地事务提交的结果上报给 TC。
二阶段:
提交异步化,非常快速地完成。
回滚通过xid找到一阶段的回滚日志进行反向补偿。

框架支持:

  • rpc框架:Dubbo,Apache HttpClient,Spring Cloud OpenFeign,Spring RestTemplate,gRPC,SOFA-RPC,Motan
  • Seata 是基于 DataSource 数据源进行代理来拓展,所以天然对主流的 ORM 框架提供了非常好的支持:MyBatis、MyBatis-Plus、JPA、Hibernate

原理解析:

  • 如果保证某个方法内的rpc调用是一个全局事务,添加@GlobalTransaction。当方法被调用,会生成xid绑定到当前线程并将xid传递到下游服务,下游服务不可再用@GlobalTransaction注解,因为会生成新的xid,无法纳入同一全局事务。带有xid的下游服务在操作数据库时,RM会将自己的事务状态告知TC。当方法执行完成,TC根据RM报告的状态决定提交还是回滚。
  • 类似日志添加流水号,seata通过threadlocal绑定的xid管理事务,有xid的线程如果执行sql会纳入对应全局事务已经完成的xid,再执行会报错。异步线程需要传递xid,如hystrix。
  • Seata会捕获@GlobalTransaction注解的方法异常,如果捕捉到,则进行全局回滚。如果操作mysql的过程中出现异常,也会回滚。
  • 回滚前可以查到更新的数据,因此数据最终一致,可能会读到脏数据
  • 支持同步调用,异步调用无法确保主线程执行完成时,全局事务是否完成。异步调用可以用基于mq的全局事务
  • 实质是一种补偿机制,解析sql生成undo log,保证undo sql与业务sql在同一事务执行,可进行回滚,避免长事务

使用:
通过feign调用

  • 如果没有定义callback

    • 下游服务执行异常未被捕获抛掷上游,由于上游未捕捉到,异常被seata捕捉则rollback
    • 下游服务执行异常由自定义逻辑捕获

      • 没有rollback(xid),上游执行正常,不会rollback
      • 执行rollback(xid),触发rollback
  • 如果定义callback

    • 上游不会触发rollback,由下游决定

解决方案:

  • 一般上游服务都会处理callback逻辑,
  • 下游服务超时,由callback向上游响应超时,可正常commit。这时下游服务无法再写入mysql,因为全局xid已经commit。这时应该在callback处rollback(xid)
  • 下游异常,响应error,由上游的callback逻辑处理,这时服务会正常commit,而下游不会保证本地事务。下游需要添加拦截全局异常的切面,出现异常时rollback(xid)

参考:
http://seata.io/zh-cn/blog/se...
http://seata.io/zh-cn/docs/overview/what-is-seata.html
https://seata.io/zh-cn/blog/seata-at-mode-design.html
https://seata.io/zh-cn/blog/seata-xa-introduce.html
https://seata.io/zh-cn/blog/seata-at-mode-start-rm-tm.html
http://www.itsoku.com/article...
https://seata.io/zh-cn/blog/d...
https://blog.csdn.net/sinat_2...
https://www.jianshu.com/p/ea4...
https://objcoding.com/2019/07/11/seata/

查看原文

赞 0 收藏 0 评论 0

吃水果毫不费力zz 发布了文章 · 10月19日

分布式存储高可用方案探究

为避免单点瓶颈,提高存储的可用性及负载能力,系统通常部署多个节点。但此时会出现一些问题:

  • 客户端写入数据成功,系统各内节点的数据是否也都写入成功
  • 如果系统内一个节点挂掉,系统是否仍旧可用
  • 如果系统内由于网络故障发生分区,系统是否仍旧可用

这些问题是多节点的分布式存储系统必须面对并解决的问题,即保证系统的数据一致,可用和分区容忍。

系统的高可用有两种策略:

  • 主从模式:分主从节点,主节点挂了从节点自动选主切换为主节点。只有主节点可进行写操作,从节点复制主节点数据,只可读以减轻负担。如:zookeeper,redis sentinel
  • 复制模式:节点角色平等,互相通信交换信息,一个节点挂掉会被踢出集群,不影响系统的使用。如:Eureka,redis cluster(数据分片,元信息通过gossip保证一致)

我们希望所有节点最终均保存完整的数据,以便客户端可从任意节点读取数据,提高读取性能。系统某一节点挂掉后也能恢复数据。

为提高存储服务的写入性能,会对数据继续分片,每个分片服务要做到高可用,一般为主从部署。如Redis cluster,es,kafka。客户端读取或写入数据时,先路由到相应节点再读取或写入。三者不同的是,读取redis cluster需要客户端找到指定分片节点,如果cluster发现数据不在客户端请求的分片(slot发生迁移),会返回客户端正确的分片地址,客户端再次发起请求。es不用客户端找到指定分片,它会在内部进行路由,客户端请求一次即可得到数据。

分布式一致性协议讨论了多个节点的数据如何达成一致,即不同节点如何交换数据,包括:

  • 如何写入数据,数据如何同步到其他节点
  • 主/从节点挂了如何切换至从节点并使其他节点知道
  • 集群状态感知,如新节点加入其他节点如何知道或者不需要知道

按节点间的关系可分为两大类:

  • 有主节点,即主从模式:raft zab paxos。集群的节点分主从,主节点负责写操作,然后同步到从节点,从节点负责读操作。当主节点挂了,一个从节点被选举提升为主节点。
  • 无主节点,即复制模式:Gossip协议。节点的角色相同,无主从之分,任何节点都可进行读写。如果一个节点写入数据,会随机同步至n个节点,这n个节点继续随机向n个节点同步,最终集群所有节点状态相同。

几个理论:
CAP:分布式系统的特性,Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可得兼。是NOSQL数据库的基石。

  • Consistency(一致性):分布式系统中各节点在同一时刻,同一key的value相同
  • Availability(可用性):集群中某一节点挂掉后仍然可用
  • Partition tolerance(分区容错性):集群内如果网络分区,一些节点的数据不会被分区外的客户端访问到,因此要求数据保存在所有的节点。

Base:是Basically Available(基本可用)、Soft state(软状态)和Eventually consistent(最终一致性)三个短语的简写,BASE是对CAP中一致性和可用性权衡的结果。其核心思想是即使无法做到强一致性(Strong consistency),但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性(Eventual consistency)。

  • Basically Available(基本可用):假设系统,出现了不可预知的故障,但还是能用,相比较正常的系统而言:

    • 响应时间上的损失:正常情况下的搜索引擎 0.5 秒即返回给用户结果,而基本可用的搜索引擎可以在 1 秒作用返回结果。
    • 功能上的损失:在一个电商网站上,正常情况下,用户可以顺利完成每一笔订单,但是到了大促期间,为了保护购物系统的稳定性,部分消费者可能会被引导到一个降级页面。
  • Soft state(软状态):相对于原子性而言,要求多个节点的数据副本都是一致的,这是一种 “硬状态”。 软状态指的是:允许系统中的数据存在中间状态,最终并认为该状态不影响系统的整体可用性,即允许系统在多个不同节点的数据副本存在数据延时。如设置key为1,其中在A节点的值已设为1,但在B节点仍为2,但最终会为1.。允许一些节点的值不为1
  • Eventually consistent(最终一致性):不可能一直是软状态,必须有个时间期限。在期限过后,应当保证所有副本保持数据一致性。从而达到数据的最终一致性。这个时间期限取决于网络延时,系统负载,数据复制方案设计等等因素。系统能够保证在没有其他新的更新操作的情况下,数据最终一定能够达到一致的状态,因此所有客户端对系统的数据访问最终都能够获取到最新的值。

Acid:atomic原子性,consistence一致性,isolation隔离性,duration持久性,保证事务的强一致性

分布式系统不会是一个完美的系统,即数据写入时所有节点会立即更新最新的数据且一直提供服务,如果系统内某个节点挂掉,不影响读写操作整个系统像是一个单体应用,满足acid。
因为数据同步到各个节点需要一定时间,在客户端收到写入成功的响应后,所有节点是否完成数据的变更,此时再请求,是否会得到最新数据。如果一个节点挂掉,系统是否会响应客户端请求。多节点的存在使得它受CAP原理的限制。满足CP:ZK,AP:eureka

查看原文

赞 0 收藏 0 评论 0

认证与成就

  • 获得 1 次点赞
  • 获得 1 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 1 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2016-03-31
个人主页被 424 人浏览