微服务注册中心

“微服务注册中心” 微服务注册中心, ZooKeeper, Eureka, Consul, Nacos 服务注册中心本质上是为了解耦服务提供者和服务消费者。对于任何一个微服务,原则上都应存在或者支持多个提供者,这是由微服务的分布式属性决定的。更进一步,为了支持弹性扩缩容特性,一个微服务的提供者的数量和分布往往是动态变化的,也是无法预先确定的。因此,原本在单体应用阶段常用的静态LB机制就不再适用了,需要引入额外的组件来管理微服务提供者的注册与发现,而这个组件就是服务注册中心。 CAP理论 CAP理论是分布式架构中重要理论 一致性(Consistency) (所有节点在同一时间具有相同的数据) 可用性(Availability) (保证每个请求不管成功或者失败都有响应) 分隔容忍(Partition tolerance) (系统中任意信息的丢失或失败不会影响系统的继续运作) P的理解,我觉得是在整个系统中某个部分,挂掉了,或者宕机了,并不影响整个系统的运作或者说使用, 而可用性是,某个系统的某个节点挂了,但是并不影响系统的接受或者发出请求,CAP 不可能都取,只能取其中2个 原因是 如果C是第一需求的话,那么会影响A的性能,因为要数据同步,不然请求结果会有差异,但是数据同步会消耗时间,期间可用性就会降低。 如果A是第一需求,那么只要有一个服务在,就能正常接受请求,但是对与返回结果变不能保证,原因是,在分布式部署的时候,数据一致的过程不可能想切线路那么快。 再如果,同事满足一致性和可用性,那么分区容错就很难保证了,也就是单点,也是分布式的基本核心,好了,明白这些理论,就可以在相应的场景选取服务注册与发现了 Zookeeper -> CP 与 Eureka 有所不同,Apache Zookeeper 在设计时就紧遵CP原则,即任何时候对 Zookeeper 的访问请求能得到一致的数据结果,同时系统对网络分割具备容错性,但是 Zookeeper 不能保证每次服务请求都是可达的。 从 Zookeeper 的实际应用情况来看,在使用 Zookeeper 获取服务列表时,如果此时的 Zookeeper 集群中的 Leader 宕机了,该集群就要进行 Leader 的选举,又或者 Zookeeper 集群中半数以上服务器节点不可用 (例如有三个节点,如果节点一检测到节点三挂了 ,节点二也检测到节点三挂了,那这个节点才算是真的挂了) ,那么将无法处理该请求。所以说,Zookeeper 不能保证服务可用性。 当然,在大多数分布式环境中,尤其是涉及到数据存储的场景,数据一致性应该是首先被保证的,这也是 Zookeeper 设计紧遵CP原则的另一个原因。 但是对于服务发现来说,情况就不太一样了,针对同一个服务,即使注册中心的不同节点保存的服务提供者信息不尽相同,也并不会造成灾难性的后果。 因为对于服务消费者来说,能消费才是最重要的,消费者虽然拿到可能不正确的服务实例信息后尝试消费一下,也要胜过因为无法获取实例信息而不去消费,导致系统异常要好 (淘宝的双十一,京东的618就是紧遵AP的最好参照) 。 当master节点因为网络故障与其他节点失去联系时,剩余节点会重新进行leader选举。问题在于,选举leader的时间太长,30~120s,而且选举期间整个zk集群都是不可用的,这就导致在选举期间注册服务瘫痪。 在云部署环境下, 因为网络问题使得zk集群失去master节点是大概率事件,虽然服务能最终恢复,但是漫长的选举事件导致注册长期不可用是不能容忍的。 zookeeper 的 CP 模型不适合注册中心 zookeeper 是一个非常优秀的项目,非常成熟,被大量的团队使用,但对于服务发现来讲,zookeeper 真的是一个错误的方案。 在 CAP 模型中,zookeeper 是 CP,意味着面对网络分区时,为了保持一致性,他是不可用的。 ...

Created 28286-12-28 · Updated 28286-12-28 · 2 min · 348 words · -

Chubby

Chubby 分布式锁管理系统,也可理解为一个小型强一致性文件系统 http://www.cnblogs.com/linhaohong/archive/2012/11/26/2789394.html 随着云计算的推广,云平台的设计和实现越来越复杂,很多系统属性如一致性和可靠性往往是在系统迭代开发时才被考虑到。如果在原生的系统上重复的实现复杂的一致性算法,这样不仅会破坏原有设计的结构,而且还带来很多开发上的负担。因此很多系统开发人员和架构师努力地进行系统划分,将系统分割成很多组件,分层设计,模块调用,从而最大限度地提高软件复用能力,降低系统设计和开发的难度。 Google在系统的可靠性方面提出了中心化的组件Chubby—粗粒度锁服务,通过锁原语为其他系统实现更高级的服务,比如组成员、域名服务和leader选举等等。Chubby本身也是一个小型的cell (通常由5个chubby结点组成) ,cell内部采用类似于状态机副本形式实现可靠容错。Google的Chubby论文在OSDI上发表后引起了很大的反响,原因很多,主要有两个: 第一,chubby很好的解决了分布式开发的一致性问题; 第二,Google Chubby采用Leslie Lamport提出的paxos算法来实现可靠容错,这是业界关于paxos第一个完整可行的实现。正因为Google Chubby,paxos这个一直沉淀在学术研究的协议,终于曝光在工业界中,之后很快地推广开去。 然而, Google Chubby 并不是开源的, 我们只能通过其论文和其他相关的文档中了解具体的细节。值得庆幸的是,Yahoo!借鉴 Chubby 的设计思想开发了 Zookeeper, 并将其开源。 和 Chubby 相比, Zookeeper 做了很多突破。不像 Chubby 的单点服务的结构, zookeeper 采用多个 server 同时处理客户端的请求, 异步读同步写, 通过 primary 节点来同步数据的 update, 这一点大大改善了读服务的性能, 当然弱化了客户端与服务器之间的一致性。另外, zookeeper 采用 block free 的服务接口, 采用 watch 机制的方式异步处理请求结果和指定数据的变更。Zookeeper 对外提供了更加低级的原语 primitive, 基于此可以实现更多更加复杂的分布式算法, 如 queue、barrier 和 lock 等等。 如今很多云计算系统或者平台都使用 Zookeeper 来做可靠容错, 进行订阅分发服务, 或者其他应用。 和Chubby一样,zookeeper采用paxos的变种来实现消息传输的一致性。Zookeeper开发了原子多播协议 Zab 来实现数据的一致性传输。Zookeeper采用的是primary-backup的结构,primary节点产生non- commutative 事务,通过协议按序的广播到其他backup节点上。在节点无错的情况下,这是非常简单的事情,然而,面对复杂的网络环境,多变的软硬件条件,节点挂掉,重启,数据重复发送,这些异常很常见。Zookeeper如何做到即便是系统出现异常,也能够保证整个系统状态是一致?paxos的变种,Zookeeper的Zab协议很好的保证了这一点。 Zab 协议以epoch的方式执行 (相当与序列号) ,在每个epoch最多只有一个进程多播数据。如果某个进程执行了协议的的第一阶段,那么进程将不再接受之前还没确定提交的epoch的数据。这样一来就保证了在进程在recovery阶段不会出现丢失已提交的数据的情况。在某个epoch下,所有参加这个epoch的进程必须此epoch之前所有已经提交的数据镜像。为了保证一致性,进程在完全恢复之前必须不能广播新的事务。Zab协议的这几个特点处理了primary异常、新旧primary以及backup节点异常的情况,的确保证了primary进程原子多播的order特性。 整个Zab协议的内容分成三个阶段: Discovery、Synchronization和Broadcast阶段。 ...

