Base:一种 Acid 的替代方案

原文链接: BASE: An Acid Alternative 数据库 ACID,都不陌生:原子性、一致性、隔离性和持久性,这在单台服务器就能搞定的时代,很容易实现,但是到了现在,面对如此庞大的访问量和数据量,单台服务器已经不可能适应了,而 ACID 在集群环境,几乎不可能达到我们的预期,保证了 ACID,效率就会大幅度下降,更要命的是,这么高的要求,不好扩展~于是又了 CAP 原则(Consistency(一致性)、Availability(可用性)、Partition tolerance(分区容错性))和 BASE 原则(Basically Available(基本可用)、Soft state(软状态)、Eventually consistent(最终一致)),看看它们的英文,Availability/Basically Available,Consistency/Eventually consistent,基本上,BASE 原则对 CAP 原则的进一步诠释。 本文是Ebay的架构师在2008年发表给ACM的文章,是一篇解释BASE原则,或者说最终一致性的经典文章. 文中Dan讨论了BASE与ACID原则的基本差异, 以及如何设计大型网站以满足不断增长的可伸缩性需求,期间如何对业务做调整与折衷. 以及一些具体的折衷技术的介绍.   在对数据库进行分区后,为了可用性(Availability)牺牲部分一致性(Consistency)可以显著的提升系统的可伸缩性(Scalability). ——By DAN PRITCHETT, EBAY ,Translated by Jametong Web应用在过去10年变得越来越普及.无论是为最终用户还是为应用开发者构建的应用,对这个应用的希望很可能都是,此应用被最广泛的用户使用-广泛的使用会带来交易的增长.业务如果依赖于持久化,数据存储就很可能成为瓶颈. 扩展任何应用都有两种策略.第一种,也是最简单的一种,就是 纵向扩展 :将应用迁移到更大更强的计算机上. 目前可用的最大的机器也满足不了它的容量是它最明显的限制.纵向扩展也很昂贵,增加交易容量通常都需要购买下一个更大的机器.纵向扩展通常还会产生对供应商的依赖,从而进一步增加成本. 横向扩展 (Horizontal Scaling)提供了更多的灵活性,但也会显著的增加复杂度.横向数据扩展可能沿着两个方向发展.按 功能扩展 (Functional Scaling)牵涉到按功能对数据进行分组,并将不同的功能组分布在多个不同的数据库上.在功能内部将数据拆分到多个数据库上,也就是进行 分片 (Sharding),它为横向扩展增加一个新的维度.图-1简要阐释了横向数据扩展策略. 图-1 如图-1所示,横向扩展的两种方法可以同时进行运用.用户信息(Users)、产品信息(Products)与交易信息 (Transactions)可以存储在不同的数据库中.另外,每个功能区域根据其交易容量(transactional capacity)可以再拆分到多个数据库中.如图所示,功能区域可以相互独立地进行扩展. 功能分区(Functional Partitioning) 功能分区对于实现高可伸缩性相当重要.每一种好的数据库架构都会根据功能将概要(Schema)分解到多张表中.用户(Users)、产品 (Products)、交易(Transactions)以及通讯都是功能分区的例子. 常用的方法是,利用诸如外键(foreign key)一类的数据库概念来维持这些功能区域之间的数据一致性. 依赖数据库的约束保证功能组之间的一致性,会导致数据库的不同概要(schema)在部署策略上高度耦合.要支持约束,表必须存在单一的数据库服务器上,当交易率(transaction rate)增长时也无法对其进行横向扩展.很多情况下, 将数据的不同功能组迁移到相互独立的数据库服务器上是最容易实现的向外扩展(Scale-out)方案. 可扩展到非常高的交易量的概要会将不同的功能的数据放置在不同的数据库服务器上.这需要将数据之间的约束从数据库迁移到应用中去. 同时这也将引入一些新的挑战,本文的后续内容会对此进行深入探讨. CAP定理(CAP Theorem) Eric Brewer,一位加州大学伯克利分校的教授,Inktomi公司的共同创办人以及首席科学家,作出了以下推测,Web服务无法同时满足以下3个属性(由其首字母构成缩写CAP): 一致性(Consistency).客户端知道一系列的操作都会同时发生(生效). 可用性(Availability).每个操作都必须以可预期的响应结束. 分区容错性(Partition tolerance).即使出现单个组件无法可用,操作依然可以完成. 具体地讲,在任何数据库设计中,一个Web应用至多只能同时支持上面的两个属性.显然,任何横向扩展策略都要依赖于数据分区;因此,设计人员必须在一致性与可用性之间做出选择. ACID解决方案 ACID数据库事务极大地简化了应用开发人员的工作.正如其缩写标识所示,ACID事务提供以下几种保证: 原子性(Atomicity).事务中的所有操作,要么全部成功,要么全部不做. 一致性(Consistency).在事务开始与结束时,数据库处于一致状态. 隔离性(Isolation). 事务如同只有这一个操作在被数据库所执行一样. 持久性(Durability). 在事务结束时,此操作将不可逆转.(也就是只要事务提交,系统将保证数据不会丢失,即使出现系统Crash,译者补充). 数据库厂商在很久以前就认识到数据库分区的必要性,并引入了一种称为2PC(两阶段提交)的技术来提供跨越多个数据库实例的ACID保证.这个协议分为以下两个阶段: 第一阶段,事务协调器要求每个涉及到事务的数据库预提交(precommit)此操作,并反映是否可以提交. 第二阶段,事务协调器要求每个数据库提交数据. 如果有任何一个数据库否决此次提交,那么所有数据库都会被要求回滚它们在此事务中的那部分信息.这样做的缺陷是什么呢? 我们可以在分区之间获得一致性.如果Brewer的猜测是对的,那么我们一定会影响到可用性,但,怎么可以这样呢? 任何系统的可用性都是执行操作的相关组件的可用性的产物.此陈述的后半段尤其重要.系统中可能会使用但又不是必需的组件,不会降低系统的可用性.在两阶段提交中涉及到两个数据库的事务,它的可用性是这两个数据库中每一个的可用性的产物.例如,如果我们假设每个数据库都有为99.9%的可用性,那么这个事务的可用性就是99.8%,或者说每月43分钟的额外停机时间. 关于两阶段提交,你可以看看"改变未来的九大算法",里边有精辟的讲解~ 一种ACID的替代方案 如果ACID为分区的数据库提供一致性的选择,那么你如何实现可用性呢?答案是BASE(基本上可用、软(弱)状态、最终一致性). BASE与ACID截然相反.ACID比较悲观,在每个操作结束时都强制保持一致性,而BASE比较乐观,接受数据库的一致性处于一种动荡不定的状态.虽然,听起来很难应付,实际上这相当好管理,并且可带来ACID无法企及的更高级别的可伸缩性. BASE的可用性是通过支持局部故障而不是系统全局故障来实现的.下面是一个简单的例子:如果用户分区在5个数据库服务器上,BASE设计鼓励类似的处理方式,这样一个用户数据库的故障只会影响这台特定主机上的那20%的用户.这里不涉及任何魔法,不过,它确实可以带来更高的可感知的系统可用性. 因此,到目前为止,你已经将数据分解到了多个功能组中,并将最繁忙的功能组分区到了多个数据库中,如何在你的应用中应用BASE原则呢?与ACID 的典型应用场景相比,BASE需要对逻辑事务中的操作进行更加深入的分析.到底该如何进行分析呢?后续的内容将提供部分指导原则. 一致性模式(Consistency Patterns) 沿着Brewer的猜测,如果BASE在分区数据库中选择保留可用性(Availability), 那么,弱化一定程度的一致性就成为必然的选择.这通常难以决策,因为商业投资方与开发人员都倾向于认为一致性(Consistency)对应用的成功至关重要.哪怕是临时的不一致也瞒不过最终用户,因此,技术部门与产品部门都需要参与进来,以决定将一致性弱化到什么程度. 图-2是一个简单的概要,它阐释了BASE中一致性要考虑的事情.用户表存储用户信息,同时还包含总销售额与总购买额.这些都是运行时的统计.交易表存储每一笔交易,将买家、卖家以及交易金额关联在一起.这些是对实际使用的表进行过度简化后的结果,不过,它已经包含阐释一致性的多个方面的必要元素. […]

