GinoBeFunny

阅读随手记 201703

关键字:微服务, 架构, Event Sourcing, CQRS, Redis, TDD, 消息中间件, 缓存, RPC, 监控, 高性能, 高并发, 高可用, 机器学习, 深度学习, 人工智能。

Martin Fowler谈如何理解事件驱动和CQRS Martin Fowler/薛命灯

原文地址:https://martinfowler.com/articles/201701-event-driven.html

  • 为了帮助读者理解“事件驱动”的含义,软件大师Martin Fowler在他的博客上总结出了四种基于事件驱动的模型。
  • 事件通知(Event Notification):事件通知是最基本也是最简单的模型,当一个系统发生了变更,它会通过发送事件消息的形式通知其他系统,发送消息的系统不要求接收消息的系统返回任何响应,即使有响应返回,它也不对其进行任何处理。事件通知的好处在于它的简单性,并且有助于降低系统间的耦合性。不过太多的事件通知可能会带来问题,太多的事件难以跟踪,发生问题难以调试;另外通知事件不会包含太多的数据,额外的请求不仅会造成延迟;
  • 事件传递状态转移(Event-Carried State Transfer):它比事件通知更进一步,这个模型最大的特点是事件里包含了发生变更的数据。对于接收事件的系统来说,无需再次向源系统发起请求,从而降低了延迟。而且就算源系统宕机,也不会影响到后续的流程。不过,既然把变更数据放在事件里进行传输,那么占用更多的带宽是不可避免的了。
  • 事件溯源(Event-Sourcing):其核心理念是在对系统的状态做出变更时,把每次变更记录为一个事件,在未来的任何时刻,都可以通过重新处理这些事件来重建系统的状态。事件存储是主要的事件来源,可以从事件存储中重建系统的状态。其好处是存储结构简单,易于存储,不需要用到事务控制从而可以避免使用锁,事件本身还可以充当审计日志的作用。不足之处在于如果事件很多,重放事件是一个耗时的过程,而且在重放过程中可能会涉及与第三方外部系统发生交互,所以需要做一些额外的操作。
  • CQRS:是Command Query Resposibility Segregation的缩写,它将读操作和写操作进行分离,不仅让逻辑更清晰,而且可以各自进行优化。对于读多写少的系统来说,就特别适合使用CQRS,因为可以针对读性能和写性能进行优化,而且可以进行横向扩展。不过CQRS的概念虽然简单,但是实现起来相对复杂,而且涉及到很多领域驱动设计的概念,最好结合事件溯源一起使用。

Event Sourcing Martin Fowler

Event Sourcing ensures that all changes to application state are stored as a sequence of events. Not just can we query these events, we can also use the event log to reconstruct past states, and as a foundation to automatically adjust the state to cope with retroactive changes.

The fundamental idea of Event Sourcing is that of ensuring every change to the state of an application is captured in an event object, and that these event objects are themselves stored in the sequence they were applied for the same lifetime as the application state itself.

The most obvious thing we’ve gained by using Event Sourcing is that we now have a log of all the changes. Not just can we see where each ship is, we can see where it’s been.

The key to Event Sourcing is that we guarantee that all changes to the domain objects are initiated by the event objects. This leads to a number of facilities that can be built on top of the event log: Complete Rebuild, Temporal Query, Event Replay.

In many applications it’s more common to request recent application states, if so a faster alternative is to store the current application state and if someone wants the special features that Event Sourcing offers then that additional capability is built on top.

There are a number of choices about where to put the logic for handling events. The primary choice is whether to put the logic in Transaction Scripts or Domain Model. As usual Transaction Scripts are better for simple logic and a Domain Model is better when things get more complicated.

As well as events playing themselves forwards, it’s also often useful for them to be able to reverse themselves.

Many of the advantages of Event Sourcing stem from the ability to replay events at will, but if these events cause update messages to be sent to external systems, then things will go wrong because those external systems don’t know the difference between real processing and replays. To handle this you’ll need to wrap any external systems with a Gateway. This in itself isn’t too onerous since it’s a thoroughly good idea in any case. The gateway has to be a bit more sophisticated so it can deal with any replay processing that the Event Sourcing system is doing.

When to Use It: Packaging up every change to an application as an event is an interface style that not everyone is comfortable with, and many find to be awkward. As a result it’s not a natural choice and to use it means that you expect to get some form of return. One obvious form of return is that it’s easy to serialize the events to make an Audit Log. Another use for this kind of complete Audit Log is to help with debugging. Event Sourcing is the foundation for Parallel Models or Retroactive Events. If you want to use either of those patterns you will need to use Event Sourcing first.

CQRS Martin Fowler

CQRS stands for Command Query Responsibility Segregation. At its heart is the notion that you can use a different model to update information than the model you use to read information. For some situations, this separation can be valuable, but beware that for most systems CQRS adds risky complexity.

The change that CQRS introduces is to split that conceptual model into separate models for update and display, which it refers to as Command and Query respectively following the vocabulary of CommandQuerySeparation. The rationale is that for many problems, particularly in more complicated domains, having the same conceptual model for commands and queries leads to a more complex model that does neither well.

CQRS

The two models might not be separate object models, it could be that the same objects have different interfaces for their command side and their query side, rather like views in relational databases. But usually when I hear of CQRS, they are clearly separate models.

When to use it: CQRS is a significant mental leap for all concerned, so shouldn’t be tackled unless the benefit is worth the jump. In particular CQRS should only be used on specific portions of a system (a BoundedContext in DDD lingo) and not the system as a whole. So far I see benefits in two directions. Firstly is that a few complex domains may be easier to tackle by using CQRS. The other main benefit is in handling high performance applications.

RxJava2实例解析 Victor Grazi/薛命灯Rays

  • 响应式编程是一种处理异步数据流的规范,它为数据流的转换和聚合以及数据流的控制管理提供了工具支持,它让考量程序整体设计的工作变得简单。但它使用起来并不简单,它的学习曲线也并不平坦。
  • 传统的编程模式以对象为基础,而响应式以事件流为基础。事件可能以多种形式出现,比如对象、数据源、鼠标移动信息或者异常。
  • 首先要记住的是,响应式里所有的东西都是流。Observable封装了流,是最基本的单元。流可以包含零个或多个事件,有未完成和已完成两种状态,可以正常结束也可以发生错误。如果一个流正常完成或者发生错误,说明处理结束了,虽然有些工具可以对错误进行重试或者使用不同的流替换发生错误的流。
  • 一个Observable对象必须要有一个订阅者来处理它所生成的事件。所幸的是,现在Java支持Lambda表达式,我们就可以使用简洁的声明式风格来表示订阅操作:
Observable<String> howdy = Observable.just("Howdy!");
howdy.subscribe(System.out::println);
  • zip操作通过成对的“zip”映射转换把源流的元素跟另一个给定流的元素组合起来,其中的映射可以使用Lambda表达式来表示。只要其中的一个流完成操作,整个zip操作也跟着停止,另一个未完成的流剩下的事件就会被忽略。zip可以支持最多9个源流的zip操作。zipWith操作可以把一个指定流合并到一个已存在的流里。我们可以使用range和zipWith操作加入编号,并用String.format做映射转换:
Observable.fromIterable(words)
 .zipWith(Observable.range(1, Integer.MAX_VALUE), (string, count)->String.format("%2d. %s", count, string))
 .subscribe(System.out::println);
  • 我们从小被告知“quick brown fox”这个全字母短句包含了英语里所有的字母,现在让我们对这些字母进行排序看看:
