Roger Guo's Blog


  • 首页

  • 关于

  • 归档

DDIA-1-可靠可扩展可维护的应用系统

发表于 2019-04-20

数据密集型应用的模块

当今许多应用大多是数据数据密集(data-intensive)而不是计算密集型(compute-intensive)的。所以CPU的处理能力往往不是应用程序的瓶颈。关键在于数据的量、数据的复杂度以及数据的快速多变性。

应用往往包含以下模块:

  1. 数据库:用于存储数据
  2. 高速缓存:缓存复杂或者操作代价昂贵的结果,加快下一次访问
  3. 索引:用户可以按照关键字搜索数据并支持各种过滤
  4. 流式处理:持续发送消息到另一个进程,处理采用异步方式
  5. 批处理:定期处理大量积累的数据

数据系统的架构

核心设计目标

可靠性

出现意外情况,比如硬件、软件故障、人为失误等,系统可以继续正常运转,至少确保功能正确。

硬件故障

比较容易出现的,硬盘崩溃,内存故障,电网停电。

第一反应是为硬件冗余来减少系统故障率。例如磁盘RAID,服务器双电源,甚至热插拔CPU,数据中心添加备用电源、发电机。

这样当一个组件发生故障时,冗余组件可以快速接管,之后运维人员可以修复或者更换坏掉的组件。

直到最近,采用硬件冗余方案对于大多数应用场景还是足够的,它让单机完全失效的概率降到最低。只要可以把备份迅速恢复到新的机器上,故障的停机时间在大多数应用中并不是灾难性的。

现在,通过软件容错的方式来容易多机失效成为新的手段,或者成为硬件容错方案的有力补充。例如滚动升级。

软件错误

这类故障更难预料。各个节点直接是由软件关联的,可能会导致更多的系统故障。

例如,

  1. 由于软件错误,导致特定的输入引发应用的崩溃。例如Linux内核bug,在2012年6月30的闰秒时候触发,导致很多应用程序被挂掉。
  2. 失控的进程把系统的资源耗尽,导致这些共享资源不能被释放。
  3. 系统的Dependency出了问题,返回值异常。
  4. 组件中的小故障触发另一个组件中的故障,进而触发更多的故障。

没有快速的解决方法。只能仔细考虑很多细节。

  1. 检查系统的假设条件和系统之间的交互
  2. 进行全面的测试
  3. 进程隔离,
  4. 允许进程崩溃后自动重启
  5. 反复评估、监控并分析生产环境中的行为表现。

例如消息队列中,输出消息的数量应等于输入消息的数量。如果发现不一致,则立即告警。

人为失误

人无法做到万无一失。运维人员的配置错误可能是系统下线的第一大原因。

要保证系统可靠,如何减少人为错误对它的影响?

  1. 用最小出错的方式来设计系统。让做错事更难。
  2. 想办法分离最容易出错的地方,容易引发故障的接口。使用Sandbox隔离真正的生产和测试环境。
  3. 充分的测试。单元测试,集成测试,手动测试。边界条件的考虑。
  4. 当出现人为失误时,有快速回滚或者回复的机制。滚动发布新代码。
  5. 监控子系统需要详细和清晰。
  6. 推行管理流程和相关培训。

可扩展性

随着规模的增长,例如数据量、流量和复杂性,系统应该可以用合理的方式进行应对,满足这种增长。

当应用负载增加的时候,比如用户从1w到100w,从100w到1000w,应用程序如何应对增长的负载。

相关参数:Web服务的QPS,数据库的写入比例,DAU,缓存命中率。有时候平均值很重要,有时候短时间内的峰值会成为系统瓶颈。

Twitter的Fan-out结构,对数据量提出了挑战。当一个人发Tweet时候,怎么处理Timeline这个请求。根据粉丝的数量,区别处理。

如何描述性能

系统负载增加后,会发生什么,两种思考方式

  1. 系统资源不变(CPU,内存,带宽),系统的性能会发生什么变化?
  2. 如果要保持性能不变,需要增加多少资源?

不同类型的系统关心的性能指标不同

  1. 批处理系统通常关心吞吐量(throughput),例如Hadoop,每秒可以处理多少条数据或者完成一个作业总共需要多少时间。
  2. Online系统中,更看重服务的响应时间(response time),即客户端从发出请求到得到回复的总时间。