龙生   16 Sep 2017
View Details

.NET Core 2.0 开源Office组件 NPOI

前言 去年 12 月,我移植了大家所熟知 NPOI 到 .NET Core 版本,这里是当时发的博客,当时得到了很多同学的支持,社区反应也很好,在这里非常感谢当时推荐的朋友们。 去年的那个版本是针对于 .NET Core 1.0 的,从发布截止现在在 NuGet 大概有 2K 多的下载量,说明还是有很多同学在使用 NPOI 的,社区中也得到了很多同学的推广。 但是上一个移植的版本也有诸多缺陷和 bug,在 Github 上也收到了一些 Issue 进行反馈,很多 Bug 可能是移植过程中的bug,但是对于这些 Bug 可能我也无能为力,因为 NPOI 的代码是非常庞大和复杂的。 随着 .NET Core 2.0 的发布,我又重新移植了一遍 NPOI,注意是重新移植而不是从 1.0 版本迁移过来,由于 .NET Standard 2.0 的 API 增加了很多,所以移植过程还算顺利,这次移植应该是最大限度的保持了 NPOI 的原汁原味,敬请客官体验。 什么是 NPOI NPOI 是 构建在POI 3.x 版本之上的一个C#库,NPOI 可以在没有安装Office的情况下对 Word 或 Excel 文档进行读写操作。 POI是一个开源 的Java 读写Excel、WORD等微软OLE2组件文档的项目。 NPOI 由瞿总和他的团队由 Apache POI 移植到 .NET 的,以下是NPOI Github 地址: https://github.com/tonyqus/npoi Getting Started 移植版的 NPOI 是基于 .NET Standard 2.0 的,也就是说你可以在基于 .NET Core 2.0, .NET Frameework 4.6.1 等项目中进行引用使用。 新的 NPOI […]

龙生   16 Sep 2017
View Details

.NET Core 在程序集中集成Razor视图

前言 有时候,我们在开发一个程序集供其他项目引用的时候,可能需要对外输出一些HTML的结构数据。 还有一些情况我们可能开发的是一个中间件,这个中间件需要提供一些界面来对外展示数据或者是内部的一些程序的运行信息,这个时候我们也需要一个界面来做这件事情。 那么,做这些界面最适合的结构非html莫属,在 ASP.NET 中那就是Razor视图了。 怎么样才能在程序集中集成 Razor 视图,并且能够以 NuGet 包的形式提供出去呢?也就是说别人只需要引用包,然后在浏览器敲指定的地址就可以看到你提供的界面了呢? 最典型的就是给你的用户提供Dashboard界面。 其实,做到这些并不难,现在,我来教你一步一步做。 Getting Started 这里我就以一个中间件程序为例,给用户提供一个Dashboard页面。 1、创建 Middleware Library 首先,我们参照我的这篇文章,使用一秒钟的时间新建一个中间件的模板项目。 这个模板项目还是 xproj 的产物,我也一直没有时间更新,所以vs打开的时候升级一下吧。 创建一个文件件,使用 yo aspnetcore-middleware生成一个模板项目。 然后使用VS打开并升级到 2.0 版本,测试项目被我卸载掉了。 我们还需要打开 Dashboard.csproj ,添加一些我们需要引用的包。