Created 11116-11-11 · Updated 11116-11-11 · 1 min · 90 words · -

VarHandle

“VarHandle” VarHandle 的出现替代了java.util.concurrent.atomic和sun.misc.Unsafe的部分操作。并且提供了一系列标准的内存屏障操作,用于更加细粒度的控制内存排序。在安全性、可用性、性能上都要优于现有的API。VarHandle 可以与任何字段、数组元素或静态变量关联,支持在不同访问模型下对这些类型变量的访问,包括简单的 read/write 访问,volatile 类型的 read/write 访问,和 CAS(compare-and-swap)等。 Unsafe 是不建议开发者直接使用的,因为 Unsafe 所操作的并不属于Java标准,会容易带来一些安全性的问题。JDK9 之后,官方推荐使用 java.lang.invoke.Varhandle 来替代 Unsafe 大部分功能,对比 Unsafe ,Varhandle 有着相似的功能,但会更加安全,并且,在并发方面也提高了不少性能。 Varhandle是对变量或参数定义的变量系列的动态强类型引用,包括静态字段,非静态字段,数组元素或堆外数据结构的组件。 在各种访问模式下都支持访问这些变量,包括简单的读/写访问,volatile 的读/写访问以及 CAS (compare-and-set)访问。简单来说 Variable 就是对这些变量进行绑定,通过 Varhandle 直接对这些变量进行操作。 import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; import java.util.Arrays; public class VarHandleX { public int publicVar = 1; protected int protectedVar = 2; @SuppressWarnings("FieldMayBeFinal") private int privateVar = 3; public int[] arrayData = new int[]{1, 2, 3}; @Override public String toString() { return "VarHandleX{" + "publicVar=" + publicVar + ", protectedVar=" + protectedVar + ", privateVar=" + privateVar + ", arrayData=" + Arrays.toString(arrayData) + '}'; } public static void main(String[] args) { try { VarHandleX instance = new VarHandleX(); VarHandle varHandle = MethodHandles.privateLookupIn(VarHandleX.class, MethodHandles.lookup()) .findVarHandle(VarHandleX.class, "privateVar", int.class); varHandle.set(instance, 33); System.out.println(instance); protectedDemo(); protectedDemo2(); publicDemo(); arrayDemo(); } catch (NoSuchFieldException | IllegalAccessException e) { e.printStackTrace(); } } private static void protectedDemo() throws NoSuchFieldException, IllegalAccessException { VarHandleX instance = new VarHandleX(); VarHandle varHandle = MethodHandles.privateLookupIn(VarHandleX.class, MethodHandles.lookup()) .findVarHandle(VarHandleX.class, "protectedVar", int.class); varHandle.set(instance, 22); System.out.println("protected: " + instance); } private static void protectedDemo2() throws NoSuchFieldException, IllegalAccessException { VarHandleX instance = new VarHandleX(); VarHandle varHandle = MethodHandles.lookup() .in(VarHandleX.class) .findVarHandle(VarHandleX.class, "protectedVar", int.class); varHandle.set(instance, 22); System.out.println("protected 2: " + instance); } private static void publicDemo() throws NoSuchFieldException, IllegalAccessException { VarHandleX instance = new VarHandleX(); VarHandle varHandle = MethodHandles.lookup() .in(VarHandleX.class) .findVarHandle(VarHandleX.class, "publicVar", int.class); varHandle.set(instance, 11); System.out.println("public: " + instance); } private static void arrayDemo() throws NoSuchFieldException, IllegalAccessException { VarHandleX instance = new VarHandleX(); VarHandle arrayVarHandle = MethodHandles.arrayElementVarHandle(int[].class); arrayVarHandle.compareAndSet(instance.arrayData, 0, 1, 11); arrayVarHandle.compareAndSet(instance.arrayData, 1, 2, 22); arrayVarHandle.compareAndSet(instance.arrayData, 2, 3, 33); System.out.println("array: " + instance); } } 获取Varhandle方式汇总 MethodHandles.privateLookupIn(class, MethodHandles.lookup())获取访问私有变量的Lookup MethodHandles.lookup() 获取访问protected、public的Lookup findVarHandle: 用于创建对象中非静态字段的VarHandle。接收参数有三个,第一个为接收者的class对象,第二个是字段名称,第三个是字段类型。 findStaticVarHandle: 用于创建对象中静态字段的VarHandle,接收参数与findVarHandle一致。 unreflectVarHandle: 通过反射字段Field创建VarHandle。 MethodHandles.arrayElementVarHandle(int[].class) 获取管理数组的 Varhandle 功能 VarHandle来使用plain、opaque、release/acquire和volatile四种共享内存的访问模式,根据这四种共享内存的访问模式又分为写入访问模式、读取访问模式、原子更新访问模式、数值更新访问模式、按位原子更新访问模式。 ...