List<String> words = Arrays.asList("the", "quick", "brown", "fox", "jumped", 
    "over", "the", "lazy", "dogs");

Observable.fromIterable(words)
 .flatMap(word -> Observable.fromArray(word.split("")))
 .distinct()
 .sorted()
 .zipWith(Observable.range(1, Integer.MAX_VALUE), (string, count) -> String.format("%2d. %s", count, string))
 .subscribe(System.out::println);
  • 但是到目前为止,所有的代码都跟Java 8里引入的Streams API很相似,不过这种相似只是一种巧合,因为响应式包含的内容远不止这些。响应式引入了执行时间、节流、流量控制等概念,而且它们可以被连接到“永不停止”的处理流程里。响应式产生的结果虽然不是集合,但你可以用任何期望的方式来处理这些结果。
  • 在RxJava的前期版本中,即使对于无需流控制的小型流,Observable也给出了流控制方法。为符合响应式的规范,RxJava2将流控制从Observable类中移除,并引入了新的Flowable类。Flowable可以看作是提供流控制的Observable。
  • 要连接到长期运行的现有数据源上,除非是提供背压控制,我们通常会选择使用Flowable,使用一种Observable的并行语法。a. 调用Flowable的publish方法生成一个新的ConnectableFlowable; b. 调用ConnectableFlowable的connect方法开始生成数据;
  • 要连接到一个已有的数据源上,可以在这个数据源上添加监听器(如果你喜欢这么做),监听器会把事件传播给订阅者,然后在每个事件发生时调用订阅者的onNext方法。在实现监听器的时候要确保每个订阅者仍然处于订阅状态,否则就要停止把事件传播给它,同时要注意回压信号。所幸的是,这些工作可以由Flowabled的create方法来处理。

Redis的内存优化 CacheCloud

  • Redis存储的所有值对象在内部定义为redisObject结构体,内部结构如下图所示:

RedisObject

  • Redis存储的数据都使用redisObject来封装,包括string,hash,list,set,zset在内的所有数据类型。理解redisObject对内存优化非常有帮助。type字段表示当前对象使用的数据类型,encoding字段表示Redis内部编码类型,lru字段记录对象最后一次被访问的时间,refcount字段记录当前对象被引用的次数,*ptr字段与对象的数据内容相关,如果是整数直接存储数据,否则表示指向数据的指针。
  • 降低Redis内存使用最直接的方式就是缩减键(key)和值(value)的长度。值对象缩减比较复杂,常见需求是把业务对象序列化成二进制数组放入Redis。首先应该在业务上精简业务对象,去掉不必要的属性避免存储无效数据。其次在序列化工具选择上,应该选择更高效的序列化工具来降低字节数组大小。
  • 对象共享池指Redis内部维护[0-9999]的整数对象池。创建大量的整数类型redisObject存在内存开销,每个redisObject内部结构至少占16字节,甚至超过了整数自身空间消耗。所以Redis内存维护一个[0-9999]的整数对象池,用于节约内存。除了整数值对象,其他类型如list,hash,set,zset内部元素也可以使用整数对象池。因此开发中在满足需求的前提下,尽量使用整数对象以节省内存。
  • 字符串优化:Redis没有采用原生C语言的字符串类型而是自己实现了字符串结构,内部简单动态字符串,简称SDS;因为字符串(SDS)存在预分配机制,日常开发中要小心预分配带来的内存浪费,从测试数据看,同样的数据追加后内存消耗非常严重;字符串之所以采用预分配的方式是防止修改操作需要不断重分配内存和字节数据拷贝,但同样也会造成内存的浪费,字符串预分配每次并不都是翻倍扩容;字符串重构指不一定把每份数据作为字符串整体存储,像json这样的数据可以使用hash结构,使用二级结构存储也能帮我们节省内存,同时可以使用hmget,hmset命令支持字段的部分读取修改,而不用每次整体存取。
  • 编码优化:Redis对外提供了string,list,hash,set,zet等类型,但是Redis内部针对不同类型存在编码的概念。Redis作者想通过不同编码实现效率和空间的平衡,比如当我们的存储只有10个元素的列表,当使用双向链表数据结构时,必然需要维护大量的内部字段如每个元素需要前置指针、后置指针、数据指针等,造成空间浪费,如果采用连续内存结构的压缩列表(ziplist),将会节省大量内存,而由于数据长度较小,存取操作时间复杂度即使为O(n2)性能也可满足需求。

类型和编码关系

  • 控制key的数量:当使用Redis存储大量数据时,通常会存在大量键,过多的键同样会消耗大量内存。Redis本质是一个数据结构服务器,它为我们提供多种数据结构,如hash,list,set,zset等结构。使用Redis时不要进入一个误区,大量使用get/set这样的API,把Redis当成Memcached使用。对于存储相同的数据内容利用Redis的数据结构降低外层键的数量,也可以节省大量内存。

Redis架构之防雪崩设计:网站不宕机背后的兵法 付磊,张益军

  • 缓存穿透预防及优化:缓存穿透是指查询一个根本不存在的数据,缓存层和存储层都不会命中;缓存穿透将导致不存在的数据每次请求都要到存储层去查询,失去了缓存保护后端存储的意义;解决方法:缓存空对象和布隆过滤器拦截;
  • 缓存雪崩问题优化:由于缓存层承载着大量请求,有效的保护了存储层,但是如果缓存层由于某些原因整体不能提供服务,于是所有的请求都会达到存储层,存储层的调用量会暴增,造成存储层也会挂掉的情况;预防和解决缓存雪崩问题,可以从以下三个方面进行着手:保证缓存层服务高可用性、依赖隔离组件为后端限流并降级、提前演练。
  • 缓存热点key重建优化:开发人员使用缓存和过期时间的策略既可以加速数据读写,又保证数据的定期更新,这种模式基本能够满足绝大部分需求。但如果热点Key和重建缓存耗时两个问题同时出现,可能就会对应用造成致命的危害;解决方法:互斥锁(只允许一个线程重建缓存)、永远不过期(唯一不足的就是重构缓存期间会出现数据不一致的情况)。