其中RazorPageGenerator是一个工具包,他主要是用来编译 Razor 视图生成我们需要的Razor视图的对象。 另外 Microsoft.Extensions.RazorViews.Sources 这个包是一个源码包,通过 dotnet resore 还原过后你会发现项目中多个几个class类,他们的图标类似一个快捷方式的样式。 这几个第三方 class 类你可以不用,自己来写,这样会更加的灵活。 BaseView.cs 是编译 Razor 的时候默认集成的基类,你可以通过修改这个类来更加另外操控编译后的Razor对象具有的行为。 2、添加视图文件 接下来,我们就需要添加我们用到的视图文件了。 在 Dashboard 项目下新建一个 Views 文件夹,注意文件夹名称要用 Views。 在 Views 中添加需要的 cshtml 文件,这里要注意和 ASP.NET Core MVC 中的用法有一点不太一样,具体关于Razor的模板引擎支持的语法可以查看这里。 在 Views 文件夹中添加如下两个文件: Home.cshtml

body.html

3、生成视图对象class 使用 dotnet razorpagegenertor Dashboard 命令来变成生成Razor对象文件。 其中最后一个参数Dashboard为生成的文件 Home.Design.cs 的命名空间。 这个时候,其实 body.html 和 Home.cshtml 已经没有用了,因为程序在执行的时候会执行Home.Design.cs里面的代码。 4、调用对象class 接下来只需要在 DashboardMiddleware.cs 里面调用 Home.Design.cs 中的 Home 类就可以了 修改一下DashboardMiddleware.cs的构造函数,把不需要的Options配置类删掉,然后修改Invoke代码: DashboardMiddleware.cs

这里不需要再await _next(context) 来接着执行下一个中间件了,因为我们提供的页面不应该受到MVC管道的控制,在启动中间件的时候放到末尾即可。 现在,我们已经做好了一个中间件。我们需要建一个示例项目测试一下。 […]

龙生   16 Sep 2017
View Details

《C# 并发编程 · 经典实例》读书笔记

前言 最近在看《C# 并发编程 · 经典实例》这本书,这不是一本理论书,反而这是一本主要讲述怎么样更好的使用好目前 C#.NET 为我们提供的这些 API 的一本书,书中绝大部分是一些实例,在日常开发中还是经常会使用到。 书中一些观点还是比较赞同,比如作者说目前绝大多数的图书对关于并发多线程等这些内容放到最后,而缺少一本介绍并发编程的入门指引和参考。另外一个观点是绝大多数国内的技术人员认为技术越底层就牛逼,而做上层应用的就是“码农”,作者反对了这一观点,其实能利用好现有的库也是一种能力,虽然说理解基础知识对日常生活仍然有帮助,但最好从更高级的抽象概念来学习。 异步基础 任务暂停,休眠 异步方式暂停或者休眠任务,可以使用 Task.Delay();

异步重试机制 一个简单的指数退避策略,重试的时间会逐次增加,在访问 Web 服务时,一般采用此种策略。

报告进度 异步操作中,经常需要展示操作进度,可以使用 IProcess<T> 和 Process<T>。

等待一组任务 同时执行几个任务,等待他们全部完成

等待任意一个任务完成 执行若干任务,只需要对其中一个的完成进行响应。主要用于对一个操作进行多种独立的尝试,只要其中一个尝试完成,任务就算完成。

集合 不可变栈和队列 需要一个不会经常修改,可以被多个线程安全访问的栈和队列。他们的API和 Stack<T> 和 Queue<T> 非常相似。性能上,不可变栈(LIFO)和队列(FIFO)与标准的栈和队列具有相同的时间复杂度。但是在需要频繁修改的简单情况下,标准栈和队列速度更快。 在内部实现上,当对一个对象进行覆盖(重新赋值)的时候,不可变集合采用的是返回一个修改过的集合,原始集合引用是不变化的,也就是说如果另外一个变量引用了相同的对象,那么它(另外的变量)是不会变化的。 ImmutableStack

实际上,两个栈内部共享了存储 11 的内存,这种实现方式效率很高,而且每个实例都是线程安全的。 ImmutableQueue

不可变列表和集合 ImmutableList 时间复杂度 操作 List ImmutableList Add O(1) O(log N) Insert O(log N) O(log N) RemoveAt O(log N) O(log N) Item[index] O(1) O(log N) 有些时候需要这样一个数据结构:支持索引,不经常修改,可以被多线程安全的访问。

ImmutableList<T> 可以索引,但是注意性能问题,不能用它来简单的替代 List<T>。它的内部实现是用的二叉树组织的数据,这么做是为了让不同的实例之间共享内存。 ImmutableHashSet 有些时候需要这样一个数据结构:不需要存放重复内容,不经常修改,可以被多个线程安全访问。时间复杂度 O(log N)。

线程安全字典 一个线程安全的键值对集合,多个线程读写仍然能保持同步。 ConcurrentDictionary 混合使用了细粒度的锁定和无锁技术,它是最实用的集合类型之一。

如果多个线程读写一个共享集合,实用 ConcurrentDictionary<TKey,TValue> 是最合适的。如果不会频繁修改,那么更适合使用 ImmutableDictionary<TKey,TValue> 。 它最适合用于在需要共享数据的场合,即多个线程共享一个集合,如果一些线程只添加元素一些线程只移除元素,那最好使用 生产者/消费者集合(BlockingCollection<T>)。 初始化共享资源 程序多个地方使用一个值,第一次访问时对它进行初始化。

本文地址:http://www.cnblogs.com/savorboard/p/csharp-concurrency-cookbook.html 作者博客:Savorboard 欢迎转载,请在明显位置给出出处及链接

龙生   16 Sep 2017
View Details

