-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcontent.json
More file actions
1 lines (1 loc) · 36.8 KB
/
content.json
File metadata and controls
1 lines (1 loc) · 36.8 KB
1
[{"title":"重新认识Linux下线程、进程和协程","date":"2018-09-06T07:35:39.000Z","path":"2018/09/06/Linux下线程、进程和协程/","text":"重新认识Linux下线程、进程和协程 \b最近在看美团技术博客时,Java NIO浅析中有一句话引起了我的注意:“\b在Linux这样的操作系统中,线程本质上就是一个进程”,让我对Linux下的线程和进程产生了更深入了解的想法,再翻阅资料后汇总到这篇文章。 如果在浏览器中查阅线程和进程,在大部分复制粘贴的博客里,我们大概可以了解到以下的信息: 进程是资源分配的最小单位,线程是cpu调度和分派的基本单位 ; 创建一个线程比创建一个进程的代价要小得多,进城创建需要系统分配地址空间,建立数据表等很多工作,而线程需要的很少 ; 线程之间切换比进程间切换需要操作系统做的工作更少,效率更高; 线程占得资源更少,大部分资源都是共享; 线程能充分利用多处理器的并行数量; 线程能在等待I/O的过程中进行其他计算任务; 线程之间通信更加方便,同一进程下的线程共享静态变量,全局变量等数据,而进程间通信需要进程间通信(IPC)方式; 在没认真深入翻看更多资料前,我对线程和进程的\b理解也止步于此,那么上述说的差异真的很大吗? \b再看线程和进程的异同在美团技术博客中Java NIO浅析所述“在Linux这样的操作系统中,线程本质上就是一个进程”,我\b在查阅资料后仍对此持保留态度,来自IBM Haifa Research Labs的Muli Ben-Yehuda这样总结了Linux中进程和线程的差异: \b进程和线程几乎(almost)相同,它们最主要的差异是线程共享了虚拟内存地址空间(virtual memory address space); \b进程的上下文切换(Context switches)已经很快了,线程甚至更快; 多线程在同一个进程中更好还是多进程分离更好,取决于当时的情况; Muli Ben-Yehuda指出了线程和进程几乎(Almost)相同,却没有给出更为详细的理由。在《嵌入式\bLinux操作系统》作者Gilad Ben-Yossef的演讲PPT中给出了进一步阐述:Gilad Ben-Yossef\b首先阐述了\bLinux下线程和进程的一些特点: Linux\b系统总是以线程为粒度进行调度; 进程之间也可以像线程间的通信:共享内存、信号量(semaphores)、互斥信号(mutexes)和消息队列等在进程间也可以使用; 进程可以像线程在不加载程序的情况下启动:fork() vs. exec() 进程创建时间比线程会长很多,但在Linux\b下,\b进程创建时间依然很短; 当内存不足,不活跃进程会调低优先级并被交换到硬盘中中来释放内存,而线程则不会; \b当一个进程崩溃不会影响其他进程,而线程会\b影响到同一进程中的其他线程; \b接下来Gilad Ben-Yossef\b给出了大量线程和进程进行上下文切换耗时的数据实例(见 Ben-Yossef-GoodBadUgly.pdf),\b得出了如下几点结论: \b进程和线程的上下文时间确实不一样; \b不能断定说线程\b上下文切换时间短于进程;(部分测试进程上下文切换时间更短)\b; 进程和线程的上下文切换时间差异非常小(quite small); 处理器架构和平台\b会影响\b进程和线程上下文切换时间; 那我们为啥大部分时候用的就是线程呢?ilad Ben-Yossef这样说:“It’s the API, Silly.POSIX thread API offers simple mental model.”对比一下Linux中进程API和线程API的区别: \bfork() \bpthread_create() 0参数,在创建完成后设置每一项属性 在创建时给出大多数属性 新的进程在父进程同一位置的虚拟拷贝启动 可以定义新线程启动时的具体功能 Copy on write操作需要设置共享存储区 可以轻松掌握分享空间 \b \b并且进程的API还存在一下的困难点: 共享内存:线程可以共享同\b一进程内的所有内存。虽然进程可以用 Shm_create\b 来轻松共享内存(注意只分享你需要的!),但每个进程都可能有不同的映射虚拟内存,并且共享内存不能共享这些指针! 文件处理:在Unix中,所有都是文件。同一进程内的线程可以共享文件描述符(file descriptors),但进程不行,虽然Unix Domain Socket可以在不同进程中传递文件描述符,但\b和线程不同,系统和信号量没有完成的值也不会\b被分享; 但如下表,进程的API也有一些好处: \bProcesses \bThreads 系统中PID是可见的 内核线程id与进程内部线程无关 可以通过program_invocation_name给进程命名 没方法去给线程命名 在系统中轻松找到出一个指定进程 找到一个指定线程很困难 \b\b协程协程是一种用户级的轻量级线程,不同于线程由内核调度,协程由应用程序调度。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。因此:协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态,换种说法:进入上一次离开时所处逻辑流的位置。在并发编程中,协程与线程类似,每个协程表示一个执行单元,有自己的本地数据,与其它协程共享全局数据和其它资源。目前主流语言基本上都选择了多线程作为并发设施,与线程相关的概念是抢占式多任务(Preemptive multitasking),而与协程相关的是协作式多任务。 协程的好处: 跨平台 跨体系架构 无需线程上下文切换的开销 无需原子操作锁定及同步的开销 方便切换控制流,简化编程模型 高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理。 缺点: 无法利用多核资源:协程的本质是个单线程,它不能同时将 单个CPU 的多个核用上,协程需要和进程配合才能运行在多CPU上.当然我们日常所编写的绝大部分应用都没有这个必要,除非是cpu密集型应用。 进行阻塞操作(如IO时)会阻塞掉整个程序:这一点和事件驱动一样,可以使用异步IO操作来解决 参考资料 10 Things Every Linux Programmer Should Know - Muli Ben-Yehuda On Threads,Processes and Co-Processes - Gilad Ben-Yossef What’s the Diff: Programs, Processes, and Threads - Roderick Baue Coroutine - Wikipedia 《深入理解计算机系统》","tags":[{"name":"Linux","slug":"Linux","permalink":"http://ipopker.com/tags/Linux/"},{"name":"操作系统","slug":"操作系统","permalink":"http://ipopker.com/tags/操作系统/"}]},{"title":"Java 网络编程基础(一)- IO","date":"2018-09-04T08:16:32.000Z","path":"2018/09/04/Java基础-OIO的使用/","text":"Java学习(一)- IOJava IO(java.io)包的内容Java IO包主要包含了输入和输出到文件、网络流、内存缓冲区等,却不包含开启网络链接的类,但只要打开了一个网络连接,我们就可以用Java IO的InputStream和OutputStream来进行读写操作; Java IO类总结 Java IO类梗概 如上图所示,Java IO流主要分为字符流和字节流,Reader和Writer等读写是基于字符的,InputStream和OutputStream是基于字节流的。除此之外,还有一个特殊的类java.io.RandomAccessFile,RandomAccessFile 虽然属于java.io下的类,但它不是InputStream或者OutputStream的子类;它也不同于FileInputStream和FileOutputStream。 FileIsnputStream 只能对文件进行读操作,而FileOutputStream 只能对文件进行写操作;RandomAccessFile与输入流和输出流不同之处就是RandomAccessFile可以访问文件的任意地方同时支持文件的读和写,并且它支持随机访问。RandomAccessFile包含InputStream的三个read方法,也包含OutputStream的三个write方法。同时RandomAccessFile还包含一系列的readXxx和writeXxx方法完成输入输出。 Java IO异常处理Java7之前的Try-Catch-Finally处理风格private static void printFile() throws IOException { InputStream input = null; try { input = new FileInputStream("file.txt"); int data = input.read(); while(data != -1){ System.out.print((char) data); data = input.read(); } } finally { if(input != null){ input.close(); } } } 看起来非常不简洁,很有可能导致许多批判Java人口中的高度问题。 Java7后的Try-with-resourcesprivate static void printFileJava7() throws IOException { try( FileInputStream input = new FileInputStream("file.txt"); BufferedInputStream bufferedInput = new BufferedInputStream(input) ) { int data = bufferedInput.read(); while(data != -1){ System.out.print((char) data); data = bufferedInput.read(); } } } 创建的资源会按照与创建/排列相反的顺去去自动关闭,只要实现了AutoClosable接口的类(需要实现close()方法)都可以使用try-with-resources结构。 O\bI\u001dO的并发问题\b对于同一个同一个InputStream/OutputStream或者Reader/Writer,不应当有多个线程进行操作,因为无法保证每个线程会读/写到哪里。","tags":[{"name":"Java SE","slug":"Java-SE","permalink":"http://ipopker.com/tags/Java-SE/"}]},{"title":"WEB开发常见漏洞与解决方案","date":"2018-09-03T02:34:40.000Z","path":"2018/09/03/WEB开发常见漏洞与解决方案/","text":"WEB开发常见漏洞与解决方案\bSQL注入定义:通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令 防范:SQL注入只对SQL语句的编译过程有破坏作 用,而PreparedStatement已经准备好了,执行 阶段只是把输入串作为数据处理,而不再对语句进 行解析,因此也就避免了SQL注入问题。(\b在Mybatis\b中,#{}:相当于JDBC中的PreparedStatement,经过预编译的,是安全的。${}:是输出变量的值,是未经过预编译的,仅仅是取变量的值,是非安全的,存在SQL注入。) \u001eXSS漏洞定义:XSS是一种经常出现在web应用中的计算机安全漏洞,它允许恶意web用户将代码植入到提供给其它用户使用的页面中。比如这些代码包括HTML代码和客户端脚本。 防范: <script>alert()</script>可以执行任意脚本,可 以从输入、输出两个角度去考虑防御。 输入不处理,输出时根据应用场景统一工具类进行转义 CSP 全称为 Content Security Policy,即内容安全策略,是一种开发者定义的安全策略声明,可以阻止恶意内容在受信 web 页面上下文中的执行,减少 XSS、clickjacking、code inject 等攻击。其主要以“白名单”的形式指定可信的内容来源,或是控制一些安全相关的选项。 任意文件上传/下载定义: 上传:文件上传漏洞是指网络攻击者上传了一个可执行的文件到服务器并执行。这里上传的文件可以是木马,病毒,恶意脚本或者WebShell等。 下载:一些网站由于业务需求,可能提供文件查看或下载的功能,如果对用户查看或下载的文件不做限制,则恶意用户就能够查看或下载任意的文件,可以是源代码文件、敏感文件等,就会造成任意文件下载漏洞。 防范: 上传:限制目录不可执行、上传文件类型检查、上传文件大小检查 下载:禁止客户端自定义下载路径 C\bSRF定义:跨站请求伪造(Cross- site request forgery),能够在用户不知情的情况下发起请求。与跨网站脚本(XSS) 相比,XSS利用的是用户对指定网站的信任, CSRF利用的是网站对用户网页浏览器的信任 。 防范: 请求带随机数:每次请求带有一次有效随机数(隐藏input) 避免跨域请求:1.校验origin、referer 2.post发送Json数据 浏览器的跨域策略:Double Submit Cookie 水平越权定义:水平权限漏洞一般出现在一个用户对象关联多个其他对象(订单、地址等)、并且要实现对关联对象的CRUD的时候。开发容易习惯性的在生成CRUD表单(或AJAX请求)的时候根据认证过的用户身份来找出其有权限的被操作对象id,提供入口,然后让用户提交请求,并根据这个id来操作相关对象。在处理CRUD请求时,往往默认只有有权限的用户才能得到入口,进而才能操作相关对象,因此就不再校验权限了。可悲剧的是大多数对象的ID都被设置为自增整型,所以攻击者只要对相关id加1、减1、直至遍历,就可以操作其他用户所关联的对象了。 \b防范:把权限的控制转移到数据接口层中,要求web层在调用数据接口层的接口时额外提供userid,而这个userid在web层看来只能通过seesion来取到。这样在面对水平权限攻击时,web层的开发者不用额外花精力去注意鉴权的事情,也不需要增加一个SQL来专门判断权限——如果权限不对的话,那个and条件就满足不了,SQL自然就找不到相关对象去操作。而且这个方案对于一个接口多个地方使用的情况也比较有利,不需要每个地方都鉴权了。 cors配置不当定义: 网站A存在cors配置不当 攻击者在自己的网站B构造ajax,请求A网站的接口 当用户访问B网站页面时,触发跨域 请求A网站接口,返回的数据存入 攻击者网站。 防范:例如www.api.com仅允许www.example.com发起的跨域请求: httpResponse.setHeader(“Access-Control-Allow-Origin”,”www.example.com"); \b其他风险URL传输token: 访问日志泄露用户认证凭证 referer泄露用户认证凭证 日志审计: 禁止记录:密码、CVV、有效期、session等C4类敏 感信息禁止打印日志。 需要记录日志:时间、地点、人物、起因、经过、结果 时间、IP、username、url、内容、成功/失败 日志管理:保存至少6个月以上、异地备份、管理员禁止删除日志","tags":[{"name":"安全","slug":"安全","permalink":"http://ipopker.com/tags/安全/"}]},{"title":"OrderBy字符串排序规则和在InnoDB中的性能优化","date":"2018-08-05T09:49:48.000Z","path":"2018/08/05/OrderBy字符串排序规则和在InnoDB中的性能优化/","text":"#OrderBy字符串排序规则和在InnoDB中的性能优化 Order by字符串排序规则对于字符串来讲,排序规则可以在配置文件中my.ini中指定,主要有以下几个种类的排序规则:(1) utf8_general_cs 和 utf8_general_ci (后缀”_cs”或者”_ci”意思是区分大小写和不区分大小写(Case Sensitive & Case Insensitve))(2) utf8_bin 规定每个字符串用二进制编码存储,区分大小写,可以直接存储二进制的内容默认采取的utf8_general_ci 不区分大小写,并且是按照逐位比较,如果相同再比较下一位的方法;如果想按照int方法排序,可以order by varchar+0。 Order by性能优化参考 MySQL 5.7 Reference Manual::ORDER BY Optimization,假如有(key_part1,key_part2)的索引,在以下两种情况下MySQL会使用索引来避免排序操作: SELECT * FROM t1 ORDER BY key_part1, key_part2;如果表项不只是key_part1,key_part2的话,那么MySQL优化器可能(不一定)不会通过索引来排序,因为扫描整个索引并且按照索引去查找出所有的行,花费的时间可能比直接查找出所有行再排序还慢。特别的,如果在InnoDB中,因为其特殊的索引存储结构(见聚簇索引),索引行和主键会存放在索引的B+树中,因此可以通过索引直接返回,极大提升排序速度:SELECT pk, key_part1, key_part2 FROM t1 ORDER BY key_part1, key_part2; SELECT * FROM t1 WHERE key_part1 = constant ORDER BY key_part2;当key_part1是常量时,自然可以先通过key_part1索引找出对应的行,再利用key_part2进行排序(常量用前缀索引去检索,另一部分用来排序)。","tags":[{"name":"MySQL","slug":"MySQL","permalink":"http://ipopker.com/tags/MySQL/"}]},{"title":"分布式一致性协议(1)- 2PC与3PC","date":"2017-11-17T07:52:05.000Z","path":"2017/11/17/分布式一致性协议(1)-2PC与3PC/","text":"分布式一致性协议(1)- 2PC与3PC2PC2PC(Two-Phase Commit),即二阶段提交,是将事务的提交过程分成两个阶段处理,其流程如下: 阶段一:提交事务请求(投票阶段) 事务询问协调者(Coordinator)向所有参与者(participant)发送事务内容,询问是否可以执行事务提交(commit)操作,并开始等待各参与者相应。 执行事务各参与者节点执行事务操作,并将Undo和Redo信息记入日志中。 各参与者向协调者反馈事务询问的响应。如果各参与者成功执行了事务操作,那么反馈给协调者YES响应,表示事物可以执行;如果参与者没有成功执行事务,就反馈no,表示事务不可执行。阶段二:事务提交执行事务提交假如协调者从所有参与者那里获得的反馈都是YES,那么就会执行事务提交。 发送提交请求协调者向所有参与者节点发出Commit请求。 事务提交参与者接受到Commit请求后,会正式执行事务提交(Commit)操作,并在完成提交之后释放在整个事务执行期间所占用的事务资源。 反馈事务提交结果参与者在完成事务提交之后,向协调者发送Ack信息。 完成事务协调者接收到所有参与者反馈的Ack消息后,完成事务。 中断事务加入任何一个参与者向协调者反馈了No,或者等待超时之后,那么就会中断事务; 发送回滚请求协调者向所有参与者节点发送Rollback请求。 事务回滚参与者接受到Rollback请求后,会利用其在阶段一记录的Undo信息来执行事务回滚操作,并在完成提交之后释放在整个事务执行期间所占用的事务资源。 反馈事务回滚结果参与者在完成事务回滚之后,向协调者发送Ack信息。 中断事务。协调者接受到所有参与者反馈的Ack消息后,完成事务中断。 2PC的局限性 同步阻塞问题。执行过程中,所有参与节点都是事务阻塞型的。当参与者占有公共资源时,其他第三方节点访问公共资源不得不处于阻塞状态。 单点故障。由于协调者的重要性,一旦协调者发生故障。参与者会一直阻塞下去。尤其在第二阶段,协调者发生故障,那么所有的参与者还都处于锁定事务资源的状态中,而无法继续完成事务操作。(如果是协调者挂掉,可以重新选举一个协调者,但是无法解决因为协调者宕机导致的参与者处于阻塞状态的问题) 数据不一致。在二阶段提交的阶段二中,当协调者向参与者发送commit请求之后,发生了局部网络异常或者在发送commit请求过程中协调者发生了故障,这回导致只有一部分参与者接受到了commit请求。而在这部分参与者接到commit请求之后就会执行commit操作。但是其他部分未接到commit请求的机器则无法执行事务提交。于是整个分布式系统便出现了数据部一致性的现象。 二阶段无法解决的问题:协调者再发出commit消息之后宕机,而唯一接收到这条消息的参与者同时也宕机了。那么即使协调者通过选举协议产生了新的协调者,这条事务的状态也是不确定的,没人知道事务是否被已经提交。 2PC 3PC三阶段提交(Three-phase commit),也叫三阶段提交协议(Three-phase commit protocol),是二阶段提交(2PC)的改进版本。与两阶段提交不同的是,三阶段提交有两个改动点。 引入超时机制。同时在协调者和参与者中都引入超时机制。 在第一阶段和第二阶段中插入一个准备阶段。保证了在最后提交阶段之前各参与节点的状态是一致的。也就是说,除了引入超时机制之外,3PC把2PC的准备阶段再次一分为二,这样三阶段提交就有CanCommit、PreCommit、DoCommit三个阶段。 阶段一:CanCommit阶段3PC的CanCommit阶段其实和2PC的准备阶段很像。协调者向参与者发送commit请求,参与者如果可以提交就返回Yes响应,否则返回No响应。 事务询问 协调者向参与者发送CanCommit请求。询问是否可以执行事务提交操作。然后开始等待参与者的响应。 响应反馈 参与者接到CanCommit请求之后,正常情况下,如果其自身认为可以顺利执行事务,则返回Yes响应,并进入预备状态。否则反馈No 阶段二:PreCommit阶段协调者根据参与者的反应情况来决定是否可以记性事务的PreCommit操作。根据响应情况,有以下两种可能。 执行事务预提交假如协调者从所有的参与者获得的反馈都是Yes响应,那么就会执行事务的预执行。 发送预提交请求 协调者向参与者发送PreCommit请求,并进入Prepared阶段。 事务预提交 参与者接收到PreCommit请求后,会执行事务操作,并将undo和redo信息记录到事务日志中。 响应反馈 如果参与者成功的执行了事务操作,则返回ACK响应,同时开始等待最终指令。 中断事务假如有任何一个参与者向协调者发送了No响应,或者等待超时之后,协调者都没有接到参与者的响应,那么就执行事务的中断。 发送中断请求 协调者向所有参与者发送abort请求。 中断事务 参与者收到来自协调者的abort请求之后(或超时之后,仍未收到协调者的请求),执行事务的中断。 阶段三:doCommit阶段该阶段进行真正的事务提交,也可以分为以下两种情况。执行提交 发送提交请求 协调接收到参与者发送的ACK响应,那么他将从预提交状态进入到提交状态。并向所有参与者发送doCommit请求。 事务提交 参与者接收到doCommit请求之后,执行正式的事务提交。并在完成事务提交之后释放所有事务资源。 响应反馈 事务提交完之后,向协调者发送Ack响应。 完成事务 协调者接收到所有参与者的ack响应之后,完成事务。 中断事务协调者没有接收到参与者发送的ACK响应(可能是接受者发送的不是ACK响应,也可能响应超时),那么就会执行中断事务。 发送中断请求 协调者向所有参与者发送abort请求 事务回滚 参与者接收到abort请求之后,利用其在阶段二记录的undo信息来执行事务的回滚操作,并在完成回滚之后释放所有的事务资源。 反馈结果 参与者完成事务回滚之后,向协调者发送ACK消息 中断事务 协调者接收到参与者反馈的ACK消息之后,执行事务的中断。在doCommit阶段,如果参与者无法及时接收到来自协调者的doCommit或者rebort请求时,会在等待超时之后,会继续进行事务的提交。(其实这个应该是基于概率来决定的,当进入第三阶段时,说明参与者在第二阶段已经收到了PreCommit请求,那么协调者产生PreCommit请求的前提条件是他在第二阶段开始之前,收到所有参与者的CanCommit响应都是Yes。(一旦参与者收到了PreCommit,意味他知道大家其实都同意修改了)所以,一句话概括就是,当进入第三阶段时,由于网络超时等原因,虽然参与者没有收到commit或者abort响应,但是他有理由相信:成功提交的几率很大。 ) 2PC与3PC的区别相对于2PC,3PC主要解决的单点故障问题,并减少阻塞,因为一旦参与者无法及时收到来自协调者的信息之后,他会默认执行commit。而不会一直持有事务资源并处于阻塞状态。但是这种机制也会导致数据一致性问题,因为,由于网络原因,协调者发送的abort响应没有及时被参与者接收到,那么参与者在等待超时之后执行了commit操作。这样就和其他接到abort命令并执行回滚的参与者之间存在数据不一致的情况。","tags":[{"name":"分布式理论基础","slug":"分布式理论基础","permalink":"http://ipopker.com/tags/分布式理论基础/"}]},{"title":"分布式事物的CAP/BASE定理","date":"2017-11-16T08:47:16.000Z","path":"2017/11/16/分布式事物的CAP-BASE定理/","text":"分布式事物的CAP/BASE定理 在分布式系统中,分布式事物处理与数据一致性成了分布式一同最为关注的焦点,本文是在阅读《从Paxos到Zookeeper:分布式一致性原理与实践》后,对分布式事物一致性的认知记录。 分布式事物的CAP/BASE定理对于集中式的事务处理或本地事务处理,ACID(原子性 Atomicity、一致性 Consistency、隔离性 Isolation、持久性 Durability)模型可以保证数据的严格的一致性。但随着分布式事物的出现,传统的ACID模型已经无法胜任在分布式下的一致性保证,所以出现了诸如CAP和BASE这样的分布式系统的理论。 CAP定理CAP理论主要是说:一个分布式系统不可能同时满足一致性(C:Consistency)、可用性(A:Availablity)和分区容错性(P:Partition tolerance)这三个基本需求,最多同时满足其中的两项。 C:一致性(Consistency)在分布式环境中,一致性是指在多个副本之间能否保持一致的特性。在分布式系统中,如果能够做到针对一个数据项的更新操作执行成功后,所有用户都能读取到其最新的值,那么这样的系统就被认为具有强一致性。 A:可用性(Availability)可用性是指系统提供的服务必须一致处于可用的状态,对于用户的每一个操作请求总是能够在有限的时间内返回结果。 P:分区容错性(Partition tolerance)分区容错性是指分布式系统遇到任何网络分区故障的时候,仍然需要能够保证对外提供满足一致性和可用性的服务,除非整个网络发生了故障。其中网络分区的意思是,在分布式系统中,不同节点分布在不同的子网络(机房或者异地网络),需要注意的是,组成一个分布式系统的每个节点都可以看成一个特殊的网络分区。 总结\b CAP理论 从CAP定理我们可以看出,一个分布式系统不可能同时满足CAP这三个需求,另外需要明确的是,对于一个分布式系统而言,分区容错性是一个最基本的要求,因为既然是分布式系统,那么分布式系统中的组件必然需要被部署到不同的节点中,否则也就无所谓是一个分布式系统,那么必然会出现子网络。因此分区容错性也就是一个分布式系统必然要面对和解决的问题。因此系统架构主要会根据业务特点在C(一致性)和A(可用性)之间寻求平衡。 BASE理论BASE是Basically Available(基本可用)、Soft state(软状态)和 Eventually consistent(最终一致性)的缩写,BASE是对CAP中一致性和可用性平衡的结果,是基于CAP定理演化而来的,其核心思想是:即使无法做到强一致性(Strong consistency),但每个应用都可以根据自身业务特点,采用适当的方式使系统达到最终一致性(Eventual consistency)。 BA:基本可用(Basically Available)基本可用好似指分布式系统出现不可预知故障时,允许损失部分可用性。以下是基本可用的典型例子: 相应时间上的损失:允许响应时间略微变长; 功能上的损失:比如电商系统,在平时正常时,消费者可以顺利完成每一笔订单,但在双十一,由于购物激增,未来保证系统稳定性,部分消费者可能被引导到降级页面。S:软状态(Soft state)与硬状态相对,指允许系统中的数据存在中间状态,并认为该中间状态的存在不会影响系统的整体可用性,即循序系统在不同节点间的数据副本之间进行数据同步过程中存在一定的延时。E:最终一致性(Eventually consistent)最终一致性情调的是系统中的所有数据副本,在经过一段时间的同步后,最终能够达到一个一致的状态。因此,最终一致性需要系统保证最终数据能够达到一致,而不需要实时保证系统数据的强一致性。总结BASE理论是面向大型高可用可拓展的分布式系统,和传统事物的ACID特性相反,它完全不同于ACID的强一致性模型,而是牺牲强一致性来获得可用性,并循序数据在一段时间内是不一致的,但最终达到一致状态。","tags":[{"name":"分布式理论基础","slug":"分布式理论基础","permalink":"http://ipopker.com/tags/分布式理论基础/"}]},{"title":"kafka特性设计和实现-翻译自官方文档","date":"2017-11-10T10:40:25.000Z","path":"2017/11/10/kafka特性设计和实现-翻译自官方文档/","text":"kafka特性设计和实现-翻译自官方文档概述kafka是最初由Linkedin公司开发,使用Scala语言编写,Kafka是一个分布式、分区的、多副本的、多订阅者的日志系统(分布式MQ系统),它的特性如下: 通过O(1)的磁盘数据结构提供消息的持久化,这种结构对于即使数以TB的消息存储也能够保持长时间的稳定性能。 高吞吐量:即使是非常普通的硬件kafka也可以支持每秒数十万的消息。 支持同步和异步复制两种HA Consumer客户端pull,随机读利用sendfile系统调用实现zero-copy ,批量拉数据 消费状态保存在客户端 消息存储顺序写 数据迁移、扩容对用户透明 支持Hadoop并行数据加载。 支持online和offline的场景。 持久化:通过将数据持久化到硬盘以及replication防止数据丢失。 scale out:无需停机即可扩展机器。 定期删除机制,支持设定partitions的segment file保留时间。 持久性设计与性能优化kafka在很大程度上依赖文件系统来存储和缓存消息。为了尽量缩小磁盘与内存带来的性能差异,kafka在设计上采用一些特殊设计提升性能和效率: kafka会保持以顺序的方式对硬盘进行读写(对于机械硬盘来说,随机读写带来的多余磁头寻道时间让读写速度远远慢于顺序读写); 依赖于OS的page cache(页高速缓冲存储器),page cache的大小为一页,通常为4K。在linux读写文件时,它用于缓存文件的逻辑内容,从而加快对磁盘上映像和数据的访问。详情见: Linux Page Cache机制 直接在文件上进行读取和添加(append),这样的磁盘数据结构比起传统消息队列用的持久化数据结构如b-tree等更快,所有操作达到了O(1),并且读和写并不会互相影响。 为了减少字节复制,提高效率,kafka采用了Linux下的sendfile来实现zero-copy。现代的unix操作系统提供了一个高度优化的代码路径,用于将数据从页面缓存转移到套接字; 在Linux中,这是通过sendfile系统调用完成的。要理解sendfile的影响,了解数据从文件传输到套接字的常见数据路径非常重要:1.操作系统将数据从磁盘读取到内核空间的pagecache中2.应用程序从内核空间读取数据到用户空间缓冲区3.应用程序将数据写回内核空间到套接字缓冲区4.操作系统将数据从套接字缓冲区复制到通过网络发送的NIC缓冲区这显然是低效的,有四个副本和两个系统调用。使用sendfile,通过允许操作系统将数据从pagecache直接发送到网络,可以避免重新复制。所以在这个优化的路径中,只需要最终拷贝到NIC缓冲区。 很多时候性能瓶颈不在cpu或者磁盘,而在于网络。Kafka以高效的批处理格式支持压缩。一批消息可以压缩在一起并以这种形式发送到服务器。这批消息将以压缩格式写入,并将保持压缩在日志中,只会由消费者解压缩。Kafka支持GZIP,Snappy和LZ4压缩协议。 生产者(Producer)的负载均衡与数据传输优化生产者负载均衡 producer 端的配置文件中,指定 Kafka 的 broker 信息; kafka集群中的任何一个broker,都可以向producer提供metadata信息;这些metadata中包含”集群中存活的servers列表”、”partitions leader列表”等信息(请参看zookeeper中的节点信息); 当producer获取到metadata信息之后,producer将会和Topic下所有partition leader保持socket连接; 消息由producer直接通过socket发送到broker,中间不会经过任何”路由层”;消息被路由到哪个partition上,由producer客户端决定,比如,可以采用”random”、”key-hash”、”轮询”等,如果一个topic中有多个partitions,那么在producer端实现”消息均衡分发”是必要的; 生产者的数据传输优化kafka会试图在内存中积累数据,并在一个请求中发送大批量的数据。批处理可以被配置为累加不超过固定数量的消息并且不超过某个固定的延迟限制(比如64k或10ms)。这允许发送更多字节的累积,并且在服务器上几个较大的I / O操作。这种缓冲是可配置的,并提供了一种机制来折中少量额外的延迟以获得更好的吞吐量。 消费者(Consumer)对于“推拉”的权衡以及处理已消费信息的优化push还是pull?消费者是应该从brokers那里pull数据,还是让brokers向消费者那里push数据?这方面,kafka采用了一种更为传统的设计方式,由大多mq共享,数据从生产者推送给brokers,由消费者从brokers那里pull。一些以日志为中心的系统,如Scribe和 Apache Flume,遵循一个非常不同的推送路径,数据被push到下游。这两种方法都有优点和缺点。然而,基于推送的系统难以处理不同的消费者,因为brokers控制数据传输的速度。目标通常是消费者能够以最大可能的速度消费; 不幸的是,在推动系统中,这意味着当消费率低于生产率(本质上是拒绝服务攻击)时,消费者容易垮掉。基于pull的系统具有更好的属性,消费者消费速度只要落后就会在它可以的时候赶上。基于pull的系统的另一个优点是,它可以将数据发送给消费者,基于推送的系统必须选择立即发送数据,或者累积更多的数据,然后在不知道下游消费者是否能够立即处理的情况下发送。如果调整为低延迟,则这将导致一次只发送单个消息,以便传输结束被缓冲,这是浪费的。基于pull的设计修复了这个问题,因为消费者总是将所有可用的消息拉到日志中的当前位置(或达到某个可配置的最大大小)之后。所以可以获得最佳的数据流量而不会引入不必要的延迟但基于pull的系统的缺点是,如果brokers没有数据,消费者可能会不断发送请求来感知数据到达。为了避免这种情况,我们在我们的pull请求中有一些参数,它们允许消费者请求以“长轮询”的方式进行阻塞,等待数据到达(并且可选地等待,直到给定数量的字节可用以确保大的传输大小)。 已消费的信息处理对于大多数mq,当消息被发送给消费者时,brokers要么立即在本地记录,要么等待消费者的确认。这是一个相当直观的选择,实际上对于单个机器服务器来说,不清楚这个数据可以去哪里。由于在许多消息传递系统中用于存储的数据结构规模较小,因此这也是一个实用的选择 - 由于代理知道消耗的是什么,它可以立即删除它,保持较小数据量。kafka处理这个不同。topic被分成一组完全有序的分区,每个分区在任何给定的时间都被每个订阅消费者组中的一个消费者消费。这意味着消费者在每个分区中的位置只是一个整数,即要消耗的下一个消息的偏移量。这使得所消耗的状态非常小,每个分区只有一个数字。这个状态可以定期检查点。这使消息确认的代价非常低。这个方案还有一个好处。消费者可以故意倒回到旧的偏移量并重新使用数据。这违反了队列的共同合同,但是对于许多消费者来说却是一个必不可少的特征。例如,如果消费者代码有一个错误,并且在一些消息被消费之后被发现,则消费者可以在错误修复后重新使用这些消息。 kafka的信息传递保证可能的信息传递保证: 最多一次 -消息可能会丢失,但永远不会重新发送。 至少一次 -消息永远不会丢失,但可以重新传递。 恰恰一次 - 这就是人们真正想要的,每个信息只传递一次。Kafka支持在KafkaStreams中一次交付,并且在Kafka topic之间传输和处理数据时,事务性生产者/消费者通常可用于提供准确一次的交付。其他目的地系统的一次交付通常需要与这些系统合作,但是Kafka提供了实现这种可行的补偿。否则,Kafka默认保证至少一次交付,并允许用户在处理一批消息之前,通过禁用生产者的重试和消费者的抵消来实现至多一次交付。","tags":[{"name":"kafka","slug":"kafka","permalink":"http://ipopker.com/tags/kafka/"}]}]