对于响应时间,如下图,有一些很长的,算异常请求,可能是由于数据大很多。但也有可能是其他因素造成的,例如上下文切换、进程调度、网络丢包、TCP重传、垃圾回收STW,缺页中断、磁盘IO。

最好使用百分位数,中位数(50%)来评估系统的响应时间。

采用较高的响应时间百分位数很重要,因为直接影响用户的总体服务体验。例如亚马逊采用99.9百分位来定义服务响应时间。优化99.9%的目标可能成本很高。能不能带来收益很关键。

​排队延迟(queueing delay)通常占了高百分位点处响应时间的很大一部分。由于服务器只能并行处理少量的事务(如受其CPU核数的限制),所以只要有少量缓慢的请求就能阻碍后续请求的处理,这种效应有时被称为头部阻塞(head-of-line blocking)

应对负载增加

垂直扩展和水平扩展。

好的系统有弹性特征,可以自动检测负载的变化,来自动添加更多的计算资源。

可扩展架构通常都是从通用模块逐步构建出来的。

可维护性

项目会随着时间的推移,项目会需要新的人员参与到开发和运维工作中,来满足系统的稳定和新场景的适应。系统应该高效的变化。

​软件的大部分开销并不在最初的开发阶段,而是在持续的维护阶段,包括修复漏洞、保持系统正常运行、调查失效、适配新的平台、为新的场景进行修改、偿还技术债、添加新的功能等等。

为此,我们将特别关注软件系统的三个设计原则:

可操作性(Operability)

​ 便于运维团队保持系统平稳运行。
良好的可操作性意味着更轻松的日常工作,进而运维团队能专注于高价值的事情。数据系统可以通过各种方式使日常任务更轻松:

简单性(Simplicity)

​ 从系统中消除尽可能多的复杂度(complexity),使新工程师也能轻松理解系统。(注意这和用户接口的简单性不一样。)
复杂度(complexity)有各种可能的症状,例如:状态空间激增、模块间紧密耦合、纠结的依赖关系、不一致的命名和术语、解决性能问题的Hack、需要绕开的特例等等,

可演化性(evolability)

​ 使工程师在未来能轻松地对系统进行更改,当需求变化时为新应用场景做适配。也称为可扩展性(extensibility),可修改性(modifiability)或可塑性(plasticity)。
组织流程方面,敏捷开发,TDD,重构。
修改数据系统并使其适应不断变化需求的容易程度,是与简单性和抽象性密切相关的:简单易懂的系统通常比复杂系统更容易修改

文明之光-近代音乐发展

发表于 2019-04-13

透过古典音乐的发展历程,可以感受到近代人文主义的兴起和发展,以及时代的变迁。

巴洛克音乐

形成时期

早起音乐与宗教有关。后来人们富裕之后,音乐的宗教气息逐渐减少,世俗的元素开始增加。欧洲音乐第二次走向世俗是发生在文艺复兴时期,而其中产生质变是在18世纪初的巴洛克时期。因此巴洛克音乐是所有古典音乐的基础。

什么是巴洛克

源于西班牙语Barocco,引申的意思是充满细节。主要是指文艺复兴后期,豪华的建筑和精致的装饰风格。后来被用到了绘画和音乐中,也是类似的意思。巴洛克音乐华丽精致,加入了大量的修饰性音符。巴洛克时期的影响力最大的音乐家是巴赫。

巴赫(1685-1750)

出身音乐世家。音乐天才。小时候因为父母趋势的早,被养在哥哥家。由于家庭负担问题15岁就要自己去挣钱养活自己。在教会表演,王公贵族的乐队里担任管风琴师。

下了很多苦功夫学习,21岁成为德意志地区有名的乐器师,担任城市乐团首席音乐总监。1717年在柯腾宫廷担任首席乐队长,在此期间创作了《勃兰登堡协奏曲》,创作水平到了炉火纯青的地步。

巴赫在音乐上的贡献非常高。