消息队列 Kafka 的基本知识及 .NET Core 客户端

前言 最新项目中要用到消息队列来做消息的传输,之所以选着 Kafka 是因为要配合其他 java 项目中,所以就对 Kafka 了解了一下,也算是做个笔记吧。 本篇不谈论 Kafka 和其他的一些消息队列的区别,包括性能及其使用方式。 简介 Kafka 是一个实现了分布式的、具有分区、以及复制的日志的一个服务。它通过一套独特的设计提供了消息系统中间件的功能。它是一种发布订阅功能的消息系统。 一些名词 如果要使用 Kafka ,那么在 Kafka 中有一些名词需要知道,文本不讨论这些名词是否在其他消息队列中具有相同的含义。所有名词均是针对于 Kafka。 Message 消息,就是要发送的内容,一般包装成一个消息对象。 Topic 通俗来讲的话,就是放置“消息”的地方,也就是说消息投递的一个容器。假如把消息看作是信封的话,那么 Topic 就是一个邮筒,如下图所示: Partition && Log Partition 分区,可以理解为一个逻辑上的分区,像是我们电脑的磁盘 C:, D:, E: 盘一样, Kafka 为每个分区维护着一份日志Log文件。 每个分区是一个有序的,不可修改的,消息组成的队列。 当消息过来的时候,会被追加到日志文件中,这个追加是根据 commit 命令来执行的。 分区中的每一条消息都有一个编号,叫做 offset id,这个 id 在当前分区中是唯一的,并且是递增的。 日志,就是用来记录分区中接收到的消息,因为每一个 Topic 可以同时向一个或者多个分区投递消息,所以实际在存储日志的时候,每个分区会对应一个日志目录,其命名规则一般为 <topic_name>-<partition_id>, 目录中就是一个分区的一份 commit log 日志文件。 Kafka 集群会保存一个时间段内所有被发布出来的信息,无论这个消息是否已经被消费过,这个时间段是可以配置的。比如日志保存时间段被设置为2天,那么2天以内发布的消息都是可以消费的;而之前的消息为了释放空间将会抛弃掉。Kafka的性能与数据量不相干,所以保存大量的消息数据不会造成性能问题。 对日志进行分区主要是为了以下几个目的:第一、这可以让log的伸缩能力超过单台服务器上线,每个独立的partition的大小受限于单台服务器的容积,但是一个topic可以有很多partition从而使得它有能力处理任意大小的数据。第二、在并行处理方面这可以作为一个独立的单元。 生产者 Producers 和其他消息队列一样,生产者通常都是消息的产生方。 在 Kafka 中它决定消息发送到指定Topic的哪个分区上。 消费者 Consumers 消费者就是消息的使用者,在消费者端也有几个名词需要区分一下。 一般消息队列有两种模式的消费方式,分别是 队列模式 和 订阅模式。 队列模式:一对一,就是一个消息只能被一个消费者消费,不能重复消费。一般情况队列支持存在多个消费者,但是对于一个消息,只会有一个消费者可以消费它。 订阅模式:一对多,一个消息可能被多次消费,消息生产者将消息发布到Topic中,只要是订阅改Topic的消费者都可以消费。 Consumer && Subscriber Group: 组,是一个消费者的集合,每一组都有一个或者多个消费者,Kafka 中在一个组内,消息只能被消费一次。 在发布订阅模式中,消费者是以组的方式进行订阅的,就是Consumer Group,他们的关系如下图: 每个发布到Topic上的消息都会被投递到每个订阅了此Topic的消费者组中的某一个消费者,也就是每个组都会被投递,但是每个组都只会有一个消费者消费这个消息。 开头介绍了Kafka 是 发布-订阅 功能的消息队列,所以在Kafka中,队列模式是通过单个消费者组实现的,也就是整个结构中只有一个消费者组,消费者之间负载均衡。 Kafka 集群 Borker: Kafka 集群有多个服务器组成,每个服务器称做一个 Broker。同一个Topic的消息按照一定的key和算法被分区存储在不同的Broker上。 上图引用自:http://blog.csdn.net/lizhitao 因为 […]

龙生   16 Sep 2017
View Details

权衡微服务