现代化的缓存设计方案 Benjamin Manes/简直

  • 缓存是提升性能的通用方法,现在大多数的缓存实现都使用了经典的技术。这篇文章中,我们会发掘Caffeine中的现代化的实现方法。Caffeine 是一个开源的Java缓存库,它能提供高命中率和出色的并发能力。期望读者们能被这些想法激发,进而将它们应用到任何你喜欢的编程语言中。
  • 驱逐策略:缓存的驱逐策略是为了预测哪些数据在短期内最可能被再次用到,从而提升缓存的命中率。LRU策略或许是最流行的驱逐策略,但LRU通过历史数据来预测未来是局限的,它会认为最后到来的数据是最可能被再次访问的。现代缓存扩展了对历史数据的使用,结合就近程度和访问频次来更好的预测数据。其中一种保留历史信息的方式是使用popularity sketch(一种压缩、概率性的数据结构)来从一大堆访问事件中定位频繁的访问者。Window TinyLFU(W-TinyLFU)算法将 sketch 作为过滤器,当新来的数据比要驱逐的数据高频时,这个数据才会被缓存接纳。这个许可窗口给予每个数据项积累热度的机会,而不是立即过滤掉。对于长期保留的数据,W-TinyLFU 使用了分段 LRU(Segmented LRU,缩写 SLRU)策略。起初,一个数据项存储被存储在试用段(probationary segment)中,在后续被访问到时,它会被提升到保护段(protected segment)中(保护段占总容量的 80%)。保护段满后,有的数据会被淘汰回试用段,这也可能级联的触发试用段的淘汰。这套机制确保了访问间隔小的热数据被保存下来,而被重复访问少的冷数据则被回收。
  • 过期策略:鉴于大多数场景里不同数据项使用的都是固定的过期时长,Caffien采用了统一过期时间的方式。这个限制让用 O(1)的有序队列组织数据成为可能。针对数据的写后过期,维护了一个写入顺序队列,针对读后过期,维护了一个读取顺序队列。缓存能复用驱逐策略下的队列以及下面将要介绍的并发机制,让过期的数据项在缓存的维护阶段被抛弃掉。
  • 并发:由于在大多数的缓存策略中,数据的读取都会伴随对缓存状态的写操作,并发的缓存读取被视为一个难点问题。在 Caffeine 中,有一组缓冲区被用来记录读写。一次访问首先会被因线程而异的哈希到 stripped ring buffer 上,当检测到竞争时,缓冲区会自动扩容。一个 ring buffer 容量满载后,会触发异步的执行操作,而后续的对该 ring buffer 的写入会被丢弃,直到这个 ring buffer 可被使用。虽然因为 ring buffer 容量满而无法被记录该访问,但缓存值依然会返回给调用方。这种策略信息的丢失不会带来大的影响,因为 W-TinyLFU 能识别出我们希望保存的热点数据。通过使用因线程而异的哈希算法替代在数据项的键上做哈希,缓存避免了瞬时的热点 key 的竞争问题。写数据时,采用更传统的并发队列,每次变更会引起一次立即的执行。

大话程序猿眼里的高并发之续篇 SFLYQ

  • 分层(将系统在横向维度上切分成几个部分,每个部门负责一部分相对简单并比较单一的职责,然后通过上层对下层的依赖和调度组成一个完整的系统),分割(在纵向方面对业务进行切分,将一块相对复杂的业务分割成不同的模块单元),分布式(分布式应用和服务,将分层或者分割后的业务分布式部署,独立的应用服务器、数据库和缓存服务器,当业务达到一定用户量的时候,再进行服务器均衡负载,数据库、缓存主从集群);
  • 集群:对于用户访问集中的业务独立部署服务器,应用服务器,数据库,nosql数据库。核心业务基本上需要搭建集群,即多台服务器部署相同的应用构成一个集群,通过负载均衡设备共同对外提供服务,服务器集群能够为相同的服务提供更多的并发支持;
  • 异步:在高并发业务中如果涉及到数据库操作,主要压力都是在数据库服务器上面,虽然使用主从分离,但是数据库操作都是在主库上操作,单台数据库服务器连接池允许的最大连接数量是有限的,像这种涉及数据库操作的高并发的业务,就要考虑使用异步了,客户端发起接口请求,服务端快速响应,客户端展示结果给用户,数据库操作通过异步同步;
  • 缓存:数据不经常变化,我们可以把数据进行缓存,Cache是直接存储在应用服务器中,读取速度快,内存数据库服务器允许连接数可以支撑到很大,而且数据存储在内存,读取速度快,再加上主从集群,可以支撑很大的并发查询;
  • 面向服务:使用服务化思维,将核心业务或者通用的业务功能抽离成服务独立部署,对外提供接口的方式提供功能。最理想化的设计是可以把一个复杂的系统抽离成多个服务,共同组成系统的业务,优点:松耦合,高可用性,高伸缩性,易维护。通过面向服务化设计,独立服务器部署,均衡负载,数据库集群,可以让服务支撑更高的并发;
  • 冗余,自动化:当高并发业务所在的服务器出现宕机的时候,需要有备用服务器进行快速的替代,在应用服务器压力大的时候可以快速添加机器到集群中,所以我们就需要有备用机器可以随时待命。 最理想的方式是可以通过自动化监控服务器资源消耗来进行报警,自动切换降级方案,自动的进行服务器替换和添加操作等,通过自动化可以减少人工的操作的成本,而且可以快速操作,避免人为操作上面的失误。

浅谈机器学习基础 我偏笑_NSNirvana

下篇的地址为:http://www.jianshu.com/p/0359e3b7bb1b

浅谈深度学习基础 我偏笑_NSNirvana

下篇的地址为:http://www.jianshu.com/p/3d1ddfce1563

机器学习算法实现解析——word2vec源码解析 zhiyong_will

分布式系列文章——Paxos算法原理与推导 linbingdong

分布式一致性算法:Raft 算法 linbingdong

解决业务代码里的分布式事务一致性问题 陶文

  • 微服务架构解决了很多问题,但是同时引入了很多问题,本文要探讨的是如何解决下面这几个问题:有大量的同步RPC依赖,如何保证自身的可靠性?RPC调用失败,降级处理之后如何保证数据可修复?消息队列是一个RPC主流程的旁路流程,怎么保证可靠性?消息队列怎么保持与数据库的事务一致?
  • 同步转异步,解决稳定性问题:在平时的时候,都是RPC同步调用,如果调用失败了,则自动把同步调用降级为异步的,消息此时进入队列,然后异步被重试。

同步转异步

  • 把消息队列放入到主流程:如果要把重要的业务逻辑挂在消息队列后面,必须要保证消息队列里的数据的完整性,不能有丢失的情况,所以不能是把消息队列的写入作为一个旁路的逻辑。如果消息队列写入失败或者超时,都应该直接返回错误,而不是允许继续执行。在无法及时写入的情况,我们需要使用本地文件充当一个缓冲。实际上是通过引入本地文件队列结合远程分布式队列构成一个可用性更高,延迟更低的组合队列方案。这个本地的队列如果能封装到一个 Kafka 的 Agent 作为本地写入的代理,那是最理想的实现方式。

把消息队列放入到主流程

  • 保障分布式事务一致性:我们需要一个延迟队列,在业务入口的时候挂一个延迟job,然后执行完了取消它。如果没有执行完,则延迟队列负责去触发这个延迟任务,把整个业务流程重复执行一遍。这样我们就可以保证任意rpc操作流程的最终一致性了。而入kafka消息队列作为RPC操作的一种,自然也是可以得到保证的了。

保障分布式事务一致性

TensorFlow和Caffe、CNTK、MXNet等其他7种深度学习框架的对比 黄文坚/唐源