Created 21216-09-21 · Updated 21216-09-21 · 4 min · 655 words · -

Spring Cloud

“Spring Cloud” Eureka [jʊ’ri:kə] Ribbon [ˈrɪbən] Feign [fen] Hystrix [hɪst’rɪks] Zuul [zulu] Sleuth [sluθ] Turbine [ˈtɜ:rbaɪn] Spring Cloud Netflix (Eureka、Hystrix、Zuul、Archaius) 、Spring Cloud Config、Spring Cloud Bus、Spring Cloud Cluster、Spring Cloud Consul、Spring Cloud Security、Spring Cloud Sleuth、Spring Cloud Data Flow、Spring Cloud Stream、Spring Cloud Task、Spring Cloud Zookeeper、Spring Cloud Connectors、Spring Cloud Starters、Spring Cloud CLI Eureka: 服务注册中心,一个基于REST的服务,用于定位服务,以实现微服务架构中服务发现和故障转移。 Hystrix: 熔断器,容错管理工具,旨在通过熔断机制控制服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。 l Turbine: Turbine是聚合服务器发送事件流数据的一个工具,用来监控集群下Hystrix的Metrics情况。 l Zuul: API网关,Zuul是在微服务中提供动态路由、监控、弹性、安全等边缘服务的框架。 l Ribbon: 提供微服务中的负载均衡功能,有多种负载均衡策略可供选择,可配合服务发现和断路器使用。 l Feign: Feign是一种声明式、模板化的HTTP客户端。 l Spring Cloud Config: 配置管理工具包,让你可以把配置放到远程服务器,集中化管理集群配置,目前支持本地存储、Git以及Subversion。 l Spring Cloud Security: 基于Spring Security的安全工具包,为微服务的应用程序添加安全控制。 ...

Created 14146-09-14 · Updated 14146-09-14 · 1 min · 140 words · -

服务治理

“服务治理” https://coolshell.me/articles/talking-about-service-governance-microservices-and-service-mesh.html 服务治理,也称为SOA治理,是指用来管理SOA的采用和实现的过程。 服务定义 (服务的范围、接口和边界) 服务部署生命周期 (各个生命周期阶段) 服务版本治理 (包括兼容性) 服务迁移 (启用和退役) 服务注册中心 (依赖关系) 服务消息模型 (规范数据模型) 服务监视 (进行问题确定) 服务所有权 (企业组织) 服务测试 (重复测试) 服务安全 (包括可接受的保护范围) Dubbo开源 直到2011年10月27日,阿里巴巴开源了自己的SOA服务化治理方案的核心框架Dubbo,服务治理和SOA的设计理念开始逐渐在国内软件行业中落地,并被广泛应用。

Created 14146-09-14 · Updated 14146-09-14 · 1 min · 25 words · -

分布式协调组件

“分布式协调组件” 常用的分布式协调组件有 全局功能数据存储/有功能的组件: K-V类: redis、memcache Zookeeper 消息队列 配置中心: Spring Cloud Config等 持久数据存储: MySQL MongoDB 分布式协调组件应用场景: (类比单机中多线程并发安全变量的操作) 分布式session 分布式计数器 分布式锁 分布式队列 先入先出队列 要等所有队列元素聚集之后才能统一安排执行的Barrier模型队列 分布式配置 分布式协调 / 通知 数据发布、订阅 软负载均衡: 域名 -> IP和端口号配置 命名服务: 在分布式环境中,上层应用需要一个全局唯一的名字,类似于数据库中的主键 集群管理: 集群监控 (进群运行时状态收集) 集群控制 (对集群进行操作和控制) Master选举 https://zhuanlan.zhihu.com/p/50901935

Created 12126-09-12 · Updated 12126-09-12 · 1 min · 40 words · -

CR3控制寄存器

“CR3控制寄存器” CR3用来存放页目录表物理内存基地址,每当进程切换时,Linux 就会把下一个将要运行进程的页目录表物理内存基地址等信息存放到CR3寄存器中。 https://blog.csdn.net/SweeNeil/article/details/106171361

Created 8086-09-08 · Updated 8086-09-08 · 1 min · 4 words · -

Pulsar

Pulsar Pulsar [ˈpʌlsɑːr] Apache Pulsar (孵化器项目)是一个企业级的发布订阅 (pub-sub)消息系统,最初由 Yahoo 开发,并于 2016 年底开源 https://www.infoq.cn/article/2017/11/apache-pulsar-brief-introduction

Created 1016-09-01 · Updated 1016-09-01 · 1 min · 12 words · -

celery

celery https://github.com/celery/celery celery 是一个基于分布式消息传输的异步任务队列,它专注于实时处理,同时也支持任务调度。它的执行单元为任务(task),利用多线程, 如 Eventlet,gevent等,它们能被并发地执行在单个或多个职程服务器(worker servers)上。任务能异步执行(后台运行)或同步执行(等待任务完成)。 # install rabbitmq https://wangyue.dev/rabbitmq # install celery pip install celery # 添加用户跟密码, rabbitmqctl add_user test test123 rabbitmqctl add_user user0 password0 # 添加虚拟主机 rabbitmqctl add_vhost test_vhost rabbitmqctl add_vhost vhost0 # 为用户添加标签, rabbitmqctl set_user_tags test test_tag rabbitmqctl set_user_tags user0 tag0 # 设置用户权限, rabbitmqctl set_permissions -p test_vhost test ".*" ".*" ".*" rabbitmqctl set_permissions -p vhost0 user0 ".*" ".*" ".*" # run celery server celery -A tasks worker --loglevel=INFO ———————————————— 版权声明:本文为CSDN博主「吴秋霖」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/qiulin_wu/article/details/106119757 ...