原文地址:http://martinfowler.com/articles/microservice-trade-offs.html 作者:Martin Fowler 很多开发团队已经认识到 微服务架构比单体架构更优越。但是也有其他团队感觉到这是一种消弱生产力的负担。就像任何软件架构,微服务架构同样有利弊。为了能做出一个明智的选择,你必须了解这些应用并将它们运用到你特定的环境中。 微服务的优势 具有边界的健壮模块:有边界的模块化业务,可以随着团队规模的扩大而陆续增加。 独立部署:服务的部署更加简单容易,另外,因为每个微服务是独立的,所以其中一个出问题不会影响到整个系统。 技术的多样性:在微服务中,可以有一种以上的语言,开发框架,以及数据库存储相结合。 微服务的附加成本 分布式:分布式系统编程相对困难,因为远程调用慢,所以可能总是会存在失败的风险。 最终一致性:在分布式系统中,保持强一致性是非常困难的,所以必须要处理最终一致性。 操作复杂性:你需要一个经验丰富的运营团队来管理很多需要定时重新部署的服务。 具有边界的健壮模块 微服务最大的好处是对功能模块的划分。这是个既重要又奇怪的好处,因为理论上没有理由来解释为何微服务比单体架构有更强大的模块划分。 那到底什么是我说的模块边界。我想大多数人同意,将软件划分成多个模块,非常好:彼此就可以分离解耦。如果我需要改变系统的一小部分,大多数的时候我只需要弄清楚这一小块,然后做些改动,我们会发现小变动真的很容易。对于任何程序而言,好的模块化结构非常有用,特别是当软件的规模变大,模块化会显得格外重要。也许,它会变得更重要,因为团队的规模也在增加。 提倡微服务,需要快速介绍一下 康威定律,该定律认为一个软件系统的结构反映了团队的沟通结构。对于较大的团队,特别对于那些分散在各地的团队,对软件实施结构化显得尤为重要,跨团队之间的沟通与一个团队内部沟通相比,将会变得不那么频繁且更加正式。微服务允许每个团队用类似的沟通模式来照看相对独立的模块单元。 正如我前面所说,没有理由可以解释为何一个单体系统不应该拥有一个良好的模块化结构[1]。但是很多人都观察到这似乎很少见,因此大杂烩成了最常见的架构模式。事实上单体架构这种固有的困惑,驱动一些团队开始使用微服务架构。模块的解耦能够起作用,是因为模块的边界就是模块之间引用的障碍。麻烦的是,一个单体系统,通常都很容易绕过障碍。这样做,可能是构建功能时有用的技术捷径,可是呢,如果广泛运用这种方法,将削弱模块化的结构和降低团队的生产力。将模块放入独立的服务,这样做使模块的边界更为牢固,使这种自杀式的解决方案更难实现。 耦合的重要部分是持久化的数据。微服务的关键特征是 去中心的数据管理,也就是说每个服务管理它自己的数据库,其它的服务必须通过该服务的API来交互。这就会消除 集成数据库,这是大系统非常讨厌耦合的主要原因。 值得强调的是在一个单体系统中,使用模块化完全可能,但是它需要纪律来保证。同样地,你也可能得到微服务的大杂烩模式,但是对于本身已是微服务的架构,要犯这样的错还是不太容易的。在我看来,使用微服务,可以帮助你实现更好的模块化。如果你对团队纪律很有信心,那么可能会消除这一优势,不过,随着团队的规模增加,保持纪律会变得越来越,这也就意味着维护模块边界将显得更为重要性。 如果你不好好管理模块的边界,这个优势就会变成障碍。这就是 单体优先策略的两个主要原因之一,也是为何我更倾向于 较早使用微服务,你只能在一个领域了解得非常透彻。 但我现在不会在这方面做出警告。只有经过时间的洗礼,你才能真正讲出要如何才能把一个系统的模块化维护好。所以只有我们看到微服务系统已经运行好多年,才可以评估它是否会带来更好的模块化特性。而且,早期接纳者更有才华,也就是说在我们能够评估一般团队构建的微服务系统的模块化优势之前,还有好几年的延迟。所以与其和高水平的团队比较成果,还不如比较那些已经使用单体架构的软件带来的变化,这是一个棘手的违背事实的评估。 所有我现在能做的,就是听听那些我认识的人,他们使用微服务架构的早期经验。他们的判断就是微服务能让他们更容易去维护功能模块。 有一个案例特别有意思。有一个团队做了一个错误的决定,在一个不那么复杂的系统上面 使用微服务。后来这个项目碰到了问题,需要帮助,然后很多人就跑了进来。这方面,微服务还是有所帮助的,因为这个系统能够吸引开发者的快速涌入,和单体架构相比,团队更容易扩张。最终这个项目的进度大大超过使用单体架构的预期,使整个团队赶上进度。当然也有负面的,和单体架构相比,微服务的方式耗费了更多的人力,但是微服务的架构能够支持未来更大的规模。 如果你想要知道更多有关于如何构建微服务系统,Sam Newman的 这本书是很重要的资料。 独立部署 微服务是DevOps革命后第一新的软件架构。-- Neal Ford 模块化和分布式系统的复杂性一直伴随着我整个职业生涯中。但是有一件事情发生了明显的变化,就是在过去的十年中,有关生产环境的发布。在二十世纪,生产环境的发布几乎总是痛苦且罕见的事情,周末的白天或晚上部署一些能用的软件。但是现在呢,熟练的团队频繁发布到生产环境,许多团队实行 持续交付,使他们一天能够在生产环境发布很多次。 这种转变已经对软件产业产生了深远的影响,并与微服务运动深深交织在一起。当部分的小变化可能导致整个部署失败,微服务终于被单体架构的部署难题所激发出来。微服务的一个关键原则是, 每个服务都是系统的一个组件,均可独立部署。所以现在当你做出改变时,你只需要测试和部署一个小服务。如果你把它搞砸了,你不会把整个系统都搞砸。毕竟,事先对故障进行了设计,即使失败了,你的组件也不应该停止其他部分的系统工作,尽管功能上有些退化。 这种关系是双向的。由于许多微服务需要频繁部署,统一部署行为显得尤为重要。这就是为什么应用的快速部署和快速配置是 微服务的先决条件。对于任何以此为基础的服务,你都需要做持续的交付。 持续交付的好处是减少了由想法变成软件的时间。那么,团队可以快速响应市场变化,并快于竞争对手先引入新的功能。 尽管许多人认为持续交付是使用微服务的一个原因,但值得注意的是,即使单体架构也可以持续交付。Facebook和Etsy是两个最好的例子。还有一些尝试微服务的例子,因为多个服务需要认真协调才能发布,因此无法独立部署[2]。同时我也听到很多人认为使用微服务能更容易做持续交付,我是不太相信模块化可以使持续发布更容易,尽管它可以大大提高交付的速度。 技术的多样性 由于每个微服务都是一个独立的部署单元,你有相当的自由选择需要的技术。微服务可以用不同的语言,使用不同的库,并使用不同的数据存储方式。这使得团队可以选择合适的工具来工作,有些语言和库更适合某些类型的问题。 技术多样性通常以最佳的工具为中心进行讨论,但往往微服务最大的好处确是更令人头疼的版本问题。在单体架构中你可以只使用一个单一的版本库,这种情况经常导致升级出现问题。系统的一部分可能需要升级,来实现使用它的新功能,但不能因为升级而中断系统的另一部分。处理库的版本问题是其中的一个难题,因为随着代码库的增大,难度会呈指数级增长。 这里有一个风险,有这么多的技术多样性,开发团队会被压倒。我所认识的大多数组织都鼓励有限的一组技术。这种鼓励是通过提供共同的工具来支持监测,使它更容易将服务稳定在一个小的通用环境中。 不要低估了支持使用新技术的价值。用单体架构系统,早期对语言和框架的决定是很难逆转的。经过十年左右,这些的决定可能会限制团队并使团队陷入尴尬的技术境地。微服务让团队尝试新工具,并逐步一次迁移系统的一个服务,使新老技术有所关联。 分布式 微服务采用分布式系统来提高模块化。但是分布式软件有一个主要的缺陷,就是分布式系统本身。一旦你开始玩分布式系统,你就会碰到一堆复杂的问题。 我不认为微服务社区对分布式系统所带来的成本没有概念,但是复杂性也确实存在。 首先是性能。这时候,你不得不以一种不常见的方式,看着进程内的函数调用转变成性能的瓶颈,远程调用是很慢的。如果你的服务调用了很多远程服务,这些远程服务本身也要调用了另外一些远程服务,这些响应时间加起来,就会带来很恐怖的延迟特征。 当然你有办法减少延迟。首先,你可以增加调用的粒度,减少调用的数目。这会使你的编程模式变得复杂,使得你必须想清楚如何批量处理跨服务交互。由于你必须调用至少一次这些所有合作的服务,因此到目前为止,你能做的就这么多了。 第二种方法就是使用异步通信。如果六个服务异步并行调用,延迟只会是那个最慢的调用,而不是所有调用延迟的总和。这大大改善了性能,但也带来了认知成本。异步编程很难,你很难用好它,而且很难调试。但我听到的大多数微服务的例子,都需要异步来获得预期的性能。 速度之后就是可靠性的问题。你期望in-process函数调用能够成功,可是一个远程调用可能在任何时间失败。在很多的微服务中,甚至有很多的潜在的故障点。明智的开发人员知道这些, 为可能发生的故障事先设计。你为异步协作做的方案,也适应于故障处理,还可提高服务的弹性。然而,这些补偿仍然不够,仍然有着额外的复杂性,你需要弄明白每一个远程调用失败的后果。 这就是分布式计算最主要的 两个难题。 对于这个问题,还有一些要注意的地方。首先,很多这类的问题出现在单体架构规模扩充的时候。很少有单体架构可以真正独立运行,通常是跟其他系统一起,这些系统大部分是遗留系统。和这些系统通过网络进行交互,同样会碰到这些类似的问题。这就是为何很多人都倾向于更快转移到微服务架构,来处理远程系统的交互问题。这个问题,同样也和经验有关系,更熟练的团队更有能力处理分布式特性所带来的问题。 当然,分布式特性永远都是一个成本。我总是不太愿意打分布式这张牌,因为想到很多很多人,因低估这些难题而太快地引入了分布式系统。 最终一致性 我想你应该知道,网站的更新着实需要一点耐心。更新某一个东西之后刷新屏幕,可是更新的东西还没有出现。你等了一两分钟后,它出现了。 这是一个非常恼人的可用性问题,几乎可以肯定是由于最终的一致性造成的。你的更新被节点P收到,可是呢,你的请求却被另一个节点G处理。直到节点G从节点P那儿得到更新之前,你一直处于数据不一致的状态。最终,它会变成一致的,但在这之前,你会疑惑是不是有什么东西弄错了。 像这样的不一致是令人恼火的,但他们可以更严重。业务逻辑会停滞在对不一致的信息上做出决策,当这种事发生时,在不一致的窗口关闭之前,也难以诊断出到底什么出了问题。 微服务带来了最终一致性的问题,是因为他们坚持对去中心的数据管理,这种坚持值得称赞。单体架构,你可以一次更新一堆东西。微服务则需要多个资源的更新和分布式的处理,这确实头疼。所以现在,开发者需要意识到一致性问题,在写任何代码之前,弄明白如何检测数据的不一致。 单体架构在这些问题上同样不能全身而退。随着系统的增长,更需要使用缓存来提高性能,缓存失效是 另一个困难的问题。大多数应用程序需要脱机锁,以避免长期的数据库事务处理。外部系统需要更新,不能与事务管理器协调。商业流程往往更具宽容的不一致性,因为企业对可用性要求更高(业务流程,一直以来对 CAP理论都是一种直觉上的理解)。 和其他分布式问题一样,单体架构也不能完全避免非一致性问题,但它们受到的困扰不多,特别当它们规模很小时。 操作复杂性 能够迅速部署独立的小单位,是项目开发的一大福音,但由于几十个应用现在转变为几百个小的微服务,给运维额外增加了负担。许多组织会发现,处理这样一个迅速变化的工具,有一种令人望而却步的难度。 这加强了持续交付的重要性。而持续交付是单体架构一项有价值的技能,也是一个微服务所必须的。如果没有自动化协作,持续交付也无法处理那么多的服务。运维的复杂性也是由管理这些服务和监控的需求的增加而增加。运维的复杂性对单体架构的应用也有所帮助,不过对微服务来说,这是必然的。 微服务的支持者指出,由于每个服务更小,所以更容易理解。但风险在于,复杂性并没有消除,它只是转移到服务之间的相互联系中。这会增加运维的复杂性,比如调试、跨服务的难度。良好的服务边界的选择将减少这个问题,但如果用错地方,则更糟糕。 处理这种运维复杂度,需要一个新的技能和工具——重点是技能。工具仍然是不成熟的,但我的直觉告诉我,即使有更好的工具,微服务还得靠高技能。 然而,对技能和工具的需求不是解决运维复杂性最难的部分。想要高效地解决,你还需要引入一个DevOps文化:开发者和运维之间的紧密合作,每个人都参与软件交付。文化变革是困难的,尤其是在更大和更老的组织中。如果你不改变这个技能和文化,单体架构的应用只会受到阻碍,而微服务应用则会受到创伤。 其他因素 在我看来,上面的因素是作为首要权衡指标来考虑的。这里要讨论的一些事情,我认为就没那么重要了。 微服务的支持者们经常说服务更容易扩展:一个服务得到大量的负载,你就可以扩展,而不用对整个应用进行扩展。然而,我努力回忆起一个过往的经验报告,它使我相信,和 千篇一律的应用复制相比,它实际上是更有效地进行选择性扩展。 微服务允许你隔离敏感数据以及给数据增加安全性。此外,在保证微服务间交互安全的前提下,微服务难以被攻入。安全问题越来越重要,这可以成为使用微服务的主要考虑因素。即使不用微服务,对于单体架构的系统创建隔离服务来处理敏感数据,也是很常见的。 批评者说微服务应用的测试要比单体架构的应用难度更大。虽然这个真正的困难部分来自于分布式应用程序的复杂性,但还是有 好的方法可以测试微服务。在这里,最重要的是要有纪律,要认真测试,单体架构和微服务应用的测试方法的区别只是第二位的。 总结 任何有关架构的综述文章都会受到 一般性建议的限制。所以读一篇这样的文章不能帮助你做决定,但这样的文章可以帮助你确保考虑到你应该考虑的各种因素。这里对不同的系统,成本和收益做了不同的权重,甚至成本收益会被颠倒过来(在更复杂的系统中强模块化更好,但对于一个简单的系统,这就增加了障碍),你所做的任何决定取决于环境中使用条件,评估哪些因素最重要,以及它们如何影响你的特定评估条件。此外,我们对微服务架构经验相对有限。在一个系统成熟后,你通常只会在这个体系中决定相应的架构。我们还没有很多关于长期运行的微服务架构的实例。 单体架构和微服务并不是简单的二选一。两者都是模糊的定义,意味着大多数系统都将在一个模糊的边界区域。当然也还有其他系统,不适合这两个类别的。大多数人,包括我自己,讨论微服务时用单体架构作对比,这是因为它们更常见,但我们必须记住,还是有系统不属于这两类的。我认为,单体架构和微服务是架构设计领域重要的两部分。它们值得被讨论,因为它们存在有趣的特性,以及有用的讨论,但是没有合适的架构师可以对它们在架构设计领域进行一个全面的区分。 总结起来,微服务的好处:微服务提高了生产效率,但同时也带来了复杂性。所以如果你可以用单体架构管理好你的系统,那么就无需微服务。 对微服务的讨论不应该让我们忘记了更重要的问题,驱动软件项目成功和失败的重要因素。软因素如团队中人的素质,以及他们如何彼此合作,与领域专家的沟通能力,这都会对是否使用微服务有更加的影响。在纯技术层面上来讲,更重要的是把重点放在干净的代码、完善的测试,并持续关注架构的演化进步。 脚注 [1] 有些人认为"单体架构"是一种侮辱,总是意味着很糟糕的模块化结构。在微服务世界,大多数人不这样做,他们将单体架构定义为将整个应用构建为一个独立的单元。当然,微服务倡导者相信大多数单体架构最终会变成大杂烩,但我不知道有谁会认为建立一个结构良好的单体架构毫无可能。 [2] 能独立部署服务是 微服务定义的一部分。所以这么说还是有道理的,即必须在其部署时协调服务的架构不是微服务架构。也可以这么说,很多团队尝试微服务架构而陷入麻烦是因为他们最终不得不协调多个服务来部署。