TensorFlow

  • TensorFlow是相对高阶的机器学习库,用户可以方便地用它设计神经网络结构,而不必为了追求高效率的实现亲自写C++或CUDA18代码。
  • 它和Theano一样都支持自动求导,用户不需要再通过反向传播求解梯度。
  • 其核心代码和Caffe一样是用C++编写的,使用C++简化了线上部署的复杂度,并让手机这种内存和CPU资源都紧张的设备可以运行复杂模型(Python则会比较消耗资源,并且执行效率不高)。
  • 除了核心代码的C++接口,TensorFlow还有官方的Python、Go和Java接口,是通过SWIG(Simplified Wrapper and Interface Generator)实现的,这样用户就可以在一个硬件配置较好的机器中用Python进行实验,并在资源比较紧张的嵌入式环境或需要低延迟的环境中用C++部署模型。SWIG支持给C/C++代码提供各种语言的接口,因此其他脚本语言的接口未来也可以通过SWIG方便地添加。
  • TensorFlow也有内置的TF.Learn和TF.Slim等上层组件可以帮助快速地设计新网络,并且兼容Scikit-learn estimator接口,可以方便地实现evaluate、grid search、cross validation等功能。
  • 同时TensorFlow不只局限于神经网络,其数据流式图支持非常自由的算法表达,当然也可以轻松实现深度学习以外的机器学习算法。事实上,只要可以将计算表示成计算图的形式,就可以使用TensorFlow。
  • 用户可以写内层循环代码控制计算图分支的计算,TensorFlow会自动将相关的分支转为子图并执行迭代运算。TensorFlow也可以将计算图中的各个节点分配到不同的设备执行,充分利用硬件资源。
  • 在数据并行模式上,TensorFlow和Parameter Server很像,但TensorFlow有独立的Variable node,不像其他框架有一个全局统一的参数服务器,因此参数同步更自由。
  • TensorFlow和Spark的核心都是一个数据计算的流式图,Spark面向的是大规模的数据,支持SQL等操作,而TensorFlow主要面向内存足以装载模型参数的环境,这样可以最大化计算效率。
  • TensorFlow的另外一个重要特点是它灵活的移植性,可以将同一份代码几乎不经过修改就轻松地部署到有任意数量CPU或GPU的PC、服务器或者移动设备上。
  • TensorBoard是TensorFlow的一组Web应用,用来监控TensorFlow运行过程,或可视化Computation Graph。
  • TensorFlow拥有产品级的高质量代码,有Google强大的开发、维护能力的加持,整体架构设计也非常优秀。

Caffe

  • Caffe全称为Convolutional Architecture for Fast Feature Embedding,是一个被广泛使用的开源深度学习框架,目前由伯克利视觉学中心进行维护。Caffe的创始人是加州大学伯克利的Ph.D.贾扬清,他同时也是TensorFlow的作者之一。
  • Caffe的主要优势包括如下几点:容易上手,网络结构都是以配置文件形式定义,不需要用代码设计网络;训练速度快,能够训练state-of-the-art的模型与大规模的数据;组件模块化,可以方便地拓展到新的模型和学习任务上。
  • Caffe的核心概念是Layer,每一个神经网络的模块都是一个Layer。Layer接收输入数据,同时经过内部计算产生输出数据。设计网络结构时,只需要把各个Layer拼接在一起构成完整的网络(通过写protobuf配置文件定义)。
  • Caffe最开始设计时的目标只针对于图像,没有考虑文本、语音或者时间序列的数据,因此Caffe对卷积神经网络的支持非常好,但对时间序列RNN、LSTM等支持得不是特别充分。同时,基于Layer的模式也对RNN不是非常友好,定义RNN结构时比较麻烦。

Theano

  • Theano诞生于2008年,由蒙特利尔大学Lisa Lab团队开发并维护,是一个高性能的符号计算及深度学习库。因其出现时间早,可以算是这类库的始祖之一,也一度被认为是深度学习研究和应用的重要标准之一。
  • Theano的核心是一个数学表达式的编译器,专门为处理大规模神经网络训练的计算而设计。它可以将用户定义的各种计算编译为高效的底层代码,并链接各种可以加速的库,比如BLAS、CUDA等。

Torch

  • Torch给自己的定位是LuaJIT上的一个高效的科学计算库,支持大量的机器学习算法,同时以GPU上的计算优先。
  • Torch的历史非常悠久,但真正得到发扬光大是在Facebook开源了其深度学习的组件之后,此后包括Google、Twitter、NYU、IDIAP、Purdue等组织都大量使用Torch。
  • Torch的目标是让设计科学计算算法变得便捷,它包含了大量的机器学习、计算机视觉、信号处理、并行运算、图像、视频、音频、网络处理的库,同时和Caffe类似,Torch拥有大量的训练好的深度学习模型。
  • 它可以支持设计非常复杂的神经网络的拓扑图结构,再并行化到CPU和GPU上,在Torch上设计新的Layer是相对简单的。

Keras

  • Keras是一个崇尚极简、高度模块化的神经网络库,使用Python实现,并可以同时运行在TensorFlow和Theano上。它旨在让用户进行最快速的原型实验,让想法变为结果的这个过程最短。
  • Theano和TensorFlow的计算图支持更通用的计算,而Keras则专精于深度学习。Theano和TensorFlow更像是深度学习领域的NumPy,而Keras则是这个领域的Scikit-learn。
  • 它提供了目前为止最方便的API,用户只需要将高级的模块拼在一起,就可以设计神经网络,它大大降低了编程开销和阅读别人代码时的理解开销。

MXNet

  • MXNet是DMLC(Distributed Machine Learning Community)开发的一款开源的、轻量级、可移植的、灵活的深度学习库,它让用户可以混合使用符号编程模式和指令式编程模式来最大化效率和灵活性,目前已经是AWS官方推荐的深度学习框架。
  • 它是各个框架中率先支持多GPU和分布式的,同时其分布式性能也非常高。MXNet的核心是一个动态的依赖调度器,支持自动将计算任务并行化到多个GPU或分布式集群。
  • 它上层的计算图优化算法可以让符号计算执行得非常快,而且节约内存,开启mirror模式会更加省内存,甚至可以在某些小内存GPU上训练其他框架因显存不够而训练不了的深度学习模型,也可以在移动设备上运行基于深度学习的图像识别等任务。

CNTK

  • CNTK(Computational Network Toolkit)是微软研究院(MSR)开源的深度学习框架。它最早由start the deep learning craze的演讲人创建,目前已经发展成一个通用的、跨平台的深度学习系统,在语音识别领域的使用尤其广泛。
  • CNTK通过一个有向图将神经网络描述为一系列的运算操作,这个有向图中子节点代表输入或网络参数,其他节点代表各种矩阵运算。CNTK支持各种前馈网络,包括MLP、CNN、RNN、LSTM、Sequence-to-Sequence模型等,也支持自动求解梯度。
  • CNTK有丰富的细粒度的神经网络组件,使得用户不需要写底层的C++或CUDA,就能通过组合这些组件设计新的复杂的Layer。
  • CNTK拥有产品级的代码质量,支持多机、多GPU的分布式训练。

Deeplearning4J

  • Deeplearning4J(简称DL4J)是一个基于Java和Scala的开源的分布式深度学习库,由Skymind于2014年6月发布,其核心目标是创建一个即插即用的解决方案原型。
  • DL4J拥有一个多用途的n-dimensional array的类,可以方便地对数据进行各种操作;拥有多种后端计算核心,用以支持CPU及GPU加速,在图像识别等训练任务上的性能与Caffe相当。
  • 可以与Hadoop及Spark自动整合,同时可以方便地在现有集群上进行扩展,同时DL4J的并行化是根据集群的节点和连接自动优化,不像其他深度学习库那样可能需要用户手动调整。