在音乐之道上,有些人认为他比之后的贝多芬莫扎特地位还要高,原因就是在于他奠定了古典音乐的基础。巴赫教会了后人如何使用音符构建出音乐作品。
另一个贡献,帮音乐从宗教走向世俗。把严肃的沉重的宗教曲目,变化成美妙的优雅的表演。
巴赫确立了德意志地区在几百年之后中古典音乐的地位。巴赫之前,意大利比较厉害。巴赫是第一个把南欧丰富新颖的音乐表现形式容易单一呆板的德意志音乐的。

在音乐之术上,有声乐和器乐两个方面。
声乐方面,巴赫的声乐作品中,康塔塔的数量占比很高。在巴赫的康塔塔中,首尾都是宗教的赞歌,中段是围绕主题的独唱咏叹调、重唱和合唱构成。这样的结构非常合理,既能表现宗教音乐的神圣,又带有民间艺术的美感。
在器乐曲上,巴赫是我们今天经常听到的奏鸣曲和协奏曲的先驱。协奏曲诞生于意大利。巴赫整天沉醉与各种乐器的组合实验。从此有了一种乐器为主,其他乐器为辅的协奏曲。配器有弦乐、羽管键琴、双簧管、小号、圆号、低音奥维尔,等等。最著名的就是《勃兰登堡协奏曲》。巴赫之后的音乐家,莫扎特、贝多芬等人在他的基础上不断完善协奏曲的创作技巧。

巴赫时代并不具备将创作的音乐普及全欧洲的条件。因此在他去世几十年后就被人们遗忘了。巴赫作品的重新发现和认识,要感谢浪漫主义时期的门德尔松(1809-1847)。门德尔松整理和出版了巴尔德音乐,在巴赫去世百年之后,人们才重新认识到这位奠定了现代音乐基础的作曲家的伟大。

巴赫一生作品之多,质量之高,让后世音乐家难以望其项背。作品包括几百首康塔塔,大量协奏曲,五部弥撒,大量的赋格。

古典主义音乐

古典主义是指欧洲18世纪末19世纪初的文化和艺术思潮。那时正是资产阶级取代贵族统治欧洲的时期,音乐和艺术都是姨资产阶级追求的自由和人性解放为背景创作的。讲究理性、秩序、和谐。

海顿

海顿、莫扎特和贝多芬。生于奥地利的海顿不仅是古典主义时期第一位重要的作曲家,也是交响乐之父。海顿不仅确立了交响乐的形式,而且一生创作了108首交响乐,超过莫扎特,贝多芬、舒伯特、舒曼、门德尔松、柴可夫斯基、马勒等人的创作总和。

海顿最大的贡献在于确定了交响乐的形式,即由四个乐章组成,分别是快板-慢板-诙谐曲或者小步舞曲-快板。直到今天大部分交响乐依然沿用这个传统。

莫扎特

神童、天才。六岁就在维也纳宫廷为特蕾莎女王表演钢琴。莫扎特音乐轻柔曼妙,充满了阳光。可能不再年轻、有一些闲暇并且积累了一些人生经历时,才会发现莫扎特很适合自己。

贝多芬

乐圣。罗曼罗兰说贝多芬时人类历史上最伟大的英雄。1770年生于波恩。爷爷是宫廷乐队长,父亲是男高音。拜师奈弗,在两年后成为乐队的管风琴师并发布了三首奏鸣曲。

波恩大公弗朗兹十分欣赏年轻的贝多芬,成了他早起的保护人。介绍他去维也纳拜师莫扎特。不过由于莫扎特当时在忙于创作,没有成功。后来贝多芬回到波恩遇到了海顿,波恩建议他到维也纳学习。贝多芬1792年第二次到维也纳,并在那里度过了他一生的大部分时间。

接受了法国启蒙运动平等自由博爱的思想。成为自由主义者。贝多芬一生追求人性的解放,他的作品一直体现出一种向上的理想主义倾向。《第二交响曲》,形成自己的风格,英雄主义倾向很明显。两年后《第三(英雄)交响曲》,本来想献给拿破仑,最后听说拿破仑成帝了,起的把拿破仑名字划掉。