龙生   16 Sep 2017
View Details

ASP.NET Core 中间件之压缩、缓存

前言 今天给大家介绍一下在 ASP.NET Core 日常开发中用的比较多的两个中间件,它们都是出自于微软的 ASP.NET 团队,他们分别是 Microsoft.AspNetCore.ResponseCompression 和 Microsoft.AspNetCore.ResponseCaching , 下面让我们一起看看的功能以及如何去使用吧。 Getting Started Microsoft.AspNetCore.ResponseCompression Microsoft.AspNetCore.ResponseCompression 这个中间件是 .NET Core 1.1 版本中新增加的,看名字应该知道,它主要是负责对输出的内容进行压缩, 那么在我们WEB开发中主要就是 GZip 压缩了。 Gzip 压缩是我们在 WEB 中经常会使用的一项性能优化技术,它可以对页面输出的内容使用压缩算法(GZip)进行体积的压缩, 那在以前的时候,我们可以使用 IIS 来做这项工作,但是现在我们的程序脱离 IIS了,就必须有一个中间件来帮我们做这件事情了,它就是我们要介绍的这个中间件。 1、添加 Microsoft.AspNetCore.ResponseCompression 包 你可以使用 Visual Studio 打开 NuGet 包管理器控制台输入一下命令安装

  也可以使用 NuGet包管理器UI界面安装。 添加完成之后,你就可以在 project.json 中看到你添加的包了。注意目前版本是 1.0.0. 2、更新 Startup.cs 文件 修改 Startup , 在ConfigureServices 和Configure 两个方法中添加如下代码:

  现在你就可以测试一下输入的 Http Response 是否被压缩了。 前: 后: 通过 前 后 对比,可以看出来,在 Response Headers 里面多了一个 Content-Encoding:gzip 的头部信息,说明我们的中间件生效了。 Microsoft.AspNetCore.ResponseCaching Microsoft.AspNetCore.ResponseCaching 这个中间件也是 .NET Core 1.1 版本中新增加的,同样看名字应该知道,它主要是负责对输出的内容进行缓存设置。在以前我们可以同样在 IIS 中设置这些东西,但是粒度可能并没有这么细。 我之前写过一篇关于 ASP.NET Core 缓存的文章,里面介绍了 ASP.NET Core MVC 中的 Response 缓存,它是通过一个 ResponseCacheAttribute 来实现的设置缓存头信息:

  那,除了 MVC […]