微服务与RPC 凤凰牌老熊

  • RPC vs Restful:RPC支持多种语言(但不是所有语言),四层通讯协议,性能高,节省带宽,相对Restful协议,使用Thrift RPC,在同等硬件条件下,带宽使用率仅为前者的20%,性能却提升一个数量级,但是这种协议最大的问题在于无法穿透防火墙。而以Spring Cloud为代表所支持的Restful协议,优势在于能够穿透防火墙,使用方便,语言无关,基本上可以使用各种开发语言实现的系统,都可以接受Restful的请求,但性能和带宽占用上有劣势。所以业内对微服务的实现,基本是确定一个组织边界,在该边界内使用RPC,边界外使用Restful。
  • RPC选型:Apache Thrift是目前最为成熟的框架,优点在于稳定、高性能,缺点在于它仅提供RPC服务,其他的功能,包括限流、熔断、服务治理等,都需要自己实现,或者使用第三方软件。Google Protobuf一直只有数据模型的实现,而2015年才推出的gRPC还缺乏重量级的用户。Thrift 提供多种高性能的传输协议,但在数据定义上,不如Protobuf强大,而Protobuf的劣势在于其RPC服务的实现性能不佳,为此,Apache Thrift + Protobuf的RPC实现,成为不少公司的选择。
  • 服务注册与发现:Spring cloud提供了服务注册和发现功能,如果需要自己实现,可以考虑使用Apache Zookeeper作为注册表,使用Apache Curator 来管理Zookeeper的链接;对服务注册来说,注册表结构需要详细设计,一般注册表结构会按照如下方式组织:机房区域-部门-服务类型-服务名称-服务器地址。
  • 连接池:RPC服务访问和数据库类似,建立链接是一个耗时的过程,连接池是服务调用的标配。目前还没有成熟的开源Apache Thrift链接池,一般互联网公司都会开发内部自用的链接池。自己实现可以基于JDBC链接池做改进,比如参考Apache commons DBCP链接池,使用Apache Pools来管理链接。连接池实现的主要难点在于如何从多个服务器中选举出来为当前调用提供服务的连接。比如目前有10台机器在提供服务,上一次分配的是第4台服务器,本次应该分配哪一台?在实现上,需要收集每台机器的QOS以及当前的负担,分配一个最佳的连接。
  • API网关:如果有一个应用需要调用多个服务,对这个应用来说,就需要维护和多个服务器之间的链接。服务的重启,都会对连接池以及客户端的访问带来影响。为此,在微服务中,广泛会使用到API网关。API网关可以认为是一系列服务集合的访问入口。从面向对象设计的角度看,它与外观模式类似,实现对所提供服务的封装。
  • 熔断与限流:熔断一般采用电路熔断器模式(Circuit Breaker Patten),当某个服务发生错误,每秒错误次数达到阈值时,不再响应请求,直接返回服务器忙的错误给调用方,延迟一段时间后,尝试开放50%的访问,如果错误还是高,则继续熔断,否则恢复到正常情况。限流指按照访问方、IP地址或者域名等方式对服务访问进行限制,一旦超过给定额度,则禁止其访问。 除了使用Hystrix,如果要自己实现,可以考虑使用使用Guava RateLimiter。
  • 服务演化:随着服务访问量的增加,服务的实现也会不断演化以提升性能,主要的方法有读写分离、缓存等。

CMU论文:一部深度学习发展史,看神经网络兴衰更替 Haohan Wang/Bhiksha Raj/张易

  • 论文地址:https://128.84.21.199/abs/1702.07800
  • 从亚里士多德的联想主义心理学到神经网络的优化方法,CMU的这篇最新论文回顾解析了深度学习的演化历史,不仅提供了一个全面的背景知识,而且总结了一座座发展里程碑背后的闪光思想,为未来的深度学习研究提供了方向。
  • 人工智能的发展或许可以追溯到公元前仰望星空的古希腊人,当亚里士多德为了解释人类大脑的运行规律而提出了联想主义心理学的时候,他恐怕不会想到,两千多年后的今天,人们正在利用联想主义心理学衍化而来的人工神经网络,构建超级人工智能,一起又一次地挑战人类大脑认知的极限;
  • 联想主义心理学是一种理论,认为人的意识是一组概念元素,被这些元素之间的关联组织在一起。受柏拉图的启发,亚里士多德审视了记忆和回忆的过程,提出了四种联想法则:邻接(空间或时间上接近的事物或事件倾向于在意识中相关联)、频率(两个事件的发生次数与这两个事件之间的关联强度成正比)、相似性(关于一个事件的思维倾向于触发类似事件的思维)、对比(关于一个事件的思维倾向于触发相反事件的思维)。
  • 1949年,Hebb提出了那条著名的规则:一起发射的神经元连在一起。更具体的表述是:“当神经元A的轴突和神经元B足够接近并反复或持续激发它时,其中一个或两个神经元就会发生增长或新陈代谢的变化,例如激发B的神经元之一——A efficiency——会增加。”
  • 尽管Hebbian学习规则被视为奠定了神经网络的基础,但今天看来它的缺陷是显而易见的:随着共同出现的次数增加,连接的权重不断增加,主信号的权重将呈指数增长。这就是Hebbian学习规则的不稳定性。
  • 将感知器放在一起,就变成了基本的神经网络。通过并列放置感知器,我们能得到一个单层神经网络。通过堆叠一个单层神经网络,我们会得到一个多层神经网络,这通常被称为多层感知器(MLP )。单层神经网络具有局限性,正是这种局限性导致了相关的研究曾经一度停滞了进二十年,但同时,也正是这种局限性刺激了神经网络向更高层结构进发,渐渐迎来了如今的深度学习时代。
  • 神经网络的一个显著特性,即众所周知的通用逼近属性,可以被粗略描述为MLP可以表示任何函数。可以从以下三方面探讨这一属性:布尔逼近(一个隐藏层的MLP可以准确的表示布尔函数)、连续逼近(一个隐藏层的MLP可以以任意精度逼近任何有界连续函数)、任意逼近(两个隐藏层的MLP可以以任意精度逼近任何函数)。
  • universal approximation成为如今神经网络与深度学习一片繁荣景象的重要理论基石,universal approximation的相关理论——一个多层神经网络具备表达任何方程的能力——已经成为深度学习的标志性特点。
  • 从八十年代的Self Organizing Map到 Hopfield Network, 再到鼎鼎大名的Boltzmann Machine和Restricted Boltzmann Machine,直到Hinton塑造的Deep Belief Network。深度学习的研究一路走来,悠长的历史之中,作者带领我们研读了这几个璀璨明星的诞生过程,以及这些作品诞生时的内在联系。下图总结了涉及的模型,水平轴代表这些模型的计算复杂度,而垂直轴代表表达能力,这是六个里程碑式的模型。

  • 卷积神经网络的谱系主要是从对人类视觉皮层的认识演变而来。卷积神经网络的视觉问题的成功原因之一是:复制人类视觉系统的仿生设计。卷积作为一个非常有效的视觉特征提取工具,几乎是深度学习在计算机视觉问题上如此成功的基石。
  • 递归神经网络(RNN)是神经网络的一种,其单位的连接形成了有向循环; 这种性质赋予了其处理时间数据的能力。
  • 优化是深度学习发展历史上不可回避的课题。目前存在的优化方式有:梯度法、剔除法、BatchNormalization。

北大AI公开课第1讲:雷鸣评人工智能前沿与行业结合点 新智元

视频回放链接:http://www.iqiyi.com/l_19rrf6l46v.html

北大AI公开课第2讲:雷鸣&余凯漫谈嵌入式AI 新智元

视频回放链接:http://www.iqiyi.com/w_19rtza2dh9.html