英雄交响曲分为四个乐章。第一乐章为快板,描写了法国大革命的情景,场面宏大。第二乐章,《葬礼进行曲》,描写英雄牺牲,人民为他送葬。第三乐章为快节奏诙谐曲。第四乐章为快板,奔放和柔美交替,越来越强烈,直到最后一个壮丽的高潮。

贝多芬有两段重要的爱情,最后都没有变成婚姻。第一段中写下了《月光奏鸣曲》,这段感情深深的伤害了贝多芬,甚至想到自杀。第二段特蕾莎,刻骨铭心,完成《第四交响曲》,《热情奏鸣曲》,《致爱丽丝》。

贝多芬30岁后耳聋越来越严重,把自己和命运抗争的心情写在《第五交响曲》,东亚又称为《命运交响曲》。恩格斯评价,如果你没听过这部壮丽的作品的话,那么可以说你一生什么作品都没听过。

《第六交响曲》,打破了四个乐章的限制,并且给每个乐章起了标题,在该曲中,用乐器模仿自然的声音。

最后的理想是想把《欢乐颂》的文字通过合唱的形式加到交响乐中,创作了被称为《合唱》的《第九交响曲》。1824年5月7日在维也纳首演,结束后整个音乐厅爆发出雷鸣般的掌声,可是贝多芬已经听不到了。女高音牵着贝多芬转过身来后,贝多芬激动的昏了过去。当时热烈的场面如同暴动,观众给予了五次掌声。而对国王也只有三次。

小说《约翰·克里斯朵夫》,前一半是以贝多芬为原型写的。

浪漫主义音乐

拿破仑战争之后,欧洲进入平稳。浪漫主义音乐家更注重感情和音乐本身的外在表现,而不看重形式和结构。

舒伯特

600首歌曲,10部交响曲,22部钢琴奏鸣曲和大量的其他作品。

门德尔松

《仲夏夜之梦》等作品。收集整理了从巴赫到舒曼等著名音乐家的大量作品,对欧洲音乐的传承贡献很大。

肖邦

1810年出生于波兰。六岁开始学钢琴,8岁登台演出,被认为是继莫扎特贝多芬后的又一位神童。
《A大调军队波兰舞曲》和《降A大调英雄波兰舞曲》最有阳刚之气。
在法国生活了半辈子,一直眷恋故乡波兰。一生创作很多钢琴曲。作品四类,一类波兰的民族音乐,二类是法国的沙龙音乐,包括圆舞曲、即兴曲、夜曲;三类是炫技类作品;四类是表述内心情感作品。

民族主义音乐

19世纪末,欧洲人的国家意识加强,浪漫主义音乐渐渐演变成民族主义音乐。成就最高的是俄罗斯的作曲家。

柴可夫斯基

出身于俄罗斯贵族家庭,父亲从小要求他学习法律,从事相关法律工作。但是他并不喜欢,22岁后考入圣彼得堡音乐学院学习音乐创作。1876年写出了《天鹅湖》芭蕾舞剧。梅克夫人与柴可夫斯基精神上交往了13年并在经济上支持他,并为他排忧解难。成为专职作曲家。但从未谋面。《1812序曲》纪念俄罗斯人在1812年打败拿破仑。1893年创作出了最重要的作品《第六(悲怆)交响曲》写尽了自己的一生酸甜苦辣。

文明之光读书笔记-反垄断

发表于 2018-11-24

垄断的形成

垄断的背景

表现形式

垄断不只是通常人们说的独占市场,任意抬价,这一种表现形式。而且美国历史上上百宗反垄断诉讼,没有一个是控诉垄断哄抬物价的。相反,控诉理由常常会是降价或者补贴。
垄断的第二层危险性,是在独占资源或者市场后,肆意的控制市场甚至社会。印象社会公平。
范德比尔特和JP摩根是初期时代非常厉害的垄断代表。
范德比尔特通过不停的建立控股公司,控制了美国当时10%的财富。并且一个人打败了华尔街。
JP摩根在美国1907年的短暂金融危机时,以一己之力力挽狂澜。他控制的美国北方证券公司一度有美国一半的铁路系统,三分之二的钢铁。

出现