龙生   16 Sep 2017
View Details

.NET Core 首例 Office 开源跨平台组件(NPOI Core)

NPOI .NET Core 2.0 http://www.cnblogs.com/savorboard/p/dotnetcore-npoi.html 前言 最近项目中,需要使用到 Excel 导出,找了一圈发现没有适用于 .NET Core的,不依赖Office和操作系统限制的 Office 组件,于是萌生了把 NPOI 适配并移植到 .NET Core 的想法。 NPOI 的介绍不多说了,不了解的可以看一下 NPOI百度百科 的介绍,在此感谢瞿总和他的团队的贡献。 NPOI 的移植之路并非想象的那么容易,因为其依赖了 System.Drawing 和 System.Window.Forms 两个组件,还有一个第三方的 SharpZipLib 库,在 GitHub 克隆了最新的代码并且转换为 NetStandrad 1.6 编译之后,出现了数不清的错误,应该有上千个吧,在经过一天的努力之后(包括删除,修改,重写),错误数量已经减少到了100多个,50多个,20多个,编译通过。 在移植的过程中可以真切感受到当初NPOI的作者在写这些代码时候的辛苦努力,因为NPOI最初是基于 .Net Framework 1.1 框架写的,那个时候没有泛型,没有var,没有很多的现成的类库,全都是靠最基础的一些数据结构来实现,虽然里面的很多种写法在目前看来可以很大程序的精简,但是在当时的条件下 真的是不容易。 在通过编译之后,心里想着应该问题不大了,于是测试了一下,不幸的是,各种问题,又经过半天的调整之后,打算放弃了。 于是又去 github 上面搜索看看有没有其他什么解决方案之类的,无意间搜索到了一个 NPOI.Core 的一个项目,是一个老外移植的 NPOI 到Core平台,原来已经有人做了Core的移植了,克隆下来之后发现编译不过,又进去看了一下代码,这个库目前依赖于Windows平台,而我们项目是运行在CentOS的,其并不能在Linux上运行,看来还是空欢喜一场。 怎么办? 于是,又一次重构开始了,有了前一次的重构经验之后,这一次可谓是轻车熟路了,NPOI Core 库 里面使用了很多.NET Core netstandrad 标准不支持的 Hashtable 和 ArrayList 等数据结构,这些已经被新的泛型 Directory 和 List 替代了,还有依赖的 SharpZipLib 等压缩组件也都替换成了 NetStandrad 的实现,当然还有其他很多杂七杂八的就不细说了,最后,终于 netstandrad 1.6 下编译通过。 通过之后,本地 visual studio 下 新建了一个项目,简单测试了导出 Excel 的功能,没问题,也没有报错,心里很开心…。 这个时候我在想,最关键的就是能不能在Linux上正常运行了,其实这个时候我心里想我已经把依赖于.NET Framework 的各种类都换成了net standrad了,应该问题不大了。 然后在一顿 dotnet publish 之后,把部署包传到了 Linux 下进行测试,果然,运行通过,并没有抛出任何异常,而且Excel也生成了,把Excel传输到windows上使用office打开,完美… 然后紧接着就是继续各种测试了,在测试到 […]