Created 16166-08-16 · Updated 16166-08-16 · 2 min · 280 words · -

tesla

“tesla” 基础版辅助驾驶 (BAP/AP 增强版自动辅助驾驶 (EAP) EAP Enhanced Autopilot,增强型自动驾驶 和完全自动驾驶 (FSD) FSD Full Self-Driving 完全自动驾驶 EAP 自动辅助导航驾驶: 自动驶入和驶出高速公路匝道或立交桥岔路口,超过行驶缓慢的车辆。 自动辅助变道: 在高速公路上自动辅助变换车道。 自动泊车: 平行泊车与垂直泊车。 智能召唤: 在合适的场景下,停在车位的车辆会响应您的召唤,驶出车位并前往您所在的位置。 FSD NOA Navigate on Autopilot自动辅助导航驾驶 (简称NOA) 启动车辆即启动, 开启该选项后,在具备NOA使用条件的路段,Model 3将自动开启NOA功能。 根据速度变道 (即自动超车) 特斯拉官方称之为速控变道,在行驶中存在两种普遍情况,一种是本车道内行驶时前方有慢车阻挡,需要变道超车;另一种是正常行驶中,有慢车从前方进入到本车道,此时需要变道以保持车速。该功能有三种变道模式: 柔和、普通、极速。柔和可以理解为以最保守的变道方式,极速则最激进的变道模式。 要求变道确认 开启这一功能后,当NOA驾驶中,Model 3判断需要变线时,将提醒驾驶员打转向灯进行确认,然后才能执行变线操作。而选择“否”,关闭这一功能后,Model 3将自动变线,而不再要求打转向灯进行确认。 变道提醒 开启后,在自动变道时,Model 3会通过蜂鸣或振动方式提醒驾驶员即将进行变道操作。

Created 6066-08-06 · Updated 6066-08-06 · 1 min · 43 words · -

中间件, Middleware

“中间件, Middleware” 中间件 中间件一词的由来中间件这个术语第一次出现是 1968 年在德国加尔米施帕滕基兴举办的 NATO 软件工程大会 结束后发表的一份报告中。这届大会正式确定了软件工程(Software Engineering)的概念,同时还探讨了软件设计、生产和分发等主题。 中间件的定义 中间件 (英语: Middleware) ,又译中间件、中介层,是一类提供系统软件和应用软件之间连接、便于软件各部件之间的沟通的软件,应用软件可以借助中间件在不同的技术架构之间共享信息与资源。中间件位于客户机服务器的操作系统之上,管理着计算资源和网络通信。 – 维基百科什么不是中间件我们按照类别来看一些经常会遇到的一些不是中间件的概念- 业务平台不是中间件,业务平台是从服务的视角抽象的能同时支撑多个业务,业务之间的信息能形成交互和增强的平台。- 营销工具不是中间件,营销工具是直接作用于最终消费者用户的软件或者插件服务。- 二方/三方工具包不是中间件,二方/三方工具包是在各种场景的程序开发过程中沉淀的一些常用工具类(功能)的集合,包含于软件代码本身。- SaaS 不是中间件,SaaS(Software as a Service) 更多的是一种软件交付模式,无需用户安装,通过网络在线访问的一种服务模式。- PaaS 不是中间件,PaaS(Platform as a Service) 将软件研发的平台做为一种服务,提供软件部署平台(强调的是屏蔽系统和软件细节的runtime平台)。评判关键从定义可以总结出评判的几个地方 性质: 中间件是软件。 作用层级: 系统软件和应用软件之间、软件各部件之间;管理客户机与系统软件之间的计算资源和网络通信。 服务对象: 中间件为应用软件服务,应用软件为最终用户服务,最终用户并不直接使用中间件。 中间件的好处 中间件能给客户带来什么?为上层应用软件的开发提供便捷的、开箱即用的服务交互和计算的能力,缩短开发周期;屏蔽底层runtime的差异;节省应用本身的系统资源,减少运行成本。 中间件分类 什么时候使用中间件 基于中间件的定义我们知道中间件是连接软件与系统之间的服务,那么我们什么时候使用了中间件,在哪些地方用到了中间件了。我们不妨假设一个http请求过程来窥视一番。当你在浏览器中输入一个网址时,它会通过 DNS 解析到目标服务注册的公网IP地址请求到达目标服务的 web 反向代理服务器 Tengine 之后,经过一定的过滤转发到目标服务A上服务A通过 RPC框架 Dubbo 请求服务B的结果做中间计算,并且从 Tair 缓存中读取计算因子,计算结果服务A接着使用 Druid 通过 TDDL 写入计算结果到 MySQL Master 节点然后返回结果异步过程中 Canal 通过模拟 Binlog 主从复制的原理,迅速将这条 Binlog 消费并下发到消息队列 RocketMQ服务C通过 RocketMQ 消费到事件之后,通过配置中心 ConfigServer 拉取到的策略进行对应策略的事件处理。这个过程中我们使用了一系列的中间件来协同各个微服务完成整个流程,如web反向代理服务器 Tengine、RPC框架 Dubbo、缓存 Tair、连接池 Driud、数据库代理层 TDDL、Binlog 同步工具 Canal、消息队列 RocketMQ、配置中心 ConfigServer。 ...

Created 29296-07-29 · Updated 29296-07-29 · 1 min · 148 words · -

负载均衡

负载均衡 负载均衡 load balancing 何为负载均衡?顾名思义就是让系统的负载根据一定的规则均衡地分配在所有参与工作的服务器上,从而最大限度地提升系统整体的运行效率。 LVS、Nginx 当前大多数的互联网系统都使用了服务器集群技术,集群是将相同服务部署在多台服务器上构成一个集群整体对外提供服务,这些集群可以是 Web 应用服务器集群,也可以是数据库服务器集群,还可以是分布式缓存服务器集群等等。 在实际应用中,在 Web 服务器集群之前总会有一台负载均衡服务器,负载均衡设备的任务就是作为 Web 服务器流量的入口,挑选最合适的一台 Web 服务器,将客户端的请求转发给它处理,实现客户端到真实服务端的透明转发。 最近几年很火的「云计算」以及分布式架构,本质上也是将后端服务器作为计算资源、存储资源,由某台管理服务器封装成一个服务对外提供,客户端不需要关心真正提供服务的是哪台机器,在它看来,就好像它面对的是一台拥有近乎无限能力的服务器,而本质上,真正提供服务的,是后端的集群。 LVS、Nginx、HAProxy 是目前使用最广泛的三种软件负载均衡软件。 一般对负载均衡的使用是随着网站规模的提升根据不同的阶段来使用不同的技术。具体的应用需求还得具体分析,如果是中小型的 Web 应用,比如日 PV 小于1000万,用 Nginx 就完全可以了;如果机器不少,可以用 DNS 轮询,LVS 所耗费的机器还是比较多的;大型网站或重要的服务,且服务器比较多时,可以考虑用 LVS。 目前关于网站架构一般比较合理流行的架构方案: Web 前端采用 Nginx/HAProxy+Keepalived 作负载均衡器;后端采用 MySQ L数据库一主多从和读写分离,采用 LVS+Keepalived 的架构。 LVS LVS 是 Linux Virtual Server 的简称,也就是 Linux 虚拟服务器。现在 LVS 已经是 Linux 标准内核的一部分,从 Linux2.4 内核以后,已经完全内置了 LVS 的各个功能模块,无需给内核打任何补丁,可以直接使用 LVS 提供的各种功能。 LVS 自从1998年开始,发展到现在已经是一个比较成熟的技术项目了。 LVS 的体系结构 LVS 架设的服务器集群系统有三个部分组成: (1) 最前端的负载均衡层,用 Load Balancer 表示 (2) 中间的服务器集群层,用 Server Array 表示 ...

Created 26266-07-26 · Updated 26266-07-26 · 3 min · 451 words · -

Java NIO Channel, buffer

“Java NIO Channel, buffer” Channel Channel Characteristics Java NIO Channel Classes buffer 什么是缓冲区? 缓冲区类型 缓冲区内部细节 NIO Buffer Characteristics How to Read from NIO Buffer How to Write to NIO Buffer Java NIO 读写文件实例程序 Channel Java NIO中,channel用于数据的传输。类似于传统IO中的流的概念。channel的两端是buffer和一个entity,不同于IO中的流,channel是双向的,既可以写入,也可以读取。而流则是单向的,所以channel更加灵活。我们在读取数据或者写入数据的时候,都必须经过channel和buffer,也就是说,我们在读取数据的时候,先利用channel将IO设备中的数据读取到buffer,然后从buffer中读取,我们在写入数据的时候,先将数据写入到buffer,然后buffer中的数据再通过channel传到IO设备中。 image.png 我们知道NIO的特点就是将IO操作更加类似于底层IO的流程。 我们可以通过底层IO的机制更好的理解channel。 所有的系统I/O都分为两个阶段: 等待就绪和操作。 等待就绪就是从IO设备将数据读取到内核中的过程。 操作就是将数据从内核复制到进程缓冲区的过程。 channel就可以看作是IO设备和内核区域的一个桥梁,凡是与IO设备交互都必须通过channel,而buffer就可以看作是内核缓冲区。这样整个过程就很好理解了。 我们看一下读取的过程 先从IO设备,网卡或者磁盘将内容读取到内核中,对应于NIO就是从网卡或磁盘利用channel将数据读到buffer中 然后就是内核中的数据复制到进程缓冲区,对应于就是从buffer中读取数据 写入的过程则是: 先从进程将数据写到内核中,对应于就是进程将数据写入到buffer中, 然后内核中的数据再写入到网卡或者磁盘中,对应于就是,buffer中的数据利用channel传输到IO设备中。 image.png 以上其实就是NIO基本的利用channel和buffer进行读取和写入的流程。 Channel Characteristics 与传统IO中的流不同,channel是双向的,可读可写 channel从buffer中读取数据,写入数据也是先写入到buffer channel可以实现异步读写操作 channel可以设置为阻塞和非阻塞的模式 非阻塞模式意味着,当读不到数据或者缓冲区已满无法写入的时候,不会把线程睡眠 只有socket的channel可以设置为非阻塞模式,文件的channel是无法设置的。文件的IO一定是阻塞的 如果是文件channel的话,channel可以在channel之间传输数据 Java NIO Channel Classes channel主要有两大类,四个具体的类 FileChannel 文件的读写是不可以设置为非阻塞模式 SocketChannel 根据tcp和udp,服务端和客户端,又可以分为, SocketChannel, ServerSocketChannel and DatagramChannel.它们是可以设置为非阻塞模式的 buffer 什么是缓冲区? Buffer 是一个对象, 它包含一些要写入或者刚读出的数据。 在 NIO 中加入 Buffer 对象,体现了新库与原 I/O 的一个重要区别。在面向流的 I/O 中,您将数据直接写入或者将数据直接读到 Stream 对象中。 在 NIO 库中,所有数据都是用缓冲区处理的。在读取数据时,它是直接读到缓冲区中的。在写入数据时,它是写入到缓冲区中的。任何时候访问 NIO 中的数据,您都是将它放到缓冲区中。 缓冲区实质上是一个数组。通常它是一个字节数组,但是也可以使用其他种类的数组。但是一个缓冲区不 仅仅 是一个数组。缓冲区提供了对数据的结构化访问,而且还可以跟踪系统的读/写进程。 ...

Created 25256-07-25 · Updated 25256-07-25 · 3 min · 481 words · -

Cuckoo filter, 布谷过滤器

“Cuckoo filter, 布谷过滤器” 过滤器系列 (二) —— Cuckoo filter 这一篇讲的是布谷过滤器(cuckoo fliter),这个名字来源于更早发表的布谷散列(cuckoo hash),尽管我也不知道为什么当初要给这种散列表起个鸟名=_= 由于布谷过滤器本身的思想就源自于布谷散列,那么我们就从布谷散列开始说它的设计思想。产生布谷散列表的一个重要背景是人们对于球盒问题的分析: 给定N个球,随机的放在N个盒子里,在装球最多的盒子里,球的个数的期望是多少?答案是Θ(logN/loglogN),这个问题其实就是散列表装填因子为1时的情况分析。后来有一天,人们发现: 每次放球的时候,如果随机选择两个盒子,将球放到当时较空的那个盒子里,那么这个期望就变成了Θ(loglogN),这个界小于之前的界,这给了布谷散列表作者启发。 一个布谷散列表通常有两张 (一般来说) 表,分别有一个对应的Hash函数,当有新的项插入的时候,它会计算出这个项在两张表中对应的两个位置,这个项一定会被存在这两个位置之一,而具体是哪一个却不确定。 下图是一个布谷散列表的初始化示意图: image 现在我们假设有一些项要存入散列表,其每个项都有其对应的两个位置,先插入第一项A image 由于插入A的时候其两个候选位置 (0,2) 都没有占用,所以选择第一张表或者是第二张表都可以,我们在这里默认先选择第一张表,然后插入第二项B image 我们看到原来的A的位置被B占用,而A被“踢”到它的备选位置表二的2号位置上了,这就是当发生位置冲突时,布谷散列表的处理逻辑,后来的数据项将会把之前占用的项踢到另一个位置上。我们接下来插入第三项C image 没有冲突,顺利搞定,接着插入D image D成功的把C踢走了,其实看到这里读者应该在猜想,会不会有一种情况,即被踢走的数据的另一个备选位置也被占用了,这样怎么办?答案是继续踢,一个踢一个,直到大家都找到自己合适的归宿为止。那么如果发现出现了循环怎么办?答案是GG,这代表布谷散列表走到了极限 image 插入E image 这里就发生了多次替换的情况,F代替了E,E代替了A,A代替了B,B找到了空余的位子 读者可以考虑一下,如果这个时候要想插入一个“G”,其备选位置是1,2,这样的话会出现什么情况? 好了,布谷散列表大概介绍完了,现在该布谷过滤器了。布谷过滤器的主要也是通过布谷散列来实现的,其主要变化是: 1.我们不将原来的数据完整的存进来,作为过滤器,当然要省点空间,选用的办法设计一个指纹,将比较大的原数据变成了一个个指纹串,这样就大大节省了空间。 2.由于第一点所说,存储的不是原数据,那么在替换位置的时候,我们需要再次计算原来的数据的备选位置,这样一来布谷散列表的方法就失效了。在这里,作者设计了一个方法,他将两个Hash函数变成了一个Hash函数,第一张表的备选位置是Hash(x),第二张表的备选位置是Hash(x)⊕hash(fingerprint(x)),即第一张表的位置与存储的指纹的Hash值做异或运算。这样做的优点就是不用再另外存储元素的备选位置,在替换时,可以直接用异或来计算出其另一个备选位置。 (读者可以想想如何通过表二的位置计算出元素在表一中的位置) 3.插入时,优先选择空位置,而不是尽可能的踢走其他元素。 插入伪代码如下: Copy Algorithm 1: Insert(x) f = fingerprint(x) i1 = hash(x) i2 = i1 xor hash(f) if bucket[i1] or bucket[i2] has an empty entry then //只要有空位就先插入空位里 add f to that bucket return Done i = randomly pick i1 or i2 for n=0;n<MaxNumKicks;++n randomly select an entry e from bucket[i]; swap f and the fingerprint stored in entry e; i = i xor hash(f) if bucket[i] has an empty entry then add f to bucket[i] return Done return Failure // 已经出现循环情况了 查找伪代码如下: ...

Created 24246-07-24 · Updated 24246-07-24 · 1 min · 199 words · -

gcm cbc

“gcm cbc” 主要区别 AES-GCM可以并行加密解密,AES-CBC的模式决定了它只能串行地进行加密。因为加密是耗时较久的步骤,且加密的方式是相同的,所以并行地实现AES-GCM算法的时候,其效率是高于AES-CBC的; AES-GCM提供了GMAC信息校验码,用以校验密文的完整性。AES-CBC没有,无法有效地校验密文的完整性; AES-GCM是流加密的模式,不需要对明文进行填充。AES-CBC是块加密的模式,需要对明文进行填充。(AES-GCM中进行AES加密的是counter,AES-CBC中进行AES加密的是明文块); 由于AES-CBC中必须要用到padding,导致最后一个明文块与其他密文块不同,因此可能会受到padding Oracle attacks,从而可以直接通过初始向量IV和密码,即可得到明文。 https://blog.csdn.net/weixin_39680609/article/details/111159600

Created 17176-07-17 · Updated 17176-07-17 · 1 min · 10 words · -

MESI

“MESI” CPU 在执行指令的时候需要从 memory 中获取指令和需要的数据,但是 CPU 的速度要比 memory 快很多,这就导致了 CPU 大部分时间都不是在做运算上而是用在了和 memory 进行数据的 I/O 过程中,这样性能是很低的。这就导致了 CPU cache 的产生,CPU 将数据从 memory 读取到 cache 中,在 cache 中对数据进行读写的速度是很快的,这样就提高了性能。CPU 执行运算时不可能需要某一个数据就去读取一次,这样就增加了 I/O 的频率,导致性能低下。所以会一次性读取一块内存的数据存放到 cache 中, 这个块称为 “cache line” , cache line 的大小是固定的, 通常是 2 的 N 次方,在 16 ~ 256 byte 不等。当某个数据首次被 CPU 访问时, cache 中不存在,这称为 “cache miss” (或 “startup” 或 “warmup” cache miss)。这意味着 CPU 必须要去 memory 中读取该数据,这时候 CPU 必须等待 (stalled)。当 cache 装满后,后续的 cache miss 需要置换 cache 中现有的数据,这样的 cache miss 被称为 “capacity miss” 。如果随后又访问了被替换的 cache line ,这时的 cache miss 被称为 “associativity miss” 。 ...

Created 10106-07-10 · Updated 10106-07-10 · 3 min · 454 words · -

Store Buffer

“Store Buffer” 就像spinlock的进化史,软件工程师会对自己的代码做足够的优化以提高性能。同样,硬件工程也不甘示弱,尽最大的努力设计硬件以获取更好的性能。我们引入高速缓存的目的就是为了降低内存访问的延迟,谁知硬件工程师依然不满足于高速缓存带来的延迟,当然软件工程师或许也不满足。为了进一步加快内存访问速度,硬件引入了新的缓存 - store buffer。随着store buffer的引入,彻底刷新了软件工程师对代码执行流的认知。store buffer又是怎么刷新我们的认知呢? 文章测试代码已经开源,可以点击这里查看。 store buffer是什么 在之前的文章介绍中,我们了解到每个CPU都会有自己私有L1 Cache。从我了解的资料来说,L1 Cache命中的情况下,访问数据一般需要2个指令周期。而且当CPU遭遇写数据cache未命中时,内存访问延迟增加很多。硬件工程师为了追求极致的性能,在CPU和L1 Cache之间又加入一级缓存,我们称之为store buffer。store buffer和L1 Cache还有点区别,store buffer只缓存CPU的写操作。store buffer访问一般只需要1个指令周期,这在一定程度上降低了内存写延迟。不管cache是否命中,CPU都是将数据写入store buffer。store buffer负责后续以FIFO次序写入L1 Cache。store buffer大小一般只有几十个字节。大小和L1 Cache相比,确实是小巫见大巫了。 store buffer对程序的影响 我们现在知道,当存在store buffer的情况下。针对写操作,CPU直接把数据扔给store buffer。后续store buffer负责以FIFO次序写回L1 Cache。这会对我们的程序产生什么影响呢?我们来看个例子。 static int x = 0, y = 0; static int r1, r2; static void int thread_cpu0(void) { x = 1; /* 1) / r1 = y; / 2) */ } static void int thread_cpu1(void) { y = 1; /* 3) / r2 = x; / 4) */ } ...

Created 10106-07-10 · Updated 10106-07-10 · 2 min · 322 words · -

文件锁

“文件锁” 文件锁也被称为记录锁,文件锁如果深讲的话,内容不少 (比如文件锁最起码分为了建议锁和强制性锁) 这里不准备深纠,深纠没有任何意义 主要是为了对比学习各种的加锁机制,比如进程有进程信号量加锁机制,线程有线程互斥锁、线程信号量等 加锁机制,学习文件锁有助于我们对比理解,对于我们后续理解驱动课程中的内核锁,c++、java等库所提供的 资源保护的锁机制,都是很有意义的。 当然还有另一个目的,那就是练习fcntl函数的使用,因为文件锁也需要用到fcntl函数。 由于文件锁的知识点是刚才所讲的这样一种存在,所以对于文件锁内容的学习,理解>实际使用。 4.1 文件锁的作用 顾名思义,就是用来保护文件数据的。 当多个进程共享读写同一个文件时,为了不让进程们各自读写数据时相互干扰,我们可以使用进程信号量来 互斥实现,除了可以使用进程信号量以外,还可以使用我们这里的的“文件锁”来实现,而且功能更丰富, 使用起来相对还更容易些。 4.2 多进程读写文件 file_lock.png 多进程共享读写同一个文件时,如果数据很重要的话,为了防止数据相互修改,应该满足如下读写条件: (1) 写与写应该互斥 当某个进程正在写文件,而且在数据没有写完时,其它进程不能写,否者会相互打乱对方写的数据 (2) 读与写也应该是互斥的 分两种情况: 某个进程正在写数据,而且在数据没有写完时,其它进程不能读数据因为别人在没有写完之前,读到的数据是不完整的,所以读和写时互斥的 某个进程正在读数据,在数据没有读完之前,其它进程不能写数据因为可能会扰乱别人读到的数据 (3) 读与读共享 某个进程在读数时,就算数据没有读完,其它进程也可以共享读数据,并不需要互斥等别人读完后才能读。 因为读文件是不会修改文件的内容,所以不用担心数据相互干扰的问题 总结起来就是,多进程读写文件时,如果你想进行资源保护的话,完美的资源保护应该满足如下这样的。 写与写之间互斥 读与写之间互斥 读与读之间共享 如何实现以上读写要求? 如果使用信号量来实现保护的话,只能是一律互斥,包括读与读都是互斥的,不能够向上面描述的,能互斥又能共享,但是文件锁可以做到 4.3 文件锁 4.3.1 文件锁的读锁与写锁 对文件加锁时可以加两种锁,分别是“读文件锁”和“写文件锁”,我们这里简称为读锁和写锁。 读锁、写锁之间关系 (1) 读锁和读锁共享: 可以重复加读锁,别人加了读锁在没有解锁之前,我依然可以加读锁,这就是共享。 (2) 读锁与写锁互斥: 别人加了读锁没有解锁前,加写锁会失败,反过来也是如此。 加锁失败后两种处理方式, 阻塞,直到别人解锁然后加锁成功为止 出错返回,不阻塞 (3) 写锁与写锁互斥: 别人加了写锁在没有解锁前,不能加写锁,加写锁会失败。 加锁失败后两种处理方式, 阻塞,直到别人解锁然后加锁成功为止 出错返回,不阻塞 常用的是阻塞加锁,至于如何实现阻塞和非阻塞,后面详细讲 4.3.2 使用文件锁对文件进行保护 读文件时加读锁,写文件时就加写锁,然后就可以很容易的实现符合如下要求的资源保护。 写与写之间互斥 读与写之间互斥 读与读之间共享 4.4 文件锁的加锁方式 (1) 对整个文件内容加锁 对整个文件加锁是最常用的文件锁的加锁方式。 当你对整个文件加锁时,如果文件的长度因为写入新数据或者截短而发生了变化,加锁内容的长度会 自动变化,保证对内容变化着的整个文件加锁。 (2) 对文件某部分内容加锁 不过一般来说是,对多少内容加锁,就对多少内容解锁,如果你是对整个文件加锁,就将整个文件解锁。 但是实际上加锁和实际解锁的长度可以不相同,比如我对1000个字节的内容加了锁,但是可以只对其中的 100字节解锁,不过这种情况用的少,知道有这么回事即可 4.5 文件锁的实现 实现文件锁时,我们还是需要使用fcntl函数 4.5.1 再看看fcntl的函数原型 ...

Created 8086-07-08 · Updated 8086-07-08 · 5 min · 890 words · -

taskset

“taskset” 一、在Linux上修改进程的“CPU亲和力” 在Linux上,可以通过 taskset 命令进行修改。以Ubuntu为例,运行如下命令可以安装taskset工具。 apt-get install schedutils 对运行中的进程,文档上说可以用下面的命令,把CPU#1 #2 #3分配给PID为2345的进程: taskset -cp 1,2,3 2345 但我尝试没奏效,于是我关掉了MySQL,并用taskset将它启动: taskset -c 1,2,3 /etc/init.d/MySQL start 对于其他进程,也可如此处理 (nginx除外,详见下文) 。之后用top查看CPU的使用情况,原来空闲的#1 #2 #3,已经在辛勤工作了。 二、配置nginx绑定CPU 刚才说nginx除外,是因为nginx提供了更精确的控制。 在conf/nginx.conf中,有如下一行: worker_processes 1; 这是用来配置nginx启动几个工作进程的,默认为1。而nginx还支持一个名为worker_cpu_affinity的配置项,也就是说,nginx可以为每个工作进程绑定CPU。我做了如下配置: worker_processes 3; worker_cpu_affinity 0010 0100 1000; 这里0010 0100 1000是掩码,分别代表第2、3、4颗cpu核心。 重启nginx后,3个工作进程就可以各自用各自的CPU了。 三、刨根问底 如果自己写代码,要把进程绑定到CPU,该怎么做?可以用sched_setaffinity函数。在Linux上,这会触发一次系统调用。 如果父进程设置了affinity,之后其创建的子进程是否会有同样的属性?我发现子进程确实继承了父进程的affinity属性。 四、Windows? 在Windows上修改“CPU亲和力”,可以通过任务管理器搞定。 个人感觉,Windows系统中翻译的“处理器关系”比“CPU亲和力”容易理解点儿 —————– 进行了这样的修改后,即使系统负载达到3以上,不带缓存打开blogkid.net首页 (有40多次查询) 依然顺畅;以前一旦负载超过了1.5,响应就很慢了。效果很明显。 linux taskset命令详解 SYNOPSIS taskset [options] [mask | list ] [pid | command [arg]…] OPTIONS -p, –pid operate on an existing PID and not launch a new task -c, –cpu-list specifiy a numerical list of processors instead of a bitmask. The list may contain multiple items, separated by comma, and ranges. For example, 0,5,7,9-11. -h, –help display usage information and exit -V, –version output version information and exit ...

Created 6066-07-06 · Updated 6066-07-06 · 1 min · 119 words · -

HyperLogLog

“HyperLogLog” HyperLogLog HyperLogLog,下面简称为HLL,它是 LogLog 算法的升级版,作用是能够提供不精确的去重计数。 127.0.0.1:6379> PFADD language "PHP" "Python" "Perl" "Ruby" (integer) 1 127.0.0.1:6379> PFCOUNT language (integer) 4 127.0.0.1:6379> PFADD language "PHP" # Redis 已经存在,不必对估计数量进行更新 (integer) 0 127.0.0.1:6379> PFCOUNT language # 元素估计数量没有变化 (integer) 4 127.0.0.1:6379> PFADD language "JAVA" # 添加一个不存在的元素 (integer) 1 127.0.0.1:6379> PFCOUNT language # 估计数量增一 5 https://juejin.cn/post/6844903785744056333 https://www.cnblogs.com/wmyskxz/p/12396393.html http://content.research.neustar.biz/blog/hll.html 存在以下的特点: 代码实现较难。 能够使用极少的内存来统计巨量的数据,在 Redis 中实现的 HyperLogLog,只需要12K内存就能统计2^64个数据。 计数存在一定的误差,误差率整体较低。标准误差为 0.81% 。 误差可以被设置辅助计算因子进行降低。 稍微对编程中的基础数据类型内存占用有了解的同学,应该会对其只需要12K内存就能统计2^64个数据而感到惊讶。为什么这样说呢,下面我们举下例子: 取 Java 语言来说,一般long占用8字节,而一字节有8位,即: 1 byte = 8 bit,即long数据类型最大可以表示的数是: 2^63-1。对应上面的2^64个数,假设此时有2^63-1这么多个数,从 0 ~ 2^63-1,按照long以及1k = 1024字节的规则来计算内存总数,就是: ((2^63-1) * 8/1024)K,这是很庞大的一个数,存储空间远远超过12K。而 HyperLogLog 却可以用 12K 就能统计完。 伯努利试验 在认识为什么HyperLogLog能够使用极少的内存来统计巨量的数据之前,要先认识下伯努利试验。 ...

Created 6066-07-06 · Updated 6066-07-06 · 1 min · 134 words · -