垄断是在第二次工业革命后才出现。
以前的生产力,物质的供应一直是供不应求的状态。单纯从供给关系讲,这种情况很容易垄断。因为供应一方可以漫天要价。但是事实不是如此。因为没有一个生产商可以提供一个国家,一个地区甚至一个城市的某种产品的全部生产量。这使那些缺乏竞争力的生产者得以生存。

垄断的形成除了生产力,还需要足够的资金。运输成本和开工厂。在第二次工业革命事情,垄断开始形成,在美国,德国。借助电气革命,采矿业,重工业,铁路运输业,制造业飞速发展。这些新的工商业大佬,对经济和政治的影响力远超以往的富豪。极少数人控制了美国的经济命脉,开始影响政治。

方式

垄断的方式。信托(Trust)。中文托拉斯。信托是一个虚拟的盒子,可以把任何资产,房子股票钱甚至艺术品的所有权装入。信托注册的地点决定了其适用的法律,税率,政策优惠等。信托好处在于,1,受益人可以有限的使用而不能无度挥霍。2.财产不在属于自然人,避免了对自然人财产的法律限制,例如对个人并购公司的限制。信托的管理者和受益者可以是不同的人,而管理者可以决定如何投资。

信托的例子,洛克菲勒的标准石油公司,联合了40多家石油公司建立了信托。变成标准石油托拉斯。不加入托拉斯的,就会被打压。整个石油市场,别人就没法玩了。垄断把行业的生产力大大提升,降低了成本,有的谋取了超额利润。
主要的危害还是这些垄断巨头的经济话语权越来越大,可以在政府中安插代言人,这样美国基层民主政治自治的传统就动摇了。这些寡头成了经济领域的国王。颠覆了社会公平,美国的立国之本。

垄断加速

1890年立法《谢尔曼反托拉斯法》,1895年,奈特案政府败诉。因为最高法院对《法》的适用范围解释的特别的窄。之后垄断加速。JP摩根为垄断形成提供了巨额资本的支持。

主流历史学家,认为中国专制时代越来越严重的土地兼并现象是导致王朝衰败的根本原因。当少数人在经济领域力获得绝对的权力后,一个政权就走向了衰落的不归路。

反垄断开始

美国至今还是一个充满活力的社会,而没有陷入贫富悬殊的国家,很大原因是因为美国的立国之本就是公平。反垄断就是其中一条。反垄断本身是人类成熟和进步的标志之一,它反映出,人类开始懂得如何避免兴衰轮回的悲剧。

20世纪初反垄断开始,老罗斯福总统。
1902年,要求拆分当时经济基础最为牢固的北方证券公司。在明尼苏达州法院败诉。上诉,在临时联邦法院审理。最终获胜。核心问题1.是北方证券公司是否限制或密谋限制跨州的商业行为和商业竞争。2。是垄断是否剥夺了他人的权利。
意义重大,赋予了《谢尔曼反托拉斯法》一个广泛的辖区。判断的重点在于,消费者有无可替代产品和服务,新的从业者能否进入这个行业。
紧接着罗斯福又将40多家公司告上法庭。

老罗斯福

拉什莫尔山的四位总统雕像,开国华盛顿,独立宣言杰佛逊,解放奴隶林肯。
美国的国父们建国时期思考的是防止政府权力过大,因此在制宪方面重点考虑了权利的平衡,制约。而他们没想到的经济的迅猛发展,导致了一些商业个人和家族的权力在许多方面甚至超过了政府。

标准石油公司,不仅毁掉了大量竞争对手的生意,而且毁掉了很多人的生活。它在美国经济中获得了无比强大的权力。控制价格,控制铁路电气采油等等上下游产业价格。劳工无法选择工作。贫富分化严重,百分之二的人控制了百分之六十的财富。全社会无能为力。美国的立国之本动摇了。
老罗斯福的伟大在于,能把这块硬骨头啃下去,将40多家大企业和托拉斯告上法庭。还给社会公平。把破坏的商业环境修复。自由竞争。

1904,1906年起诉标准石油公司。在1911年要求解散托拉斯。工商业得到一个信号,只要大企业不做控制价格和非正当竞争的事情,他们可以继续发展。

老罗斯福,豪门出身,首先想到的是社会公平,劳工利益以及让发展中国家近代化(庚子赔款退还)。他追求正义和进步的道德力量,称得上是第四位伟人。