北大AI公开课第3讲:蚂蚁金服漆远 人工智能驱动的金融生活服务 新智元

视频回放链接:http://www.iqiyi.com/l_19rrfk4wof.html

北大AI公开课第4讲:吴甘沙:智能驾驶,有多少AI可以重来 新智元

视频回放链接:http://www.iqiyi.com/w_19ru020epp.html

北大AI公开课第5讲:小米黄江吉 产品化引领人工智能硬件发展 新智元

视频回放链接:http://www.iqiyi.com/l_19rrfgd203.html

实例化DevOps原则 伍斌

  • DevOps的起源可以分为两条线:比利时独立咨询师Patrick Debois思考能否把敏捷的实践引入Ops团队;图片分享网站Flickr的两个开发者于2009年发表了一个引燃DevOps的演讲--《每天部署10次以上:Flickr公司的Dev与Ops的合作》;
  • Flickr公司的两位演讲者所表达的“Dev和Ops的共同目标是让业务所要求的那些变化能随时上线可用”这一观点,其实就是DevOps的愿景。而要达到这一点,可以使用一个现成的工具:精益。源自丰田生产方式的“精益”的愿景就是“Shortest lead time”,即用最短的时间来完成从客户下订单到收到货物的全过程。这恰好能帮助实现DevOps的上述愿景。
  • 从上面DevOps的起源中能够看出三点:DevOps源自草根社区,最初并没有什么自上而下设计出来的理论框架;DevOps背后的原则,就是上面两条线中所涉及的敏捷和精益的原则;DevOps的愿景是让业务所要求的那些变化能随时上线可用。
  • 一些DevOps从业者,纷纷设定自己的DevOps框架。其中比较有名的框架有Damon Edwards所定义并被Jez Humble所修订的CALMS(Culture, Automation, Lean, Metrics, ),和Gene Kim所定义的The Three Ways。

TDD is dead. Long live testing. David Heinemeier Hansson

  • DHH是Ruby on Rails的创始人,Basecamp公司的创始人和CTO;
  • DHH先批判了测试先行的教条主义,虽然它能提升开发人员对软件质量的自信,且对于自动回归测试是有帮助的,但它不应该作为每日工作的教条;而业界近些年来对于TDD的推崇以及对未采用TDD开发的嘲讽更让DHH发火;当DHH多次尝试TDD而发现这会伤害程序的设计时,DHH声明不以TDD的方式开发软件了;
  • DHH的建议:重新平衡单元测试和系统测试,减少对于单元测试的重视,将更多的精力投入到系统测试;但千万不要跳入只做系统测试的极端里。

Is TDD Dead? Martin Fowler/David Heinemeier Hansson/Kent Beck

让我们再聊聊TDD 刘冉

  • 总结一下,技术人员拒绝TDD的主要原因在于难度大、工作量大、Mock的大量使用导致很难测试业务价值等。这些理解主要是建立在片面的理解和实践之上,而在我的认知中,TDD的核心是:先写测试,并使用它帮助开发人员来驱动软件开发
  • 首先是先写测试,这里的测试并不只是单元测试,也不是说一定要使用mock和stub来做测试。这里的测试就是指软件测试本身,可以是基于代码单元的单元测试,可以是基于业务需求的功能测试,也可以是基于特定验收条件的验收测试。其次是帮助开发人员,主要是帮助开发人员理解软件的功能需求和验收条件,帮助其思考和设计代码,从而达到驱动开发的目的
  • TDD是包含两部分:ATDD(验收驱动测试开发,首先BA或者QA编写验收测试用例,然后Dev通过验收测试来理解需求和验收条件,并编写实现代码直到验收测试用例通过)与UTDD(单元驱动测试开发,首先Dev编写单元测试用例,然后编写实现代码直到单元测试通过)。

TDD包括ATDD和UTDD

  • TDD不是银弹,不要期望它能解决任何问题,无论是UTDD、EDD还是BDD,根据自己项目的实际情况,比如资金、人力资源、时间、组织架构等,合理的选择。
  • TDD并没有死,死的是你的持续学习、思考、实践与总结。TDD其实早已融入日常的软件开发工作中,只是很多人还没有意识到。

让我们再聊聊TDD 续——人人都在做TDD 刘冉

  • 现实世界中TDD的实施一般分为三个阶段,即无意识的TDD、被动通过技术实现的TDD、以及有意识和主动通过技术实现的TDD。
  • 无意识的TDD:当拿到一个新的软件需求时,首先会思考如何实现,其中包括当前软件架构、业务分解、实现设计、代码分层、代码实现等,然后通过思考和设计所得到的产出物来驱动代码实现,进而在代码实现中会思考如何通过一个或多个函数或者算法来实现业务逻辑,这类思考其实已经是意识思维上的TDD,它帮助开发人员先在大脑里面设计并验证代码实现,甚至帮助其重构代码;其实开发人员在开发前思考测试逻辑和用例的过程就是在做TDD了;只不过这是初级的无意识的TDD,没有明确的产出来协助和规范这个测试驱动开发方式,也缺乏快速反馈、度量、传递和协作等;
  • 被动通过技术实现TDD:由于意识层面上的难易程度和工作量都比技术层面上相对较小,所以前者实施起来相对容易一些,而后者则相对较难,所以如果通过了各种手段强行实施TDD,而没有主动去摆正做TDD的意识,甚至没有足够的技术能力,那么这样的TDD就是一个倒三角,非常容易倒塌;
  • 有意识和主动通过技术实现TDD:首先要突破思维意识的局限,认识到TDD的普遍存在性和适用性,不要害怕和排斥TDD这种思维和开发模式;其次要主动学习,并刻意练习TDD的技术实现,提升自己的技术能力,从而在技术层面能更容易的实现TDD,摆脱被动TDD的困境;只有大量的刻意练习才能让你在真实的代码编写过程中去思考和理解TDD,去运用你通过学习得到的知识,最终才能做到有意识和主动的通过技术去实现TDD,TDD的倒三角才能变成一个稳定的砖块,然后哪里需要往哪里搬。

TDD

推行TDD的思考 张逸

  • 开发人员的质量意识:由开发人员编写测试带来的收益,最重要的一点不在于测试本身,而在于它能促进开发、测试以及需求分析人员的交流与沟通;也能让开发者从消费者角度去思考接口的设计;软件质量除了外部质量之外,内部质量同等重要,维护成本的增加主要归咎于内部质量的糟糕;
  • 需求分析与任务分解:TDD要求我们在编写测试之前要做好合理的任务分解。若没有很好地理解需求,任务分解就无法顺利进行;任务分解应该是有层次的,即业务价值——>业务功能——>业务实现;任务分解是TDD的核心,是驱动设计和开发的重要力量;
  • 测试先行的编程习惯:任务分解应该是TDD的起点,多数开发者未能形成任务分解的习惯,因此在改变为测试先行的时候,错以为应该一上来就写测试;测试驱动开发仍可进行事先设计,设计并不仅包含技术层面的设计如对OO思想乃至设计模式的运用,它本身还包括对需求的分析与建模;测试驱动开发提倡的任务分解,实际上就是一种需求的分析,如何寻找职责,以及识别职责的承担者则可以视为建模设计;在开始测试驱动开发之前,做适度的事先设计,还有利于我们仔细思考技术实现的解决方案,它与测试驱动接口的设计并不相悖;
  • 重构能力:TDD的核心是红——绿——重构,没有好的重构能力,TDD就会有缺失,若说代码的内部质量是生命的话,重构就是灵魂;重构手法与代码坏味道一一对应,若有测试保障,重构就变得安全;重要的是要找到重构的节奏感,即小步前行,每次重构必运行测试的良好习惯;在TDD过程中,若能结对自然是上佳选择,当一个人在掌控键盘时,另一个人就可以重点关注代码的可读性,看看代码是否散发出臭味;
  • 单元测试的基础设施:最好能找到一些开源的测试框架,包括生成测试数据,模拟测试行为等,因为你遇到的问题,别人可能早已遇见过。