龙生   16 Sep 2017
View Details

ASP.NET Core 性能对比评测(ASP.NET,Python,Java,NodeJS)

前言 性能是我们日常生活中经常接触到的一个词语,更好的性能意味着能给我们带来更好的用户体检。比如我们在购买手机、显卡、CPU等的时候,可能会更加的关注于这样指标,所以本篇就来做一个性能评测。 性能也一直是我们开发人员一直追求的一个目标,我们在做语言选择,平台选择,架构选择的过程中都需要在性能之间做衡量。 同样性能对 .NET Core 团队来说也是至关重要的,一项新技术的诞生,除了对生产力的提高,还有技术团队对性能的追求。 今天,我们就来做一个对比测试,来看看微软的这样新技术性能到底怎么样,俗话说的好:“是骡子是马,拉出来溜溜”。 下面让我开始吧。 目录 测试目标 测试工具 环境准备 开始测试 ASP.NET Core Kestrel vs ASP.NET Core IIS ASP.NET Core IIS vs ASP.NET IIS ASP.NET Core Kestrel vs ASP.NET IIS ASP.NET Core vs Python Django ASP.NET Core vs Java Servlet ASP.NET Core vs NodeJS 总结 测试目标 在测试之前,我们必须要明确我们本次测试想达到的一个目标。本次测试主要是测试应用程序的一个吞吐量。其中QPS,并发数,响应时间是我们衡量吞吐量的几个重要指标。 以下是本次对比测试的任务目标: 编号 对比方 系统环境 宿主环境 测试目标 1 ASP.NET Core vs ASP.NET Core Windows Kestrel vs IIS 相同平台不同宿主间性能差距 2 ASP.NET Core vs ASP.NET Windows IIS vs IIS 相同平台相同宿主不同框架间性能差距 3 ASP.NET Core vs ASP.NET Windows Kestrel vs IIS 相同平台不同宿主不同框架间性能差距 4 ASP.NET Core vs Python Django Linux Kestrel vs uwsgi 相同平台不同语言不同宿主不同框架间性能差距 5 ASP.NET Core vs Java Servlet Linux Kestrel vs Tomcat […]

龙生   16 Sep 2017
View Details
1 223 224 225 432