进步时代

老罗斯福,塔夫脱,威尔逊,被称为进步时代。不仅仅是反垄断,而是全方位的变革。
鼓励海外投资。征收联邦税。遗产税。《克莱顿反托拉斯法》。妇女获得投票权。

世纪反垄断

20世纪的三起诉讼。AT&T,IBM和微软。

AT&T被拆分后,形成了更大的电信市场。后来的衰落也和反垄断没有太大的关系,是因为一个战略失误。

IBM捆绑了硬件软件,占据了巨大的市场份额,每年还要收取8-10%的服务费。其他的计算机公司也只能模仿IBM的商业模式,无法创新。反垄13年后,软件硬件捆绑销售的行为有所缓解。为之后的甲骨文,苹果,微软,英特尔兴起。IT服务市场也变大。

微软,三招垄断,1.通过免费软件挤压竞争对手。2.通过软件和操作系统捆绑销售占领市场。3独占操作系统市场后,联合上下游控制PC产业。反垄断很漫长,从1993年到2000年。这个案件帮助互联网公司兴起,也使得Google和苹果在市值上超过了微软。

Javascript函数式编程

发表于 2018-08-31

纯函数

Side Effect, 副作用

非纯函数会产生副作用。我们一般调用一个函数,是要得到一个返回值,或者处理一些数据。但是如果这个函数在给你返回结果之后,影响了其他对象的状态,包括传入参数,甚至一些全局变量,那么我们就说这个函数又Side Effect,副作用。

所谓纯函数,就是不论你调用他多少次,他都会给你返回相同的结果,并且不会改变其他的变量的状态。

非纯函数在易读性易理解方面,是没有纯函数强的。因为我们需要记住每一次调用非纯函数后产生的各种状态转化,这就增加了程序阅读的难度。

非纯函数,会增加代码阅读时的困难。但这并不是说明我们要远离非纯函数,非纯函数是程序的必备部分。

构造一个纯函数

一个比较直接的方式,传入所有依赖的状态并做copy。这可能带来开销问题,内存,时间等等。

什么时候选择写一个纯函数,是要根据场景来判断的。纯函数不是解决任何问题的最好方法。

函数组合

函数组合是为了避免产生连续的纯函数调用时,依赖的中间状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 1
function sum(x,y) {
return x + y;
}
function multi(x,y) {
return x * y;
}

var z = multi(3,4);
z = sum(z,5);

// 手动组合2
var z = sum(multi(3,4),5);

// 手动组合3
function multiAndSum(x,y,z) {
return sum(multi(x,y),z);
}
multiAndSum(3,4,5);

组合的函数

1
2
3
4
5
6
7
8
9
function compose(fn1, fn2) {
return function comp() {
var args = [].slice.call(arguments);
refurn fn2(fn1(args.shift(), args.shift()), args.shift());
}
}

var multiAndSum = compose2(multi, sum);
multiAndSum(3,4,5);

Immutability

1
2
3
4
5
6
7
8
9
10
11
12
13
var x = 2;
x++; // allowed

const y = 3;
y++; //not allowed

const z = [4,5,6];
z = 10; //not allowed
z[0] = 0; // allowed

const w = Object.freeze([4,5,6])
w = 10; /not allowed
w[0] = 10; /not allowed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function doubleThemMutable(list) {
for(var i = 0; i < list.length; i++>) {
list[i] = list[i] * 2;
}
}
var arr = [3,4,5];
doubleThemMutable(arr);
arr; // [6,8,10];
function doubleThemImmutable(list) {
var newList = [];
for(var i = 0; i < list.length; i++>) {
newList[i] = list[i] * 2;
}
}
var arr = [3,4,5];
var arr2 = doubelThemImmutable(arr);
arr; // [3,4,5]
arr2; //[6,8,10]

Nginx配置笔记

发表于 2018-08-31

安装

在yum repo配置目录下创建nginx镜像配置, /etc/yum.repos.d/nginx.repo, 文件nginx.repo填入如下内容