大数据时代的新型数据库 — 图数据库 Neo4j 的应用 张帜

  • 什么是图数据库:是基于数学里的图论的理论和算法而实现的高效处理复杂关系网络的新型数据库系统,它实际上就是处理关系的、处理网络的数据库系统。图数据库是善于处理大量的、复杂的、互联的、多变的数据,它处理这些数据的效率,远远高于关系型数据库。
  • 从数据库的结构来看,它包含的概念非常的简单,他包含的概念只有节点和关系。节点可以带标签,节点和关系也都可以带属性。

节点、关系和属性

创建节点、关系

查询

  • 为什么要用图数据库:世界本来就是由各种关系组成的;关系型数据库处理复杂关系的时候,建模难、性能低、查询难、扩展难;图数据库它是专门为处理复杂关系而创建出来的,它具有开发的优势和部署的优势。
  • Neo4j的关键产品特征:社区版不支持集群,免费;企业版支持集群,是收费的。

Neo4j的关键产品特征

Apache Spark和Apache Storm的区别 fuqingchuan

  • Apache Spark是基于内存的分布式数据分析平台,旨在解决快速批处理分析任务、迭代机器学习任务、交互查询以及图处理任务。其最主要的特点在于,Spark使用了RDD或者说弹性分布式数据集。RDD非常适合用于计算的流水线式并行操作。RDD的不变性(immutable)保证,使其具有很好的容错能力。

Spark架构

  • Apache Storm专注于流处理或者一些调用复杂的事件处理。Storm实现了一种容错方法,用于在事件流入系统时执行计算或流水线化多个计算。人们可以使用Storm在非结构化数据流入系统到期望的格式时对其进行转换。

Storm架构

  • Storm和Spark专注于相当不同的应用场景。对比Storm Trident和Spark Streaming,应该是更加公平的比较。由于Spark的RDD本质上是不可变的,Spark Streaming实现了一种方法,用于在用户定义的时间间隔中“批处理”传入的更新,并将其转换为自己的RDD。 然后Spark通用的并行运算符就可以对这些RDD执行计算。这与Storm处理每个事件不同,Storm是真正的流式处理。
  • 总而言之,这两种技术之间的一个主要区别是Spark执行数据并行计算,而Storm执行任务并行计算,这两种设计都是各自领域内的权衡。

大数据系统的Lambda架构 张逸

  • 在大数据处理系统中,如何有效地将real time与batch job结合起来,既发挥前者对响应的实时性,又能解决对海量数据的分析与处理?答案就是Lambda架构思想。
  • 传统系统的问题:无法很好地支持系统的可伸缩性;数据库对于分区是不了解的,无法帮助你应对分区、复制与分布式查询;最糟糕的问题是系统并没有为人为错误进行工程设计,仅靠备份是不能治本的;
  • 数据系统的概念:如果数据系统通过查找过去的数据去回答问题,则通常需要访问整个数据集。因此可以给data system的最通用的定义:Query = function(all data)
  • 一个大数据系统必须具备的属性包括:健壮性和容错性(Robustness和Fault Tolerance)、低延迟的读与更新(Low Latency reads and updates)、可伸缩性(Scalability)、通用性(Generalization)、可扩展性(Extensibility)、内置查询(Ad hoc queries)、维护最小(Minimal maintenance)、可调试性(Debuggability);
  • Lambda架构:主要思想就是将大数据系统构建为多个层次,如下图所示

Lambda架构分层

  • 理想状态下,任何数据访问都可以从表达式Query = function(all data)开始,但是,若数据达到相当大的一个级别(例如PB),且还需要支持实时查询时,就需要耗费非常庞大的资源。一个解决方式是预运算查询函数(precomputed query funciton)。Mathan Marz将这种预运算查询函数称之为Batch View,当需要执行查询时,可以从Batch View中读取结果。这样一个预先运算好的View是可以建立索引的,因而可以支持随机读取。于是系统就变成:
batch view = function(all data)
query = function(batch view)
  • Batch Layer:在Lambda架构中,实现batch view = function(all data)的部分被称之为batch layer。它承担了两个职责:
    存储Master Dataset,这是一个不变的持续增长的数据集;针对这个Master Dataset进行预运算。利用Batch Layer进行预运算的作用实际上就是将大数据变小,从而有效地利用资源,改善实时查询的性能。
  • Serving Layer:Serving Layer负责对batch view进行操作,从而为最终的实时查询提供支撑。因此Serving Layer的职责包含:对batch view的随机访问;更新batch view。Serving Layer应该是一个专用的分布式数据库,以支持对batch view的加载、随机读取以及更新。注意,它并不支持对batch view的随机写,因为随机写会为数据库引来许多复杂性。
  • Speed Layer:从对数据的处理来看,speed layer与batch layer非常相似,它们之间最大的区别是前者只处理最近的数据,后者则要处理所有的数据。另一个区别是为了满足最小的延迟,speed layer并不会在同一时间读取所有的新数据,相反,它会在接收到新数据时,更新realtime view,而不会像batch layer那样重新运算整个view。speed layer是一种增量的计算,而非重新运算(recomputation)。因而,Speed Layer的作用包括:对更新到serving layer带来的高延迟的一种补充;快速、增量的算法;最终Batch Layer会覆盖speed layer;
  • 总结下来,Lambda架构就是如下的三个等式:
batch view = function(all data)
realtime view = function(realtime view, new data)
query = function(batch view . realtime view)

整个Lambda架构如下图所示:

Lambda架构

Lambda架构与推荐在电商网站实践 王富平

  • Lambda架构:Lambda架构由Storm的作者Nathan Marz提出。旨在设计出一个能满足实时大数据系统关键特性的架构,具有高容错、低延时和可扩展等特性。Lambda架构整合离线计算和实时计算,融合不可变性(Immutability)、读写分离和复杂性隔离等一系列架构原则,可集成Hadoop、Kafka、Storm、Spark、HBase等各类大数据组件。
  • Lambda有两个假设:不可变假设(Lambda架构要求data不可变)、Monoid假设(理想情况下满足Monoid 的function可以转换为 query = function(all data/ 2) + function(all data/ 2));
  • Lambda三层架构:批处理层(批量处理数据,生成离线结果)、实时处理层(实时处理在线数据,生成增量结果)、服务层(结合离线、在线计算结果,推送上层);