1
2
3
4
5
[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=0
enabled=1

然后运行

1
yum install nginx -y

如果出错,需要检查配置文件内容是否有误,也有可能是cache问题,运行yum clean all 即可。

常用命令

1
2
3
4
5
/etc/init.d/nginx start # 启动Nginx服务

/etc/init.d/nginx stop # 停止Nginx服务

/etc/nginx/nginx.conf # Nginx配置文件

配置

HTTPS

将域名 www.domain.com 的证书文件1_www.domain.com_bundle.crt 、私钥文件2_www.domain.com.key保存到同一个目录,例如/usr/local/nginx/conf目录下。
更新Nginx根目录下 conf/nginx.conf 文件如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
server {
listen 443;
server_name www.domain.com; #填写绑定证书的域名
ssl on;
ssl_certificate 1_www.domain.com_bundle.crt;
ssl_certificate_key 2_www.domain.com.key;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #按照这个协议配置
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;#按照这个套件配置
ssl_prefer_server_ciphers on;
location / {
root html; #站点目录
index index.html index.htm;
}
}

HTTP rewrite to HTTPS

1
2
3
4
5
server {
listen 80;
server_name www.domain.com;
return 301 https://$server_name$request_uri;
}

参考资料

https://serverfault.com/questions/67316/in-nginx-how-can-i-rewrite-all-http-requests-to-https-while-maintaining-sub-dom

https://www.nginx.com/resources/wiki/start/topics/tutorials/install/

https://nginx.org/en/docs/beginners_guide.html

https://cloud.tencent.com/document/product/400/4143

iOS-编译Category遇到的问题

发表于 2018-06-09

问题

一个iOS菜鸟,最近工作中遇到了一个有关Category的问题。Objective-C语言。

遇到问题的原因是,我给AppDelegate加一个Category的时候,忘记Check 中Production的Deployment Target。

造成的结果是,我在Category中加的方法,在运行时怎么都不能被找到,然后就会Crash。运行期间报selector not recognized,不是property找不到,是method。

解决方法当然就是把这个m文件选入Production的Target就好了。这些都很简单。

问题在于,为什么Xcode没有在Compile Time找到这个问题呢?如果我编译,链接PROD的Target,按一般的静态语言,这个错误很容易被发现。所以就跟同事讨论了一下Obj-C的编译与Runtime的问题。

再简单总结一下遇到的情况:

测试1, 如果把普通类(非Category)的m文件从target排除掉,那么compiler在编译期,准确的说链接时,就会报错。

测试2,把Category的m文件从target排除掉,编译期就不会报错,只会在运行到那一行Category的方法时,发现找不到Symbol。

原因

为什么呢?

短的答案是,Category的方法调用不会被链接时确定,而是在Runtime去找。

长的答案参考自Apple文档 https://developer.apple.com/library/archive/qa/qa1490/_index.html

里边这一段说明了情况,是说我们的主类去引用一个Category的method的时候并不能产生一个undefined symbol去给链接器处理

1
2
3
4
5
The dynamic nature of Objective-C complicates things slightly. Because the code that implements a method is not determined until the method is actually called, Objective-C does not define linker symbols for methods. Linker symbols are only defined for classes.

For example, if main.m includes the code [[FooClass alloc] initWithBar:nil]; then main.o will contain an undefined symbol for FooClass, but no linker symbols for the -initWithBar: method will be in main.o.

Since categories are a collection of methods, using a category's method does not generate an undefined symbol. This means the linker does not know to load an object file defining the category, if the class itself is already defined. This causes the same "selector not recognized" runtime exception you would see for any unimplemented method.

最终的结果是,在RUNTIME用@selector产生SEL的后,在Dispatch Table中找不该SEL ID到IMP

其他参考

在研究有关Category的问题时候,又通过一些博客学习了一些新知识。

https://tech.meituan.com/DiveIntoCategory.html

https://www.jianshu.com/p/5aed848e283d

https://www.zhihu.com/question/27179396

https://hit-alibaba.github.io/interview/iOS/ObjC-Basic/Runtime.html

https://www.jianshu.com/p/4a09d5ebdc2c

https://blog.csdn.net/fengsh998/article/details/8614486

ES6-Arrow_Function

发表于 2018-04-01

Arrow Function

能做什么

能够简化一些代码的行数,省些单词,在有些情境下可以提高代码可读性

this的闭包捕获是最有用的功能

不能做什么

  1. 当箭头函数被用在callback中,因为debug stack trace是匿名函数,出错debug不方便找到出错位置

  2. 调用自己,递归

经验之谈

灵活选择function定义和Arrow Function。

Java并发编程-内存模型

发表于 2018-04-01

内存模型

并发程序中,多个线程之间可见性的问题。如果线程和处理器之间过度协调,同步数据,那么开销太大。需要一个最低标准来保证可见性,这引入了JMM。

JMM规定了JVM必须遵循一组最小的保证,这组保证规定了对变量的写入操作在任何时候将对与其他线程可见。内存栅栏指令也数据JMM定义范围内的东西。JMM保证了在不同平台上也可以获得同样的并发执行结果。

重排序

重排序会发生在,编译器,运行时,硬件对内存的操作重排序。同步操作,保证在重排序之后,JMM的可见性保证不变。

JMM

Happens-Before 规则

程序顺序规则
监视器锁规则
volatile规则
线程启动规则
线程结束规则
中断规则
终结器规则
传递性

发布

不正确的发布的真正原因,就是在发布一个共享对象和另一个线程对这个对象的访问操作之间,没有Happens-Before关系。

延迟初始化的例子

安全发布

用Syncrhonized

提前初始化

用Holder内部类的方式,static

双重检查加锁,很容易出错。要用volatile

Java并发编程-原子变量与非阻塞同步

发表于 2018-04-01

原子变量与非阻塞同步

近年来的非阻塞并发算法很火热。这种算法底层使用原子机器指令代替锁,来保证一致性。

非阻塞算法应用在操作系统和JVM中,实现线程/进程调度机制、垃圾回收机制等其他并发数据结构。

锁的劣势

线程挂起,恢复的开销。线程挂起后不能做任何事情。
Volatile提供了可见性的保证,但是不能构建复合操作。

硬件对并发的支持

早期处理器中的Test-and-Set,Fetch-and-increment。
现代处理器的,Compare-and-Swap,Load-Linked/Store-Conditional

CAS操作的使用场景。先读取,再计算,最后使用CAS修改。一般有while循环。

CAS的性能比锁要高。

JVM对CAS会有支持,在不持支的处理器上使用自旋锁。

原子变量类

AtomicInteger/Long/Boolean/Reference

是一种更好的volatile。

原子变量与锁的性能差异的测试。在竞争程度较高的情况下差不多。在竞争程度适中的情况下原子变量表现更好。

非阻塞算法

使用原子变量可以构造非阻塞的数据结构,例如栈,链表。
非阻塞算法的设计和实现非常困难,但是通常会提供更好的可伸缩性,并有效地防止活跃性问题发生。

ABA问题

原子操作CAS,看到的A是之前的A还是ABA后的A?如果这个问题会影响系统的正确性,那么需要有额外的方式来保证正确性。例如加上版本号。

Java并发编程-显示锁

发表于 2018-04-01

显示锁

Java 5.0增加的新的协调共享对象访问的机制:ReentrantLock。是一个可提供高级功能的工具。

Lock and ReentrantLock

Lock接口

1
2
3
4
5
6
7
8
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();
}

内置锁Syncrhonized的局限。

可定时的与可轮询的锁

死锁的处理可以有更多的选择。例如,同时试图获取两个锁,失败时可以在随机时间再重试。

transferMoney的代码例子。两层trylock

trylock带超时参数,可以在超时后发送一条消息给外部。

LockInterruptily

性能

Java 6 之后内置锁和ReentrantLock的性能上差距不大。

公平性

RenetrantLock可以提供非公平的选择。非公平锁可以再某种程度上提高系统吞吐率,但是也有可能会造成优先级低的线程一直被插队饥饿。

在Syncrhonized无法提供需要的高级特性时再去选择ReentrantLock

读写锁

复杂性比独占锁高,所以在被频繁读取的时候有性能优势,其他情况下要稍差。

1
2
3
4
public interface ReadWriteLock {
Lock readLock();
Lock writeLock();
}
123

Roger Guo

25 日志
12 标签
© 2019 Roger Guo
由 Hexo 强力驱动
|
主题 — NexT.Muse v5.1.4