Lambda三层架构

  • Lambda架构缺点:Lambda需要将所有的算法实现两次,一次是为批处理系统,另一次是为实时系统,还要求查询得到的是两个系统结果的合并。考虑到将复杂算法正确地实现一次都是一个挑战,执行两次这样的任务以及调试不可避免的问题显然是难上加难。除此之外,运维两个分布式多节点的服务肯定比运维一个更难。【参加http://www.infoq.com/cn/news/2014/09/lambda-architecture-questions】
  • 推荐系统的最终目的是提高转化率,手段是推送用户感兴趣的、需要的产品。1号店会根据你实时浏览、加车、收藏、从购物车删除、下单等行为,计算相关产品的权重,把相应的产品立刻更新到猜你喜欢栏位。
  • Netflix推荐架构:批处理层(从Hive、pig数据仓库,离线计算推荐模型,生成离线推荐结果)、实时处理层(从消息队列实时拉取用户行为数据与事件,生成在线推荐结果)、服务层(结合离线、在线推荐结果,为用户生成推荐列表);

Netflix推荐架构

  • 1号店推荐系统实践:推荐引擎组件包括用户意图、用户画像、千人千面、情境推荐、反向推荐、主题推荐;

1号店推荐系统实践

技术的边界 阮一峰

  • 当代社会就像一座机器组成的监狱,学会技术可以摆脱牢房。
  • 我们理应享受技术成果,问题是,技术正变得越来越先进,也越来越难懂,大多数人已经不能够理解技术了。
  • 技术已经到了这样一个地步:我们走一步看一步,谁也不知道十年后,技术会突破到什么程度。
  • 我最近常想,技术有没有边界呢?一旦接近”绝境长城”,我们会自觉停在那里,不再往下发展吗?举例来说,人工智能领域有一个概念,叫做”终极智能”。意思是,当机器的智能达到这种程度时,就不需要人类再做发明创造了,因为机器自己就会发明创造。如果这种”终极智能”真的可能实现,技术要不要去实现它呢?
  • 一个依赖技术的高科技、高度自动化的社会,也是一个非常脆弱的社会。整个人类正坐在一架软件驾驶的飞机里面,只能祈祷软件运行永远不发生错误。一旦发生问题,人类就会坠机。

分布式开放消息系统(RocketMQ)的原理与实践 CHEN川

  • 分布式消息系统作为实现分布式系统可扩展、可伸缩性的关键组件,需要具有高吞吐量、高可用等特点。而谈到消息系统的设计,就回避不了两个问题:消息的顺序问题和消息的重复问题;
  • 消息有序指的是可以按照消息的发送顺序来消费。要实现严格的顺序消息,简单且可行的办法就是:保证“生产者 - MQServer - 消费者”是一对一对一的关系,但这样并行度就会成为消息系统的瓶颈,而且还需要更多的异常处理;有些问题,看起来很重要,但实际上我们可以通过合理的设计或者将问题分解来规避。如果硬要把时间花在解决问题本身,实际上不仅效率低下,而且也是一种浪费。从这个角度来看消息的顺序问题,我们可以得出两个结论:不关注乱序的应用实际大量存在、队列无序并不意味着消息无序;RocketMQ通过轮询所有队列的方式来确定消息被发送到哪一个队列,在获取到路由信息以后,会根据MessageQueueSelector实现的算法来选择一个队列,同一个OrderId获取到的肯定是同一个队列。
  • 造成消息重复的根本原因是:网络不可达。只要通过网络交换数据,就无法避免这个问题。所以解决这个问题的办法就是绕过这个问题。那么问题就变成了:如果消费端收到两条一样的消息,应该怎样处理?消费端处理消息的业务逻辑保持幂等性,保证每条消息都有唯一编号且保证消息处理成功与去重表的日志同时出现;RocketMQ不保证消息不重复,如果你的业务需要保证严格的不重复消息,需要你自己在业务端去重。
  • RocketMQ除了支持普通消息,顺序消息,另外还支持事务消息。将大事务拆分成多个小事务异步执行,这样基本上能够将跨机事务的执行效率优化到与单机一致。
  • Producer轮询某topic下的所有队列的方式来实现发送方的负载均衡,如果Producer发送消息失败,会自动重试;
  • RocketMQ的消息存储是由consume queue和commit log配合完成的。
  • RocketMQ消息订阅有两种模式,一种是Push模式,即MQServer主动向消费端推送;另外一种是Pull模式,即消费端在需要时,主动到MQServer拉取。但在具体实现时,Push和Pull模式都是采用消费端主动拉取的方式。

当前服务器配置能承受多大的QPS?如何评估? Susie Xia/Anant Rao/薛命灯

  • LinkedIn的基础设施上运行着数百个应用,它们为4.67亿的LinkedIn会员提供服务。为了能够准确地对服务容量极限进行评估,并有效地识别出容量瓶颈,我们的解决方案需要具备如下特点:利用生产环境来突破实验室的局限、使用真实的流量作为负载、最小化对用户体验造成的影响、低运营成本和开销、自动伸缩;
  • 我们使用生产环境的真实流量,通过Redliner实现自动化的容量评估和准确的余量分析。Redliner在目标服务上运行压力测试,逐步增加流量,直到服务无法处理更多的流量为止,以此来评估服务的吞吐量。
  • Redliner自动从生产环境引入流量,并确保对用户只有很小的影响。在设计Redliner时,我们遵循了两个原则:影响最小化和完全自动化。
  • 在进行流量重定向时,最主要的问题是如何避免对站点和用户造成影响。Redliner使用以下的策略来缓解对生产环境性能造成的影响。首先,通过增量的方式将流量导向redline实例。其次,Redliner对服务进行实时的监控,并根据实际情况来分发流量。Redliner捕捉实时的性能指标,并基于EKG健康评估规则的计算结果来确定服务的健康状况。
  • 我们借助LinkedIn的技术平台保证Redliner自动化的健壮性和伸缩性。Redliner能够运行调度测试,通过EKG检测性能状态,还能利用A/B测试平台XLNT来动态地调整导向目标服务的流量。在经过几轮的迭代之后,Redliner就可以确定单个服务能够处理的最大QPS。一般整个过程需要不到一个小时的时间。
  • 下图是Redliner的架构图,包含导流组件和容量评估组件。主要组件如下:导流层(代理/负载均衡器)、服务健康状态分析器和服务度量指标收集器。

Redliner的架构图

解读GraphQL 王亦凡

  • GraphQL是Facebook推出的一个查询语言,可能和听起来不同,它并不是一个数据库查询语言,而是你可以用任何其他语言实现的一个用于查询的抽象层。
  • 通常你可以通过GraphQL让你的客户端请求有权决定获取的数据结构,也可以通过GraphQL获得更好的多版本API兼容性。并且与大多数查询语言不同的是,GraphQL是一个静态类型的查询语言,这意味着你可以通过GraphQL获得更强大更安全的开发体验。
  • 为什么要选择GraphQL? GraphQL的核心目标就是取代RESTful API。
  • REST有什么问题? 资源分类导致性能受限、在现代场景中难于维护、缺乏约束、严格,抽象,但并不能解决客户端问题;
  • GraphQL好在哪?Github宣布他们打算拥抱GraphQL;GraphQL用来构建客户端API,但它并不关心视图,也不关心服务的到底是什么客户端;至于请求什么数据,数据怎么组织,全都是客户端说了算;GraphQL的意思,顾名思义就是图查询语言;静态类型;兼容多版本;
  • GraphQL潜在的问题? 可能会存在服务器性能隐患、安全问题、需要重新思考Cache策略。

免责声明:相关链接版本为原作者所有,如有侵权,请告知删除。

随手记系列:

Gino Zhang wechat
扫一扫,关注我的微信公众号