黑暗已经来临
所有数据均采集与网络或网友提供, 仅供学习参考使用
科创人 / 2019-12-23 4 years ago / 未收藏/ SegmentFault 最新的文章发送到 kindle

2009 丧|心|病|狂|的九零后科创人
科创人(以下简称科):作为福布斯评选出的30位30岁以下创业者,以及更了不起的——《科创人》采访的首位90后科创人(叉腰厚颜),您当初为何选择创业这条路?
高阳(以下简称高):高中时我被定义成了问题学生,高三那年我很迷惘:大家都说高考重要,可高考之后的人生是一团雾。互联网让我接触到了另一个世界,有勇气、有知识、有未来,在社交网络上我和很多互联网行业前辈交上了朋友,看到听到的跟学校教的完全不一样。一直纠结到了大一,我下决心跑到北京,加入了社交游戏创业团队MagnetJoy Games。
科:父母有没有给你施加压力?
高:先斩后奏。我和MG的创始人AW是校内网的好友,有一天他跟我说,我这需要个做运营的,看你这人特能搞事情,要不来试试?我日思夜想的就是进入互联网行业,秒确认。来了北京,在一个20多平的房间里,公司一共7个人忙得昏天黑地,浑身上下每一个细胞都告诉我这才是我要的生活。来的时候是年底,过年也没回去,父母知道后肯定是不太愿意,但知道我有收入、有地方住就没有过度干预。
科:当年您四舍五入还是位高中生,工作中有没有遇到能力瓶颈?
高:专业能力上我比较自信,工作之外我会花比较多的时间用来在网上学习公开资料,与行业内的朋友交流,不断提升自己的专业能力。回过头来看,我从MG离职是因为自己当时对团队的认知没能匹配团队的发展速度。两年时间团队从7个人发展到70个人,在全球10几个国家和地区有产品合作,运营难度非常大。可我当时并没有意识到,野心勃勃地以为自己能胜任运营负责人,但业务发展很快、团队规模变大,习惯了单兵作战的自己一时无法与新加入的团队有效协作,老板只能去找更有能力带团队的人。后来当上CEO才知道,一把手就是要做对企业整体最有利的选择。
科:这件事有没有打击到您的信心?
高:没有。我有一个身份,是C14Z这个90后创业组织的创始人,这个组织最初只是一个十几个人微信群,群名叫“丧心病狂的九零后”。6 年多下来,现在已经发展到近 150 人,基本都是我见过面的好友,不对外公开招募。大家散落在世界各地,但平时联络非常多,今年初我们大致统计了下,群成员创立的几十家公司市值已经超过一百亿。
“丧心病狂”代表了90 后年轻群体的一些特征:我们都经历过同龄人没有体会过的迷惘期,在某一阶段体现出很的状态,甚至被周围的人看做很疯狂的人。,就是信念、自我,追随初心,当我们这些人找到了自己想做的事情时,会义无反顾、不计代价投入。,别人看来是狂妄,我们自己觉得就是自信,挫折什么的都是学费。从MG离开的时候很遗憾,在那的两年经历让我形成了影响至今的价值观:做事业,人的因素最重要。如果没有前老板AW,我现在可能在某个学校教书,或者在某个工厂当技术工人。另外,在MG我还遇到了现在SegmentFault的技术合伙人祁宁。

▲C14Z Group(Crazy Generation Z),C14Z公开的官方命名取名规则参考了世界顶级投资机构 A16Z,数字 14 同时也是向 PayPal Mafia 的 14 人致敬,内部则一直沿用“丧心病狂的九零后”这个名称。
2013 出于担当,自荐CEO
科:您离开了MG两年之后,与MG的同事创业并一直走到了今天,这深刻的基(划掉)羁绊是如何形成的?
高:那两年时间我做为早期成员参与创办了天使投融资平台AngelCrunch,在媒体Tech2IPO工作过,积累了实战运营经验、也看了很多创业项目。在AC我的工作是帮助创业者,可很多创业者遇到的问题我答不上来,虽然在创业公司待过但我没有真正意义上一个人从0到1做成一个项目,就此萌生了创业的想法。可我也清楚,以自己的技能树,想要创业一定要找到能搭起好产品的合伙人祁宁、董锋就是出色的研发和产品人,我们三人多年前就通过祁宁发起的开源项目Typecho结识了,线上协作磨合得不错,几个人一拍即合决定辞职创业。我当时已经离开了MG两年,但是跟祁宁的关系一直很好,总去他在北京租的房子蹭饭(笑)。

▲2012年10 月,杭州莲花街,在SegmentFault第一个办公室中奋斗的少年们。
科:三位创始人中您年龄最小,SegmentFault的雏形最初也是祁宁构建的,但在广州的一次酒局后您成为了CEO,当时发生了什么?
高:去杭州创业的时候合伙人有四位,那时候问答社区正火,我们的竞品大概有七八家,但大都没撑过半年。**我们也是在半年左右的时候遇到了一些问题,有一位合伙人因个人原因离开,社区数据增长比较慢,发展得举步维艰。于我本人而言,创业最初那半年挺迷茫,团队工作重点是产品研发、迭代,运营没有工具也没有钱,干着急使不上劲。
但这些不是我自荐CEO的原因,我要站出来,是因为我的技能树更适合做社区类项目的CEO,我有信心让SegmentFault发展得更好。当时遇到的问题有两个,第一要加快发展速度,祁宁超级聪明但喜欢自己思考、不太喜欢对外表达,而我认为创业初期需要提速,需要有人快速决断并且愿意为结果负责;第二,小型团队一定会有很多外部化的工作要做,CEO要站在前台对接外部资源、协调各种关系,团队三个人中只有我最适合做这个工作,别人做就会耽误产品的进度。这个决定完全没有私心,只是想让事情更好的发展,大家彼此又很信任,一边喝着一边就做了决定。
科:那一晚你们喝了多少?
高:很多,不过没喝醉(笑)。

祁宁(左一)、高阳董锋,三人间建立起的牢固信任关系是SegmentFault顺利发展至今的地基。
科:面对七八家竞品,SegmentFault的差异化优势是什么?
高:差异化方面并没有考虑同样处于起步阶段的竞品,倒不是自负,而是感觉大家做的事类似,但做事的方法差别很大,很难比较。有的竞品融资比我们多,但都花在送礼物吸引用户上,很烧钱。SegmentFault从没有直接花钱买过用户,我们坚信价值吸引才能吸引真正的用户,买来的流量留不住。我们的使命是帮助开发者成功,产品层迭代都极其注重开发者的需求和使用价值,站在开发者的角度去思考问题。
对比国内一些前辈大站,SegmentFault产品优势比较明显,新一代技术社区成长起来后,倒逼很多前代大站改版升级,证明我们的产品方向更符合用户需求。说一个细节,很多社区都能写文章,但绝大部分都用富文本编辑器,是SegmentFault率先支持Markdown这款开发者最喜欢用的文本编辑器。这一项改动能明显提升用户体验,可当时国内的技术社区几乎没有人做。
2016 一处知识盲点引来公司生死劫
科:SegmentFault创立以来有没有经历过比较大的风险或者事故?
高:2016年底,SegmentFault差点就没了。我们的天使轮投资者是IDG和梦工场,梦工场的背后是一家国有传媒集团。天使轮我们拿的钱不多,人民币250万元,2016年准备做A轮融资,软银赛富领投,这时候出现了问题:软银赛富只做美元投资,SegmentFault必须做VIE结构,但天使轮国资背景投资者的存在让VIE无法实现,更可怕的是当时SegmentFault的账面资金只够撑一个月,我们必须在一个月之内搞定这个复杂的局面。
当时的难点在于:能否说服天使轮投资者以彼此能接受的价格退出,而且它退出只能走招拍挂,不仅需要时间,也需要承担被恶意竞标的开放性风险。我们迅速意识到在自己不懂的领域,得让专业的人来做专业的事情,当时最正确的决定就是聘请君合律所合伙人李辰亮帮我们梳理局面。SegmentFault能活下来,首先要感谢我们天使轮的投资人,还要感谢李律师和为我们回购股权提供贷款的投资人,最后要特别感谢当时软银赛富的投资人、现在名川资本创始合伙人王求乐,给了我们足够的时间去解决问题。
科:很多创业项目发生的风险都是因为创始人犯下的错误,可SegmentFault这次命悬一线,似乎看不出自身有明显的问题?
高:创业就是这样,看上去你并没有作死,可只是有一处知识点没有掌握到就可能暴毙。这件事也让我更加相信,事情的变化是无穷的,个体不可能对抗创业带来的种种变化,创业最重要的元素就是人和、人和、人和,只有遇到对的人才有可能解决全部问题,让专业的人去做专业的事,这个人不一定是你的团队成员,也可能外部的合作团队。我们只是一个小团队,但这些年积累了一批紧密合作的外部赋能团队,协助我们完成复杂的项目合作。另外当时只有美元投资者对我们有兴趣,人民币投资者没有投资意愿,也激励我们后续要努力地优化产品。
2019 坚持做长期创造价值的事,不为盈利担心
科:SegmentFault当初构想的盈利模式是怎样的,如今实现了么?
高:经过7年积累, 我们的注册开发者已经远超300万,也有超过1000多个技术团队——诸如腾讯、阿里、百度的技术团队入驻。我们一直追求纯粹极致的产品体验,在年轻新一代的开发者中有极好的口碑和影响力,随着优质技术内容的沉淀,社区流量大幅上涨,整合营销的广告收益近两年已经能够覆盖团队成本(当然我们本来也是一个非常小的团队)。
我觉得创业这个事,最重要的是做出用户需要的、有价值的产品和服务,在创业初期我们花了大量的精力用来打磨产品、构建核心用户群,坚定做长期有价值的事情,我一直跟团队强调要脚踏实地做好产品。另外,技术行业迭代变化非常快,所以也要抬头看天、关注行业。事实证明,量变引起了质变,现在我们可能是国内DGC最活跃的综合性开发者社区之一,技术问答板块也成为了行业领跑者。
五六年前我们只是一个几万用户的技术问答社区,当时根本想不到未来可以有这么多的创收方式。比如SegmentFault如今在输出社区整体技术以及运营方案,帮助科技厂商一起构建开发者生态,今年我们与Arm中国建立了战略合作,通过我们的能力协助Arm中国一起构建AIOT的开发者社区生态。还有我们一直运营着各类线下活动,尤其是黑客马拉松这类创新力赛事,如今也为一些中大型科技企业定制内部的创新力赛事,帮助客户吸引外部人才以、激活团队内开发者的创新力,今年我们帮助新网银行举办的算法大赛,就吸引了上千名高校人才参与,活动也得到了共青团四川省委、四川省教育厅等政府部门的关注和支持。
同时今年我们也发布了媒体业务,招了全职技术编辑,可以更好地帮助科技厂商传播技术品牌,更顺畅地与社区开发者沟通。所有这些服务都围绕同一个核心:通过我们的产品和服务让开发者进步成长、获得更好的发展机会,同时通过我们的连接帮助到企业客户。
科:我个人好奇一件事,思否是个很好的名字,这个名字是谁起的?
高:我(骄傲脸)。思否,我们的理解是“独立思考、敢于否定”,代表了开发者精神,当时马上让技术合伙人祁宁用比特币从一个老外手里买回了“sifou.com”的域名(捂脸),思否这个名字去年才开始正式启用,社区板块会继续以SegmentFault英文名为主,思否可能会作为更高一级的品牌形象出现。说到名字,我们正在规划一个培训项目思否编程,最初打算叫思否大学,但是感觉不聚焦、听不懂,还是编程直接纯粹。目前还是一个栏目,未来会独立运营。
科:起名、定位、品牌、战略、融资,您的CEO技能树是怎么培养起来的?
高:又要说到人,我一直坚持社交型学习,先自己阅读各种资料,建立对某个行业的框架认知,然后去和这个行业内最出色的朋友讨论,一边向他们学习一边大胆反馈我的想法和建议,再从他们的反应总判断自己对事情的理解程度是否足够深刻。
科:选择培训教育这个行业,是因为培训能够带来更大的市场规模、更高的估值吗?
高:商业性一定是决策的核心因素之一,这没什么可避讳的,更重要的是教育有长期价值,好的教育项目可以帮助更多的人,坚持做长期有价值的事情是我们团队一直以来的追求。还有一个想法,我至今都能记得自己在19岁那年经历的迷惘,你可选的成长之路只有一条,你有自己想做的事,但选择做自己就意味着要自己解决成长问题,自己学、自己悟。
这个世界的未来一定需要更多的技术人才,现在的年轻人中一定也会有很多人有意愿走这条路,那么思否编程会成为年轻人走向未来的选择之一,一个可靠的成长方向。希望通过我们的努力,找到行业最好的技术人一起制作更多优质课程,帮助到更多的年轻开发者成长。
iyacontrol / 2019-12-23 4 years ago / 未收藏/ SegmentFault 最新的文章发送到 kindle
Kubernetes是用于构建高度可扩展系统的强大工具。结果,许多公司已经开始或正在计划使用它来协调生产服务。不幸的是,像大多数强大的技术一样,Kubernetes也很复杂。我们整理了以下清单,以帮助你生产环境最佳实践Kubernetes。
best-06.jpg

容器最佳实践

Kubernetes提供了一种编排容器化服务的方法,因此,如果您没有按顺序实践你的容器,那么集群一开始就不会处于良好状态。请按照以下提示开始。
1: 使用精简基础镜像
what:容器是内置在系统镜像中的应用程序堆栈。从业务逻辑到内核的所有内容都打包在一起。最小的镜像会占用尽可能多的OS,并迫使您显式添加所需的任何组件。
why:仅在您的容器中包括要使用的软件,同时具有性能和安全性方面的好处。磁盘上的字节数更少,复制镜像的网络流量更少,并且潜在的攻击者无法访问的工具也更少。
how:Alpine Linux是一个流行的选择,并具有广泛的支持。
2:使用提供最佳正常运行时间的注册表
what:注册表是镜像的存储库,使这些镜像可供下载和启动。在指定部署配置时,您需要指定从何处获取路径为<registry> / <remote name>:<tag>的镜像:
best-01.jpg
why:您的集群需要镜像去运行。
how:大多数云提供商都提供私有镜像注册表服务:Google提供Google容器注册表,AWS提供Amazon ECR,Microsoft提供Azure容器注册表。
仔细调研,并选择提供最佳正常运行时间的私人注册表。由于您的群集将依靠您的注册表来启动软件的较新版本,因此任何停机时间都将阻止对正在运行的服务进行更新。
3:使用ImagePullSecrets对您的注册表进行身份验证
what:ImagePullSecrets是Kubernetes对象,可让您的群集通过注册表进行身份验证,因此注册表可以选择谁可以下载镜像。
why:如果您的注册表足够公开,可以让集群从中提取镜像,则表明注册表足够公开,需要身份验证。
how:Kubernetes网站在配置ImagePullSecrets方面有很好的演练,该示例使用Docker作为示例注册表。

管理你的集群

微服务本质上是一团糟。使用微服务的许多好处来自在服务级别上强制职责分离,有效地为后端的各个组件创建了抽象。一些很好的例子是运行与业务逻辑分离的数据库,运行软件的单独开发和生产版本,或分离出水平可伸缩的流程。
具有不同服务执行不同职责的阴暗面是,它们不能被平等对待。值得庆幸的是,Kubernetes为您提供了许多解决此问题的工具。
4:使用命名空间隔离环境
what:命名空间是Kubernetes中最基本,最强大的分组机制。它们几乎像虚拟集群一样工作。默认情况下,Kubernetes中的大多数对象仅限于一次影响单个名称空间。
why:大多数对象都是在命名空间范围内定义的,因此您必须使用命名空间。鉴于它们提供了强大的隔离性,因此它们非常适合隔离具有不同目的的环境,例如用户服务的生产环境和严格用于测试的环境,或者分离支持单个应用程序的不同服务堆栈,例如保持安全解决方案的工作负载与您自己的应用程序分开。一个好的经验法则是按资源分配划分名称空间:如果两组微服务将需要不同的资源池,请将它们放在单独的名称空间中。
how:它是大多数对象类型的元数据的一部分:
best-2.jpg
请注意,您应该始终创建自己的名称空间,而不要依赖“默认”名称空间。 Kubernetes的默认设置通常会为开发人员优化以最小的摩擦,这通常意味着甚至放弃最基本的安全措施。
5:通过Labels 管理您的集群
what:Labels是组织集群的最基本且可扩展的方法。它们允许您创建用于分隔Kubernetes对象的任意key:value对。例如,您可以创建一个标签密钥,将处理敏感信息的服务与不处理敏感信息的服务区分开。
why:如前所述,Kubernetes使用标签进行组织,但更具体地说,它们用于选择。这意味着,当您想给Kubernetes对象引用某个命名空间中的一组对象时(例如告诉网络策略允许哪些服务相互通信),请使用它们的标签。由于它们代表了这种开放式组织类型,因此请尽最大努力使事情简单化,并且仅在需要选择权的地方创建标签。
how:标签是一个简单的规范字段,您可以将其添加到YAML文件中:
best-03.jpg
6:使用注释来跟踪重要的系统更改等
what:注释是可以附加到pod的任意键值元数据,就像标签一样。但是,Kubernetes不会读取或处理批注,因此围绕您可以和不能使用批注进行注释的规则相当宽松,并且不能用于选择。
why:它们可帮助您跟踪容器化应用程序的某些重要功能,例如版本号或首次启动的日期和时间。仅在Kubernetes的上下文中,注释是一种无能为力的构造,但是当用于跟踪重要的系统更改时,注释可以成为开发人员和运营团队的资产。
how:注释是类似于标签的规格字段。
best-05.png

让你的集群更加安全

好了,您已经建立了集群并按所需方式组织了-现在呢?好吧,接下来是要确保一些安全。您可能会花费一生的时间来学习,但仍未发现有人可以侵入您系统的所有方式。博客文章的内容空间要比一生少得多,因此您必须满足一些强烈的建议。
7:使用RBAC实施访问控制
what:RBAC(基于角色的访问控制)使您可以控制谁可以查看或修改群集的不同方面。
why:如果要遵循最小特权原则,则需要设置RBAC来限制群集用户和部署能够执行的操作。
how:如果要设置自己的集群(即不使用托管的Kube服务),请确保使用''--authorization-mode = Node,RBAC“启动您的kube apiserver。如果使用托管的Kubernetes例如,您可以通过查询用于启动kube apiserver的命令来检查它是否设置为使用RBAC。唯一通用的检查方法是在kubectl cluster-info dump的输出中查找“ --authorization-mode ...”。
RBAC打开后,您需要更改默认权限以适合您的需求。 Kubernetes项目站点在此处提供了有关设置角色和RoleBindings的演练。托管的Kubernetes服务需要启用RBAC的自定义步骤-请参阅Google的GKE指南或Amazon的AKS指南。
8: 使用Pod安全策略防止危险行为
what:Pod安全策略是一种资源,非常类似于Deployment或Role,可以通过kubectl以相同的方式创建和更新。每个都有一个标志集合,可用来防止集群中特定的不安全行为。
why:如果创建Kubernetes的人认为限制这些行为足够重要,可以创建一个特殊的对象来处理它,那么它们很重要。
how:让他们工作可能会令人沮丧。我建议启动并运行RBAC,然后在此处查看Kubernetes项目的指南。在我看来,最重要的使用是防止特权容器和对主机文件系统的写访问,因为它们代表了容器抽象中一些较泄漏的部分。
9: 使用网络策略实施网络控制/防火墙
what:网络策略是允许您明确声明允许哪些流量的对象,而Kubernetes将阻止所有其他不符合标准的流量。
why:限制群集中的网络流量是一项基本且重要的安全措施。默认情况下,Kubernetes启用所有服务之间的开放式通信。保留此“默认开放”配置意味着与Internet连接的服务与存储敏感信息的数据库仅一步之遥。
how:有一篇文章写的很好,具体详情查看这里
10:使用Secrets来存储和管理必要的敏感信息
what:Secrets是您如何在Kubernetes中存储敏感数据,包括密码,证书和令牌。
why:无论您是实施TLS还是限制访问,您的服务都可能需要相互认证,与其他第三方服务或您的用户进行认证。
how:Kubernetes项目在此处提供了指南。一个关键建议:避免将机密作为环境变量加载,因为在您的环境中拥有机密数据通常是不安全的。相反,将机密装入容器中的只读卷中-您可以在本 Use Secrets中找到一个示例。
11:使用镜像扫描识别和修复镜像漏洞
what:扫描仪检查镜像中安装的组件。从操作系统到应用程序堆栈的所有内容。扫描程序对于找出镜像所包含的软件版本中存在哪些漏洞非常有用。
why:漏洞一直在流行的开源软件包中发现。一些著名的例子是Heartbleed和Shellshock。您将想知道这些漏洞在系统中的什么位置,以便您知道哪些镜像可能需要更新。
how:扫描仪是基础设施中相当常见的部分-大多数云提供商都提供了产品。如果您想自己托管一些东西,那么开源Clair项目是一个受欢迎的选择。

保持您进群稳定

Kubernetes代表很高的技术栈。您拥有在嵌入式内核上运行的应用程序,在VM中运行的应用程序(在某些情况下甚至在裸机上),以及Kubernetes自己的服务共享硬件。考虑到所有这些因素,在物理和虚拟领域中很多事情都会出错,因此尽可能降低开发周期的风险非常重要。 Kubernetes周围的生态系统已经开发了一系列最佳实践,以使事情尽可能保持一致。
12:遵循CI / CD方法
what:持续集成/持续部署是一种过程哲学。相信对代码库进行的每次修改都应增加增量值,并准备投入生产。因此,如果代码库中的某些内容发生了更改,则可能要启动服务的新版本,以运行测试。
why:遵循CI / CD可以帮助您的工程团队在日常工作中牢记质量。如果出现问题,修复问题将成为整个团队的当务之急,因为此后依赖于已分解的提交的所有更改也将被分解。
how:由于云部署软件的兴起,CI / CD越来越流行。因此,您可以从托管或自托管的众多出色产品中进行选择。如果您的团队比较小,我建议您采用托管路线,因为节省的时间和精力绝对值得您付出额外的费用。
13:使用Canary方法进行更新
what:Canary是一种将服务更改从代码库中的提交带给用户的方法。您启动了一个运行最新版本的新实例,然后将用户缓慢迁移到新实例,从而逐渐获得了对更新的信心,而不是一次全部交换。
why:无论您的单元测试和集成测试有多广泛,它们都无法完全模拟生产中的运行-总是有可能某些功能无法按预期运行。使用金丝雀可以限制用户接触这些问题。
how:Kubernetes的可扩展性提供了许多途径来逐步推出服务更新。最直接的方法是创建一个单独的部署,与当前正在运行的实例共享一个负载平衡器。这个想法是您扩展新的部署,同时缩减旧的部署,直到所有正在运行的实例都是新版本。
14:实施监控并将其与SIEM集成
what:监视意味着跟踪和记录您的服务正在做什么。
why:让我们面对现实吧-不管您的开发人员多么出色,无论您的安全专家如何努力地发挥他们的聪明才智,事情都会出错。当他们这样做时,您将想知道发生了什么,以确保您不会两次犯相同的错误。
how:成功监视服务有两个步骤-需要对代码进行检测,并且需要将该检测的输出馈送到某个地方以进行存储,检索和分析。执行检测的方式在很大程度上取决于您的工具链,但是快速的网络搜索应该可以让您有所作为。就存储输出而言,除非您有专门知识或需求,否则我建议使用托管SIEM(例如Splunk或Sumo Logic)-根据我的经验,DIY始终是与任何存储相关的期望时间和精力的10倍。

深度建议

一旦集群达到一定规模后,您将发现手动执行所有最佳做法将变得不再可行,结果将给系统的安全性和稳定性带来挑战。超过此阈值后,请考虑以下主题:
15:使用服务网格管理服务间通信
what:服务网格是管理服务间通信的一种方法,可以有效地创建在实施服务时使用的虚拟网络。
why:使用服务网格可以减轻管理群集的一些较繁琐的方面,例如确保对通信进行正确的加密。
how:根据您对服务网格的选择,启动和运行的复杂性可能千差万别。作为最常用的服务网格,Istio似乎正在蓬勃发展,并且您的配置过程将在很大程度上取决于您的工作负载。
一个警告:如果您需要采用一个服务网格,请尽早采用它而不是稍后采用它-逐渐改变集群中的通信样式可能会非常痛苦。
16:使用准入控制器解锁Kubernetes中的高级功能
what:准入控制器是一种很好的万能工具,可用于管理集群中发生的一切。它们允许您设置Kubernetes在启动时将参考的Webhook。它们有两种形式:变异和验证。突变准入控制器会在部署启动之前更改其配置。验证准入控制器会与您的webhook一致,以允许启动给定的部署。
why:它们的用例广泛且数量众多–它们提供了一种通过自行开发的逻辑和限制来迭代地提高集群稳定性的好方法。
how:查看有关如何开始使用Admission Controllers的指南
思否编程 / 2019-12-23 4 years ago / 未收藏/ SegmentFault 最新的文章发送到 kindle
思否编程打造全新公开课系列,邀请大神级讲师分享技术干货,快来围观看直播~

主题

PostgreSQL 12 新特性

内容介绍:

PostgreSQL 12 新版本开发者特性介绍,数据库行业未来风向标。
PostgreSQL 12 版本的典型新特性如下:

  • 支持 SQL/JSON path
  • 支持 Generated Columns
  • CTE 支持 Inlined With Queries
  • 新增 Pluggable Table Storage Interface
  • 分区表性能大辐提升
  • 在线重建索引(Reindex Concurrently)
详情可看直播~

讲师介绍:

digoal
PostgreSQL中文社区发起人之一,生态常委
digoal照片.jpg

直播地址:

扫码入群 限时免费看直播
12月27日直播活码.jpeg

直播时间:

2019 年 12 月 27 日 周五 晚上 8:30

直播海报

分享海报到朋友圈可领取回放免费劵
领取方式:入群联系sf小姐姐,提供朋友圈截图即可
PostgreSQL 12 海报.png


戳这里查看:更多思否编程课程
扫码关注思否编程公众号
思否编程是由中国最大的新一代开发者社区 SegmentFault 孵化的在线编程培训平台,通过提升开发者 IT 职业技能,帮助开发者获得成功。

默认标题_横版配图_2019.09.17 (4).png
cxuan / 2019-12-23 4 years ago / 未收藏/ SegmentFault 最新的文章发送到 kindle
如果只是为了开发 Kafka 应用程序,或者只是在生产环境使用 Kafka,那么了解 Kafka 的内部工作原理不是必须的。不过,了解 Kafka 的内部工作原理有助于理解 Kafka 的行为,也利用快速诊断问题。下面我们来探讨一下这三个问题
  • Kafka 是如何进行复制的
  • Kafka 是如何处理来自生产者和消费者的请求的
  • Kafka 的存储细节是怎样的
如果感兴趣的话,就请花费你一些时间,耐心看完这篇文章。

集群成员间的关系

我们知道,Kafka 是运行在 ZooKeeper 之上的,因为 ZooKeeper 是以集群形式出现的,所以 Kafka 也可以以集群形式出现。这也就涉及到多个生产者和多个消费者如何协调的问题,这个维护集群间的关系也是由 ZooKeeper 来完成的。如果你看过我之前的文章(真的,关于 Kafka 入门看这一篇就够了),你应该会知道,Kafka 集群间会有多个 主机(broker),每个 broker 都会有一个 broker.id,每个 broker.id 都有一个唯一的标识符用来区分,这个标识符可以在配置文件里手动指定,也可以自动生成。
Kafka 可以通过 broker.id.generation.enable 和 reserved.broker.max.id 来配合生成新的 broker.id。broker.id.generation.enable参数是用来配置是否开启自动生成 broker.id 的功能,默认情况下为true,即开启此功能。自动生成的broker.id有一个默认值,默认值为1000,也就是说默认情况下自动生成的 broker.id 从1001开始。

Kafka 在启动时会在 ZooKeeper 中 /brokers/ids 路径下注册一个与当前 broker 的 id 相同的临时节点。Kafka 的健康状态检查就依赖于此节点。当有 broker 加入集群或者退出集群时,这些组件就会获得通知。
  • 如果你要启动另外一个具有相同 ID 的 broker,那么就会得到一个错误 —— 新的 broker 会试着进行注册,但不会成功,因为 ZooKeeper 里面已经有一个相同 ID 的 broker。
  • 在 broker 停机、出现分区或者长时间垃圾回收停顿时,broker 会从 ZooKeeper 上断开连接,此时 broker 在启动时创建的临时节点会从 ZooKeeper 中移除。监听 broker 列表的 Kafka 组件会被告知该 broker 已移除。
  • 在关闭 broker 时,它对应的节点也会消失,不过它的 ID 会继续存在其他数据结构中,例如主题的副本列表中,副本列表复制我们下面再说。在完全关闭一个 broker 之后,如果使用相同的 ID 启动另一个全新的 broker,它会立刻加入集群,并拥有一个与旧 broker 相同的分区和主题。

Broker Controller 的作用

我们之前在讲 Kafka Rebalance 重平衡的时候,提过一个群组协调器,负责协调群组间的关系,那么 broker 之间也有一个控制器组件(Controller),它是 Kafka 的核心组件。它的主要作用是在 ZooKeeper 的帮助下管理和协调整个 Kafka 集群,集群中的每个 broker 都可以称为 controller,但是在 Kafka 集群启动后,只有一个 broker 会成为 Controller 。既然 Kafka 集群是依赖于 ZooKeeper 集群的,所以有必要先介绍一下 ZooKeeper 是什么,可以参考作者的这一篇文章(ZooKeeper不仅仅是注册中心,你还知道有哪些?)详细了解,在这里就简单提一下 znode 节点的问题。
ZooKeeper 的数据是保存在节点上的,每个节点也被称为znode,znode 节点是一种树形的文件结构,它很像 Linux 操作系统的文件路径,ZooKeeper 的根节点是 /
image.png
znode 根据数据的持久化方式可分为临时节点和持久性节点。持久性节点不会因为 ZooKeeper 状态的变化而消失,但是临时节点会随着 ZooKeeper 的重启而自动消失。
znode 节点有一个 Watcher 机制:当数据发生变化的时候, ZooKeeper 会产生一个 Watcher 事件,并且会发送到客户端。Watcher 监听机制是 Zookeeper 中非常重要的特性,我们基于 Zookeeper 上创建的节点,可以对这些节点绑定监听事件,比如可以监听节点数据变更、节点删除、子节点状态变更等事件,通过这个事件机制,可以基于 ZooKeeper 实现分布式锁、集群管理等功能。

控制器的选举

Kafka 当前选举控制器的规则是:Kafka 集群中第一个启动的 broker 通过在 ZooKeeper 里创建一个临时节点 /controller 让自己成为 controller 控制器。其他 broker 在启动时也会尝试创建这个节点,但是由于这个节点已存在,所以后面想要创建 /controller 节点时就会收到一个 节点已存在 的异常。然后其他 broker 会在这个控制器上注册一个 ZooKeeper 的 watch 对象,/controller 节点发生变化时,其他 broker 就会收到节点变更通知。这种方式可以确保只有一个控制器存在。那么只有单独的节点一定是有个问题的,那就是单点问题
image.png
如果控制器关闭或者与 ZooKeeper 断开链接,ZooKeeper 上的临时节点就会消失。集群中的其他节点收到 watch 对象发送控制器下线的消息后,其他 broker 节点都会尝试让自己去成为新的控制器。其他节点的创建规则和第一个节点的创建原则一致,都是第一个在 ZooKeeper 里成功创建控制器节点的 broker 会成为新的控制器,那么其他节点就会收到节点已存在的异常,然后在新的控制器节点上再次创建 watch 对象进行监听。
image.png

控制器的作用

那么说了这么多,控制是什么呢?控制器的作用是什么呢?或者说控制器的这么一个组件被设计用来干什么?别着急,接下来我们就要说一说。
Kafka 被设计为一种模拟状态机的多线程控制器,它可以作用有下面这几点
  • 控制器相当于部门(集群)中的部门经理(broker controller),用于管理部门中的部门成员(broker)
  • 控制器是所有 broker 的一个监视器,用于监控 broker 的上线和下线
  • 在 broker 宕机后,控制器能够选举新的分区 Leader
  • 控制器能够和 broker 新选取的 Leader 发送消息
再细分一下可以具体分为如下 5 点
  • 主题管理 : Kafka Controller 可以帮助我们完成对 Kafka 主题创建、删除和增加分区的操作,简而言之就是对分区拥有最高行使权。
换句话说,当我们执行kafka-topics 脚本时,大部分的后台工作都是控制器来完成的。
  • 分区重分配: 分区重分配主要是指,kafka-reassign-partitions 脚本提供的对已有主题分区进行细粒度的分配功能。这部分功能也是控制器实现的。
  • Prefered 领导者选举 : Preferred 领导者选举主要是 Kafka 为了避免部分 Broker 负载过重而提供的一种换 Leader 的方案。
  • 集群成员管理: 主要管理 新增 broker、broker 关闭、broker 宕机
  • 数据服务: 控制器的最后一大类工作,就是向其他 broker 提供数据服务。控制器上保存了最全的集群元数据信息,其他所有 broker 会定期接收控制器发来的元数据更新请求,从而更新其内存中的缓存数据。这些数据我们会在下面讨论
当控制器发现一个 broker 离开集群(通过观察相关 ZooKeeper 路径),控制器会收到消息:这个 broker 所管理的那些分区需要一个新的 Leader。控制器会依次遍历每个分区,确定谁能够作为新的 Leader,然后向所有包含新 Leader 或现有 Follower 的分区发送消息,该请求消息包含谁是新的 Leader 以及谁是 Follower 的信息。随后,新的 Leader 开始处理来自生产者和消费者的请求,Follower 用于从新的 Leader 那里进行复制。
这就很像外包公司的一个部门,这个部门就是专门出差的,每个人在不同的地方办公,但是中央总部有一个部门经理,现在部门经理突然离职了。公司不打算外聘人员,决定从部门内部选一个能力强的人当领导,然后当上领导的人需要向自己的组员发送消息,这条消息就是任命消息和明确他管理了哪些人,大家都知道了,然后再各自给部门干活。
当控制器发现一个 broker 加入集群时,它会使用 broker ID 来检查新加入的 broker 是否包含现有分区的副本。如果有控制器就会把消息发送给新加入的 broker 和 现有的 broker。
上面这块关于分区复制的内容我们接下来会说到。

broker controller 数据存储

上面我们介绍到 broker controller 会提供数据服务,用于保存大量的 Kafka 集群数据。如下图
image.png
可以对上面保存信息归类,主要分为三类
  • broker 上的所有信息,包括 broker 中的所有分区,broker 所有分区副本,当前都有哪些运行中的 broker,哪些正在关闭中的 broker 。
  • 所有主题信息,包括具体的分区信息,比如领导者副本是谁,ISR 集合中有哪些副本等。
  • 所有涉及运维任务的分区。包括当前正在进行 Preferred 领导者选举以及分区重分配的分区列表。
Kafka 是离不开 ZooKeeper的,所以这些数据信息在 ZooKeeper 中也保存了一份。每当控制器初始化时,它都会从 ZooKeeper 上读取对应的元数据并填充到自己的缓存中。

broker controller 故障转移

我们在前面说过,第一个在 ZooKeeper 中的 /brokers/ids下创建节点的 broker 作为 broker controller,也就是说 broker controller 只有一个,那么必然会存在单点失效问题。kafka 为考虑到这种情况提供了故障转移功能,也就是 Fail Over。如下图
image.png
最一开始,broker1 会抢先注册成功成为 controller,然后由于网络抖动或者其他原因致使 broker1 掉线,ZooKeeper 通过 Watch 机制觉察到 broker1 的掉线,之后所有存活的 brokers 开始竞争成为 controller,这时 broker3 抢先注册成功,此时 ZooKeeper 存储的 controller 信息由 broker1 -> broker3,之后,broker3 会从 ZooKeeper 中读取元数据信息,并初始化到自己的缓存中。
注意:ZooKeeper 中存储的不是缓存信息,broker 中存储的才是缓存信息。

broker controller 存在的问题

在 Kafka 0.11 版本之前,控制器的设计是相当繁琐的。我们上面提到过一句话:Kafka controller 被设计为一种模拟状态机的多线程控制器,这种设计其实是存在一些问题的
  • controller 状态的更改由不同的监听器并罚执行,因此需要进行很复杂的同步,并且容易出错而且难以调试。
  • 状态传播不同步,broker 可能在时间不确定的情况下出现多种状态,这会导致不必要的额外的数据丢失
  • controller 控制器还会为主题删除创建额外的 I/O 线程,导致性能损耗
  • controller 的多线程设计还会访问共享数据,我们知道,多线程访问共享数据是线程同步最麻烦的地方,为了保护数据安全性,控制器不得不在代码中大量使用ReentrantLock 同步机制,这就进一步拖慢了整个控制器的处理速度。

broker controller 内部设计原理

在 Kafka 0.11 之后,Kafka controller 采用了新的设计,把多线程的方案改成了单线程加事件队列的方案。如下图所示
image.png
主要所做的改变有下面这几点
第一个改进是增加了一个 Event Executor Thread,事件执行线程,从图中可以看出,不管是 Event Queue 事件队列还是 Controller context 控制器上下文都会交给事件执行线程进行处理。将原来执行的操作全部建模成一个个独立的事件,发送到专属的事件队列中,供此线程消费。
第二个改进是将之前同步的 ZooKeeper 全部改为异步操作。ZooKeeper API 提供了两种读写的方式:同步和异步。之前控制器操作 ZooKeeper 都是采用的同步方式,这次把同步方式改为异步,据测试,效率提升了10倍。
第三个改进是根据优先级处理请求,之前的设计是 broker 会公平性的处理所有 controller 发送的请求。什么意思呢?公平性难道还不好吗?在某些情况下是的,比如 broker 在排队处理 produce 请求,这时候 controller 发出了一个 StopReplica 的请求,你会怎么办?还在继续处理 produce 请求吗?这个 produce 请求还有用吗?此时最合理的处理顺序应该是,赋予 StopReplica 请求更高的优先级,使它能够得到抢占式的处理。

副本机制

复制功能是 Kafka 架构的核心功能,在 Kafka 文档里面 Kafka 把自己描述为 一个分布式的、可分区的、可复制的提交日志服务。复制之所以这么关键,是因为消息的持久存储非常重要,这能够保证在主节点宕机后依旧能够保证 Kafka 高可用。副本机制也可以称为备份机制(Replication),通常指分布式系统在多台网络交互的机器上保存有相同的数据备份/拷贝。
Kafka 使用主题来组织数据,每个主题又被分为若干个分区,分区会部署在一到多个 broker 上,每个分区都会有多个副本,所以副本也会被保存在 broker 上,每个 broker 可能会保存成千上万个副本。下图是一个副本复制示意图
image.png
如上图所示,为了简单我只画出了两个 broker ,每个 broker 指保存了一个 Topic 的消息,在 broker1 中分区0 是Leader,它负责进行分区的复制工作,把 broker1 中的分区0复制一个副本到 broker2 的主题 A 的分区0。同理,主题 A 的分区1也是一样的道理。
副本类型分为两种:一种是 Leader(领导者) 副本,一种是Follower(跟随者)副本。

Leader 副本

Kafka 在创建分区的时候都要选举一个副本,这个选举出来的副本就是 Leader 领导者副本。

Follower 副本

除了 Leader 副本以外的副本统称为 Follower 副本,Follower 不对外提供服务。下面是 Leader 副本的工作方式
image.png
这幅图需要注意以下几点
  • Kafka 中,Follower 副本也就是追随者副本是不对外提供服务的。这就是说,任何一个追随者副本都不能响应消费者和生产者的请求。所有的请求都是由领导者副本来处理。或者说,所有的请求都必须发送到 Leader 副本所在的 broker 中,Follower 副本只是用做数据拉取,采用异步拉取的方式,并写入到自己的提交日志中,从而实现与 Leader 的同步
  • 当 Leader 副本所在的 broker 宕机后,Kafka 依托于 ZooKeeper 提供的监控功能能够实时感知到,并开启新一轮的选举,从追随者副本中选一个作为 Leader。如果宕机的 broker 重启完成后,该分区的副本会作为 Follower 重新加入。
首领的另一个任务是搞清楚哪个跟随者的状态与自己是一致的。跟随者为了保证与领导者的状态一致,在有新消息到达之前先尝试从领导者那里复制消息。为了与领导者保持一致,跟随者向领导者发起获取数据的请求,这种请求与消费者为了读取消息而发送的信息是一样的。
跟随者向领导者发送消息的过程是这样的,先请求消息1,然后再接收到消息1,在时候到请求1之后,发送请求2,在收到领导者给发送给跟随者之前,跟随者是不会继续发送消息的。这个过程如下
image.png
跟随者副本在收到响应消息前,是不会继续发送消息,这一点很重要。通过查看每个跟随者请求的最新偏移量,首领就会知道每个跟随者复制的进度。如果跟随者在10s 内没有请求任何消息,或者虽然跟随者已经发送请求,但是在10s 内没有收到消息,就会被认为是不同步的。如果一个副本没有与领导者同步,那么在领导者掉线后,这个副本将不会称为领导者,因为这个副本的消息不是全部的。
与之相反的,如果跟随者同步的消息和领导者副本的消息一致,那么这个跟随者副本又被称为同步的副本。也就是说,如果领导者掉线,那么只有同步的副本能够称为领导者。
关于副本机制我们说了这么多,那么副本机制的好处是什么呢?
  • 能够立刻看到写入的消息,就是你使用生产者 API 成功向分区写入消息后,马上使用消费者就能读取刚才写入的消息
  • 能够实现消息的幂等性,啥意思呢?就是对于生产者产生的消息,在消费者进行消费的时候,它每次都会看到消息存在,并不会存在消息不存在的情况

同步复制和异步复制

我在学习副本机制的时候,有个疑问,既然领导者副本和跟随者副本是发送 - 等待机制的,这是一种同步的复制方式,那么为什么说跟随者副本同步领导者副本的时候是一种异步操作呢?
我认为是这样的,跟随者副本在同步领导者副本后会把消息保存在本地 log 中,这个时候跟随者会给领导者副本一个响应消息,告诉领导者自己已经保存成功了,同步复制的领导者会等待所有的跟随者副本都写入成功后,再返回给 producer 写入成功的消息。而异步复制是领导者副本不需要关心跟随者副本是否写入成功,只要领导者副本自己把消息保存到本地 log ,就会返回给 producer 写入成功的消息。下面是同步复制和异步复制的过程
同步复制
  • producer 通知 ZooKeeper 识别领导者
  • producer 向领导者写入消息
  • 领导者收到消息后会把消息写入到本地 log
  • 跟随者会从领导者那里拉取消息
  • 跟随者向本地写入 log
  • 跟随者向领导者发送写入成功的消息
  • 领导者会收到所有的跟随者发送的消息
  • 领导者向 producer 发送写入成功的消息
异步复制
和同步复制的区别在于,领导者在写入本地log之后,直接向客户端发送写入成功消息,不需要等待所有跟随者复制完成。

ISR

Kafka动态维护了一个同步状态的副本的集合(a set of In-Sync Replicas),简称ISR,ISR 也是一个很重要的概念,我们之前说过,追随者副本不提供服务,只是定期的异步拉取领导者副本的数据而已,拉取这个操作就相当于是复制,ctrl-c + ctrl-v大家肯定用的熟。那么是不是说 ISR 集合中的副本消息的数量都会与领导者副本消息数量一样呢?那也不一定,判断的依据是 broker 中参数 replica.lag.time.max.ms 的值,这个参数的含义就是跟随者副本能够落后领导者副本最长的时间间隔。
replica.lag.time.max.ms 参数默认的时间是 10秒,如果跟随者副本落后领导者副本的时间不超过 10秒,那么 Kafka 就认为领导者和跟随者是同步的。即使此时跟随者副本中存储的消息要小于领导者副本。如果跟随者副本要落后于领导者副本 10秒以上的话,跟随者副本就会从 ISR 被剔除。倘若该副本后面慢慢地追上了领导者的进度,那么它是能够重新被加回 ISR 的。这也表明,ISR 是一个动态调整的集合,而非静态不变的。

Unclean 领导者选举

既然 ISR 是可以动态调整的,那么必然会出现 ISR 集合中为空的情况,由于领导者副本是一定出现在 ISR 集合中的,那么 ISR 集合为空必然说明领导者副本也挂了,所以此时 Kafka 需要重新选举一个新的领导者,那么该如何选举呢?现在你需要转变一下思路,我们上面说 ISR 集合中一定是与领导者同步的副本,那么不再 ISR 集合中的副本一定是不与领导者同步的副本了,也就是不再 ISR 列表中的跟随者副本会丢失一些消息。如果你开启 broker 端参数 unclean.leader.election.enable的话,下一个领导者就会在这些非同步的副本中选举。这种选举也叫做Unclean 领导者选举
如果你接触过分布式项目的话你一定知道 CAP 理论,那么这种 Unclean 领导者选举其实是牺牲了数据一致性,保证了 Kafka 的高可用性。
你可以根据你的实际业务场景决定是否开启 Unclean 领导者选举,一般不建议开启这个参数,因为数据的一致性要比可用性重要的多。

Kafka 请求处理流程

broker 的大部分工作是处理客户端、分区副本和控制器发送给分区领导者的请求。这种请求一般都是请求/响应式的,我猜测你接触最早的请求/响应的方式应该就是 HTTP 请求了。事实上,HTTP 请求可以是同步可以是异步的。一般正常的 HTTP 请求都是同步的,同步方式最大的一个特点是提交请求->等待服务器处理->处理完毕返回 这个期间客户端浏览器不能做任何事。而异步方式最大的特点是 请求通过事件触发->服务器处理(这是浏览器仍然可以作其他事情)-> 处理完毕
那么我也可以说同步请求就是顺序处理的,而异步请求的执行方式则不确定,因为异步需要创建多个执行线程,而每个线程的执行顺序不同。
这里需要注意一点,我们只是使用 HTTP 请求来举例子,而 Kafka 采用的是 TCP 基于 Socket 的方式进行通讯
那么这两种方式有什么缺点呢?
我相信聪明的你应该能马上想到,同步的方式最大的缺点就是吞吐量太差,资源利用率极低,由于只能顺序处理请求,因此,每个请求都必须等待前一个请求处理完毕才能得到处理。这种方式只适用于请求发送非常不频繁的系统
异步的方式的缺点就是为每个请求都创建线程的做法开销极大,在某些场景下甚至会压垮整个服务。

响应式模型

说了这么半天,Kafka 采用同步还是异步的呢?都不是,Kafka 采用的是一种 响应式(Reactor)模型,那么什么是响应式模型呢?简单的说,Reactor 模式是事件驱动架构的一种实现方式,特别适合应用于处理多个客户端并发向服务器端发送请求的场景,如下图所示
image.png
Kafka 的 broker 端有个 SocketServer组件,类似于处理器,SocketServer 是基于 TCP 的 Socket 连接的,它用于接受客户端请求,所有的请求消息都包含一个消息头,消息头中都包含如下信息
  • Request type (也就是 API Key)
  • Request version(broker 可以处理不同版本的客户端请求,并根据客户版本做出不同的响应)
  • Correlation ID --- 一个具有唯一性的数字,用于标示请求消息,同时也会出现在响应消息和错误日志中(用于诊断问题)
  • Client ID --- 用于标示发送请求的客户端
broker 会在它所监听的每一个端口上运行一个 Acceptor 线程,这个线程会创建一个连接,并把它交给 Processor(网络线程池), Processor 的数量可以使用 num.network.threads 进行配置,其默认值是3,表示每台 broker 启动时会创建3个线程,专门处理客户端发送的请求。
Acceptor 线程会采用轮询的方式将入栈请求公平的发送至网络线程池中,因此,在实际使用过程中,这些线程通常具有相同的机率被分配到待处理请求队列中,然后从响应队列获取响应消息,把它们发送给客户端。Processor 网络线程池中的请求 - 响应的处理还是比较复杂的,下面是网络线程池中的处理流程图
image.png
Processor 网络线程池接收到客户和其他 broker 发送来的消息后,网络线程池会把消息放到请求队列中,注意这个是共享请求队列,因为网络线程池是多线程机制的,所以请求队列的消息是多线程共享的区域,然后由 IO 线程池进行处理,根据消息的种类判断做何处理,比如 PRODUCE 请求,就会将消息写入到 log 日志中,如果是FETCH请求,则从磁盘或者页缓存中读取消息。也就是说,IO线程池是真正做判断,处理请求的一个组件。在IO 线程池处理完毕后,就会判断是放入响应队列中还是 Purgatory 中,Purgatory 是什么我们下面再说,现在先说一下响应队列,响应队列是每个线程所独有的,因为响应式模型中不会关心请求发往何处,因此把响应回传的事情就交给每个线程了,所以也就不必共享了。
注意:IO 线程池可以通过 broker 端参数 num.io.threads 来配置,默认的线程数是8,表示每台 broker 启动后自动创建 8 个IO 处理线程。

请求类型

下面是几种常见的请求类型
生产请求
我在 真的,关于 Kafka 入门看这一篇就够了 文章中提到过 acks 这个配置项的含义
简单来讲就是不同的配置对写入成功的界定是不同的,如果 acks = 1,那么只要领导者收到消息就表示写入成功,如果acks = 0,表示只要领导者发送消息就表示写入成功,根本不用考虑返回值的影响。如果 acks = all,就表示领导者需要收到所有副本的消息后才表示写入成功。
在消息被写入分区的首领后,如果 acks 配置的值是 all,那么这些请求会被保存在 炼狱(Purgatory)的缓冲区中,直到领导者副本发现跟随者副本都复制了消息,响应才会发送给客户端。
获取请求
broker 获取请求的方式与处理生产请求的方式类似,客户端发送请求,向 broker 请求主题分区中特定偏移量的消息,如果偏移量存在,Kafka 会采用 零复制 技术向客户端发送消息,Kafka 会直接把消息从文件中发送到网络通道中,而不需要经过任何的缓冲区,从而获得更好的性能。
客户端可以设置获取请求数据的上限和下限,上限指的是客户端为接受足够消息分配的内存空间,这个限制比较重要,如果上限太大的话,很有可能直接耗尽客户端内存。下限可以理解为攒足了数据包再发送的意思,这就相当于项目经理给程序员分配了 10 个bug,程序员每次改一个 bug 就会向项目经理汇报一下,有的时候改好了有的时候可能还没改好,这样就增加了沟通成本和时间成本,所以下限值得就是程序员你改完10个 bug 再向我汇报!!!如下图所示
image.png
如图你可以看到,在拉取消息 ---> 消息 之间是有一个等待消息积累这么一个过程的,这个消息积累你可以把它想象成超时时间,不过超时会跑出异常,消息积累超时后会响应回执。延迟时间可以通过 replica.lag.time.max.ms 来配置,它指定了副本在复制消息时可被允许的最大延迟时间。
元数据请求
生产请求和响应请求都必须发送给领导者副本,如果 broker 收到一个针对某个特定分区的请求,而该请求的首领在另外一个 broker 中,那么发送请求的客户端会收到非分区首领的错误响应;如果针对某个分区的请求被发送到不含有领导者的 broker 上,也会出现同样的错误。Kafka 客户端需要把请求和响应发送到正确的 broker 上。这不是废话么?我怎么知道要往哪发送?
事实上,客户端会使用一种 元数据请求 ,这种请求会包含客户端感兴趣的主题列表,服务端的响应消息指明了主题的分区,领导者副本和跟随者副本。元数据请求可以发送给任意一个 broker,因为所有的 broker 都会缓存这些信息。
一般情况下,客户端会把这些信息缓存,并直接向目标 broker 发送生产请求和相应请求,这些缓存需要隔一段时间就进行刷新,使用metadata.max.age.ms 参数来配置,从而知道元数据是否发生了变更。比如,新的 broker 加入后,会触发重平衡,部分副本会移动到新的 broker 上。这时候,如果客户端收到 不是首领的错误,客户端在发送请求之前刷新元数据缓存。

Kafka 重平衡流程

我在 真的,关于 Kafka 入门看这一篇就够了 中关于消费者描述的时候大致说了一下消费者组和重平衡之间的关系,实际上,归纳为一点就是让组内所有的消费者实例就消费哪些主题分区达成一致。
我们知道,一个消费者组中是要有一个群组协调者(Coordinator)的,而重平衡的流程就是由 Coordinator 的帮助下来完成的。
这里需要先声明一下重平衡发生的条件
  • 消费者订阅的任何主题发生变化
  • 消费者数量发生变化
  • 分区数量发生变化
  • 如果你订阅了一个还尚未创建的主题,那么重平衡在该主题创建时发生。如果你订阅的主题发生删除那么也会发生重平衡
  • 消费者被群组协调器认为是 DEAD 状态,这可能是由于消费者崩溃或者长时间处于运行状态下发生的,这意味着在配置合理时间的范围内,消费者没有向群组协调器发送任何心跳,这也会导致重平衡的发生。

在了解重平衡之前,你需要知道这两个角色

群组协调器(Coordinator):群组协调器是一个能够从消费者群组中收到所有消费者发送心跳消息的 broker。在最早期的版本中,元数据信息是保存在 ZooKeeper 中的,但是目前元数据信息存储到了 broker 中。每个消费者组都应该和群组中的群组协调器同步。当所有的决策要在应用程序节点中进行时,群组协调器可以满足 JoinGroup 请求并提供有关消费者组的元数据信息,例如分配和偏移量。群组协调器还有权知道所有消费者的心跳,消费者群组中还有一个角色就是领导者,注意把它和领导者副本和 kafka controller 进行区分。领导者是群组中负责决策的角色,所以如果领导者掉线了,群组协调器有权把所有消费者踢出组。因此,消费者群组的一个很重要的行为是选举领导者,并与协调器读取和写入有关分配和分区的元数据信息。
消费者领导者: 每个消费者群组中都有一个领导者。如果消费者停止发送心跳了,协调者会触发重平衡。

在了解重平衡之前,你需要知道状态机是什么

Kafka 设计了一套消费者组状态机(State Machine) ,来帮助协调者完成整个重平衡流程。消费者状态机主要有五种状态它们分别是 Empty、Dead、PreparingRebalance、CompletingRebalance 和 Stable
image.png
了解了这些状态的含义之后,下面我们用几条路径来表示一下消费者状态的轮转

消费者组一开始处于 Empty 状态,当重平衡开启后,它会被置于 PreparingRebalance 状态等待新消费者的加入,一旦有新的消费者加入后,消费者群组就会处于 CompletingRebalance 状态等待分配,只要有新的消费者加入群组或者离开,就会触发重平衡,消费者的状态处于 PreparingRebalance 状态。等待分配机制指定好后完成分配,那么它的流程图是这样的
image.png
在上图的基础上,当消费者群组都到达 Stable 状态后,一旦有新的消费者加入/离开/心跳过期,那么触发重平衡,消费者群组的状态重新处于 PreparingRebalance 状态。那么它的流程图是这样的。
image.png
在上图的基础上,消费者群组处于 PreparingRebalance 状态后,很不幸,没人玩儿了,所有消费者都离开了,这时候还可能会保留有消费者消费的位移数据,一旦位移数据过期或者被刷新,那么消费者群组就处于 Dead 状态了。它的流程图是这样的
image.png
在上图的基础上,我们分析了消费者的重平衡,在 PreparingRebalance或者 CompletingRebalance 或者 Stable 任意一种状态下发生位移主题分区 Leader 发生变更,群组会直接处于 Dead 状态,它的所有路径如下
image.png
这里面需要注意两点:一般出现 Required xx expired offsets in xxx milliseconds 就表明Kafka 很可能就把该组的位移数据删除了
只有 Empty 状态下的组,才会执行过期位移删除的操作。

重平衡流程

上面我们了解到了消费者群组状态的转化过程,下面我们真正开始介绍 Rebalance 的过程。重平衡过程可以从两个方面去看:消费者端和协调者端,首先我们先看一下消费者端

从消费者看重平衡

从消费者看重平衡有两个步骤:分别是 消费者加入组等待领导者分配方案。这两个步骤后分别对应的请求是 JoinGroupSyncGroup
新的消费者加入群组时,这个消费者会向协调器发送 JoinGroup 请求。在该请求中,每个消费者成员都需要将自己消费的 topic 进行提交,我们上面描述群组协调器中说过,这么做的目的就是为了让协调器收集足够的元数据信息,来选取消费者组的领导者。通常情况下,第一个发送 JoinGroup 请求的消费者会自动称为领导者。领导者的任务是收集所有成员的订阅信息,然后根据这些信息,制定具体的分区消费分配方案。如图
image.png
在所有的消费者都加入进来并把元数据信息提交给领导者后,领导者做出分配方案并发送 SyncGroup 请求给协调者,协调者负责下发群组中的消费策略。下图描述了 SyncGroup 请求的过程
image.png
当所有成员都成功接收到分配方案后,消费者组进入到 Stable 状态,即开始正常的消费工作。

从协调者来看重平衡

从协调者角度来看重平衡主要有下面这几种触发条件,
  • 新成员加入组
  • 组成员主动离开
  • 组成员崩溃离开
  • 组成员提交位移
我们分别来描述一下,先从新成员加入组开始

新成员加入组

我们讨论的场景消费者集群状态处于Stable 等待分配的过程,这时候如果有新的成员加入组的话,重平衡的过程
image.png
从这个角度来看,协调者的过程和消费者类似,只是刚刚从消费者的角度去看,现在从领导者的角度去看

组成员离开

组成员离开消费者群组指的是消费者实例调用 close() 方法主动通知协调者它要退出。这里又会有一个新的请求出现 LeaveGroup()请求 。如下图所示
image.png

组成员崩溃

组成员崩溃是指消费者实例出现严重故障,宕机或者一段时间未响应,协调者接收不到消费者的心跳,就会被认为是组成员崩溃,崩溃离组是被动的,协调者通常需要等待一段时间才能感知到,这段时间一般是由消费者端参数 session.timeout.ms 控制的。如下图所示
image.png

重平衡时提交位移

这个过程我们就不再用图形来表示了,大致描述一下就是 消费者发送 JoinGroup 请求后,群组中的消费者必须在指定的时间范围内提交各自的位移,然后再开启正常的 JoinGroup/SyncGroup 请求发送。
文章参考:
《Kafka 权威指南》
https://blog.csdn.net/u013256...
https://learning.oreilly.com/...
https://www.cnblogs.com/kevin...
https://www.cnblogs.com/huxi2...
《极客时间-Kafka核心技术与实战》
https://cwiki.apache.org/conf...
https://cwiki.apache.org/conf...
kafka 分区和副本以及kafaka 执行流程,以及消息的高可用
Http中的同步请求和异步请求
Reactor模式详解
https://kafka.apache.org/docu...
https://www.linkedin.com/puls...
https://cwiki.apache.org/conf...
羽飞 / 2019-12-23 4 years ago / 未收藏/ SegmentFault 最新的文章发送到 kindle
上周,JavaScript 引擎「V8」的开发团队在该项目官方网站上正式宣布推出最新的 8.0 版本。这次更新的重点主要集中在错误修复及性能改善上,正式的版本将在数周后随着谷歌 Chrome 80 稳定版一起发布。
V8 是谷歌公司推出的开源高性能 JavaScript 引擎,主要用于提升 Web 浏览器内部 JavaScript 脚本执行的性能。V8 通过 C++ 语言编写,主要用在 Chrome 浏览器以及 Node.js 上,实现了对 ECMAScript 与 WebAssembly 的支持,可运行于 Windows 7、macOS 10.12 以及使用 x64、IA-32、ARM、MIPS 处理器的 Linux 系统,或更新版本的操作系统环境下。V8 既可以单独运行,也能够内嵌到任何基于 C++ 的应用中。V8 项目开发至今已拥有超过 11 年的历史。

性能

指针压缩

V8 引擎的这次更新,对内存占用量与速度都进行了优化。V8 堆包含了很多东西,例如浮点值、字符串字符、编译代码以及标定值,开发团队发现这些标记值占据了堆的很大一部分空间。
标定值与系统指针一样大,32 位宽度对应 32 位架构,64 位宽度对应 64 位架构。当开发团队对比 32 位版本与 64 位版本时发现,每个标记值使用了两倍的堆内存。
因为高位可以由低位合成,所以只需要将唯一的低位存储到堆中就能实现降低内存的占用量,平均可节省 40% 堆内存,这一方法被称为「指针压缩」。
V8 8.0.png
通常情况下,在优化内存占用的同时也会牺牲部分性能,但经过这一改进之后,在 V8 及其垃圾收集器中却出现了真实网站的性能提升。

优化高阶内建

在优化高阶内建上,新版本解除了 TurboFan 优化流程中的一处限制,这处限制使高阶内建无法得到优化。
const charCodeAt = Function.prototype.call.bind(String.prototype.charCodeAt);

charCodeAt(string, 8);
到目前为止,对 charCodeAt 的调用对 TurboFan 来说是完全不透明的,从而引发了对用户定义函数通用调用。通过这一改变,现在可以识别出实际上是在调用内置 String.prototype.charCodeAt 函数,从而能够触发 TurboFan 库中的进一步优化来改善对内建的调用,进而获得与以下相同的性能:
string.charCodeAt(8);
这一变化也会影响到其他一些内建,比如 Function.prototype.apply、Reflect.apply,以及很多其他的高阶数组内建。

JavaScript

新版本在 JavaScript 方面出现了两个新特性 ── 可选链和 null 判断合并。

可选链

在编写属性访问链时,程序员经常需要检查中间值是否为空。没有经过错误检查的链可能会扔出,而经过了明确的错误检查的链是冗长的,并且会产生不必要的结果,即检查所有的真实值,而不是只检查非空值。
// Error prone-version, could throw.const nameLength = db.user.name.length;// Less error-prone, but harder to read.let nameLength;if (db && db.user && db.user.name)  nameLength = db.user.name.length;
可选链(?.)允许程序员编写更精炼、鲁棒性更强的属性访问链,检查中间值是否为空。如果中间值为空,则整个表达式的计算结果为未定义的。
// Still checks for errors and is much more readable.const nameLength = db?.user?.name?.length;
除了静态属性访问外,动态属性访问和调用也能得到支持。

null 合并

null 合并操作符 ?? 是一个新的用于处理默认值的短路二进制操作符。目前,默认值有时由逻辑 || 操作符处理,如下所示:
function Component(props) {  const enable = props.enabled || true;  // …}
对 || 的使用,并不适合计算默认值,因为当 a 为非真时 a || b 的结果为 b。如果 props.enabled 明确被设置为假,那么 enable 仍然为真。
通过 null 合并操作符,当 a 为空时,a ?? b 的结果为 b,否则结果为 a。这是理想的默认值行为,并使用 ?? 修正了之前提到的错误。
function Component(props) {  const enable = props.enabled ?? true;  // …}
null 合并操作符与可选链是相伴而生的特性,可协同工作。当没有任何 props 参数传入时,它们可以对示例进行进一步修改以作为应对。
function Component(props) {  const enable = props?.enabled ?? true;  // …}

V8 应用程序接口

请通过「git log branch-heads/7.9.. ..branch-heads/8.0 include/v8.h」指令来获得接口变化列表。
开发人员目前可通过指令「git checkout -b 8.0 -t branch-heads/8.0」来体验 V8 引擎 8.0 版本中的新特性。

相关项目地址:
高性能 JavaScript 引擎 V8 官方网站
高性能 JavaScript 引擎 V8 GitHub 地址

clipboard.png
日拱一兵 / 2019-12-23 4 years ago / 未收藏/ SegmentFault 最新的文章发送到 kindle

前言

上一篇文章 如何妙用 Spring 数据绑定?灵魂追问 环节留下了一个有关 equals 和 hashcode 问题 。基础面试经常会碰到与之相关的问题,这不是一个复杂的问题,但很多朋友都苦于说明他们二者的关系和约束,于是写本文做单独说明,本篇文章将循序渐进 ( 通过举例,让记忆与理解更轻松 ) 说明这些让你有些苦恼的问题,Let's go .......

面试问题

1. Java 里面有了 == 运算符,为什么还需要 equals ?

== 比较的是对象地址,equals 比较的是对象值
先来看一看 Object 类中 equals 方法:
public boolean equals(Object obj) {
    return (this == obj);
}
我们看到 equals 方法同样是通过 == 比较对象地址,并没有帮我们比较值。Java 世界中 Object 绝对是"老祖宗" 的存在,== 号我们没办法改变或重写。但 equals 是方法,这就给了我们重写 equals 方法的可能,让我们实现其对值的比较:
@Override
public boolean equals(Object obj) {
    //重写逻辑
}
新买的电脑,每个电脑都有唯一的序列号,通常情况下,两个一模一样的电脑放在面前,你会说由于序列号不一样,这两个电脑不一样吗?
如果我们要说两个电脑一样,通常是比较其「品牌/尺寸/配置 」(值) ,比如这样:
@Override
public boolean equals(Object obj) {
    return 品牌相等 && 尺寸相等 && 配置相等
}
当遇到如上场景时,我们就需要重写 equals 方法。这就解释了 Java 世界为什么有了 == 还有equals 这个问题了.

2. equals相等 和 hashcode 相等问题

关于二者,你经常会碰到下面的两个问题:
  • 两个对象 equals 相等,那他们 hashCode 相等吗?
  • 两个对象 hashCode 相等,那他们 equals 相等吗?
为了说明上面两个问题的结论,这里举一个不太恰当的例子,只为方便记忆,我们将 equals 比作一个单词的拼写;hashCode 比作一个单词的发音,在相同语境下:
sea / sea 「大海」,两个单词拼写一样,所以 equals 相等,他们读音 /siː/ 也一样,所以 hashCode 就相等,这就回答了第一个问题:

两个对象 equals 相等,那他们 hashCode 一定也相等

sea / see 「大海/看」,两个单词的读音 /siː/ 一样,显然单词是不一样的,这就回答了第二个问题:

两个对象 hashCode 相等,那他们 equals 不一定相等


查看 Object 类的 hashCode 方法:
public native int hashCode();
继续查看该方法的注释,明确写明关于该方法的约束

其实在这个结果的背后,还有的是关于重写 equals 方法的约束

3. 重写 equals 有哪些约束?

关于重写 equals 方法的约束,同样在该方法的注释中写的很清楚了,我在这里再说明一下:

赤橙红绿青蓝紫,七彩以色列;哆来咪发唆拉西, 一曲安哥拉 ,这些规则不是用来背诵的,只是在你需要重写 equals 方法时,打开 JDK 查看该方法,按照准则重写就好

4. 什么时候需要我们重写 hashCode

为了比较值,我们重写 equals 方法,那什么时候又需要重写 hashCode 方法呢?
通常只要我们重写 equals 方法就要重写 hashCode 方法
为什么会有这样的约束呢?按照上面讲的原则,两个对象 equals 相等,那他们的 hashCode 一定也相等。如果我们只重写 equals 方法而不重写 hashCode 方法,看看会发生什么,举个例子来看:
定义学生类,并通过 IDE 只帮我们生成 equals 方法:
public class Student {

    private String name;

    private int age;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age &&
                Objects.equals(name, student.name);
    }
}
编写测试代码:
Student student1 = new Student();
student1.setName("日拱一兵");
student1.setAge(18);

Student student2 = new Student();
student2.setName("日拱一兵");
student2.setAge(18);

System.out.println("student1.equals(student2)的结果是:" + student1.equals(student2));

Set<Student> students = new HashSet<Student>();
students.add(student1);
students.add(student2);
System.out.println("Student Set 集合长度是:" + students.size());

Map<Student, java.lang.String> map = new HashMap<Student, java.lang.String>();
map.put(student1, "student1");
map.put(student2, "student2");
System.out.println("Student Map 集合长度是:" + map.keySet().size());
查看运行结果:
student1.equals(student2)的结果是:true
Student Set 集合长度是:2
Student Map 集合长度是:2
很显然,按照集合 Set 和 Map 加入元素的标准来看,student1 和 student2 是两个对象,因为在调用他们的 put (Set add 方法的背后也是 HashMap 的 put)方法时, 会先判断 hash 值是否相等,这个小伙伴们打开 JDK 自行查看吧
所以我们继续重写 Student 类的 hashCode 方法:
@Override
public int hashCode() {
    return Objects.hash(name, age);
}
重新运行上面的测试,查看结果:
student1.equals(student2)的结果是:true
Student Set 集合长度是:1
Student Map 集合长度是:1
得到我们预期的结果,这也就是为什么通常我们重写 equals 方法为什么最好也重写 hashCode 方法的原因
  • 如果你在使用 Lombok,不知道你是否注意到 Lombok 只有一个 @EqualsAndHashCode 注解,而没有拆分成 @Equals 和 @HashCode 两个注解,想了解更多 Lombok 的内容,也可以查看我之前写的文章 Lomok 使用详解
  • 另外通过 IDE 快捷键生成重写方法时,你也会看到这两个方法放在一起,而不是像 getter 和 setter 那样分开

以上两点都是隐形的规范约束,希望大家也严格遵守这个规范,以防带来不必要的麻烦,记忆的方式有多样,如果记不住这个文字约束,脑海中记住上面的图你也就懂了

5. 重写 hashCode 为什么总有 31 这个数字?

细心的朋友可能注意到,我上面重写 hashCode的方法很简答, 就是用了 Objects.hash 方法,进去查看里面的方法:
public static int hashCode(Object a[]) {
    if (a == null)
        return 0;

    int result = 1;

    for (Object element : a)
        result = 31 * result + (element == null ? 0 : element.hashCode());

    return result;
}
这里通过 31 来计算对象 hash 值
如何妙用 Spring 数据绑定? 文章末尾提到的在 HandlerMethodArgumentResolverComposite 类中有这样一个成员变量:
private final Map<MethodParameter, HandlerMethodArgumentResolver> argumentResolverCache =
            new ConcurrentHashMap<MethodParameter, HandlerMethodArgumentResolver>(256);
Map 的 key 是 MethodParameter ,根据我们上面的分析,这个类一定也会重写 equalshashCode 方法,进去查看发现,hashCode 的计算也用到了 31 这个数字
@Override
public boolean equals(Object other) {
    if (this == other) {
        return true;
    }
    if (!(other instanceof MethodParameter)) {
        return false;
    }
    MethodParameter otherParam = (MethodParameter) other;
    return (this.parameterIndex == otherParam.parameterIndex && getMember().equals(otherParam.getMember()));
}

@Override
public int hashCode() {
    return (getMember().hashCode() * 31 + this.parameterIndex);
}
为什么计算 hash 值要用到 31 这个数字呢?我在网上看到一篇不错的文章,分享给大家,作为科普,可以简单查看一下:
String hashCode 方法为什么选择数字31作为乘子

总结

如果还对equalshashCode 关系及约束含混,我们只需要按照上述步骤逐步回忆即可,更好的是直接查看 JDK 源码;另外拿出实际的例子来反推验证是非常好的办法。如果你还有相关疑问,也可以留言探讨.

灵魂追问

  1. Thread 类就没有重写 equals 方法,你还知道哪些情况没必要重写 equals 方法吗?
  2. 从上面 HandlerMethodArgumentResolverComposite 类中定义的 Map 成员变量,你注意到哪些知识点,比如 final,ConcurrentHashMap,初识容量,为什么要这样写?你能解释出原因吗?
欢迎关注我的公众号 「日拱一兵」,趣味原创解析Java技术栈问题,将复杂问题简单化,将抽象问题图形化落地
如果对我的专题内容感兴趣,或抢先看更多内容,欢迎访问我的博客 dayarch.top

Peter谭金杰 / 2019-12-23 4 years ago / 未收藏/ SegmentFault 最新的文章发送到 kindle

启动一个Express负责回吐wasm格式文件的服务非常简单
Express的源码、以及目前现在主流库已经全部使用TypeScript编写,呼吁大家全面切换到TypeScript 
由于本文是自己项目中的一段服务代码临时拼凑而成,所以这里没有使用TypeScript
注:无论是javaScript还是Node.js的框架源码其实都不难,稍微花点心思就可以看得很透彻,本文只是在使用wasm中顺手一写,可能不像其他人分析得那么专业

众所周知,Express引入后,它需要调用才会获得app对象,那么可以得知,我们引入的Express一开始是一个函数,进入源码查看
先分析@types的包  关于TypeScirpt源码 
再分析javaScript

Express初始引入的是一个函数,可是它身上有一些例如express.static的方法,是怎么回事呢?那么我们进入core.Express中查看它的接口
初始引入函数遵循的接口继承了Application

这里request和response遵循的接口格式应该比较简单,待会下面在写
发现Application接口一次性继承了 EventEmitter  IRouter Express.Application 
系统学习过TypeScript的我们肯定知道,接口是可以一次继承多个接口,但是类只可以通过extends一次继承一个,要想多个继承就要连续继承子类

里面发现了一些重要的API定义:

通过这里,我们能知道这些重要API的参数需要等、
下面开始正式解析Express的javaScript部分源码

看过@types中的源码,那么我们进来看javaScript部分源码,简直轻轻松松
源码入口:

确实源码入口暴露的是一个函数,跟@types中的源码一致
一起看看createApplication函数做了什么

{ configurable: true, enumerable: true, writable: true, value: app }
这段代码是属性描述符,vue 2.x版本中的get和set和访问描述符,不懂的去搜下
最重要的初始化,app.init()这段,可是这里是局部变量,没有init这个方法啊。上面有调用mixin,听函数名就知道是混合,不懂的去搜索下,五分钟包会
进入proto中:

发现初始化,就是在app挂载了四个属性,初始值都是空对象
发现 app.listen的实现也是依靠http模块,跟koa差不多

再看static静态资源服务器实现的模块

依靠serve-static这个库实现,小编本人也用原生Node.js写过静态资源服务器,感觉入门级的Node.js可以去玩玩~
进入serve-static中发现,默认暴露是一个函数~
module.exports = serveStatic
function serveStatic (root, options) {
    return serveStatic(req,res,next) {
    ...
     if (path === '/' && originalUrl.pathname.substr(-1) !== '/') {
      path = ''
    }
    var stream = send(req, path, opts)
    stream.on('directory', onDirectory)
    if (setHeaders) {
      stream.on('headers', setHeaders)
    }
    if (fallthrough) {
      stream.on('file', function onFile () {
        forwardError = true
      })
    }
    stream.on('error', function error (err) {
      if (forwardError || !(err.statusCode < 500)) {
        next(err)
        return
      }
      next()
    })
    // pipe
    stream.pipe(res)
    }
}
原来调用express-static后会返回一个函数,也是接受请求返回响应~
这段函数代码其实很多,但是核心跟我返回wasm二进制数据一样,通过send()方法返回一个可读流,然后调用pipe导入到res中,返回给客户端,不同的是这里的pipe方法是自己定义在原型链上的
send方法依赖send这个库
进入查看,发现默认导出
 
function send (req, path, options) {
  return new SendStream(req, path, options)
}

function SendStream(){
  Stream.call(this)
  ../若干代码
}
一开始我以为调用pipe是可读流的pipe,但是没有发现SendStream有返回值,后面一看,pipei是自己定义在原型链上的方法~

SendStream.prototype.pipe = function pipe (res) {
  //..中间很多容错处理 头部处理等
   var path = decode(this.path)
   //若干代码
   this.sendFile(path)
}
原来返回文件的核心在这里:
这里比较绕,需要一点耐心
 fs.stat(path, function onstat (err, stat) {
  if (err && err.code === 'ENOENT' && !extname(path) && path[path.length - 1] !== sep) {
    // not found, check extensions
    return next(err)
  }
  if (err) return self.onStatError(err)
  if (stat.isDirectory()) return self.redirect(path)
  self.emit('file', path, stat)
  self.send(path, stat)
})
这里通过一些容错机制处理后,把path和文件stat信息对象,传入this.send中,这里的send,跟默认暴露的function send不是一个函数,整个源码这里是最绕的

发现进入这个函数后,最终调用this.stream  
到现在已经绕了三个库,将近2000行代码了,还是没有返回响应,但是Node.js里面就是那几个原生API可以返回响应,这次应该到了返回响应的时候了
进入this.stream中,发现头部就返回了响应

原来绕了这么久,还是小编开头的那段代码返回了响应,只是由于遵循commonJS模块化规范,把很多属性都挂载到了每个模块的prototype和this上,导致了阅读难度提升~
至此,静态资源服务器源码和app.listen源码模块源码解析完毕
小编的静态资源服务器,源码更容易阅读~
https://github.com/JinJieTan/util-static-server

app.get原理解析:

函数首先针对get方法只有一个参数时作出了定义,此时get方法返回app的设定属性,跟我们没有关系。
this.lazyrouter()为app实例初始化了基础router对象,并调用router.use方法为这个router添加了两个基础层,回调函数分别为query和middleware.init。我们不去管这个过程。
下一句var route = this._router.route(path)就以第一个参数path调用了router.route方法(router在lazyrouter初始化)。router在router目录中index.js文件中声明,它的属性stack存储了以layer描述的各个中间层。route方法定义在proto.route函数中,代码如下:

可以看到,首先创建了一个新的route实例;然后将route.dispatch函数作为回调函数创建了一个新的layer实例,并将layer的route属性设置为这个route实例之后,将这个layer推入router(this.stack的this是router)的stack中。
形象地说,这个过程就是新建了一个layer作为中间层放入了router的stack数组中。这个layer的回调为route.dispatch。
执行完这个router.route方法后,又通过route[method].apply(route, slice.call(arguments, 1));让生成的这个route(不是router)调用了route.get。route.get中的关键流如下:

到此,程序就完成了对get方法的加载。我们简短地回顾下这个过程:首先为app实例化一个router对象,这个对象的stack属性是一个数组,保存了app的不同中间层。一个中间层以一个layer实例表征,这个layer的handle属性引用了回调函数。对于get等方法创建的layer,它的handle为route.dispatch函数,而在get方法中自定义的回调函数是存放在route的stack中的。如果例程中继续为app添加其他路由,则router对象会继续生成新的layer存储这些中间件,并放入自己的stack中。

app.use,添加中间件源码:
同样第一次都会调用,初始化一个 new Layer 中间层

app.use = function use(fn) {
var offset = 0;
var path = '/';
var fns = flatten(slice.call(arguments, offset));
this.lazyrouter();
var router = this._router;

fns.forEach(function (fn) {
  router.use(path, function mounted_app(req, res, next) {
    var orig = req.app;
    fn.handle(req, res, function (err) {
      setPrototypeOf(req, orig.request)
      setPrototypeOf(res, orig.response)
      next(err);
    });
  });
  fn.emit('mount', this);
}, this);

return this;
};
lazyrouter,每次初始化都会生成一个新的Layer
app.lazyrouter = function lazyrouter() {
  if (!this._router) {
    this._router = new Router({
      caseSensitive: this.enabled('case sensitive routing'),
      strict: this.enabled('strict routing')
    });

    this._router.use(query(this.get('query parser fn')));
    this._router.use(middleware.init(this));
  }
};
上面省掉了很多的容错处理,这里有一个flatten函数,扁平化数组的
依赖一个独立的第三方库,里面代码也很简单

function flattenForever (array, result) {
  for (var i = 0; i < array.length; i++) {
    var value = array[i]

    if (Array.isArray(value)) {
      flattenForever(value, result)
    } else {
      result.push(value)
    }
  }

  return result
}
这里也是很巧妙,forEach时候传入了this的值给函数,我以前不知道forEach能传两个值,

然后传入相应回调函数

app.handle = function handle(req, res, callback) {
  var router = this._router;

  // final handler
  var done = callback || finalhandler(req, res, {
    env: this.get('env'),
    onerror: logerror.bind(this)
  });
  // no routes
  if (!router) {
    debug('no routes defined on app');
    done();
    return;
  }

  router.handle(req, res, done);};

先取出第一层,判断与request的path是否match。第一、二层是router初始化时的query函数和middleware.init函数,它们都会进入执行trim_prefix(layer, layerError, layerPath, path);的分支,并调用其中的layer.handle_request(req,res, next);,这个next就是router.handle函数里的闭包next。执行了这两层后,继续回调next函数。
while (match !== true && idx < stack.length) {
  layer = stack[idx++];
  match = matchLayer(layer, path);
  route = layer.route;
  //...若干d代码
  trim_prefix(layer, layerError, layerPath, path);
   function trim_prefix(layer, layerError, layerPath, path) {
    if (layerPath.length !== 0) {
  // Validate path breaks on a path separator
  var c = path[layerPath.length]
  if (c && c !== '/' && c !== '.') return next(layerError)

  // Trim off the part of the url that matches the route
  // middleware (.use stuff) needs to have the path stripped
  debug('trim prefix (%s) from url %s', layerPath, req.url);
  removed = layerPath;
  req.url = protohost + req.url.substr(protohost.length + removed.length);

  // Ensure leading slash
  if (!protohost && req.url[0] !== '/') {
    req.url = '/' + req.url;
    slashAdded = true;
  }

  // Setup base URL (no trailing slash)
  req.baseUrl = parentUrl + (removed[removed.length - 1] === '/'
    ? removed.substring(0, removed.length - 1)
    : removed);
}

debug('%s %s : %s', layer.name, layerPath, req.originalUrl);

if (layerError) {
  layer.handle_error(layerError, req, res, next);
} else {
  layer.handle_request(req, res, next);
}
}
  
}
这时就执行到了加载时生成的route所在的层,判断request路径是否匹配,这里的匹配执行的是严格匹配,比如这层的regexp属性(从加载时的路由确定)是'/',那么'/a'也不能匹配。
若路径不匹配,while循环会直接跳过当此循环,对router.stack的下一层进行匹配;如果path与这个route的regexp匹配,就会执行layer.handle_request(req, res, next);。
layer.handle_request函数:
 Layer.prototype.handle_request = function handle(req, res, next) {
  var fn = this.handle;

  if (fn.length > 3) {
    // not a standard request handler
    return next();
  }

  try {
    fn(req, res, next);
  } catch (err) {
    next(err);
  }
};
这里非常巧妙,也是最绕的,我们知道调用red.end就会返回响应结束匹配,否则express就会逐个路由匹配执行,这里确定执行所有的匹配请求后,就会调用finalhandler(最终的处理),返回响应
finalhandler是另外一个独立的第三方库,专门用来处理响应的
里面核心函数:

if (isFinished(req)) {
  write()
  return
}

  function write () {
  // response body
  var body = createHtmlDocument(message)

  // response status
  res.statusCode = status
  res.statusMessage = statuses[status]

  // response headers
  setHeaders(res, headers)

  // security headers
  res.setHeader('Content-Security-Policy', "default-src 'none'")
  res.setHeader('X-Content-Type-Options', 'nosniff')

  // standard headers
  res.setHeader('Content-Type', 'text/html; charset=utf-8')
  res.setHeader('Content-Length', Buffer.byteLength(body, 'utf8'))

  if (req.method === 'HEAD') {
    res.end()
    return
  }
  res.end(body, 'utf8')
}

通过以下函数判断: 

function isFinished(msg) {
  var socket = msg.socket
  if (typeof msg.finished === 'boolean') {
    // OutgoingMessage
    return Boolean(msg.finished || (socket && !socket.writable))
  }

  if (typeof msg.complete === 'boolean') {
    // IncomingMessage
    return Boolean(msg.upgrade || !socket || !socket.readable || (msg.complete && !msg.readable))
  }
  // don't know
  return undefined
}
判断有没有协议升级事件(例如websocket的第一次握手时)、有没有socket对象、socket是不是可读等
最终调用createHtmlDocument拼装数据,返回响应~
 function createHtmlDocument (message) {
  var body = escapeHtml(message)
    .replace(NEWLINE_REGEXP, '<br>')
    .replace(DOUBLE_SPACE_REGEXP, ' &nbsp;')

  return '<!DOCTYPE html>\n' +
    '<html lang="en">\n' +
    '<head>\n' +
    '<meta charset="utf-8">\n' +
    '<title>Error</title>\n' +
    '</head>\n' +
    '<body>\n' +
    '<pre>' + body + '</pre>\n' +
    '</body>\n' +
    '</html>\n'
}
至此,花费4000字解析了express的核心所有API,感觉有一点绕,这里特别是get路由的触发,是整个源码的核心。
express目前的地位还是不可以撼动,koa更像是一个玩具,源码非常轻量级,可以先看koa,再看express,再接着看Node.js核心模块的源码


ken_io / 2019-12-23 4 years ago / 未收藏/ SegmentFault 最新的文章发送到 kindle

一、Code Review的好处

想要做好Code Review,必须让参与的工程师充分认识到Code Review的好处

1、互相学习,彼此成就

无论是高手云集的架构师团队,还是以CURD为主的业务开发团队,大家的技术能力、经验都是有差异的。
通过Code Review,对于同样的功能实现,有经验的工程师可以给经验尚浅的工程师提供合理的优化建议。经验尚浅的工程师可以通过阅读优质代码,快速学习相关技术运用的最佳实践。如果大家技术实力相当,可能就是互相刷新思想了。
你有一个苹果,我有一个苹果,彼此交换一下,我们仍然是各有一个苹果;但你有一种思想,我有一种思想,彼此交换,我们就都有了两种思想,甚至更多。

2、知识共享,自动互备

在大部分团队,尤其是采用服务化架构以及微服务架构的团队,通常都是1个开发人员负责多个服务/项目(Project),如果没有Code Review,那么项目中所涉及的架构知识,或者业务知识,就只存在于项目执行过程中产出的架构文档,以及核心流程、功能的说明文档了。
文档可以帮助其他工程师了解服务/项目的情况,但通常其他工程师不会主动去阅读这些文档,等到真的要维护别的工程师写的代码,文档的完整性往往没有最初的效果好了,文档跟代码实现的匹配度也会下降。
Code Review的过程,就是根据提交者的描述阅读代码的逻辑,看代码实现是否跟描述一致。在这个时候,Reviewer就必须阅读文档,知识的传播性就更好,也基本上不会出现只有1个人了解某个项目的情况了。

3、统一风格,提升质量

如果要给代码质量分一下等级的话,那应该是:
可以编译通过->可以正常运行->可以测试通过->容易阅读->容易维护。那么,通过Code Review的代码最起码可以达到易阅读这个级别。

要做到易阅读,可不是说只要有Code Review这个环节就可以了,还要有相关的规范,让大家按照同样的工程风格、编码风格去构建项目和编写代码。统一风格一方面是让大家无论是维护项目还是阅读代码,不用互相适应各自的编码习惯,另外也是给Reviewer一个Code Review的基本依据。
发现Bug不是Code Review的必需品,而是附属品。至于那些低级的问题/bug交给代码扫描工具就可以了,这不是Code Review的职责。

二、推动Code Review落地执行

1、选定工具

可以用来做Code Review的工具很多,这里主要介绍相对主流的Gerrit、GitLab
  • Gerrit
Gerrit是Google开源的代码审查工具,Gerrit也是一个基于Git构建的版本管理工具,Gerrit支持将其他Git仓库的代码跟Gerrit自己的仓库做同步。所有的代码审查的操作以及权限控制都是在Gerrit自己的仓库上进行的。
Gerrit是面向代码审查来构建的,所以在代码审查的权限控制,以及功能上都是非常完善的。
Gerrit是可以强制CodeReview的,支持Develop、Reviewer、Approver三种角色支持对每个Project配置不同的CodeReview的人员以及权限。

如果要根据Gerrit的数据做一些统计报表,就直接访问Gerrit的数据库,如果功能上不满足要求,反正是开源的,有Java研发团队就可以自己定制
总之,Gerrit的Code Review功能是非常完善的,缺点可能就是UI、交互太老了以及平台的管理功能较弱。
  • GitLab家族
GitLab是基于Git构建的源代码管理系统,基于GitLab构建的 GitLab.com 是仅次于 GitHub.com 的在线源代码管理平台。
GitLab分GitLab CE(社区版)和 GitLab EE(企业版)两个版本,开源的社区版功能相对会弱一点,但是免费使用,可以自由部署、定制、维护。企业版功能强大,但是需要收费的。
GitLab可以通过MergeRequest来Review代码,也可以做到强制CodeReview,社区版支持Develop、Reviewer两种角色,企业版支持Develop、Reviewer、Approver三种角色,可以给给项目/组分配不同的角色(Master、Developer)来控制Merge代码的权限。
如果需要根据GitLab的数据做一些统计报表,GitLab提供了非常友好的restful API,如果要定制化,建议是通过API来做定制化的工具,不受编程语言限制。
GitLab的Code Review的功能没有Gerrit功能完善,但是GitLab附带的文档功能、以及GitLab完善的管理后台都要比Gerrit更好,如果要做CI/CD,GitLab的社区版几乎是最佳选择
  • Gerrit VS GitLab 综合对比
工具 权限
控制
UI
交互
源代码
管理
可维护 数据
统计
工具
配套
Gerrit
















GitLab社区版


























GitLab企业版


























Gerrit强项只有Code Review的控制,GitLab的功能更全面,但GitLab的企业版是收费的。所以,综合来说,我更推荐GitLab社区版
基于GitLab的CodeReview教程:https://ken.io/note/gitlab-co...

2、制定开发规范

没有规则,就没有执行。规则中首当其冲的就是开发规范。
规范中建议包含:

  • 工程规范(工程结构,分层方式及命名等等)
  • 命名规范(接口、类、方法名、变量名等)
  • 代码格式(括号、空格、换行、缩进等)
  • 注释规范(规定必要的注释)
  • 日志规范(合理的记录必要的日志)
  • 各种推荐与不推荐的代码示例
如果团队人数较少,项目的工程复杂度较低,可以自行制定规范。毕竟适合团队的就是最好的。
如果团队有一定规模,且还会不断扩张,还是建议根据大厂的规范进行制定,或者是直接采用。

Java开发手册:https://github.com/alibaba/p3c
Google代码风格指南:https://zh-google-styleguide.... (涵盖:C++、Python等)

3、制定流程规范

  • 确定Code Review实施环节
image
CodeReview建议是放在代码提交测试前,也就是开发人员完成代码开发及自测后将代码提交到测试分支时进行Code Review。毕竟,如果测试通过后再进行Code Review,如果需要代码变更,势必会增加测试的工作量,甚至影响项目进度。亦或是顶着项目上线的压力,干脆“以后再说”了
以通用的Git Workflow来说,那就是把Code Review放在Feature分支合并到Develop分支时了。
  • 制定角色行为规范
角色 规则
Developer 1、一次提交的功能必须是完整的
2、默认细粒度提交(以独立的方法/功能/模块为单位)。如需粗粒度提交,需提前跟Reviewer沟通确认
3、Commit Message中要清晰描述变更的主题
必要时,可以以链接或者文件的形式附上需求文档/设计文档
Reviewer 1、不允许自我Review并Merge代码
2、Review不通过打回前需跟Developer说明原因并达成一致
3、Review不通过需明确填写打回的原因
4、单次Review时长需控制在2分钟~2小时内完成(特殊情况请说明原因)
Approver 1、审批不通过需注明原因<br/>2、审批时长需要控制在1小时以内
3、对于放行的非质量问题,需持续跟进
这样规范,主要是为了:
  1. 控制提交Code Review的代码的粒度
  2. 控制单次Code Review的时间
  3. 提升Commit/MergeRequest描述的质量,减少沟通成本
这样,我们就可以通过细粒度高频次的方式尽可能利用工程师碎片化的时间进行Code Review,一定程度上保证Code Review的效率。
毕竟,粗粒度甚至是集中式的Code Review,时间上难以把控。发现了问题的时候,修复的成本也往往更高。

3、分享与统计

有了工具、开发规范、流程规范,就可以指引参与的工程师参与Code Review,那么我们也要对Code Review的过程以及结果进行检验,毕竟不进行检查/验收的规则,是无法达到预期效果的。
Code Review毕竟不是数学题,我们无法通过简单的计算去验证。所以我们要通过侧面验证,来帮助Code Review的执行
  • 定期分享
我们是期望CodeReview可以让工程师之间互相学习的,那么对于一次Code Review通常只有参与的2-3个工程师有互相学习的机会,那么在这个过程中学到的知识,定期的分享出来,既可以加强知识的流动,又可以检查大家究竟有没有在Code Review过程中学习到知识,或者有没有认真的进行Code Review
至于分享的内容,可以是开发规范中的范例代码,也可以是规范中的正例代码,也可以是针对某个功能实现的最佳算法/最佳实践,也可以是Code Review过程中的争议代码,也可以是自己踩过的坑。
总之,Code Review之后的代码分享,不但可以加强知识的流动,还可以检验Code Review的效果。
  • 数据统计
为了在一定程度上保证Code Review的效率,我们在规范里是要求参与的工程师:
  1. Developer控制提交Code Review的粒度,或者控制每个Commit的粒度
  2. Developer要准确清晰的描述所提交的代码
  3. Reviewer&Approver要在规定时间内完成Code Review
这些情况纯粹靠人工是无法检验的,还是需要有一定的数据统计。
如果用Gerrit,可以查询Gerrit的数据库,里面会有Code Review的信息,
如果用GitLab,可以通过WebHook或者restful API获取Code Review信息

我们可以做成报表,来展示Code Review的情况:
  1. 每人每周Code Review所消耗的时间
  2. 每人每周被Code Review所消耗的平均时间
  3. 超过规定时间的Code Review情况
  4. 代码提交描述字数过少的情况
  5. 等等(根据自己的需要来)
以上情况只是Code Review的侧面反馈,用来帮我们发现Code Review执行过程中可能出现的问题。不过,出现问题并不意味着Code Review的质量/效率一定受到了影响。
比如,工程师A被Code Review的耗时是团队内最高,有可能是有某次代码是周五晚上提交的CodeReviw,这单次CodeReview的耗时就会超过48小时。也有可能是对应的Reviewer是团队新人,要通过相关业务项目了解对应Project的承担的职责及代码,这是个学习的过程,自然耗时加长。
又比如工程师B提交的代码描述文字过少,可能就是中间件团队对某些基础组件进行升级,或者安全团队要求升级某个依赖的开源组件,以修复某个安全漏洞。
但是通过这种的数据,可以让Code Review的情况直观的展示出来。来发现大家执行过程中需要优化的事项, 不断帮助大家完善规则,做好执行。

三、保证Code Review质量的关键

1、工程师对研发规范的认真学习

无论Code Review的工具以及流程是怎么样的,都少不了开发规范作为支撑,毕竟我们期望Code Review达到的效果之一就是,团队中的工程师可以写出像规范中描述那样的高质量代码。
工程师对研发规范的掌握程度,决定了自己编码代码的质量,也决定了自己Review通过的代码的质量。所以,无论如何,加强对研发规范的学习和理解,都是保证Code Review质量的重中之重

2、资深工程师的认真对待

Code Review目的是帮助工程师交流和学习进步的。无论是技术能力还是编码习惯,亦或是业务知识。无论规则怎么制定,终究还是需要参与的工程师来执行,如果大家互相睁一只眼闭一只眼,互相降低要求,那么执行的效果一定会打折扣。
虽说三人行必有我师,但收益最大的一定是经验(技能/业务知识)尚浅的工程师,收益最低的一定是团队中最资深的工程师。而恰恰经验尚浅的工程师的收益大部分都要来自资深工程师的付出。
所以,一定要跟资深工程师最好沟通,让他们严格要求,不能对经验尚浅的工程师放水,以帮助他们提升编码能力以及业务知识。
这也可以减少甚至避免他们为经验尚浅的工程师的代码“善后”。

四、备注

附录


本文首发于我的个人博客:https://ken.io/note/how-to-do-code-review-in-a-team
云音乐前端团队 / 2019-12-23 4 years ago / 未收藏/ SegmentFault 最新的文章发送到 kindle
Title image of Streams API by Mozilla Contributors is licensed under CC-BY-SA 2.5.
Streams API 示意图,作者 Mozilla Contributors,基于 CC-BY-SA 2.5 协议使用。
本文篇幅较长,建议配合目录食用分次阅读。本文作者:ccloli


自第一个实现的浏览器开始计算,Fetch API 已经快要五岁了。这五年 Chrome 和 Firefox 刷了不少版本号,IE 也不知死了多少年,而它的继任者更是上演了一出名为《Edge: Become Chromium》的好剧。再加上 ES6+ 的普及,我们早已习惯了基于 Promise 和 async/await 的异步编程,所以估计不少同学也转而使用 Fetch API 作异步请求。陪伴了我们将近 20 年历史的 XMLHttpRequest 也被不少同学「打入冷宫」,毕竟谁让 Fetch API 那么好用呢?可怜的 XHR 只能独守空房终日以泪洗面,看着你和 Fetch API 嬉戏的样子,口中喃喃说着「是我,是我先,明明都是我先来的」——呃,不好意思扯歪了。

Fetch API 不香吗?

不不不,没有这个意思。相比较于 XMLHttpRequest 来说,fetch() 的写法简单又直观,只要在发起请求时将整个配置项传入就可以了。而且相较于 XHR 还提供了更多的控制参数,例如是否携带 Cookie、是否需要手动跳转等。此外 Fetch API 是基于 Promise 链式调用的,一定程度上可以避免一些回调地狱。举个例子,下面就是一个简单的 fetch 请求:
fetch('https://example.org/foo', {
    method: 'POST',
    mode: 'cors',
    headers: {
        'content-type': 'application/json'
    },
    credentials: 'include',
    redirect: 'follow',
    body: JSON.stringify({ foo: 'bar' })
}).then(res => res.json()).then(...)
如果你不喜欢 Promise 的链式调用的话,还可以用 async/await
const res = await fetch('https://example.org/foo', { ... });
const data = await res.json();
再回过头来看久经风霜的 XMLHttpRequest,如果你已经习惯使用诸如 jQuery 的 $.ajax() 或者 axios 这类更为现代的封装 XHR 的库的话,估计已经忘了裸写 XHR 是什么样子了。简单来说,你需要调用 open() 方法开启一个请求,然后调用其他的方法或者设置参数来定义请求,最后调用 send() 方法发起请求,再在 onload 或者 onreadystatechange 事件里处理数据。看,这一通下来你已经乱了。
课后习题 Q0:试试看将上面的 fetch 请求用原生 XMLHttpRequest 实现一遍,看看你还记得多少知识?

Fetch API 真香吗?

看起来 Fetch API 相比较于传统的 XHR 优势不少,不过在「真香」之前,我们先来看三个在 XHR 上很容易实现的功能:
  1. 如何中断一个请求?XMLHttpRequest 对象上有一个 abort() 方法,调用这个方法即可中断一个请求。此外 XHR 还有 onabort 事件,可以监听请求的中断并做出响应。
  2. 如何超时中断一个请求?XMLHttpRequest 对象上有一个 timeout 属性,为其赋值后若在指定时间请求还未完成,请求就会自动中断。此外 XHR 还有 ontimeout 事件,可以监听请求的超时中断并做出响应。
  3. 如何获取请求的传输进度?
    在异步请求一个比较大的文件时,由于可能比较耗时,展示文件的下载进度在 UI 上会更友好。XMLHttpRequest 提供了 onprogress 事件,所以使用 XHR 可以很方便地实现这个功能。
    const xhr = new XMLHttpRequest();
    xhr.open('GET', '/foo');
    xhr.addEventListener('progress', (event) => {
        const { lengthComputable, loaded, total } = event;
        if (lengthComputable) {
            console.log(`Downloaded ${loaded} of ${total} (${(loaded / total * 100).toFixed(2)}%)`);
        } else {
            console.log(`Downloaded ${loaded}`);
        }
    });
    xhr.send();
对于第一个问题其实已经有比较好的解决方案了,只是在浏览器上的实现距离 Fetch API 晚了近三年。随着 AbortControllerAbortSignal 在各大浏览器上完整实现,Fetch API 也能像 XHR 那样中断一个请求了,只是稍微绕了一点。通过创建一个 AbortController 实例,我们得到了一个 Fetch API 原生支持的控制中断的控制器。这个实例的 signal 参数是一个 AbortSignal 实例,还提供了一个 abort() 方法发送中断信号。只需要将 signal 参数传递进 fetch() 的初始化参数中,就可以在 fetch 请求之外控制请求的中断了:
const controller = new AbortController();
const { signal } = controller;
fetch('/foo', { signal }).then(...);
signal.onabort = () => { ... };
controller.abort();
对于第二个问题,既然已经稍微绕路实现中断请求了,为何不再绕一下远路呢?只需要 AbortController 配合 setTimeout() 就能实现类似的效果了。
但是第三个获取请求进度的问题呢?你打开了 MDN,仔细地看了 fetch() 方法的所有参数,都没有找到类似 progress 这样的参数,毕竟 Fetch API 并没有什么回调事件。难道 Fetch API 就不能实现这么简单的功能吗?当然可以,这里就要绕一条更远的路,提一提和它相关的 Streams API 了——不是 Web Socket,也不是 Media Stream,更不是只能在 Node.js 上使用的 Stream,不过和它很像。

Streams API 能做什么?

对于非 Web 前端的同学来说,流应该是个很常见的概念,它允许我们一段一段地接收与处理数据。相比较于获取整个数据再处理,流不仅不需要占用一大块内存空间来存放整个数据,节省内存占用空间,而且还能实时地对数据进行处理,不需要等待整个数据获取完毕,从而缩短整个操作的耗时。
此外流还有管道的概念,我们可以封装一些类似中间件的中间流,用管道将各个流连接起来,在管道的末端就能拿到处理后的数据。例如,下面的这段 Node.js 代码片段实现了解压 zip 中的文件的功能,只需要从 zip 的中央文件记录表中读取出各个文件在 zip 文件内的起止偏移值,就能将对应的文件解压出来。
const input = fs.createReadStream(null, {
    fd, start, end, autoClose: false
});
const output = fs.createWriteStream(outputPath + name);
// 可以从流中直接读取数据
input.on('data', (chunk) => { ... });
// 或者直接将流引向另一个流
input.pipe(zlib.createInflateRaw()).pipe(output);
其中的 input 是一个可读取的流,output 是一个可写入的流,而 zlib.createInflateRaw() 就是创建了一个既可读取又可写入的流,它在写入端以流的形式接受 Deflate 压缩的数据,在读取端以流的形式输出解压缩后的数据。我们想象一下,如果输入的 zip 文件是一个上 GB 的大文件,使用流的方式就不需要占用同样大小的上 GB 的内存空间。而且从代码上看,使用流实现的代码逻辑同样简洁和清晰。
很可惜,过去在客户端 JavaScript 上并没有原生的流 API——当然你可以自己封装实现流,比如 JSZip 在 3.0 版本就封装了一个 StreamHelper,但是基本上除了使用这些 stream 库的库以外,没有其它地方能 产生 兼容这个库的流了。没有能产生流的数据源才是大问题,比如想要读取一个文件?过去 FileReader 只能在 onload 事件上拿到整个文件的数据,或者对文件使用 slice() 方法得到 Blob 文件片段。现在 Streams API 已经在浏览器上逐步实现(或者说,早在 2016 年 Chrome 就开始支持一部分功能了),能用上流处理的 API 想必也会越来越多,而 Streams API 最早的受益者之一就是 Fetch API。


Streams API 赋予了网络请求以片段处理数据的能力,过去我们使用 XMLHttpRequest 获取一个文件时,我们必须等待浏览器下载完整的文件,等待浏览器处理成我们需要的格式,收到所有的数据后才能处理它。现在有了流,我们可以以 TypedArray 片段的形式接收一部分二进制数据,然后直接对数据进行处理,这就有点像是浏览器内部接收并处理数据的逻辑。甚至我们可以将一些操作以流的形式封装,再用管道把多个流连接起来,管道的另一端就是最终处理好的数据。
Fetch API 会在发起请求后得到的 Promise 对象中返回一个 Response 对象,而 Response 对象除了提供 headersredirect() 等参数和方法外,还实现了 Body 这个 mixin 类,而在 Body 上我们才看到我们常用的那些 res.json()res.text()res.arrayBuffer() 等方法。在 Body 上还有一个 body 参数,这个 body 参数就是一个 ReadableStream
既然本文是从 Fetch API 的角度出发,而如前所述,能产生数据的数据源才是流处理中最重要的一个部分,那么下面我们来重点了解下这个在 Body 中负责提供数据的 ReadableStream
这篇文章不会讨论流的排队策略(也就是下文即将提到的构造流时传入的 queuingStrategy 参数,它可以控制流的缓冲区大小,不过 Streams API 有一个开箱即用的默认配置,所以可以不指定),也不会讨论没有浏览器实现的 BYOR reader,感兴趣的同学可以参考相关规范文档

ReadableStream

The image of ReadableStream Concept by Mozilla Contributors is licensed under CC-BY-SA 2.5.
ReadableStream 示意图,作者 Mozilla Contributors,基于 CC-BY-SA 2.5 协议使用。
下面是一个 ReadableStream 实例上的参数和可以使用的方法,下文我们将会详细介绍它们:
ReadableStream
  • locked
  • cancel()
  • pipeThrough()
  • pipeTo()
  • tee()
  • getReader()
其中直接调用 getReader() 方法会得到一个 ReadableStreamDefaultReader 实例,通过这个实例我们就能读取 ReadableStream 上的数据。

ReadableStream 中读取数据

ReadableStreamDefaultReader 实例上提供了如下的方法:
ReadableStreamDefaultReader
  • closed
  • cancel()
  • read()
  • releaseLock()
假设我们需要读取一个流中的的数据,可以循环调用 reader 的 read() 方法,它会返回一个 Promise 对象,在 Promise 中返回一个包含 value 参数和 done 参数的对象。
const reader = stream.getReader();
let bytesReceived = 0;
const processData = (result) => {
    if (result.done) {
        console.log(`complete, total size: ${bytesReceived}`);
        return;
    }
    const value = result.value; // Uint8Array
    const length = value.length;
    console.log(`got ${length} bytes data:`, value);
    bytesReceived += length;
    // 读取下一个文件片段,重复处理步骤
    return reader.read().then(processData);
};
reader.read().then(processData);
其中 result.value 参数为这次读取得到的片段,它是一个 Uint8Array,通过循环调用 reader.read() 方法就能一点点地获取流的整个数据;而 result.done 参数负责表明这个流是否已经读取完毕,当 result.donetrue 时表明流已经关闭,不会再有新的数据,此时 result.value 的值为 undefined
回到我们之前的问题,我们可以通过读取 Response 中的流得到正在接收的文件片段,累加各个片段的 length 就能得到类似 XHR onprogress 事件的 loaded,也就是已下载的字节数;通过从 Responseheaders 中取出 Content-Length 就能得到类似 XHR onprogress 事件的 total,也就是总字节数。于是我们可以写出下面的代码,成功得到下载进度:
let total = null;
let loaded = 0;
const logProgress = (reader) => {
    return reader.read().then(({ value, done }) => {
        if (done) {
            console.log('Download completed');
            return;
        }
        loaded += value.length;
        if (total === null) {
            console.log(`Downloaded ${loaded}`);
        } else {
            console.log(`Downloaded ${loaded} of ${total} (${(loaded / total * 100).toFixed(2)}%)`);
        }
        return logProgress(reader);
    });
};
fetch('/foo').then((res) => {
    total = res.headers.get('content-length');
    return res.body.getReader();
}).then(logProgress);

看着好像没问题是吧?问题来了,数据呢?我那么大一个返回数据呢?上面的代码只顾着输出进度了,结果并没有把返回数据传回来。虽然我们可以直接在上面的代码里处理二进制数据片段,可是有时我们还是会偷懒,直接得到完整的数据进行处理(比如一个巨大的 JSON 字符串)。
如果我们希望接收的数据是文本,一种解决方案是借助 TextDecoder 得到解析后的文本并拼接,最后将整个文本返回:
let text = '';
const logProcess = (res) => {
    const reader = res.body.getReader();
    const decoder = new TextDecoder('utf-8');
    const push = ({ value, done }) => {
        if (done) return JSON.parse(text);
        text += decoder.decode(value, { stream: true });
        // ...
        return reader.read().then(push);
    };
    return reader.read().then(push);
};
fetch('/foo').then(logProgress).then((res) => { ... });
不过如果你犯了强迫症,一定要像原来那样显示调用 res.json() 之类的方法得到数据,这该怎么办呢?既然 fetch() 方法返回一个 Response 对象,而这个对象的数据已经在 ReadableStream 中读取下载进度时被使用了,那我再构造一个 ReadableStream,外面再包一个 Response 对象并返回,问题不就解决了吗?

构造一个 ReadableStream

构造一个 ReadableStream 时可以定义以下方法和参数:
const stream = new ReadableStream({
    start(controller) {
        // start 方法会在实例创建时立刻执行,并传入一个流控制器
        controller.desiredSize
            // 填满队列所需字节数
        controller.close()
            // 关闭当前流
        controller.enqueue(chunk)
            // 将片段传入流的队列
        controller.error(reason)
            // 对流触发一个错误
    },
    pull(controller) {
        // 将会在流的队列没有满载时重复调用,直至其达到高水位线
    },
    cancel(reason) {
        // 将会在流将被取消时调用
    }
}, queuingStrategy); // { highWaterMark: 1 }
而构造一个 Response 对象就简单了,Response 对象的第一个参数即是返回值,可以是字符串、BlobTypedArray,甚至是一个 Stream;而它的第二个参数则和 fetch() 方法很像,也是一些初始化参数。
const response = new Response(source, init);
了解以上的内容后,我们只需要构造一个 ReadableStream,然后把「从 reader 中循环读取数据」的逻辑放在这个流的 start() 方法内,它会在流实例化后立即调用。当 reader 读取数据时可以输出下载进度,同时调用 controller.enqueue() 把得到的数据推进我们构造出来的流,最后在读取完毕时调用 controller.close() 关闭这个流,问题就能轻松解决。
const logProgress = (res) => {
    const total = res.headers.get('content-length');
    let loaded = 0;
    const reader = res.body.getReader();
    const stream = new ReadableStream({
        start(controller) {
            const push = () => {
                reader.read().then(({ value, done }) => {
                    if (done) {
                        controller.close();
                        return;
                    }
                    loaded += value.length;
                    if (total === null) {
                        console.log(`Downloaded ${loaded}`);
                    } else {
                        console.log(`Downloaded ${loaded} of ${total} (${(loaded / total * 100).toFixed(2)}%)`);
                    }
                    controller.enqueue(value);
                    push();
                });
            };
            push();
        }
    });
    return new Response(stream, { headers: res.headers });
};
fetch('/foo').then(logProgress).then(res => res.json()).then((data) => { ... });

分流一个 ReadableStream

感觉是不是绕了一个远路?就为了这点功能我们居然构造了一个 ReadableStream 实例?有没有更简单的方法?其实是有的,如果你稍有留意的话,应该会注意到 ReadableStream 实例上有一个名字看起来有点奇怪的 tee() 方法。这个方法可以将一个流分流成两个一模一样的流,两个流可以读取完全相同的数据。
The image of Teeing a ReadableStream by Mozilla Contributors is licensed under CC-BY-SA 2.5.
分流 ReadableStream 示意图,作者 Mozilla Contributors,基于 CC-BY-SA 2.5 协议使用。
所以我们可以利用这个特性将一个流分成两个流,将其中一个流用于输出下载进度,而另一个流直接返回:
const logProgress = (res) => {
    const total = res.headers.get('content-length');
    let loaded = 0;
    const [progressStream, returnStream] = res.body.tee();
    const reader = progressStream.getReader();
    const log = () => {
        reader.read().then(({ value, done }) => {
            if (done) return;
            // 省略输出进度
            log();
        });
    };
    log();
    return new Response(returnStream, { headers: res.headers });
};
fetch('/foo').then(logProgress).then(res => res.json()).then((data) => { ... });
另外其实 fetch 请求返回的 Response 实例上有一个一看就知道是什么意思的 clone() 方法,这个方法可以得到一个克隆的 Response 实例。所以我们可以将其中一个实例用来获取流并得到下载进度,另一个实例直接返回,这样就省去了构造 Response 的步骤,效果是一样的。其实这个方法一般用在 Service Worker 里,例如将请求得到的结果缓存起来等等。
课后习题 Q1:如果我们调用了流的 tee() 方法得到了两个流,但我们只读取了其中一个流,另一个流在之后读取,会发生什么吗?
很好,下载进度的问题完美解决了,那么让我们回到最早的问题。Fetch API 最早是没有 signal 这个参数的,所以早期的 fetch 请求很难中断——对,是「很难」,而不是「不可能」。如果浏览器实现了 ReadableStream 并在 Response 上提供了 body 的话,是可以通过流的中断实现这个功能的。

中断一个 ReadableStream

总结一下我们现在已经知道的内容,fetch 请求返回一个 Response 对象,从中可以得到一个 ReadableStream,然后我们还知道了如何自己构造 ReadableStreamResponse 对象。再回过头看看 ReadableStream 实例上还没提到的方法,想必你一定注意到了那个 cancel() 方法。
通过 ReadableStream 上的 cancel() 方法,我们可以关闭这个流。此外你可能也注意到 reader 上也有一个 cancel() 方法,这个方法的作用是关闭与这个 reader 相关联的流,所以从结果上来看,两者是一样的。而对于 Fetch API 来说,关闭返回的 Response 对象的流的结果就相当于中断了这个请求。
所以,我们可以像之前那样构造一个 ReadableStream 用于传递从 res.body.getReader() 中得到的数据,并对外暴露一个 aborter() 方法。调用这个 aborter() 方法时会调用 reader.cancel() 关闭 fetch 请求返回的流,然后调用 controller.error() 抛出错误,中断构造出来的传递给后续操作的流:
let aborter = null;
const abortHandler = (res) => {
    const reader = res.body.getReader();
    const stream = new ReadableStream({
        start(controller) {
            let aborted = false;
            const push = () => {
                reader.read().then(({ value, done }) => {
                    if (done) {
                        if (!aborted) controller.close();
                        return;
                    }
                    controller.enqueue(value);
                    push();
                });
            };
            aborter = () => {
                reader.cancel();
                controller.error(new Error('Fetch aborted'));
                aborted = true;
            };
            push();
        }
    });
    return new Response(stream, { headers: res.headers });
};
fetch('/foo').then(abortHandler).then(res => res.json()).then((data) => { ... });
aborter();

课后习题 Q2:从上面的结果来看,当我们调用 aborter() 方法时,请求被成功中止了。不过如果不调用 controller.error() 抛出错误强制中断流,而是继续之前的流程调用 controller.close() 关闭流,会发生什么事吗?

流的锁机制

或许你还是很奇怪,既然流本身就有一个 cancel() 方法,为什么我们不直接暴露这个方法,反而要绕路构造一个新的 ReadableStream 呢?例如像下面这样:
let aborter = null;
const abortHandler = (res) => {
    aborter = () => res.body.cancel();
    return res;
};
fetch('/foo').then(abortHandler).then(res => res.json()).then((data) => { ... });
aborter();
可惜这样执行会得到下面的错误:这个流被锁了。
TypeError: Failed to execute 'cancel' on 'ReadableStream': Cannot cancel a locked stream
你不信邪,既然流的 reader 被关闭时会关闭相关联的流,那么只要再获取一个 reader 并 cancel() 不就好了?
let aborter = null;
const abortHandler = (res) => {
    aborter = () => res.body.getReader().cancel();
    return res;
};
fetch('/foo').then(abortHandler).then(res => res.json()).then((data) => { ... });
aborter();
可惜这样执行还是会得到下面的错误:
TypeError: Failed to execute 'getReader' on 'ReadableStream': ReadableStreamReader constructor can only accept readable streams that are not yet locked to a reader
或许你还会想,像之前那样使用 tee() 克隆一个流,然后关闭克隆的流不就好了?可惜即便成功调用了其中一个流的 cancel() 方法,请求还是没有中断,因为另一个流并没有被中断,并且还在不断地接收数据。
于是我们接触到了流的锁机制。一个流只能同时有一个处于活动状态的 reader,当一个流被一个 reader 使用时,这个流就被该 reader 锁定了,此时流的 locked 属性为 true。如果这个流需要被另一个 reader 读取,那么当前处于活动状态的 reader 可以调用 reader.releaseLock() 方法释放锁。此外 reader 的 closed 属性是一个 Promise,当 reader 被关闭或者释放锁时,这个 Promise 会被 resolve,可以在这里编写关闭 reader 的处理逻辑:
reader.closed.then(() => {
  console.log('reader closed');
});
reader.releaseLock();
可是上面的代码似乎没用上 reader 啊?再仔细思考下 res => res.json() 这段代码,是不是有什么启发?
让我们翻一下 Fetch API 的规范文档,在 5.2. Body mixin 中有如下一段话:
Objects implementing the Body mixin also have an associated consume body algorithm, given a _type_, runs these steps:
  1. If this object is disturbed or locked, return a new promise rejected with a TypeError.
  2. Let stream be body’s stream if body is non-null, or an empty ReadableStream object otherwise.
  3. Let reader be the result of getting a reader from _stream_. If that threw an exception, return a new promise rejected with that exception.
  4. Let promise be the result of reading all bytes from stream with _reader_.
  5. Return the result of transforming promise by a fulfillment handler that returns the result of the package data algorithm with its first argument, type and this object’s MIME type.

简单来说,当我们调用 Body 上的方法时,浏览器隐式地创建了一个 reader 读取了返回数据的流,并创建了一个 Promise 实例,待所有数据被读取完后再 resolve 并返回格式化后的数据。所以,当我们调用了 Body 上的方法时,其实就创建了一个我们无法接触到的 reader,此时这个流就被锁住了,自然也无法从外部取消。

示例:断点续传

现在我们可以随时中断一个请求,以及获取到请求过程中的数据,甚至还能修改这些数据。或许我们可以用来做些有趣的事情,比如各个下载器中非常流行的断点续传功能。
首先我们先来了解下断点续传的原理,简述如下:
  1. 发起请求
  2. 从响应头中拿到 Content-Length 属性
  3. 在响应过程中拿到正在下载的数据
  4. 终止下载
  5. 重新下载,但是此时根据已经拿到的数据设置 Range 请求头
  6. 重复步骤 3-5,直至下载完成
  7. 下载完成,将已拿到的数据拼接成完整的
在过去只能使用 XMLHttpRequest 或者还没有 Stream API 的时候,我们只能在请求完成时拿到数据。如果期间请求中断了,那也不会得到已经下载的数据,也就是这部分请求的流量被浪费了。所以断点续传最大的问题是获取已拿到的数据,也就是上面的第 3 步,根据已拿到的数据就能算出还有哪些数据需要请求。
其实在 Streams API 诞生之前,大家已经有着各种各样奇怪的方式实现断点续传了。例如国外的 Mega 网盘在下载文件时不会直接通知浏览器下载,而是先把数据放在浏览器内,传输完成后再下载文件。此外它还可以暂停传输,在浏览器内实现了断点续传的功能。仔细观察网络请求就会发现,Mega 在下载时不是下载整个文件,而是下载文件的一个个小片段。所以 Mega 是通过建立多个小的请求获取文件的各个小片段,待下载完成后再拼接为一个大文件。即便用户中途暂停,已下载的块也不会丢失,继续下载时会重新请求未完成的片段。虽然暂停时正在下载的片段还是会被丢弃(注意下面的视频中,暂停下载后重新请求的 URL 和之前的请求是一样的),不过相比较于丢弃整个文件来说,现在的实现已经是很大的优化了。

除了建立多个小请求得到零散文件块,变相实现断点续传外,其实 Firefox 浏览器上的私有特性允许开发者获取正在下载的文件片段,例如云音乐就使用了该特性优化了 Firefox 浏览器上的音频文件请求。Firefox 浏览器的 XMLHttpRequestresponseType 属性提供了私有的可用参数 moz-chunked-arraybuffer。请求还未完成时,可以在 onprogress 事件中请求 XHR 实例的 response 属性,它将会返回上一次触发事件后接收到的数据,而在 onprogress 事件外获取该属性将始终是 null
let chunks = [];
const xhr = new XMLHttpRequest();
xhr.open('GET', '/foo');
xhr.responseType = 'moz-chunked-arraybuffer';
xhr.addEventListener('progress', (event) => {
    chunks.push(xhr.response);
});
xhr.addEventListener('abort', () => {
    const blob = new Blob(chunks);
});
xhr.send();
看起来是个很不错的特性,只可惜在 Bugzilla 上某个 和云音乐相关的 issue 里,有人发现这个特性已经在 Firefox 68 中移除了。原因也可以理解,Firefox 现在已经在 fetch 上实现 Stream API 了,有标准定义当然还是跟着标准走(虽然至今还是 LS 阶段),所以也就不再需要这些私有属性了。
从之前的示例我们已经知道,我们可以从 fetch 请求返回的 ReadableStream 里得到正在下载的数据片段,只要在请求的过程中把它们放在一个类似缓冲区的地方就可以实现之前的第 3 步了,而这也是在浏览器上实现这个功能的难点。请求中断后再次请求时,只需要根据已下载片段的字节数就可以算出接下来要请求哪些片段了。简单来看,逻辑大概是下面这样:
const chunks = [];
let length = 0;
const chunkCache = (res) => {
    const reader = res.body.getReader();
    const stream = new ReadableStream({
        start(controller) {
            const push = () => {
                reader.read().then(({ value, done }) => {
                    if (done) {
                        let chunk;
                        while (chunk = chunks.shift()) {
                            controller.enqueue(chunk);
                        }
                        controller.close();
                        return;
                    }
                    chunks.push(value);
                    length += value.length;
                    push();
                });
            };
            push();
        }
    });
    return new Response(stream, { headers: res.headers });
};
const controller = new AbortController();
fetch('/foo', {
    headers: {
        'Range': `bytes=${length}-`
    },
    signal: controller.signal
}).then(chunkCache).then(...);
// 请求中断后再次执行上述 fetch() 方法
下面的例子对上述代码简单封装得到了 ResumableFetch,并使用它实现了图片下载的断点续传。示例完整代码可在 CodePen 上查看。
注意:该示例中的代码仅进行了简单封装,没有做诸如 If-RangeRangeContent-Length 等 header 的校验,也没有做特殊的错误处理,也没有包含之前提到的中断请求兼容代码,使用上可能也不够友好,仅供示例使用,请谨慎用于生产环境。

封装的 ResumableFetch 类会在请求过程中创建一个 ReadableStream 实例并直接返回,同时已下载的片段将会放进一个数组 chunks 并记录已下载的文件大小 length。当请求中断并重新下载时会根据已下载的文件大小设置 Range 请求头,此时拿到的就是还未下载的片段。下载完成后再将片段从 chunks 中取出,此时不需要对片段进行处理,只需要逐一传递给 ReadableStream 即可得到完整的文件。

管道

到这里 ReadableStream 上的方法已经描述的差不多了,最后只剩下 pipeTo() 方法和 pipeThrough() 方法没有提到了。从字面意思上来看,这就是我们之前提到的管道,可以将流直接指向另一个流,最后拿到处理后的数据。Jake Archibald 在他的那篇《2016 — 属于 web streams 的一年》中提出了下面的例子,或许在(当时的)未来可以通过这样的形式以流的形式得到解析后的文本:
var reader = response.body
    .pipeThrough(new TextDecoder()).getReader();
reader.read().then(result => {
    // result.value will be a string
});
现在那个未来已经到了,为了不破坏兼容性,TextEncoderTextDecoder 分别扩展出了新的 TextEncoderStreamTextDecoderStream,允许我们以流的方式编码或者解码文本。例如下面的例子会在请求中检索 It works! 这段文字,当找到这段文字时返回 true 同时断开请求。此时我们不需要再接收后续的数据,可以减少请求的流量:
fetch('/index.html').then((res) => {
    const decoder = new TextDecoderStream('gbk', { ignoreBOM: true });
    const textStream = res.body.pipeThrough(decoder);
    const reader = textStream.getReader();
    const findMatched = () => reader.read().then(({ value, done }) => {
        if (done) {
            return false;
        }
        if (value.indexOf('It works!') >= 0) {
            reader.cancel();
            return true;
        }
        return findMatched();
    });
    return findMatched();
}).then((isMatched) => { ... });
或者在未来,我们甚至在流里实现实时转码视频并播放,或者将浏览器还不支持的图片以流的形式实时渲染出来:
const encoder = new VideoEncoder({
    input: 'gif', output: 'h264'
});
const media = new MediaStream();
const video = document.createElement('video');
fetch('/sample.gif').then((res) => {
    response.body.pipeThrough(encoder).pipeTo(media);
    video.srcObject = media;
});
从中应该可以看出来这两种方法的区别:pipeTo() 方法应该会接受一个可以写入的流,也就是 WritableStream;而 pipeThrough() 方法应该会接受一个既可写入又可读取的流,也就是 TransformStream
The image of Stream Pipe Chains Concept by Mozilla Contributors is licensed under CC-BY-SA 2.5.
Stream 管道链示意图,作者 Mozilla Contributors,基于 CC-BY-SA 2.5 协议使用。
接下来我们将介绍这两种流,不过在继续之前,我们先来看看 ReadableStream 在浏览器上的支持程度:
Image of Stream API Browser Compatibilty Table by Mozilla Contributors is licensed under CC-BY-SA 2.5.
ReadableStream 浏览器兼容表,作者 Mozilla Contributors,本图片为表格的截图,基于 CC-BY-SA 2.5 协议使用。
从表中我们注意到,这两个方法支持的比较晚。而原因估计你也能猜得到,当数据从一个可读取的流中流出时,管道的另一端应该是一个可写入的流,问题就在于可写入的流实现的比较晚。

WritableStream

The image of WritableStream Concept by Mozilla Contributors is licensed under CC-BY-SA 2.5.
WritableStream 示意图,作者 Mozilla Contributors,基于 CC-BY-SA 2.5 协议使用。
我们已经从 ReadableStream 中了解到很多关于流的知识了,所以下面我们简单过一下 WritableStreamWritableStream 就是可写入的流,如果说 ReadableStream 是一个管道中流的起点,那么 WritableStream 可以理解为流的终点。下面是一个 WritableStream 实例上的参数和可以使用的方法:
WritableStream
  • locked
  • abort()
  • getWriter()
可用的方法和参数很少,估计大家从名字就能知道它们是做什么的。其中直接调用 getWriter() 方法会得到一个 WritableStreamDefaultWriter 实例,通过这个实例我们就能向 WritableStream 写入数据。同样的,当我们激活了一个 writer 后,这个流就会被锁定(locked = true)。这个 writer 上有如下属性和方法:
WritableStreamDefaultWriter
  • closed
  • desiredSize
  • ready
  • abort()
  • close()
  • write()
  • releaseLock()
看起来和 ReadableStreamDefaultReader 没太大区别,多出的 abort() 方法相当于抛出了一个错误,使这个流不能再被写入。另外这里多出了一个 ready 属性,这个属性是一个 Promise,当它被 resolve 时,表明目前流的缓冲区队列不再过载,可以安全地写入。所以如果需要循环向一个流写入数据的话,最好放在 ready 处理。
同样的,我们可以自己构造一个 WritableStream,构造时可以定义以下方法和参数:
const stream = new WritableStream({
    start(controller) {
        // 将会在对象创建时立刻执行,并传入一个流控制器
        controller.error(reason)
            // 对流抛出一个错误
    },
    write(chunk, controller) {
        // 将会在一个新的数据片段写入时调用,可以获取到写入的片段
    },
    close(controller) {
        // 将会在流写入完成时调用
    },
    abort(reason) {
        // 将会在流强制关闭时调用,此时流会进入一个错误状态,不能再写入
    }
}, queuingStrategy); // { highWaterMark: 1 }
下面的例子中,我们通过循环调用 writer.write() 方法向一个 WritableStream 写入数据:
const stream = new WritableStream({
    write(chunk) {
        return new Promise((resolve) => {
            console.log('got chunk:', chunk);
            // 在这里对数据进行处理
            resolve();
        });
    },
    close() {
        console.log('stream closed');
    },
    abort() {
        console.log('stream aborted');
    }
});
const writer = stream.getWriter();
// 将数据逐一写入 stream
data.forEach((chunk) => {
    // 待前一个数据写入完成后再写入
    writer.ready.then(() => {
        writer.write(chunk);
    });
});
// 在关闭 writer 前先保证所有的数据已经被写入
writer.ready.then(() => {
    writer.close();
});
下面是 WritableStream 的浏览器支持情况,可见 WritableStream 在各个浏览器上的的实现时间和 pipeTo()pipeThrough() 方法的实现时间是吻合的,毕竟要有了可写入的流,管道才有存在的意义。
Image of Stream API Browser Compatibilty Table by Mozilla Contributors is licensed under CC-BY-SA 2.5.
WritableStream 浏览器兼容表,作者 Mozilla Contributors,本图片为表格的截图,基于 CC-BY-SA 2.5 协议使用。

TransformStream

从之前的介绍中我们知道,TransformStream 是一个既可写入又可读取的流,正如它的名字一样,它作为一个中间流起着转换的作用。所以一个 TransformStream 实例只有如下参数:
TransformStream
  • readable: ReadableStream
  • writable: WritableStream
TransformStream 上没有其他的方法,它只暴露了自身的 ReadableStreamWritableStream。我们只需要在数据源流上链式使用 pipeThrough() 方法就能实现流的数据传递,或者使用暴露出来的 readablewritable 直接操作数据即可使用它。
TransformStream 的处理逻辑主要在流内部实现,下面是构造一个 TransformStream 时可以定义的方法和参数:
const stream = new TransformStream({
    start(controller) {
        // 将会在对象创建时立刻执行,并传入一个流控制器
        controller.desiredSize
            // 填满队列所需字节数
        controller.enqueue(chunk)
            // 向可读取的一端传入数据片段
        controller.error(reason)
            // 同时向可读取与可写入的两侧触发一个错误
        controller.terminate()
            // 关闭可读取的一侧,同时向可写入的一侧触发错误
    },
    transform(chunk, controller) {
        // 将会在一个新的数据片段传入可写入的一侧时调用
    },
    flush(controller) {
        // 当可写入的一端得到的所有的片段完全传入 transform() 方法处理后,在可写入的一端即将关闭时调用
    }
}, queuingStrategy); // { highWaterMark: 1 }
有了 ReadableStreamWritableStream 作为前置知识,TransformStream 就不需要做太多介绍了。下面的示例代码摘自 MDN,是一段实现 TextEncoderStreamTextDecoderStream 的 polyfill,本质上只是对 TextEncoderTextDecoder 进行了一层封装:
const tes = {
    start() { this.encoder = new TextEncoder() },
    transform(chunk, controller) {
        controller.enqueue(this.encoder.encode(chunk))
    }
}
let _jstes_wm = new WeakMap(); /* info holder */
class JSTextEncoderStream extends TransformStream {
    constructor() {
        let t = { ...tes }
        super(t)
        _jstes_wm.set(this, t)
    }
    get encoding() { return _jstes_wm.get(this).encoder.encoding }
}
const tes = {
    start() {
        this.decoder = new TextDecoder(this.encoding, this.options)
    },
    transform(chunk, controller) {
        controller.enqueue(this.decoder.decode(chunk))
    }
}
let _jstds_wm = new WeakMap(); /* info holder */
class JSTextDecoderStream extends TransformStream {
    constructor(encoding = 'utf-8', { ...options } = {}) {
        let t = { ...tds, encoding, options }
        super(t)
        _jstes_wm.set(this, t)
    }
    get encoding() { return _jstds_wm.get(this).decoder.encoding }
    get fatal() { return _jstds_wm.get(this).decoder.fatal }
    get ignoreBOM() { return _jstds_wm.get(this).decoder.ignoreBOM }
}
Polyfilling TextEncoderStream and TextDecoderStream 源代码,作者 Mozilla Contributors,基于 CC-BY-SA 2.5CC0 协议使用。

到这里我们已经把 Streams API 中所提供的流浏览了一遍,最后是 caniuse 上的浏览器支持数据,可见目前 Streams API 的支持度不算太差,至少主流浏览器都支持了 ReadableStream,读取流已经不是什么问题了,可写入的流使用场景也比较少。不过其实问题不是特别大,我们已经简单知道了流的原理,做一些简单的 polyfill 或者额外写些兼容代码应该也是可以的,毕竟已经有不少第三方实现了。
Image of Streams Support Table by caniuse.com is licensed under CC-BY 4.0.
Streams 浏览器支持总览,作者 caniuse.com,本图片为图表的截图,基于 CC-BY 4.0 协议使用。

在 Service Worker 中使用 Streams API

控制请求的响应速度

首先让我们来模拟体验一下龟速到只有大约 30B/s 的网页看起来是什么样子的:

你会注意到页面中的文字是一个个显示出来的(甚至标题栏也是这样的),其实这是借助 Service Worker 的 onfetch 事件配合 Streams API 实现的。熟悉 Service Worker 的同学应该知道 Service Worker 里有一个 onfetch 事件,可以在事件内捕获到页面所有的请求,onfetch 事件的事件对象 FetchEvent 中包含如下参数和方法,排除客户端 id 之类的参数,我们主要关注 request 属性以及事件对象提供的两个方法:
addEventListener('fetch', (fetchEvent) => {
    fetchEvent.clientId
    fetchEvent.preloadResponse
    fetchEvent.replacesClientId
    fetchEvent.resultingClientId
    fetchEvent.request
        // 浏览器原本需要发起请求的 Request 对象
    fetchEvent.respondWith()
        // 阻止浏览器默认的 fetch 请求处理,自己提供一个返回结果的 Promise
    fetchEvent.waitUntil()
        // 延长事件的生命周期,例如在返回数据后再做一些事情
});
使用 Service Worker 最常见的例子是借助 onfetch 事件实现中间缓存甚至离线缓存。我们可以调用 caches.open() 打开或者创建一个缓存对象 cache,如果 cache.match(event.request) 有缓存的结果时,可以调用 event.respondWith() 方法直接返回缓存好的数据;如果没有缓存的数据,我们再在 Service Worker 里调用 fetch(event.request) 发出真正的网络请求,请求结束后我们再在 event.waitUntil() 里调用 cache.put(event.request, response.clone()) 缓存响应的副本。由此可见,Service Worker 在这之间充当了一个中间人的角色,可以捕获到页面发起的所有请求,然后根据情况返回缓存的请求,所以可以猜到我们甚至可以改变预期的请求,返回另一个请求的返回值。
Streams API 在 Service Worker 中同样可用,所以我们可以在 Service Worker 里监听 onfetch 事件,然后用上我们之前学习到的知识,改变 fetch 请求的返回结果为一个速度很缓慢的流。这里我们让这个流每隔约 30 ms 才吐出 1 个字节,最后就能实现上面视频中的效果:
globalThis.addEventListener('fetch', (event) => {
    event.respondWith((async () => {
        const response = await fetch(event.request);
        const { body } = response;
        const reader = body.getReader();
        const stream = new ReadableStream({
            start(controller) {
                const sleep = time => new Promise(resolve => setTimeout(resolve, time));
                const pushSlowly = () => {
                    reader.read().then(async ({ value, done }) => {
                        if (done) {
                            controller.close();
                            return;
                        }
                        const length = value.length;
                        for (let i = 0; i < length; i++) {
                            await sleep(30);
                            controller.enqueue(value.slice(i, i + 1));
                        }
                        pushSlowly();
                    });
                };
                pushSlowly();
            }
        });
        return new Response(stream, { headers: response.headers });
    })());
});
在 Service Worker 里 Streams API 可以做出更多有趣的事情,感兴趣的同学可以参考下之前提到的那篇《2016 - the year of web streams》

下载一个前端生成的大文件

看着不是很实用?那么再举一个比较实用的例子吧。如果我们需要让用户在浏览器中下载一个文件,一般都是会指向一个服务器上的链接,然后浏览器发起请求从服务器上下载文件。那么如果我们需要让用户下载一个在客户端生成的文件,比如从 canvas 上生成的图像,应该怎么办呢?其实让客户端主动下载文件已经有现成的库 FileSaver.js 实现了,它的原理可以用下面的代码简述:
const a = document.createElement('a');
const blob = new Blob(chunk, options);
const url = URL.createObjectURL(blob);
a.href = url;
a.download = 'filename';
const event = new MouseEvent('click');
a.dispatchEvent(event);
setTimeout(() => {
    URL.revokeObjectURL(url);
    if (blob.close) blob.close();
}, 1e3);
这里利用了 HTML <a> 标签上的 download 属性,当链接存在该属性时,浏览器会将链接的目标视为一个需要下载的文件,链接不会在浏览器中打开,转而会将链接的内容下载到设备的硬盘上。此外在浏览器中还有 Blob 对象,它相当于一个类似文件的二进制数据对象(File 就是继承于它)。我们可以将需要下载的数据(无论是什么类型,字符串、TypedArray 甚至是其他 Blob 对象)传进 Blob 的构造函数里,这样我们就得到了一个 Blob 对象。最后我们再通过 URL.createObjectURL() 方法可以得到一个 blob: 开头的 Blob URL,将它放到有 download 属性的 <a> 链接上,并触发鼠标点击事件,浏览器就能下载对应的数据了。
顺带一提,在最新的 Chrome 76+ 和 Firefox 69+ 上,Blob 实例支持了 stream() 方法,它将返回一个 ReadableStream 实例。所以现在我们终于可以直接以流的形式读取文件了——看,只要 ReadableStream 实现了,相关的原生数据流源也会完善,其他的流或许也只是时间问题而已。
不过问题来了,如果需要下载的文件数据量非常大,比如这个数据是通过 XHR/fetch 或者 WebRTC 传输得到的,直接生成 Blob 可能会遇到内存不足的问题。
下面是一个比较极端的糟糕例子,描述了在浏览器客户端打包下载图片的流程。客户端 JavaScript 发起多个请求得到多个文件,然后通过 JSZip 这个库生成了一个巨大的 ArrayBuffer 数据,也就是 zip 文件的数据。接下来就像之前提到的那样,我们基于它构造一个 Blob 对象并用 FileSaver.js 下载了这个图片。如你所想的一样,所有的数据都是存放在内存中的,而在生成 zip 文件时,我们又占用了近乎一样大小的内存空间,最终可能会在浏览器内占用峰值为总文件大小 2-3 倍的内存空间(也就是下图中黄色背景的部分),流程过后可能还需要看浏览器的脸色 GC 回收。

现在有了 Streams API,我们就有了另一种解决方式。StreamSaver.js 就是这样的一个例子,它借助了 Streams API 和 Service Worker 解决了内存占用过大的问题。阅读它的源码,可以看出它的工作流程类似下面这样:

StreamSaver.js 包含两部分代码,一部分是客户端代码,一部分是 Service Worker 的代码(对于不支持 Service Worker 的情况,作者在 GitHub Pages 上提供了一个运行 Service Worker 的页面供跨域使用)。
在初始化时客户端代码会创建一个 TransformStream 并将可写入的一端封装为 writer 暴露给外部使用,在脚本调用 writer.write(chunk) 写入文件片段时,客户端会和 Service Worker 之间建立一个 MessageChannel,并将之前的 TransformStream 中可读取的一端通过 port1.postMessage() 传递给 Service Worker。Service Worker 里监听到通道的 onmessage 事件时会生成一个随机的 URL,并将 URL 和可读取的流存入一个 Map 中,然后将这个 URL 通过 port2.postMessage() 传递给客户端代码。
客户端接收到 URL 后会控制浏览器跳转到这个链接,此时 Service Worker 的 onfetch 事件接收到这个请求,将 URL 和之前的 Map 存储的 URL 比对,将对应的流取出来,再加上一些让浏览器认为可以下载的响应头(例如 Content-Disposition)封装成 Response 对象,最后通过 event.respondWith() 返回。这样在当客户端将数据写入 writer 时,经过 Service Worker 的流转,数据可以立刻下载到用户的设备上。这样就不需要分配巨大的内存来存放 Blob,数据块经过流的流转后直接被回收了,降低了内存的占用。
所以借助 StreamSaver.js,之前下载图片的流程可以优化如下:JSZip 提供了一个 StreamHelper 的接口来模拟流的实现,所以我们可以调用 generateInternalStream() 方法以小文件块的形式接收数据,每次接收到数据时数据会写入 StreamSaver.js 的 writer,经过 Service Worker 后数据直接被下载。这样就不会再像之前那样在生成 zip 时占用大量的内存空间了,因为 zip 数据在实时生成时被划分成了小块并迅速被处理掉了。

课后习题 Q3:StreamSaver.js 在不支持 TransformStream 的浏览器下其实是可以正常工作的,这是怎么实现的呢?

总结

经过了这么长时间的学习,我们从 Fetch API 的角度出发探索 Streams API,大致了解了以下几点:
  • Streams API 允许我们以流的形式实时处理数据,每次只需要处理数据的一小部分
  • 可以使用 pipeTo()pipeThrough() 方法方便地将多个流连接起来
  • ReadableStream 是可读取的流,WritableStream 是可写入的流,TransformStream 是既可写入又可读取的流
  • Fetch API 的返回值是一个 Response 对象,它的 body 属性是一个 ReadableStream
  • 借助 Streams API 我们可以实现中断 fetch 请求或者计算 fetch 请求的下载速度,甚至可以直接对返回的数据进行修改
  • 我们学习了如何构造一个流,并将其作为 fetch 请求的返回值
  • 在 Service Worker 里也可以使用 Streams API,使用 onfetch 事件可以监听所有的请求,并对请求进行篡改
  • 顺带了解了如何中断一个 fetch 请求,使用 download 属性下载文件,Blob 对象,MessageChannel 双向通信……
Streams API 提出已经有很长一段时间了,由于浏览器支持的原因再加上使用场景比较狭窄的原因一直没有得到广泛使用,国内的相关资料也比较少。随着浏览器支持逐渐铺开,浏览器原生提供的可读取流和可写入流也会逐渐增加(比如在本文即将写成时才注意到 Blob 对象已经支持 stream() 方法了),能使用上的场景也会越来越多,让我们拭目以待吧。

参考答案

  1. 试试看将上面的 fetch 请求用原生 XMLHttpRequest 实现一遍,看看你还记得多少知识?
    const xhr = new XMLHttpRequest();
    xhr.open('POST', 'https://example.org/foo');
    xhr.setRequestHeader('Content-Type', 'application/json');
    xhr.responseType = 'json';
    xhr.withCredentials = true;
    xhr.addEventListener('load', () => {
        const data = xhr.response;
        // ...
    });
    xhr.send(JSON.stringify({ foo: 'bar' }))
    在使用 XHR 初始化请求时会有较多的配置项,虽然这些配置项可以发出更复杂的请求,但是或许你也注意到了,发送请求时既有方法的调用,又有参数的赋值,看下来还是不如 Fetch API 那样直接传入一个对象作为请求参数那么简洁的。此外,如果需要兼容比较早的不支持 XHR 2 的浏览器,你可能还需要改成使用 onreadystatechange 事件并手动解析 xhr.responseText
  2. 如果我们调用了流的 tee() 方法得到了两个流,但我们只读取了其中一个流,另一个流在之后读取,会发生什么吗?
    使用 tee() 方法分流出来的两个流之间是相互独立的,所以被读取的流会实时读取到传递的数据,过一段时间读取另一个流,拿到的数据也是完全一样的。不过由于另一个流没有被读取,克隆的数据可能会被浏览器放在一个缓冲区里,即便后续被读取可能也无法被浏览器即时 GC。
    const file = document.querySelector('input[type="file"]').files[0];
    const stream = file.stream();
    const readStream = (stream) => {
        let total = 0;
        const reader = stream.getReader();
        const read = () => reader.read().then(({ value, done }) => {
            if (done) return;
            total += value.length;
            console.log(total);
            read();
        });
        read();
    };
    
    const [s1, s2] = stream.tee();
    readStream(s1);
    readStream(s2);
    例如在上述代码中选择一个 200MB 的文件,然后直接调用 readStream(stream),在 Chrome 浏览器下没有较大的内存起伏;如果调用 stream.tee() 后得到两个流 s1s2,如果同时对两个流调用 readStream() 方法,在 Chrome 浏览器下同样没有较大的内存起伏,最终输出的文件大小也是一致的;如果只对 s1 调用的话,会发现执行结束后 Chrome 浏览器下内存占用多了约 200MB,此时再对 s2 调用,最终得到的文件大小虽然一致,但是内存并没有及时被 GC 回收,此时浏览器的内存占用还是之前的 200MB。
    可能你会好奇,之前我们尝试过使用 tee() 方法得到两段流,一个流直接返回另一个流用于输出下载进度,会有这样的资源占用问题吗?会不会出现两个流速度不一致的情况?其实计算下载进度的代码并不会非常耗时,数据计算完成后也不会再有多余的引用,浏览器可以迅速 GC。此外计算的速度是大于网络传输本身的速度的,所以并不会造成瓶颈,可以认为两个流最终的速度是基本一样的。
  3. 如果不调用 controller.error() 抛出错误强制中断流,而是继续之前的流程调用 controller.close() 关闭流,会发生什么事吗?从上面的结果来看,当我们调用 aborter() 方法时,请求被成功中止了。不过如果不调用 controller.error() 这个方法抛出错误的话,由于我们主动关闭了 fetch 请求返回的流,循环调用的 reader.read() 方法会接收到 done = true,然后会调用 controller.close()。这就意味着这个流是被正常关闭的,此时 Promise 链的后续操作不会被中断,而是会收到已经传输的不完整数据。
    如果没有做特殊的逻辑处理的话,直接返回不完整的数据可能会导致错误。不过如果能好好利用上的话,或许可以做更多事情——比如断点续传的另一种实现,这就有点像 Firefox 的私有实现 moz-chunked-arraybuffer 了。
  4. StreamSaver.js 在不支持 TransformStream 的浏览器下其实是可以正常工作的,这是怎么实现的呢?记得我们之前提到过构造一个 ReadableSteam 然后包装成 Response 对象返回的实现吧?我们最终的目的是需要构造一个流并返回给浏览器,这样传入的数据可以立即被下载,并且没有多余引用而迅速 GC。所以对于不支持 TransformStream 甚至 WritableStream 的浏览器,StreamSaver.js 封装了一个模拟 WritableStream 实现的 polyfill。当 polyfill 得到数据时,会将得到的数据片段通过 MessageChannel 直接传递给 Service Worker。Service Worker 发现这不是一个流,会构造出一个 ReadableStream 实例,并将数据通过 controller.enqueue() 方法传递进流。后续的流程估计你已经猜到了,和当前的后续流程是一样的,同样是生成一个随机 URL 并跳转,然后返回封装了这个流的 Response 对象。
    事实上,现在的 Firefox Send 就使用了这样的实现,当用户下载文件时会发出请求,Service Worker 接收到下载请求后会建立真实的 fetch 请求连接服务器,将返回的数据实时解密后直接下载到用户的设备上。这样的直观效果是,浏览器直接下载了文件,文件会显示在浏览器的下载列表中,同时页面上还会有下载进度:

参考资料

本文发布自 网易云音乐前端团队,基于 CC BY-SA 4.0 协议 进行许可,欢迎自由转载,转载请保留出处。我们一直在招人,如果你恰好准备换工作,又恰好喜欢云音乐,那就 加入我们
徐九 / 2019-12-23 4 years ago / 未收藏/ SegmentFault 最新的文章发送到 kindle
clipboard.png
2003 年,保罗·格雷厄姆(Paul Graham) 曾撰文提到,他的公司决定使用 Lisp。在该文章中他将 Lisp 描绘成计算机语言界的法语,它独特、深邃,能够表达难以描述的事物。
如果 Lisp 像法语,那么现如今的 JavaScript 就像英语一般。尽管二者的语法不一致,但英语是世界上最广泛使用的语言,JavaScript 是最广泛应用的计算语言。
然而,JavaScript 似乎没有得到相应的尊重。很多大公司的高级工程师声称它不是一门「真正的」编程语言,不知道它除了操作像素外它还有什么用途...
但其实除了样式效果外,JavaScript 也被越来越多地应用于软件开发,例如后端任务、Web 服务器以及数据处理。Zeit 首席执行官 Guillermo Rauch 指出,JavaScript“不是人为设计出来的,它是在进化过程中得到的结果。是市场的力量改造了这门语言。
借着这次 State Of JS 发布的第四版「JavaScript 现状调查报告」,我们一起来看一看 JavaScript 现在到底是个什么情况?

JavaScript 是什么?

clipboard.png
JavaScript(通常缩写为JS)是一种高级的、解释型的编程语言。JavaScript 是一门多范式的,基于原型、函数先行的语言,它支持面向对象编程,命令式编程,以及函数式编程。目前已被世界上的绝大多数网站所使用,也被世界主流浏览器(Chrome、IE、Firefox、Safari、Opera)支持。
虽然 JavaScript 与 Java 这门语言不管是在名字上,或是在语法上都有很多相似性,但这两门编程语言从设计之初就有很大的不同,JavaScript 的语言设计主要受到了 Self(一种基于原型的编程语言)和 Scheme(一门函数式编程语言)的影响。在语法结构上它又与 C 语言有很多相似(例如 if 条件语句、switch 语句、while 循环、do-while 循环等)。
在客户端,JavaScript 在传统意义上被实现为一种解释语言,但在最近,它已经可以被即时编译(JIT)执行。随着最新的 HTML5 和 CSS3 语言标准的推行它还可用于游戏、桌面和移动应用程序的开发和在服务器端网络环境运行,如 Node.js。

2019 年 JavaScript 现状调查报告

State Of JS 方面表示,因 JavaScript 的生态系统发展未能尽如人意的原因,其又发布了 2019年的 JavaScript 现状调查报告。今年的 JavaScript 状态是该调查的第四版,覆盖了21,717位受访者。客户数据可视化专家 Amelia Wattenberger 针对 JavaScript 生态系统进行了全面的概述。
clipboard.png
2016年 - 2019年 趋势图
1.JavaScript 现状
“风味(Flavors)”
截止到 2019 年度,排名第一的为 TypeScript。此外,背后有着 Facebook 支持的Reason 也值得关注,但关注度层面,TypeScript 一枝独秀。
clipboard.png
clipboard.png
前端框架
截止到 2019 年度,排名第一的为 React,但前三名的竞争依然很激烈,
clipboard.png
clipboard.png
资料层
排名第一的依然是 graphql,但 Redux 是目前使用最广泛的工具。同时,Apollo 的用户数也在持续上涨,已从 2018 年的 11.1% 增加到了 24.9%。
clipboard.png
clipboard.png
后端框架
JavaScript 在后端领域近年来似乎没有取得任何重大突破唯一比较亮眼的是 Next.js 表现良好,用户数已从 2018 年的 8.6% 上升到了 24.7%。
clipboard.png
clipboard.png
测试
clipboard.png
clipboard.png
移动和桌面
clipboard.png
clipboard.png
React Native 和 Electron 仍是使用 Web 技术构建移动和桌面应用的两个主要解决方案。
2.其他工具
您经常使用哪些实用程序库?
clipboard.png
其他实用程序
clipboard.png
您经常使用哪种文本编辑器?
clipboard.png
在初始开发过程中,您主要使用哪种浏览器?
clipboard.png
您还使用其他哪些编程语言?
clipboard.png
3.JavaScript 相关意见汇总
JavaScript 朝着正确的方向发展
clipboard.png
现在构建 JavaScript 应用程序过于复杂
clipboard.png
JavaScript在线使用过多
clipboard.png
我喜欢构建 JavaScript 应用程序
clipboard.png
我希望 JavaScript 成为我的主要编程语言
clipboard.png
JavaScript 生态系统变化太快
clipboard.png
4.结论
您知道有时候您有时会开始在这里和那里调整几行代码,然后意识到几个函数可以使用一些重构,然后在建立全新堆栈的几天后发现一件导致另一件事情的事情。完成数据库和GraphQL API?
这就是我们今年发生的事情。虽然这导致了很多紧张的夜晚,但我们试图总结一下并在2019年发布JavaScript 2019状态报告; 这也意味着我们现在有了一个功能更强大,更灵活的堆栈,可以收集和分析以后的数据。
这将有助于分析所有新工具的发展,这些新工具开始引起轰动,例如Svelte,Stencil,Sporky,Nest或Flutter。是的,我只是组成了那五个中的一个,但是承认了:您有片刻的怀疑不是吗?
但是不用担心。无论社区不断向您扔出多少个怪异的库,我们都会在那里帮助您解决所有这些混乱情况……也就是说,如果我们不忙于进行另一次重构以使我们的代码完美无缺!
–萨莎和拉斐尔

官方表示,他们将发布自己的👕JavaScript T恤👕,以资助之后的工作。这件衬衫的特征是“ JavaScript周期表”,其中列出了按类别分组的最受欢迎的库,以及它们的 GitHub 星级数(以千计)。
不得不说,这件衬衫是熟悉 JavaScript 生态系统的路径之一,也是个充值信仰的好方法哈哈哈哈~
clipboard.png
查阅 《The State of Javascript 2019》 完整报告,请扫码关注下方 SegmentFault 官方微信,回复关键字「javascript」即可。
clipboard.png
2019-12-23 4 years ago / 未收藏/ behance发送到 kindle

2019-12-23 4 years ago / 未收藏/ behance发送到 kindle

Design and illustration of the children's book The Nutcracker and the Mouse King, done for Mattson Creative in behalf of their client FAO Schwarz. Illustration of the classic tale originally by ETA Hoffman.
2019-12-23 4 years ago / 未收藏/ behance发送到 kindle

Un objet de collection conjugué au Présent Deux fois par an, la Banque Nationale édite une revue destinée à ses clients les plus exclusifs. Mais comment se distinguer auprès d'un auditoire déjà sursollicité par les publications promotionnelles de luxe? Avec un magazine vraiment centré sur les intérêts de cette cible: Art de vivre, gastronomie, technologie, mode, voyage... Le tout réuni sous un thème porteur : le mouvement. Un service sans lien direct avec les finances, mais qui augmente la valeur de la Banque auprès des clients.
2019-12-23 4 years ago / 未收藏/ behance发送到 kindle

How the 751 Design Store, a commercial entity that focuses on design, can distinguish itself from the 798 style that many people are familiar with through visual language.
2019-12-22 4 years ago / 未收藏/ behance发送到 kindle

Walt Disney Publishing Worldwide Here's some of the very early development and final pieces for Wreck It Ralph 2 at Disney Publishing. Some early development and some final pages. A simple graphic and appealing style as well as dynamic composition being consistent with the movie was the challenge. All you are seeing here can't be copied and it belongs to The Walt Disney Company - Copyright
2019-12-22 4 years ago / 未收藏/ behance发送到 kindle

Fork is a brunch atelier that brings together Parisian heritage with Mexican breakfast tradition. Located in San Luis Potosí, Mexico, the brand identity embraces romantic and nostalgic feelings of chic cafes, along with interiors that pair different materials for a cozy atmosphere.
2019-12-23 4 years ago / 未收藏/ behance发送到 kindle

ENIGMA photo project is an incredibly beautiful series of artistic photographs, fascinating and captivating with their naturalness and mysteriousness. The exhibition features Renaissance-styled shots capturing sensual and romantic, sight-arresting female characters. The project was inspired by the German music band Enigma, which is also the Ancient Greek word for mystery. This fact was vividly reflected in the photo series created by Ruslan.
Bogdan Rancea / 2019-12-23 4 years ago / 未收藏/ ecommerce发送到 kindle
Yes, we’ve heard your requests and finally, here’s the ultimate guide to 10 of the best CRM software you can get for your business.
But, before we begin exploring them, have you ever wondered why most people in the ecommerce space now consider sales CRM systems to be critically important?
Well, the thing is, managing a growing business, however small, is a lot like having an extensive circle of tight friends.
So, imagine trying to keep up with hundreds or possibly thousands of friends at the same time. Where would you even begin?
Now that sums up a problem that today’s businesses are grappling with. While they are always seeking to expand their business processes and take in more customers, maintaining the subsequent relationships over the long haul can be a huge challenge.
Well, that’s exactly where CRM software comes in.

What is CRM Software?

CRM, or Customer Relationship Management Software in full, is a specialized system for driving your customer interactions. And to achieve that, a typical CRM platform comes with a set of tools for managing customer information, running email campaigns, messaging customers via social media, task management, workflow management, etc.
Make no mistake though. The concept of customer relationship management is not new at all. It existed way before ecommerce and online selling was introduced.
But, the thing is, while the previous generation sales CRM systems were entirely manual, today’s SaaS solutions have automated the bulk of lead management tasks. In other words, we’ve moved from writing down customer details in notebooks to streamlining everything via SaaS systems.
CRM software is the secret to seamlessly integrating business intelligence with marketing campaigns, sales automation, and customer support in one centralized platform. That means sales reps get to work hand in hand with their marketing counterparts for the sake of common business goals. All the team members are kept on the same page, with workflow management tools setting a clear path for each individual’s roles.
The success of the entire system depends on how you capitalize on your customer interaction opportunities without compromising the corresponding customer experience. Hence, your CRM software should gather relevant contact information, and then combine it with its business intelligence for meaningful conversations.
And speaking of which, a quick scan through a customer’s profile should give you insights into what they’ve purchased in the past, how they handled the purchase processes, their contact details, as well as the subsequent customer satisfaction levels.
Ok, I know what you might be thinking right now. Don’t these features make a CRM system sound like a regular business spreadsheet? So, what’s the difference between leveraging CRM software and keeping your contact information in one long spreadsheet?
Interestingly, that happens to be a common question when it comes to CRM applications. And as it turns out, quite a huge chunk of small to mid-sized businesses (SMBs) all over the world actually rely on simple spreadsheets for lead management.
However, the fact is, spreadsheets are nowhere close to CRM software. While the former relies on a manual system of managing information, the latter goes beyond that by combining contact insights with automated follow-up processes.
In essence, modern CRM platforms come with a brain that can collect and record your lead information, then consequently use it to drive sales conversations based on the specific rules you set. A CRM software is capable of piecing together all every single bit of data it collects about your leads, and then use the resultant insights to systematically target individuals with personalized messages across multiple channels.

Choosing The Best CRM Systems – State of The CRM Solutions Market

A word of caution though. Although there are numerous CRM software options on the web right now, not all of them offer the functionalities we’ve mentioned. The current CRM market is quite extensive, with all sorts of solutions providing different types of marketing, sales, and pipeline management features.
While the resultant competition between them makes CRM software conveniently cheaper, it also presents a fairly difficult challenge. Sorting through all that forest of potential solutions is no walk in the park.
But, luckily for you, we understand that you may not have the time to conduct a comprehensive evaluation. You’re probably too busy running your business and worrying about acquiring new customers.
So, we took the time to do the heavy lifting for you. Our team here at Ecommerce-Platforms has been evaluating different types of CRM software for the past couple of months, and here’s the resultant report.
This article goes beyond the standard stuff for CRM systems. It’s not just about good CRM software. Rather, we comprehensively evaluated the leading options down to the last detail to establish the ultimate list of the 10 best CRM software.
So, let’s explore every single one of them as we highlight their core features for marketing automation, customer data management, lead scoring, sales forecasting, pipeline management, lead generation, workflow management, project management, task management, sales automation, email marketing, etc.
This guide, therefore, is for serious marketing professionals, sales teams, and business owners that want the best for their companies.
But, if you’re in a hurry, here’s a brief breakdown of our overall best pick, the best budget CRM software, the best CRM software for small businesses, as well as the best free CRM software.
And in case you’d like to discover the formula we used to evaluate them, this article provides pointers on how to choose the best CRM software for your business. You’ll find them towards the end, right after our list of the 10 best CRM software.
So, without further ado…
No time to read? Here's our no.1 pick when it comes to the best overall POS system in the market:
🏁 Our pick:
hubspot crm logo
Although it’s not the most advanced CRM software on the market, HubSpot has managed to establish itself as the overall best CRM software option for numerous businesses and sales teams. Instead of focusing on enterprise-level capabilities, HubSpot’s secret has been maintaining a favorable balance between customer relationship management features, ease of use, and pricing.
The cost here is zero. Hubspot CRM then goes ahead and sets itself apart from the rest of the free CRM software solutions by providing full customer relationship management experience. Small to medium-sized businesses get the benefit of using an intuitive well-designed user interface, coupled with tools for sales management, lead management, pipeline management, sales tracking, marketing campaign management, social media marketing, plus AI insights.
hubspot crm homepage
Setting up the system is extremely simple even for beginners, and you can bet HubSpot CRM will handle a huge chunk of the CRM processes for you. All the customer interactions, for instance, are tracked automatically to generate valuable insights.
You can also expect to have your sales and marketing processes integrated and streamlined accordingly through the HubSpot CRM platform.
Overall rating: 10/10
💰 Budget pick:
zoho crm logo
In all honesty, HubSpot CRM happens to also be a serious contender for our best CRM software budget pick since it’s completely free of charge. But, considering we’ve selected it as the ultimate best CRM software, I guess we could choose the next best budget option.
And yes, just like Hubspot CRM, Zoho CRM is an elegantly-designed solution that allows you to run automated campaigns for free.
Now, to be more precise, Zoho CRM is a lightweight CRM software that’s specially optimized for marketing and sales automation, help desk and customer support, reporting and customer analytics, inventory management, etc. You get to run campaigns on channels like email, social media, live chat, phone calls, web conferencing, and self-service customer portals.
zoho crm homepage
That said, the Zoho CRM free plan offers a document library, plus tools for team collaboration, web forms, marketing automation, basic reports and analytics, workflow management, product customization, and sales force automation. You can leverage it as a team of up to three members.
Then if you choose to upgrade your package, Zoho CRM grants you advanced CRM functionalities on a budget. You can, for example, take advantage of its gamification feature to gamify your entire sales framework and subsequently reward high-performing workers.
Overall rating: 7/10

What Is The Best CRM Software For a Small Business?

All things considered, the likes of HubSpot CRM, Zoho CRM, Agile CRM, and Insightly CRM are the overall best CRM software solutions for small businesses. That said, HubSpot and Zoho are particularly outstanding here because of the exceptionally rich feature sets they provide for free.
Now, as for the ultimate small business CRM, I guess that depends on you. What you’d consider as the overall best depends on your company structure, plus the needs of your sales and marketing processes.
So, to help you make an informed selection, here’s our detailed guide on the best CRM software options for small businesses in 2020.

What Is The Best Free CRM Software?

While there are multiple free CRM software solutions with decent feature offerings, HubSpot CRM is the most dominant one.
This cloud-based CRM software solution is in a world of its own since it provides pretty much all the CRM functionalities you can think of for free. Plus, all that comes with a pleasant, user-friendly interface.
Come to think of it, I guess HubSpot affords to pull this off because the company generates its profits from a wide range of other supplementary ecommerce tools.
Whatever the case, however, don’t get it twisted. While HubSpot CRM is doing a great job in this space, it ‘s not the only perfect option. Of late, several of its competitors have seemingly been giving it a good run for its money. Find out all about them from our comprehensive review of the best free CRM software in 2020.

The 10 Best CRM Software Solutions in 2020

Best CRM Software #1: HubSpot CRM


Ok, I’ll admit that previously, CRM tools were essentially reserved for large enterprises with advanced business processes. But then with time, we started seeing small businesses growing increasingly active in this space, as they began adopting simple but effective CRM systems.
Well, HubSpot CRM happens to be one of the few solutions that triggered this revolution, and it continues to hold on to its top CRM position to date.
That said, there are many reasons why HubSpot CRM is still the talk of the town. The most obvious one is, of course, it’s among the best free CRM software.
Yes, that’s right. HubSpot CRM is completely free of charge. You won’t pay even a dime to use it, despite the fact that it comes with a pretty solid feature set. As a matter of fact, HubSpot CRM even manages to outshine quite a number of premium CRM software when you compare their respective functionalities.
And speaking of which, HubSpot CRM covers all the core channels where ecommerce sales processes occur. You can use it to drive sales via email campaigns, social media, websites, phone calls, and several other digital channels.
In case you’re wondering where to start, integrating HubSpot CRM with your ecommerce ecosystem is fairly easy. You don’t have to be a coding geek to install, customize, and use the app. Plus, it also happens to be one of the best CRM software when it comes to integration capabilities.
Some of the popular platforms you can embed with HubSpot CRM right off the bat include Microsoft Dynamics, WooCommerce, Shopify, Magento, Salesforce, Zapier, Office 365, G-Suite, etc.
Then once you roll it out, you’ll notice that HubSpot CRM offers a pleasantly simple and refined system of managing your customer interactions. The interface itself is very intuitive and user-friendly, thanks to its well-designed layout, straightforward controls, and smooth navigation.
The sales funnel here is displayed in the form of a graphical chart on a visual dashboard, from where you can monitor performance, coordinate scheduled appointments, and manage assigned tasks. Every single customer interaction at this level is logged automatically by the HubSpot platform, and then it proceeds to sync the details with linked services.
Lead management is another task you’ll find to be satisfyingly easy, thanks to HubSpot’s CRM timeline view. It allows you to systematically track your communication with each individual lead, plus launch quick follow-up messages.
Beyond that, HubSpot CRM offers some of the best CRM software tools for pipeline management, tracking all your sales activities, assigning deals to team members, plus running simple marketing campaigns.

HubSpot CRM Software Pricing

Considering it’s a free CRM software, HubSpot won’t charge you anything to use its tools. That means you get to leverage its CRM, marketing, sales, and customer support functionalities for the long haul without incurring any costs.
Some of the platform’s CRM features include: Messenger integration, meeting scheduling, calling, documents, canned snippets, email templates, email tracking and notifications, reporting dashboards, conversations inbox, ad management, forms, ticketing, prospects, custom support form fields, HubSpot Connect integrations, Gmail and Outlook integration, company insights, tasks and activities, deals, companies, contact website activity, plus contact management.
Marketing features, on the other hand, come in the form of: Messenger integration, reporting dashboards, user roles, mobile optimization, conversational bots, live chat, team email, conversations inbox, list segmentation, ad management, email marketing, plus forms.

Who Should Consider Using HubSpot CRM Software?

HubSpot CRM, in a nutshell, is a perfect balance between simplicity, pricing, and functionality. Hence, it’s perfectly ideal for startups and small businesses on a budget. They get to use one of the best CRM tools for free on a permanent basis.
👉 Check out our detailed HubSpot CRM review.

Best CRM Software #2: Salesforce CRM

salesforce crm homepage
While HubSpot CRM is the best at simplicity, Salesforce is the king of advanced cloud CRM.
And why is that so?
Well, interestingly, it turns out that before Salesforce came along, most CRM solutions were fundamentally on-premise systems. Businesses spent a lot of resources plus time to install and deploy CRM software on their in-house servers.
All that changed when Salesforce moved everything to the cloud. Then almost immediately, the costs of installing and running CRM software dropped drastically, allowing even small businesses to join the bandwagon. And as they say, the rest is history.
Salesforce has never looked back since. It, in fact, remains a world leader in not only CRM, but also the entire SaaS spectrum. And as a result, it’s currently recognized as the fifth largest software company across the globe.
Now, for a brand with such a reputation, it comes as no surprise that Salesforce CRM is a holistic, multifaceted suite composed of several different software products. Instead of bundling all its tools into one comprehensive software, Salesforce offers its functionalities through multiple specially-optimized applications.
Apart from Salesforce Sales Cloud, there’s Salesforce Marketing Cloud, Salesforce Service Cloud, Salesforce Analytics Cloud, Salesforce Commerce Cloud, Salesforce Community Cloud, Salesforce IoT Cloud, and Salesforce App Cloud.
That said, the four most essential software products here are Salesforce Marketing Cloud, Salesforce Service Cloud, Salesforce Analytics Cloud, plus Salesforce Sales Cloud.
And as you’ve probably guessed already, the Marketing Cloud is optimized for marketing, Service Cloud comes with customer support capabilities, while Analytics Cloud is all about data and reporting. Then last is the Salesforce Sales Cloud, which happens to be the principal tool when it comes to CRM.
You see, the one thing that makes Salesforce Sales Cloud special is its smooth blend of functionalities from the other three Salesforce applications. It basically provides the full CRM experience by combining marketing automation features from Salesforce Marketing Cloud with customer service functionalities from Salesforce Service Cloud, plus business intelligence capabilities from Salesforce Analytics Cloud.
The result is a well-streamlined CRM platform with everything you’d want for project management, pipeline management, workflow management, contact management, partner management, task management, campaign management, and much more.
This specific Salesforce CRM software was developed to help you personalize conversion experiences for each individual customer. You get to streamline your customer service, billing, finance, marketing, and sales teams to offer customer journeys. Consequently, you should be able to boost your conversion rates, close more deals, and increase your overall sales numbers.
In the meantime, your sales reps will have an easy time handling everything thanks to automated tasks, standardized processes, as well as impactful insights and accurate forecasts from the Salesforce built-in AI.

Salesforce CRM Software Pricing

salesforce crm pricing
Salesforce Sales Cloud offers four subscription-based pricing plans, whose primary features are:
  • Essentials: For $25 per user per month with annual billing, the Essentials plan comes with Google Apps integration, email integration with Outlook, file storage per user, data storage per user, Lightning App Builder, Lightning flow automation (flow and process builder), case management, chatter, topics and recommendations, customizable reports and dashboards, full offline mobile functionality, Salesforce mobile app, task management, activity feed, customizable sales process, opportunity management, account and contact management, email templates, campaign management, mass email, web-to-lead capture, duplicate blocking, lead assignment and routing, lead management. It’s optimized for small businesses with up to 10 users.
  • Professional: The Professional plan will cost you $75 per user with annual billing. It combined all the features on the Essential plan with web services API, developer pro sandbox at an extra fee, developer sandbox, record types (per object), roles and permissions, customizable profiles and page layouts, unlimited custom applications, quotes, products and price books, orders, contracts, forecasting mobile app, collaborative forecasting, sales console app, person accounts, campaign influence, lead registration, plus rules-based lead scoring. The company describes the package as a comprehensive Salesforce CRM that can accommodate both small and large sales teams.
  • Enterprise: The Enterprise plan takes things up a notch by supplementing all the features on the Professional plan with workflow and approvals, Lightning platform, full sandbox, partial sandbox, advanced reporting features, enterprise territory management, opportunity splits, custom opportunity fields in forecasting, calendar, and sales teams. This is an extensively customizable package for enterprise sales teams and it will cost you $150 per user per month with annual billing.
  • Unlimited Plan: The Unlimited Plan gives you all the Enterprise plan features plus multiple sandboxes for development and testing, custom objects, custom tabs, unlimited custom apps, 24/7 toll-free support, over 100 admin services, and unlimited online training. This will cost you $300 per user month when billed annually.

Who Should Consider Using Salesforce CRM Software?

Salesforce Sales Cloud, which happens to be the principal Salesforce CRM software, is ideal for sales teams that want to make insightful decisions, accelerate productivity, get more leads, as well as close more deals.
That said, Salesforce is undeniably the best CRM software for advanced business processes. It’s flexible enough to accommodate even large enterprises managing numerous dynamic workflows at the same time.
👉 Check out our detailed Salesforce CRM review.

Best CRM Software #3: Zoho CRM

zoho crm homepage
Zoho CRM comes from Zoho, a well-renowned company that has been providing a host of SaaS business solutions for a couple of years now. And apart from CRM solutions, it specializes in human resources, IT and help desk, email and collaboration, finance, sales and marketing, plus accounting tools.
While all these software products are pretty good, Zoho CRM is the one that arguably takes the crown. It’s a cloud-based SaaS solution that powers multichannel customer relationship management via an intuitive system.
Social media, phone, surveys, live chat, self-service portals, web conferences, and email are the core engagement channels on the Zoho CRM platform, and they work in tandem to provide a seamless and continuous customer lifecycle. You get to assign every single team member a specific target, and then track their subsequent progress using real-time analytics.
Then to make sales processes much easier for everyone, Zoho CRM includes user-friendly tools for building, as well as customizing automated campaigns. And while you’re at it, you can even take advantage of its libraries plus the REST API to create your own custom solutions and integrations.
That might not be necessary though, since the system comes with some of the best CRM software integrations by default. There are literally hundreds of third-party extensions you could easily embed with your Zoho CRM system to expand its capabilities.
And speaking of which, it’s also possible to connect the platform with your individual leads’ Facebook plus Twitter profiles. The goal here is to capitalize on the full potential of multi-channel experiences and consequently enhance lead generation, customer loyalty, as well as customer satisfaction.
Now, if you haven’t noticed already, there are two sides to the Zoho CRM tale. On one hand, we have customers while on the other, you get to handle leads. Each of them is a distinct entity on its own, and Zoho CRM gives you the privilege to freely categorize your contacts accordingly.
Another benefit that comes with Zoho CRM is mobile app access. It helps you track your sales and marketing processes on the go by providing real-time analytics on both its iOS and Android apps.
It doesn’t stop there though. Turns out you could take your CRM framework a notch higher by upgrading from Zoho CRM to Zoho CRM Plus.
But, make no mistake. I’m not suggesting that the standard Zoho CRM is weak as it is. Far from it to be honest, because this system remains to be one of the best CRM software for small to medium-sized businesses.
Zoho CRM Plus essentially extends the boundaries to unify your sales, marketing, and customer support teams. Their processes are systematically integrated within one centralized interface for a more holistic approach to the touchpoints in your overall customer lifecycle.

Zoho CRM Software Pricing

zoho crm pricing
  • Free: This Zoho CRM option is permanently free and it supports a maximum of three users. Some of its features include: basic customer support, 5, 000 API credits per day per organization, export to Google Tasks, export events to Google Calendar, Zoho CRM contextual gadget for Gmail, integration with Zoho software products, organizational hierarchy with 2 roles, data backup for $10 per request, export module data, import data, data storage (all modules) with 5,000 modules, 1 GB free storage, reviews, file versioning, attach documents, folder sharing, follow-up rules, attach files to feeds, direct messages, status updates, calendar, web-to-contact form, web-to-lead form, 10 email templates, standard reports, 5 task actions, 1 workflow rule per module, custom list views, page customization, advanced filters, CRM views, tasks, events, call log, notes, deals, accounts, contacts, and leads.
  • Standard: The Standard plan will cost you $18 per user per month when billed monthly, or alternatively, $12 per user per month when billed annually. It combines all the features on the Free package with classic customer support, 1000 email sends per day, synchronize Google Contact, Synchronize Google Calendar, Zoho CRM web forms for Google Sites, Slack Integration, allowed IPs, audit logs, 20 groups, organizational hierarchy with 5 roles, 5 profiles, 2 data backups per month, data storage with 100,000 records, additional storage for $4 per month per 5 GB, 512 MB free storage per user, tagging, groups for team collaboration, calendar booking, calendar sync through CalDAV, Notify owner, marketing campaigns, 250 mass emails per day, 100 email templates, social lite, social interaction with leads/contacts, automated lead generation from social media, social tab, enrich data with Facebook profile, enrich data with Twitter profile, 10 custom dashboards, standard dashboards, KPI, charts, schedule reports, 100 custom reports, standard reports, convert action, custom fields, reminders, scoring rules, email insights, BCC dropbox for email, and sales forecasting.
  • Professional: The Professional plan costs $30 per user per month when billed monthly, or $20 per user per month when billed annually. It combines all the features on the Standard package with a plug-in for Microsoft Office, a plug-in for Microsoft Outlook, Zoho Finance Suite integration, Zoho Writer add-on, Zoho Mail add-on, 30 groups, field-level security, unlimited data storage, web-to-case form, solutions, cases, purchase orders, vendors, invoices, sales orders, sales quotes, price books, products, inventory management, gamescope, Zoho CRM for Google Ads, email relay, business process management, assignment rules, webhook action, field update action, validation rules, custom links, email association with deals, and SalesSignals.
  • Enterprise: The Enterprise plan costs $45 per user per month when billed monthly, or alternatively, $35 per user per month when billed annually. It supplements all the features on the Standard package with connections, widgets, web apps SDK, mobile SDK, Zoho SalesInbox, integration with Zoho Creator, Zoho Backstage integration, Zoho Webinar integration, territory management, data encryption (EAR), record-level sharing, 50 groups, organizational hierarchy with 250 roles, 25 profiles, anomaly detectors (powered by Zia), comparator, target meter, funnels, macro suggestion, best time to contact, Zia Reminder, Zia Voice, data enrichment, deal closure prediction, lead conversion prediction, case escalation rules, schedules, approval process, scheduled actions, functions, reporting hierarchy, translations, subforms, layout rules, canvas view, page layouts, sandbox, custom buttons, custom modules, web tabs, picklist history tracking, email parser, contextual custom related lists, and multiple currencies.

Who Should Consider Using Zoho CRM Software?

While Zoho CRM is considerably light, it packs a robust feature set that provides some of the best CRM software functionalities. Plus, the platform itself has a well-structured framework with multiple control points for medium-sized teams.
Hence, it’s evident Zoho CRM was built for midsize businesses that interact with their customers across email, social media, phone, live chat, phone, surveys, and web conferences. They’d certainly appreciate the capabilities that come with Zoho’s enterprise-level tools.
Companies already using other Zoho software products are noticeably the best candidates here, since they’d combine multiple Zoho tools to achieve a comprehensively streamlined business ecosystem.

Best CRM Software #4: Agile CRM


Agile CRM is another all-in-one customer relationship management solution with just enough tools for all the typical business scales. In addition to contact management, it’s specially-optimized for social media marketing, sales and marketing automation, targeted landing pages, project management, email tracking, scheduling, customer service, plus much more.
That said, one of the first things that’ll strike you about Agile CRM is its free plan, which supports up to 10 users concurrently and 1,000 contacts. It then combines that with basic sales management, marketing management, and customer support features.
So, freelancers, startups, plus small businesses on a budget should feel right at home.
But, don’t feel obligated to start off with the free plan. If your needs extend beyond its capabilities, you can go ahead and settle for any of the premium packages. I find them to be quite affordable.
Now, when you finally start using Agile CRM to drive customer interaction, you should be able to methodically combine its multiple tools to boost both your marketing and sales teams’ efforts.
You could, for instance, manage your contacts using lead scoring, custom contact fields, and contact grouping functionalities. Agile CRM is even capable of diving deep into social media to help you with social media marketing, social CRM, plus social monitoring.
And speaking of which, Agile CRM is one heck of an effective customer relationship management system when it comes to monitoring and tracking. In fact, I’d say it’s one of the best CRM software options for teams that are particularly concerned about tracking and analytics.
They can comfortably use Agile CRM for monitoring their website visitors, assessing their subsequent behaviors, email tracking, social monitoring, contact level analytics, and custom deal tracking. The resultant analytics come in handy when you’re building and following up on your campaign workflows.
All the workflows here are built via a smooth drag and drop interface, which allows you to customize automation triggers, campaign nodes, system actions, groups, etc. Agile CRM also happens to come with a landing page builder, an email templates builder, and a form builder.
As you continue leveraging them, you could coordinate your team’s efforts using Agile CRM’s task management features. All it takes is just dragging and dropping custom tasks into well-organized lists, arranging tasks accordingly, making notes on them, as well as updating their status every now and then.
Another benefit you can expect from Agile CRM is extra functionalities thanks to a host of smart integrations. Some of its email integrations, for example, include IMAP, Office 365, Microsoft Exchange, and Gmail services.
Otherwise, you should also be able to link it with social media channels like Twitter, LinkedIn, and Facebook. This allows you to easily publish posts as well as respond to your followers’ messages.
But, make no mistake. Social media and email and not the only channels you can use on Agile CRM. This platform is, rather, built for omnichannel engagement, with the primary channels being email, social media, text messaging, as well as web messaging.
And to be more precise, web messaging entails push notifications, landing pages, forms, and popups.

Agile CRM Software Pricing

agile crm pricing
  • Free: The Free plan facilitates 10 users with 500 API calls per day, landing pages, basic email and team performance reports, Chrome extension, Google sync, email support, 1 app integration, reports, activities, views, canned responses, labels, groups, helpdesk, 1 automation rule (trigger), 5 nodes per campaign, 1 campaign workflow, web-to-lead, 5,000 emails (branded), landing page builder, email templates builder, form builder, contact level analytics, web engagement, email campaigns, email tracking, custom deal milestones, appointment scheduling, unlimited deals, unlimited tasks, unlimited documents, lead scoring, custom data fields, plus 1,000 contacts.
  • Starter: The Starter plan starts at $8.99 per month and it supplements all the features on the Free plan with 5, 000 API calls per day, phone support, 3 integrations, knowledgebase, 5 automation rules (triggers), 10 nodes per campaign, 5 campaign workflows, social monitoring, marketing automation, 2-way email integration, plus 10,000 contacts.
  • Regular: The Regular plan starts at $29.99 per month and it supplements all the features on the Starter plan with 10,000 API calls per day, more than 50 integrations, 10 automation rules (triggers), 25 nodes per campaign, 10 campaign workflows, mobile marketing, 2-way telephony, custom deal tracking, plus 50,000 contacts.
  • Enterprise: The Enterprise plan starts at $47.99 per month and it supplements all the features on the Regular plan with 25,000 API calls per day, Access Controls (ACLs), dedicated account rep, more than 50 integrations, unlimited automation rules (triggers), 50 nodes per campaign, unlimited campaign workflows, SES/Mandrill/Sendgrid integration, Automated Voicemails, Post-call Automation, plus unlimited contacts.

Who Should Consider Using Agile CRM Software?

Going by its pricing and features, we can agree that Agile CRM is best for freelancers, startups and small businesses. The free plan provides a perfect opportunity for beginners to learn what customer relationship management is all about, plus try out various basic customer interaction and marketing tools that come with it.
Beyond that, the farthest Agile CRM can stretch is probably mid-sized businesses. Its Enterprise plan can easily accommodate midsize companies that have typical marketing and CRM needs. They get to interact with an unlimited number of contacts using an unlimited number of campaign workflows, as well as an unrestricted volume of automation triggers.

Best CRM Software #5: Insightly CRM


Insightly CRM is an advanced SaaS customer relationship management system with extensive experience in delivering projects, accelerating sales, generating quotes, and building relationships. It’s one of those top CRM software products that provide robust Outlook and Gmail plugins for seamless email integration.
So far, Insightly CRM has attracted more than 1.5 million users, who’ve leveraged its tools for call management, advanced reporting, project management, workflow automation, quote generation, opportunity management, lead management, email tracking, contact management, and relationship linking.
While some of these might seem like complex enterprise-level functionalities, Insightly CRM has managed to simplify everything for SMBs. Consequently, you don’t need extensive technical knowledge or expert guidance to build and manage your campaigns. The simple tools combined with third-party app integrations make it conveniently easy to do anything here.
More specifically, you should be able to manage not only your contacts and leads, but also suppliers, vendors, partners, plus organizations. Insightly CRM has a well-streamlined information system that allows you to follow up on each entity’s important dates, email history, background, plus much more.
And if you ever need to link opportunities, Insghtly’s project management function has got you covered. It can quickly connect opportunities and projects to make tracking and follow-ups easier.
And speaking of which, opportunities here can be managed in several different ways. Insightly CRM gives you the power to come up with your own opportunities, as well as coordinate the subsequent activities via pipeline management.
You also get to dive into various funnels to tweak their categories, priorities and stages based on their close date predictions, plus the corresponding win probabilities.
It doesn’t end there though. Insightly CRM happens to be extremely robust in Social CRM too. By integrating your system with social media, you should be able to access your contacts’ Facebook posts, LinkedIn profiles, as well as Tweets from their Twitter accounts.
Other notable integrations you’ll find here include QuickBooks, DropBox, Box, Evernote, MailChimp, Outlook, Google Docs, Google Calendar, Office 365, Google Drive, etc. Some of these can also work well with Insightly CRM’s Android and iOS mobile applications.

Insightly CRM Software Pricing


  • Plus: For $29 per user per month with annual billing, the Plus plan offers standard support, Outlook integration, Gmail integration, over 250 integrations to ecommerce apps, customized report scheduling and emailing, built-in business intelligence dashboards, conversion of won opportunities to projects, calendar synchronization, milestones and associated task tracking, integrated project management, native Android and iOS mobile apps, batch update records, assign sales teams, apply task and event activity sets, customizable sales processes, opportunity management, relationship linking, task management, event management, contact management, organization management, automatic social profile enrichment, web to lead capture, visualize leads with kanban, schedule outbound emails, email templates, mass emailing, automatic duplicate detection, lead assignment and routing, plus lead management.
  • Professional: For $49 per user per month with annual billing, this plan supplements all the features on the Plus plan with customized support and services, configurable webhooks, unlimited configurable profiles and page layouts, unlimited account roles and permissions, custom object creation, complete workflow automation, 100 customizable real-time insight cards, automatic call analysis and reporting, plus integrated phone dialer and call logging.
  • Enterprise: For $99 per user per month with annual billing, this plan supplements all the features on the Professional plan with developer API support and professional services at an extra cost, schedule serverless lambda functions, dynamic page layout rules, Lambda function authoring and execution, deploy custom apps on any platform, calculated fields on any object, custom validation rules, products, price books, quotes, unlimited customizable real-time insight cards, plus extensive mobile business card scanning.

Who Should Consider Using Insightly CRM Software?

While Insightly CRM has extremely powerful tools, everything has been well-structured to make the platform user-friendly without compromising scalability. Additionally, this is one of the few best CRM software products that are flexible enough to cover pretty much all the core business processes, from opportunities to project delivery.
Now, when you combine all that with its relatively affordable pricing, you have yourself the right CRM tool for both experienced and inexperienced SMB teams.

Best CRM Software #6: Pipedrive CRM


Pipedrive isn’t just a good CRM software. It’s much more than that. And, in fact, it touts itself as the best CRM software and pipeline management system.
The system itself was built after sales professionals and web app developers embarked on a project with one common vision. They all wanted a platform that would make selling easy.
Well, it turns out Pipedrive has actually managed to achieve that, thanks to its well-balanced feature set. The trick Pipedrive CRM has seemingly used is blending its solid direct sales foundation with smart tracking tools and an intuitive interface. The end result is a strategically streamlined CRM system that offers a clear view of the entire sales process.
Now, in simple terms, Pipedrive CRM provides a simplified but detailed overview of your entire sales system. You get to arrange all the leads’ paths accordingly so you can track their progress, as well as identify priority deals right off the bat.
All that is delivered through a streamlined sales pipeline that follows a standard pipeline structure. The deals here are organized in stages to help you assess the progressive performance of your sales teams, plus the specific deals they should focus on.
And that’s not all. Pipedrive further allows you to go beyond that level by providing additional tools for tracking your individual sales team members. You can judge their progress in real-time from either your PC or the mobile app version of Pipedrive CRM.
It’s worth noting, however, that the sales pipeline here is not restrictive. Pipedrive comes with customization privileges for adjusting its fields based on your specific business setup. Hence, you should have an easy time editing various pipeline stages, disabling modules, and introducing your own parameters.
Such system features help users align Pipedrive CRM with their core business processes. In the end, this translates into increased data accuracy and efficient pipeline management. So, of course, you can rely on Pipedrive CRM to spot the most promising deals and activities.
Now, if you like to stay on top of your business at all times, then you should consider combining the Pipedrive PC system with its corresponding mobile app. Yes, that’s right – apart from a web SaaS version, Pipedrive CRM comes in the form of iOS and Android apps. They’re pretty handy when you need to follow up on things remotely and on the go.

Pipedrive CRM Software Pricing


  • Essential: The Essential Plan is for following up deals using simple pipelines. It will cost you either $15 per month per user when billed monthly, or $12.50 per user per month when billed annually. Some of its outstanding features include: user and company goal-setting, Pipedrive Developer Center access, Pipedrive Academy, 24/7 customer support, Pipedrive Chrome extension for Gmail, contact sync with Google and Microsoft, native mobile apps for Android and iOS, sales and activity reports, automatic call logging, real-time sync across all devices, customizable dashboard, performance goals, BCC email inbox, manual linking of emails to contacts and deals, activities and calendar view, custom fields, plus custom sales pipeline and stages.
  • Advanced: The Advanced Plan offers additional tools for seamlessly automating and tracking your workflows. It will cost you either $29 per month per user when billed monthly, or $24.90 per user per month when billed annually. Some of its main features include: important complete fields, product catalog, one-click contact data, email templates and automation, email opens and clicks, full email integration, AI sales assistant, sales task automation, plus workflows and activities.
  • Professional: The Pipedrive CRM Professional Plan provides additional tools for collaborating with your team to increase sales. It will cost you either $59 per month per user when billed monthly, or $49.90 per user per month when billed annually. Some of its principal features include: contact and deal visibility settings, custom user, admin and manager permissions, revenue forecasting, multiple dashboards, team management, batch emailing, 24/7 phone support, unlimited meeting scheduler, plus click-to-call and call tracking.
  • Enterprise: The Pipedrive CRM Enterprise Plan supplements Professional Plan features with specialized customer support and onboarding. It will cost you $99 per user per month when billed annually for a minimum of 10 users. Some of its benefits include: extra user permissions to control what users see and do, enhanced security to protect revenue and data, supplementary customization capabilities to suit your business, managed data import, phone support, plus a dedicated account manager.

Who Should Consider Using Pipedrive CRM Software?

Pipedrive CRM is extremely easy to use, intuitive, and perfectly optimized for pipeline management. It was built by sales professionals for sales teams to conveniently manage their sales funnels at an affordable price.
That alone makes it ideal for anyone who’d like to close deals faster by identifying and subsequently prioritizing the most promising sales opportunities.

Best CRM Software #7: SugarCRM


In addition to featuring prominently among the best CRM software, SugarCRM happens to be one of the most widely used CRM tools on the web. So far, it has been downloaded and tried out more than 7 million times across numerous countries, and the numbers are still growing.
While there are multiple reasons behind SugarCRM’s increased uptake, many users have particularly applauded its excellent customer support, brand promotion tools, customer engagement capabilities, and extensive customizability.
Well, by now, we’ve explored quite a number of top CRM software that provide solid customization options. But, get this- the bulk of them can’t possibly match up to SugarCRM. The flexibility it offers makes it one of the most customizable CRM systems on the market.
And its trick, as a matter of fact, is pretty simple. You see, SugarCRM is an open-source CRM software. That means it provides access to its underlying source code, subsequently allowing developers to extensively tweak its default CRM functionalities. So, as long as you have the skills, you can customize the whole platform down to the smallest detail.
But then again, come to think of it, you probably won’t need that at all since SugarCRM comes fully loaded with pretty much all the essentials. It’s particularly reliable in generating intelligent reports, team management, launching and managing campaigns, streamlining the help desk, as well as integrating with a host of helpful third-party apps.
If you’re working with a team, you’ll be pleased that the default SugarCRM platform is optimized for up to 8 user types. The primary admin account can be supplemented by different types of secondary accounts, from customer support to sales reps and possibly marketers running campaigns on various channels. SugarCRM even allows you to roll this out on a large scale with groups of individuals sharing the same account privileges.
In the end, you should have a well-networked CRM system that facilitates seamless collaboration between team members. That means marketers, sales reps, and customer service professionals complement each other’s roles as they gradually enhance customer relationships.
Then to help you build effective campaigns, SugarCRM provides an intelligent campaign wizard. It packs a wide range of tools to support your campaign creation process, as well as the subsequent execution phase.
You can, for instance, use it to come up with strategic campaigns based on your expected ROI, budget, desired audience, plus target goals. It’s also quite handy in picking up and compiling leads, plus disseminating forms within the interaction framework. Otherwise, you might also want to analyze your leads, rate them accordingly, and then match them with the right agents.
Another area where SugarCRM really shines is contact management. It’s built to dig deep into your contact data, analyze everything comprehensively, and then compile any related profile info. That means it’s even capable of picking up the tiniest behavioral indicators that often go unnoticed.
SugarCRM doesn’t stop there though. The intelligent system goes ahead and offers additional assistance in the subsequent contact organization process. You should be able to sort out your contacts based on priority, mark them accordingly, link huge files, as well as customize notes and documents.
When it comes to integrations, however, it turns out SugarCRM doesn’t link natively with third party services. That shouldn’t get you worried though, because the customer relationship management software manages to leverage an API code called SugarExchange to connect with other applications.
With that, you should be able to integrate SugarCRM with a wide range of stuff including Pardot, Zendesk, MailChimp, CloudAgent, Zapier, etc.

SugarCRM Software Pricing


  • Sugar Professional: Sugar Professional is On-premise CX software built to serve small businesses that are increasingly growing. It starts at $40 per user per month with annual billing. Some of its features include; unlimited online support, support for MySQL and SQL Server-On site, unlimited studio customizations, cloud or on-premise deployment, 15 GB storage, SugarCRM mobile, call center automation, reporting and dashboard, marketing lead management, 2 support-authorized contacts, support automation, plus sales automation and forecasting. It hosts a minimum of 10 users.
  • Sugar Enterprise: Sugar Enterprise is a highly customizable on-premise CX package. It will cost you at least $65 per user per month with annual billing. Some of its features include; 4 support-authorized contacts, support via phone and SugarCRM Support Portal, accelerated support SLA, SQL-based reporting, advanced workflow with SugarBPM, 60 GB Storage, Support for Oracle and DB2-On site, role-based views, Product level quotes, revenue line item level opportunity tracking forecasting, plus all the features from the professional plan. It supports a minimum of 10 users.
  • Sugar Serve
sugar serve
Sugar Serve offers tools optimized for customer support. It will cost you at least $80 per user per month with annual billing. Some of its features include; phone support, 60 GB storage, 2 sandbox instances, 4 support-authorized contacts, reporting and analytics, a self-service portal, time-based reassignment and escalations, case routing, automatic SLA management, case management, plus service console. This package supports a minimum of 10 users.
  • Sugar Sell:
sugar sell
Sugar Sell is for boosting your sales and helping you establish lasting relationships with your customers. It will cost you at least $80 per user per month with annual billing. Some of its features include; robust business process management, phone support, 60 GB storage, 2 sandbox instances, 4 support-authorized contacts, lead routing, quote approvals, customer email notifications, sales forecasting, product catalog management, quote management, reporting and dashboards, lead management, opportunity management, account management, plus contact management. This package supports a minimum of 10 users.
  • Sugar Market:
sugar market
Sugar Market provides marketing features for running automated marketing campaigns. It starts at $1,000 for 10,000 contacts with annual billing. Some of its features include; real-time website data, social media marketing tools, 4 support-authorized contacts, phone support, unlimited emails, unlimited landing pages, Google Ads integration, built-in SEO audit, custom surveys, event marketing management, form builder, landing page builder, email marketing tools, integration with all major CRMs, lead scoring, nurture marketing, plus marketing reports and analytics. This package supports an unlimited number of users

Who Should Consider Using SugarCRM Software?

SugarCRM manages to cater for not only marketers and sales teams, but also customer service teams in SMBs. Sales professionals in small businesses would find the Sugar Professional package to be quite handy, while their mid-size business counterparts would probably go for Sugar Enterprise or Sugar Sell.
Customer service teams, on the other hand, should be comfortable with SugarCRM’s Sugar Serve solution, which offers a host of customer support tools. Then marketing teams would be better off with the Sugar Market package, which is perfectly optimized for digital marketing campaigns across social media, email, landing pages, forms, and Google Ads.

Best CRM Software #8: Freshsales CRM


By now, you’ve probably heard about Freshworks. It’s a renowned business applications company and it happens to one behind the eighth tool in our best CRM software list.
Now, going by the experience Freshworks has had in the ecommerce software space, you can bet Freshsales CRM is a quality, well-developed CRM tool. As a matter of fact, many of its users consider it a comprehensive contact management system that enhances their lead control and engagement capabilities through an intuitive, user-friendly interface.
In essence, they continue to take advantage of Freshsales CRM to attract, nurture, manage, and successfully convert their leads.
To achieve that, this elegantly-designed system offers customizable reports and dashboards, intelligent workflow automation, a visual sales pipeline, AI-powered lead scoring, built-in phone call and email, event tracking, sales lead tracking, activity monitoring, and more.
Now, as you’ve probably guessed already, the user interface is one of the most notable things about Freshsales CRM. Freshworks has designed it with a focus on ease of use as well as comprehensive control of the customer lifecycle.
That means the last thing you can expect here is a steep learning curve. Even beginners should be able to hit the ground running as soon as they sign up for the service.
And when it comes to managing the customer journey, Freshsales CRM comes with an incredibly rich but simple lead management feature set. The tools here help you prioritize the most important leads while getting rid of the ones that seem to provide little value.
Then as you set up a solid interaction framework, you should be able to organize your leads by grouping them systematically. Such groups could subsequently be assigned to various team members, who would then proceed to target the leads with relevant conversion strategies.
In the meantime, you get to track their overall progress from your sales pipeline. Freshsales CRM even provides quite a handy drag-and-drop editor for building and customizing your pipelines. It allows you to configure different deals based on how your entire sales framework is set up.
Well, all things considered, the CRM pipeline here fundamentally seeks to help you sell more and much faster. Its intuitive visual structure is perfectly optimized to give you a seamless experience while you assess, organize, and prioritize your sales deals. Then by focusing on the most critical ones, you should be able to close them much faster, before moving on to the rest.
That said, a single click should be enough to reveal the status of any deal along the pipeline. Otherwise, you could also generate detailed reports for deeper analysis of how your lead engagements are progressing.
And speaking of reports, Freshsales CRM offers advanced reporting and analytics tools, which you can capitalize on for accurate insights about your leads. The platform’s AI engine is capable of assessing not only your leads’ behaviours, but also their respective behaviors. Then subsequently, Freshsales CRM’s lead scoring system grades them from the warmest leads to the ones who might need some additional nurturing.
In addition to that, the analytics function generates details about the context plus engagement levels of every single lead. And if you want more, Freshsales CRM’s 360 degree tool provides comprehensive info about all the leads and customers, including their corresponding tasks, appointments, touchpoints, as well as conversations.
With that, therefore, your sales team should have an easy time establishing the best approaches for various deal opportunities.
And to help you with the subsequent lead engagement process, Freshsales CRM comes with a host of pre-built integrations. You can easily embed ecommerce, accounting, social media, marketing, and task management apps.
They include; Xero, QuickBooks, MailChimp, Google Apps for Work, Hubspot Marketing, the Freshdesk suite, Facebook, Trello, Magento, etc.

Freshsales CRM Software Pricing

freshsales crm pricing
  • Blossom: The Blossom plan is intended to facilitate small sales teams and small businesses. They, in turn, are required to pay either $12 per user per month when billed annually, or $19 per user per month. The perks include: API access, marketplace apps, Freshmarketer, Freshworks integrations, SMS integration, phone powered by Freshcaller, lead scoring, 2-way email sync, visual sales pipeline, lead management, contact management, account management, and deal management.
  • Garden: The Garden plan is intended to facilitate growing sales teams and businesses. They, in turn, are required to pay either $25 per user per month when billed annually, or $35 per user per month for advanced reports, custom roles, smart matches, territories and lead assignment, 10 user teams, 10 workflow automation, 5 sales sequences per user, 250 bulk emails per user per day, API access, marketplace apps, Freshmarketer, Freshworks integrations, SMS integration, phone powered by Freshcaller, lead scoring, 2-way email sync, visual sales pipeline, lead management, contact management, account management, deal management, and custom roles.
  • Estate: The Estate plan is intended to serve large sales teams and businesses. It costs either $49 per user per month when billed annually, or $65 per user per month. The resultant features include: sales goals, multi-currency support, smartforms and event tracking, reports dashboard, auto profile enrichment, advanced CRM customization, advanced reports, custom roles, smart matches, territories and lead assignment, 25 user teams, 25 workflow automation, 10 sales sequences per user, and 1,000 bulk emails per user per day.
  • Forest: The Forest plan targets enterprises and you can only purchase it on with the annual billing option for $79 per month. This will give you a dedicated account manager, audit logs, IP whitelisting, EEA data center, sales goals, multi-currency support, smartforms and event tracking, reports dashboard, auto profile enrichment, advanced CRM customization, advanced reports, custom roles, smart matches, territories and lead assignment, 50 user teams, 100 workflow automation, 25 sales sequences per user, and 5,000 bulk emails per user per day.

Who Should Consider Using Freshsales CRM Software?

Freshsales CRM is essentially what you get when you combine ease of use, a visual but customizable sales pipeline, plus a rich leads management feature set on a platform that can accommodate both small and large sales teams.
Hence, this is one of the best CRM systems for SMB and enterprise sales teams working in a dynamically demanding business environment. it helps you enhance your sales force by streamlining your leads pipelines through intelligent insights.

Best CRM Software #9: Nimble CRM

nimble crm homepage
Before we get to the specifics, here’s an interesting fact about Nimble CRM. It happens to be closely related to yet another renowned CRM tool called Goldmine. Turns out they were both founded by a guy named Jon V. Ferrara.
While Goldmine CRM doesn’t feature in our best CRM software list, it’s still a legend in the customer relationship management industry. The reason being, it was among the premier sales force automation CRM tools for small to medium-sized businesses. And believe it or not, that occurred in 1989.
But, although Goldmine CRM has been dominant to this day, Nimble CRM is the one that has managed to perfectly implement modern automation advancements.
Consider the software hosting environment, for instance. While Goldmine CRM is still an on-premise solution, Nimble CRM has been developed to be a cloud-based Saas platform. This allows it to not only scale easily, but also extend its functionalities beyond the scope of traditional customer relationship management systems.
Consequently, Nimble CRM is now one of the best CRM software products for sales force automation, social CRM, as well as contact management. The automation process itself can be controlled on both its web-based PC dashboard, as well as its corresponding Android and iOS mobile apps.
In fact, it’s pretty darn good at automating stuff that it can lift details from social media, email conversations, plus multiple other platforms, and then automatically update the corresponding customer profiles.
This, in other words, is called social listening and you can take advantage of it to find out what your contacts are saying about your business on social media. Otherwise, you might also want to use the resultant insights for touchpoints.
If you discover a contact is celebrating their birthday, for instance, you could convert the milestone into an engagement opportunity that might possibly develop into a successful sale. Then in the meantime, you’d get to use Nimble CRM’s pipeline management, task management and deal tracking tools to progressively nurture the lead.
Nimble CRM even makes the follow ups much easier through its “Today Page”. This is essentially a section that comprehensively displays your day to day engagement opportunities, key deals, and to-dos.
Now, the opportunities themselves are derived from Nimble CRM’s AI engine, which I tested and found to be quite impressive. It’s capable of identifying the most valuable opportunities and contacts by analyzing user behavior.
Another tool you’ll find loaded with your contacts’ info is Nimble CRM’s widget. In essence, the software comes with an array of widgets that allow you to conveniently access contextual details without necessarily logging into the system.
Now, as you continue shifting between the various tools, you’ll notice that everything here is holistically connected. From the sales pipeline, for example, you can easily view a history of your interactions with a specific contact by simply clicking on a deal.
And the same, as a matter of fact, applies to the contact profiles. Nimble CRM automatically updates your contact profiles with information from your sales pipeline deals, as well as other sources within and beyond the platform.
That said, the deals come in two display formats. You could either track them from the sales pipeline or simply view them in a list. Nimble CRM even provides a couple of sales stages by default to help you organize everything accordingly.
But, make no mistake. You don’t have to use the default framework. If your sales process is structured differently, you’re free to customize the stages.
Whichever system you choose to adopt in the end, Nimble CRM will still provide you with insights about the most promising opportunities.
Now, if you’re working with a team, you can go ahead and use Nimble CRM’s task management tools to assign the opportunities accordingly. You could also create new tasks, as well as configure their parameters to link them with specific deals and contacts.
You might also want to embed your system with Gmail or any IMAP account before you roll out your email campaigns. The good thing about this approach is, the system will maintain a detailed log of all your interactions.

Nimble CRM Software Pricing

nimbl ecrm pricing
For $25 per user per month when billed monthly or $19 per user per month when billed annually, Nimble CRM offers API access, two-way sync with over 160 applications, Office 365 and G Suite support, one on one consultation, free onboarding assistance, comprehensive support center, online and email support, iPhone and Android apps, smart contacts app, team permissions, plus sales forecasts and reports.
Then when it comes to building relationships, you get deal pipeline management, Nimble Prospector, call logging, task reminders, stay-in-touch reminders, activity tracking, email tracking, email templates, 100 group messages per user per day, business insights capture, email signature capture, social profile match, social media signals, 2 GB per user storage, saved search segments, unlimited saved search segments, 1,000 custom fields, email message syncing, 25,000 contact records, calendar sync, contact management, and a unified message inbox.

Who Should Consider Using Nimble CRM Software?

Nimble CRM is a holistically interlinked platform that provides a host of smart sales force automation capabilities, without the complications that usually accompany advanced marketing automation.
It’s the type of customer relationship management system you could use as an individual or team for powering dynamic sales pipelines. You get to combine smart actionable insights with a reliable AI engine and integrations.
Therefore, all in all, Nimble CRM is perfectly ideal for sales teams that need to make well-informed decisions about their pipelines.

Best CRM Software #10: NetSuite CRM

netsuite crm homepage
Tenth on our list of the best CRM software in 2020 is NetSuite CRM, which is yet another cloud-based SaaS customer relationship management solution.
Now, to be more specific, you can think of Netsuite CRM as a platform that facilitates a smooth uninterrupted flow of info across the entire customer lifecycle. Its info funnel starts from the lead level and it subsequently follows a customer through every single stage, including opportunity, sales order, cross-selling, upselling, fulfillment, renewal, and customer support.
All this information is held in a well-protected data center, which is part of Netsuite CRM’s dynamic multi-tenant architecture. The resultant details give you a strategic 360 degree view of all the critical parameters relating to your leads and customers.
Netsuite CRM, on the other hand, uses the intelligence to power its entire customer relationship management ecosystem. Sales force automation tools, for instance, heavily rely on the data to run effective campaigns based on actionable insights.
This, however, is only one part of Netsuite CRM. The platform happens to be pretty extensive, with customer relationship management functionalities being supplemented by marketing automation tools, customer service management features, as well as integrated ecommerce capabilities like order management.
Hence, you can leverage Netsuite CRM for functions like marketing analytics, partner relationship management, customer data management, sales forecasting, and much more. All the tools are holistically integrated for form an all-in-one suite that’s accessible via PC, plus Android and iOS mobile apps.
That said, customer relationship management here relies on both traditional approaches as well as advanced intelligence. You get to drive business growth using automated tools, workflow management, contact management, collaboration capabilities, campaign management, sales forecasting, third-party integrations, and partner relationship management.
You could begin from the contact management area, which offers a detailed view of all your contacts and leads. Apart from the basic details, the customer profiles here give you an insight into your interaction history with every single contact, their company roles, sales parameters, etc.
Then if you happen to form a partnership with another company, you could capitalize on Netsuite CRM’s partner relationship tools. They’ll help you coordinate and organize all the marketing campaigns you engage in as partners. Plus, you should be able to monitor every single thing as it occurs thanks to Netsuite’s real-time reporting capabilities.
Now, for the campaigns you choose to run solo, Netsuite has just the right workflow management features. They help you keep tabs on each employee’s activities, the corresponding task completion ratios, as well as their overall progress.
There’s also a group calendar feature that can be accessed by all team members. So, every single person should be able to view their to-do lists, and subsequently complete the assigned tasks.
Speaking of which, the engagement campaigns here should be fairly easy, considering Netsuite CRM’s AI system and its accompanying automation tools will have your back.
The AI system, more specifically, will save you from wasting time on low-value opportunities. By monitoring your prospects’ behaviors, Netsuite CRM manages to provide actionable insights into the most promising opportunities.
So, the fact is, you don’t always have to rely on just the info you have on your contact profiles. Rather, you should target leads based on their profile data as well as the supplementary insights generated by the system.
The trick that Netsuite CRM employs here is simply keeping track of all the paths and actions taken by your web visitors. It determines their interests based on what they click on and the webpages they spend time viewing.
Such information is also pretty handy when it comes to sales force automation. You can leverage the insights along with Netsuite CRM’s sales tools to drive sales and boost your overall business growth.
Netsuite CRM further tops that off with sales forecasting capabilities. So, you get to predict your sales potential using automatic real-time forecasts.

NetSuite CRM Software Pricing

If you were hoping to get the specific product costs, it turns out Netsuite CRM doesn’t offer a standard pricing schedule. Rather, what you end up paying is determined based on your particular CRM needs.
Some of the primary factors that come into play here include: the required add-ons, your contract length, the number of users you intend to host, the add-ons required, and of course, the system configuration.
That said, you can expect to pay at least $129 per user per month for maintenance and support, all Oracle Database and application server operating system licenses, 100 MB of Oracle database file cabinet space, 1,200,000 campaign emails per year, 3,600,000 integrated mass merge emails per year, incentive management, basic CRM analytics, basic ERP and ecommerce capability, limited web hosting capability, mobile project tracking, partner relationship management with basic partner portal, partner relationship management with basic partner portal, customer support and service management, marketing automation, sales force automation, territory management, quotes/proposals, marketing automation integration, internal chat integration, document storage, task management, segmentation, lead scoring, email marketing, and a calendar/reminder system.
Ultimately, you get to subscribe to the CRM product for a predetermined period, after which you could proceed to renew or scale the package.

Who Should Consider Using NetSuite CRM Software?

Netsuite CRM is not only one of the best CRM software solutions, but it also happens to be among the most extensive customer relationship management systems.
Its sales force automation tools are intended to help you maximize your sales opportunities, while customer service management attempts to boost customer satisfaction through data-driven support. Marketing automation, on the other hand, is all about leveraging real-time insights to power automated campaigns for qualifying leads.
Then considering everything here runs on scalable enterprise-level cloud infrastructure, we can agree that Netsuite CRM is a specialized customer relationship management system for large businesses, enterprise teams, and B2B companies. It can comfortably host multiple departments at the same time, with each one leveraging a specific feature set.

How to Choose the Best CRM Software

Well, there you have it. The ultimate guide to 10 of the best CRM software in 2020.
A word of caution though. While each of these CRM solutions provides some of the best customer relationship management features, it would be unwise to pick one randomly.
And why is that so?
Well, as we’ve established, every single one of them is unique in its own sense. Hence, they’d have varying impacts on your business.
So, how exactly should you compare different options to choose the best CRM software?
The truth is, there’s no two ways about it. You have to systematically evaluate not just the pricing, but also the type of functionalities each tool offers.
And to help you with that, here are the main features you should focus on when it comes to customer relationship management systems:
  • Lead Management and Sales Management: The best CRM software products are capable of hunting for leads across multiple sources. They employ an omnichannel strategy that integrates several digital engagement channels to form seamless lead management and sales management platform.
Apart from emails, you should be able to tap into other popular channels like social media, web forms, inbound calls, text messages, etc. The specifics depend on what your target leads commonly use.
Then after lead generation, the CRM software should allow you to follow up all the way to the end of the customer lifecycle. That means applying different automated approaches while keeping tabs on every single prospect’s progress on the sales pipeline.
  • Marketing: Since customer relationships are nurtured through personalized engagements, the top CRM software happen to double up as digital marketing tools. The marketing functionalities here are used to reach out to leads, customers, plus prospects.
And when it comes to that, the best CRM software for your business should come with AI-driven marketing automation. Plus, the campaigns should be fully intertwined with your sales pipeline.
  • Workflows: Customer relationship management admittedly entails numerous stages and multiple intricate elements, which are best managed with project management tools.
Hence, the best CRM software usually provide dynamic project management capabilities through customizable workflows. This is where you get to streamline your sales pipelines accordingly while coordinating your sales teams, tracking progress, and organizing the corresponding entities.
  • Reports and Analytics: All things considered, customer relationship management is a data-driven process. The type of leads you go for, how you target them, and how you engage with them depends on the insights you’re able to gather about them. So, reports and analytics is one thing you cannot afford to compromise.
You should particularly prioritize CRM products that offer accurate real-time analytics about all your core engagement channels, marketing campaigns, sales pipeline, contact behaviors, as well as your team’s performance. That said, it would also be advisable to choose a software product that can further generate sales forecasting insights. This will help you identify the most promising sales opportunities.
  • Ecommerce: The best CRM software go beyond sales and marketing to focus on other closely-related ecommerce processes. Some platforms, for instance, supplement their core CRM functionalities with customer support tools, while others tend to lean towards team collaboration.
That said, you should choose the extra ecommerce functionalities based on your business setup, plus the specific entities you consider to be critical to your relationship with customers.
  • Integrations: At the end of the day, a customer relationship management tool forms just one part of the entire ecommerce ecosystem. So, a CRM software solution can only be a great fit for your business if it perfectly integrates with all your core systems. Plus, it should come with pre-built integrations for third-party applications you intend to use in our campaigns.
With that in mind, here are some of the questions you should ask yourself to analyze the suitability of each CRM software option:
  • What type of businesses does it target?
  • What types of automated marketing campaigns can you run and manage with the CRM software?
  • Is the sales pipeline customizable?
  • How’s the overall interface design? Is it easy to use and navigate? Does it offer any onboarding features?
  • How many team members can the CRM software host concurrently?
  • Does the CRM software integrate with your current ecommerce platform? How well does it work with other marketing tools?
  • Will you be able to identify the best deals to prioritize?
  • How many contacts and leads can the CRM tool hold? How many can you engage concurrently?
  • Does the CRM software offer comprehensive data protection? What other security features does it come with?
  • How much does the entire CRM system cost? Does it fit your budget?
  • What’s the possible ROI based on the value you get from the software? Are there any extra charges that you might incur along the way?
  • What do past and current users say about the software? Any notable negatives you can identify from the reviews?
Then to top it off, be sure to check out our unbiased reviews about various tools in this space. We’ve covered all the best CRM software solutions on the market in detail.
The post The Best CRM Software Guide (10 Top CRM Tools in 2020) appeared first on Ecommerce Platforms.
Catalin Zorzini / 2019-12-22 4 years ago / 未收藏/ ecommerce发送到 kindle
You know the drill. A customer stops by your store and tries to buy a product. They're pretty pumped about this new pair of shoes, completing a few hours of research and forking over a large chunk of money during the checkout process. Only one problem lurks in the shadows: The shoes are out of stock. Inventory management software could be the difference between losing customers for life and keeping people around for years to come.

Why? Because no one wants to come to your website and waste minutes, or even hours, considering an item that's not even available.
The same goes for you on the backend.
Inventory is your store's life force. If an item isn't available, or you fail to move a product through the proper logistical channels, you're bound to run into problems with both present and future customers.
That's where an inventory management software comes into play.
We've highlighted your best options below, with some of them offering full sales and inventory suites, while others providing simpler, more compact inventory management tools.
Editor's note: This post was originally published on October 14, 2016 and has completely revamped and updated for accuracy and comprehensiveness.

What is Inventory Management Software?

Inventory management is often a time-consuming and tedious process for many business owners. However, a software tool with the right features can help you to deal with some of the headaches and stress that inventory management can bring.
Whether you’re running an ecommerce website, a brick and mortar store, or a supermarket, you’re always going to need a way to keep track of your inventory. These tools are how you get an insight into every aspect of your products. What’s more, tracking the way that your inventory moves can also give you a better insight into your customers and what they want from your business too.
Inventory management software can come with a lot of common features, including:
  • Barcode scanners: To track and manage your products
  • Inventory optimization: To ensure that you always have the right amount of each product available for your customers
  • Stock notifications: Alerts that warn you when you’re over or under-stocking on a specific item according to defined thresholds.
  • Multi-location management: The option to manage a range of warehouses and point of sale systems at once.
  • Material grouping: Collect inventory into predefined categories so you can measure which products are most in demand.
  • Report generation: View a history of your sales, track your most popular product, and manage the items in your inventory that don’t reach expected sales levels
  • Purchase order records: Keep track of the items that you’re stocking, and where you’re getting those products from.
Ultimately, inventory management tools will allow you to track the goods across your business supply chain, so you can track everything from your sales orders, to your unique business needs. Some software solutions even come with integrations that connect to your offline barcoding strategy, your accounting system, and more.

Best Inventory Management Software

There’s no one-size-fits-all solution for the best inventory management solution. Depending on what you need from your inventory system, you could find that you even need to combine multiple tools into one back-end process.
To help you get started, here are a few of the most popular tools on the market.

Zoho Inventory Management

zoho inventory management homepage
Zoho Inventory Management is one of the best-known tools in the market, designed to help companies increase their sales, expand their business operations and more. Not only does Zoho Inventory help with things like managing your offline and orders, but it can also integrate with other tools in your sales strategy.
You can link Zoho to your Amazon, eBay and Shopify accounts, create purchase orders, manage drop shipments and more, all from within a single application. What’s more, there’s end-to-end tracking for every item in your inventory with serial number features and batch tracking. You can even choose from multiple shipping integrations to make sure that your items get to your customers as quickly as possible.
Zoho inventory even comes with advanced inventory management tools, such as access to accounting software and asset management tools to help you make more informed business decisions. If you’re looking for a complete software solutions for modern business owners, this could be it.

Quickbooks Inventory Management

quickbooks inventory
QuickBooks is another state-of-the-art inventory management tool that integrates seamlessly with Quickbooks accounting system. This means that you can track things like bill of materials, stock levels, and invoicing in the same convenient location.
The Quickbooks solution is a powerful inventory software solution that allows you to quickly and easily view the quantities that you have on hand of a particular piece of stock. You can also get real-time inventory valuations, and stay on top of your purchase orders from inside the same user-friendly customer interface. Quickbooks makes it incredibly easy to keep track of everything that you buy and sell. You can organize your products with unique images, and automatically calculate the cost of products using FIFO.
The QuickBooks inventory management solution is an intuitive tool, ideal for keeping track of inventory reports, product life cycle information and more.

Fishbowl Inventory


Fishbowl inventory might not be as well known as Zoho inventory or Quickbooks when it comes to asset management, FIFO tools, and accounting systems, but it’s gaining popularity quick. If you need an inventory management system that helps you to track inventory and assess your business needs, then Fishbowl could be it.
Whatever you want to accomplish with your business, Fishbowl is there to help, with everything from dropshipping tracking, to inventory data, barcoding, and other advanced features. What’s more, because this powerful software also integrates with some of the other most popular tools on the market you can get more done. For instance, you can integrate with Salesforce CRM, Quickbooks for accounting software, and ShipStation for shipping.
Fishbowl covers everything from manufacturing tools with work orders and asset management insights that are crucial to help smaller businesses grow, to asset tracking and warehousing. You can get real-time inventory updates, track your items by lot numbers, and monitor asset level in multiple locations at once.

StitchLabs


Finally, whether you’re a smaller business, or an enterprise, Stitch Labs is all about making your life easier. This is a comprehensive software solution designed for tracking everything from invoicing, to on-premise orders, and inventory items. It offers the same kind of simplicity that Quickbooks users and Zoho customers would expect, with a user-friendly interface. What’s more, growing brands can use Stitch to sell their items flexibly and check stock levels quickly on multiple channels.
The customizable experience from Stitch Labs is great for companies that want to merchandise creatively with everything from pre-discounted inventory items, to loyalty programs and more. You’ll get complete control over your inventory system, without having to use time-consuming tools.
Stitch features order management solutions, B2B wholesale management, purchasing and replenishment features, and in-depth reporting and analytics. Whatever you need to figure out how you should be selling online, Stitch Labs is there to help.

Best Inventory Management Software for Ecommerce

Inventory Management Software: Seller Cloud

SellerCloud boasts a strong set of features, as it's one of the suites we were talking about above. With scheduled listings, shipment handling, inventory tracking, customer feedback management and product catalog building, you receive a little bit more than a regular inventory management app. The main reason we like Seller Cloud so much is because of its multitude of integrations. For example, you can connect with channels like Amazon and eBay, along with vendors, shopping carts, repricers, payment gateways and more.
inventory management software: seller-cloud
Since we're mainly focusing on inventory management for this article, let's take a look at some of the core features. To start, you can keep your inventory in-sync throughout all platforms, and the system allows for multi-warehouse management solutions. There's a tool for reserving certain inventory for deals and channels, while all vendor feeds are updated with changes with your inventory. Low stock alerts are sent to you, just in case you'd like to send out a new order. Finally, inventory tracking is packaged in for both you and the customer.

Inventory Management Software for Ecommerce: Channel Advisor

Channel Advisor is yet another ecommerce suite filled with products for bringing in new customers, managing inventory, handling social commerce and expanding your product intelligence. The Channel Advisor software assists companies that would like to get products posted all over the internet, without adding too much work into the process. For example, Channel Advisor has a digital marketing “channel,” in which all of your products are distributed on places like Google. Bing, Yahoo Facebook.
inventory management software: channel-advisor
The system then moves onto marketplaces. So, for example, it would ensure that all of your items are stocked and ready to sell on sites like Amazon, Walmart, Sears and eBay. After that, Channel Advisor has features for gaining insights and understanding whether people are buying certain products online or offline. This product data is turned into insights for better establishing how you're going to improve your conversions over time. Once you take a look at it, most of the products inside the suite are solely dedicated to your inventory, making it one of the top choices out there.

Inventory Management Software for Ecommerce: nChannel

nChannel is a cloud-based multichannel platform that connects your sales channels with your fulfillment channels. It integrates front-end sales channels like Shopify, Magento, Bigcommerce, Amazon, and eBay with your back-end systems such as NetSuite, MS Dynamics GP, NAV, and RMS. Through integration of your systems, nChannel centralizes inventory, order, and product information management into one place.

This inventory management software ensures you don't oversell by updating inventory levels in real-time across all your sales channels as orders are placed. It also automatically sends online order information back to your fulfillment system to ensure your orders are fulfilled quickly and accurately. Overall, these components help you create a better customer experience. Customers will know when inventory is available and the status of their order. If you're serious about multichannel, then nChannel helps you overcome those complexities.

Inventory Management Software: TradeGeko

TradeGeko inventory management
The TradeGeko Platform lets commerce brands manage their retail and wholesale operations and applications from a single, central system. Its core capabilities and extensive app ecosystem, provide businesses with the ability to easily automate order workflows, increasing operational efficiency and customer satisfaction. It helps you manage multiple sales channels (including Shopify, Amazon, WooCommerce, Wayfair, and more), fulfillment locations, expedite payments, create private B2B eCommerce experiences for wholesale customers and seamlessly automate the connection between systems across multiple channels.
TradeGecko was designed for operations, eCommerce and supply chain professionals looking to automate their business, effectively freeing their time from repetitive, low-level tasks that are prone to human error and inefficiency. It allows you to spend more time on building on customer relationships and growing your business.
TradeGecko is perfect for small and medium businesses, and its Founder plan starts from $39/month.

Inventory Management Software: Katana


Unlike most of the inventory management tools we’ve already featured, Katana tends to focus entirely on small manufacturers. It’s a dynamic online-based software that helps small and medium-sized factories manage their operations through holistic inventory control, as well as production scheduling and planning.
That said, smart management of raw materials and finished goods is the principal function here. And to facilitate that, Katana compiles data about product recipes, sales and purchase orders, warehouse statuses, product operations, manufacturing orders, and materials, through an intuitive dashboard for convenient and easy tracking of all the critical processes.
One thing you’d particularly enjoy here is seamless transitions between information spreadsheets, as well as integrations with a host of business platforms. When it comes to online selling, for instance, you can comfortably embed your Katana system with the likes of WooCommerce and Shopify. Then when you need to keep track of your payment information, stock, purchase orders, and accounting, QuickBooks and Xero integrations come in handy.

Inventory Management Software: 4PSite

The 4PSite software is a multi-channel and cloud-based order and inventory management program, and it connects with some of the most popular ecommerce platforms such as Bigcommerce, Magento, Shopify and Volusion. In addition, you can connect your inventory and make sales through marketplaces like eBay, Amazon, Rakuten, Etsy and Newegg. This is an entirely web-based inventory management center, so you only have to open up one dashboard for all of these platforms and marketplaces. For example, you could check the availability of a product in real time, as a customer is placing it in their shopping cart.
inventory management software: 4psite
Quite a few features are packaged into the 4PSite software. To start, customers will never make an order on a product that is already out of stock. You get barcodes and cost tracking, along with searching, filtering and bundles. The inventory overview module is useful for forecasting, while the shipping management integrates right into the system. Inventory forecasting is a huge bonus, and you can always go in there to manage reorders and suppliers. With solid optimization, inventory reports and QuickBooks integration, 4PSite allows your business to have accurate unit counts, and it makes purchasing quick and easy.

Inventory Management Software: Dear Systems

Dear Systems is primarily for small businesses, making it a little cheaper and more manageable than some of the other inventory management solutions we've talked about so far. The main feature you'll want to look at for ecommerce is the way it lets you sell and manage through multiple sales channels. Manufacturing, purchasing, sales, accounting and inventory management is all wrapped into the software, bringing about a nicely integrated suite.
dear-systems
In terms of the inventory management tools, you gain access to product families, or variations of the same product with different SKUs. You'll also find options for loading products from your online shop, along with multi-warehouse management. The barcode scanning streamlines and automates the entire inventory management process, while the numerous integrations with platforms like Shopify, eBay and WooCommerce come in handy as well.

Inventory Management Software: StitchLabs

Along with integrations with platforms like Magento, WooCommerce, Shopify and Amazon, the StitchLabs inventory software has a nice set of primary features. For example, you get full inventory control with a beautiful dashboard and impressive insights. The cross-departmental unification allows your team to collaborate, while the multi-channel inventory module is completely automated.
stitchlabs
This inventory management software is designed for you to accurately forecast and act when the demand changes. We also like that it integrates with QuickBooks, considering your books are going to thank you at the end of the year because of the real-time inventory tracking.

Best Inventory Management Software for Small Businesses

As a smaller business, the inventory management system that you need might be very different to the kind of software solution that a larger brand is looking for. You might need more help with things like dropshipping, and less support with things like barcode scanning and work orders.
Fortunately, whatever the key features are for your asset management tool, there are tools out there for all company sizes. Some of the most popular inventory management software for smaller organizations include:

Veeqo


Veeqo is an all-in-one software solution that helps small businesses to keep track of things like inventory management, orders and shipping. Because you get everything that you need to grow your company in one user-friendly back-end, it’s much easier to help your brand grow quickly.
With Veeqo, business owners can access a multi-channel inventory management platform that integrates with up to 16 different channels. There’s also support for multiple warehouses, returns and wholesale orders, and in-depth revenue reports. Veeqo provides state-of-the-art solutions for picking and packing, with barcode scanner picking included. There’s also support for up to 21 different shipping order integrations.
If you’re not sure whether the Veeqo software is right for you, then you can always start with a free trial too.

Ordoro


Odoro is another sensational tool designed to reduce the time-consuming process of running your business. This asset tracking tool comes with plenty of advanced features for kitting out smaller businesses. Inside this software, you’ll find everything you need to run a comprehensive business, including the option to batch-print shipping labels with a couple of clicks. You can also secure the most cost-effective shipping rates with huge USPS discounts.
You can plug Ordoro into each of your sales channels, assign barcodes to SKUs and issue unique purchase orders when your product levels start to get low. Ordoro will even automatically sync and track your inventory updates for you.
Ordoro is particularly useful for businesses with an interest in dropshipping, because it supports the routing of dropshipping requests for vendors, and allows dropshippers to fulfil their orders within a unique vendor portal.

CIN7


CIN7 is another fantastic way to save time inventory managing and creating inventory reports. This compelling inventory management solution will help you to track your product life cycles quickly and effectively, and ensure that you always reorder your most valuable products before they run out of stock.
CIN7 simplifies the process for connected inventory management, allowing smaller and growing brands to sell more products to customers all around the world. There’s a free trial to help get you started, and once you log in, you’ll find a complete inventory management environment, where you can keep stock in line with your orders across every location and stock channel. CIN7 also comes with access to a host of point of sale integrations, warehouse management tools and unique features for automation too.
With CIN7 you can centralize everything from order workflows, to inventory assets, stock locations and different sales channels in a single solution.

Other Top Choices for Inventory Management

Just as there’s no one-size-fits-all solution for accounting systems, or enterprise resource planning, there’s no ultimate best inventory management software that will work for every business. The tool that’s best for you will be dependant on the kind of business that you run, as well as a multitude of other factors.
The good news? There are a lot of excellent inventory management tools out there for companies with specific requirements too. For instance, if you’re looking for a piece of inventory software that will streamline your restaurant, Shopify or WooCommerce experience, try these options:

Best Inventory Management Software for Restaurants

Currently, the best inventory management and POS system available for restaurants has to be the solution that comes from Lightspeed. This industry-leading software is specifically designed for the hectic restaurant environment, allowing business owners to quickly and seamlessly manage their business from a single location.
Lightspeed comes with access to customizable floor plans, multiple menu management tools, timed events, workflow strategies, and staff management applications. What’s more, you can access the same tools that you need to keep your restaurant running, regardless of whether you’re working online or offline.
Lightspeed even offers tableside ordering, where you can assign specific orders to tables or seats, and send orders to the kitchen or bar when necessary.

The Best Inventory Management Software for Shopify

There are a lot of excellent inventory management solutions out there that are specifically designed to hook up with your Shopify store. However, one of our favorite options has to be the Katana for Shopify app. This is a software solution designed specifically for Shopify order management if you’re a product-making business.
Katana is an accredited Shopify partner – so you know you’re getting a top-of-the-line experience. This order management system will help you to track your sales, orders, and inventory, ensuring that you always have the environment you need in place for profits.
Katana features an automatic booking engine, a complete sales fulfillment syncing portal, and a host of smooth production processes to keep your team running smoothly. Katana is extra useful if you’re in the manufacturing business, as it helps to keep complex processes as simple as possible.

The Best Inventory Management Software for WooCommerce

So we have an excellent inventory management software for Shopify – but what if you prefer to use WooCommerce for WordPress? Well, tools like Veeqo are great for use with WooCommerce, but there are also a range of other plugins that you can explore too. One of the most popular multi-channel inventory management tools from WooCommerce is TradeGecko.
TradeGecko continuously and seamlessly makes stock adjustments, stock updates and purchase orders to suit the needs of your store. The inventory management tools sync with your sale strategies across multiple channels. There are even mobile apps available for your Android and iOS phones so you can manage your products and orders anywhere.
TradeGecko gives you all of the tools you need to manage a wholesale business, by creating a wholesale list of prices for your products too. The whole experience is exceptionally user-friendly, and the smooth integration with WooCommerce just makes life a lot easier.

Over to You

If you're running a small business, I would highly recommend Dear Systems. Other than that, it's completely up to you when deciding which of the best inventory management software to go with. If you've had any experience with any of the tools above, feel free to share your thoughts in the comments below.
The post Best Inventory Management Software (December 2019) appeared first on Ecommerce Platforms.
Everett Griffiths / 2019-12-23 4 years ago / 未收藏/ Telerik Blogs发送到 kindle
In this post, I'll provide a contextual overview of WebAssembly and its role in transforming web development.
You may be wary to paw through another article heralded by a headline announcing some mastermind solution to a problem so complex its nuances mere mortals can barely grasp. Christen the technology with an unpronounceable acronym and we have all the makings for a Wagnerian opera (or a technical sketch comedy, I can’t tell which). Like those fat-lady musical dramas, these long-winded articles usually require translation, program notes and three operas worth of backstory just to make sense of the thing. Reading about WebAssembly feels like watching the season finale of Game of Thrones without having seen any of the previous episodes.
So let’s try to keep things practical for our dark horse protagonist and focus on answering three questions: 1) What is WebAssembly (Wasm)? 2) How does it compare to Docker? and 3) How does this all relate to Atwood’s law? You should leave this Wagnerian saga with the equivalent of a couple tunes in your head.

WASM WTF

Perhaps the easiest way to think about WebAssembly is that it offers a way to take code written in programming languages other than JavaScript and run it within a web browser. The initial focus was on C/C++, but many languages are now getting onto the Wasm bandwagon. WebAssembly whispers in your ear the same alluring “write once, run anywhere” seduction that Java did (right before it poisoned your programmatic dreams with memory leaks). But there is some hope shining through the pessimism that WebAssembly may finally deliver on those promises because the kingdom of geeks is already way ahead of us: all major browsers offer basic support for WebAssembly! (Apologies to Internet Explorer.)
That “write once, run anywhere” prophecy can be fulfilled because WebAssembly is a compilation target. If you think of an internet browser as something of a virtual machine, then you can think of compiling code for that virtual machine in the same way you might compile code for machines with 32- or 64-bit architectures. Admittedly, that comparison isn’t 100% technically correct, but it’s easier to understand than the long-winded discussion of how compilers work. WebAssembly is an assembly language for a “conceptual machine” — a sort of simplified and abstracted way of thinking generally about all the machines that it might run on, and aren’t all problems in computing solved by some layer of abstraction?
Wasm does not require a separate container engine to run, so it does not have the additional performance overhead that a container or virtual machine layer might introduce. And that brings us to the next critical point: The execution of Wasm code can be very fast, running at near native speeds.
So what does that get us? First, this setup provides us with some diversity — JavaScript still gets used to interface with the WebAssembly modules and talk to a webpage via the DOM, but it no longer reigns supreme as the de facto ruler of the web. Secondly, by opening up the door to lower-level languages, your applications can make sensible tradeoffs, e.g. they can hand things over to WebAssembly when computationally heavy operations are required.
Admittedly, there aren’t many homesteaders out there on the WebAssembly frontier: Changes this fundamental will take a while to gain traction, but the future looks promising. Games have been ported (e.g. Doom3, and many others written in Unity) and some desktop applications too (e.g. Autocad). Even if you don’t want to write your own WebAssembly modules, you may want your web applications to leverage WebAssembly packages, available via the WebAssembly package manager (wapm). Ever wanted to do things like client-side image sizing or video editing? There are WebAssembly packages for those, but we’re getting ahead of ourselves…

WebAssembly and Docker

Maybe Wagner and George R. R. Martin were right… maybe you do need to keep introducing more characters to tell your story. The average discussion of WebAssembly doesn’t mention Docker, so we are really going into the deep plot of this tech opera, but now that you understand a bit of what WebAssembly is and does, I challenge you to unsee its similarity with Docker.
Like WebAssembly, Docker provides a sandboxed environment to run your application code. Like WebAssembly, you can package your applications written in any language into a container for simpler deployment. Like WebAssembly, Docker provides a kind of common currency so that various players can collaborate within a system. They are distinct technologies accomplishing different goals, but they are like armies allied against a common enemy.
Still not convinced? Consider the words of Solomon Hykes, the founder of Docker:
Dobrinka Yordanova / 2019-12-23 4 years ago / 未收藏/ Telerik Blogs发送到 kindle

Need to generate Docx, HTML, RTF or PDF documents in your Xamarin App? Telerik UI for Xamarin has a solution for you - our new WordsProcessing library.


telerik-ui-for-xamarin-wordsprocessing

It has never been easier to generate, modify and export a document in your mobile application! The RadWordsProcessing library enables you to easily generate and export documents in various formats, including:

  • PDF
  • Docx
  • RTF
  • HTML
  • Plain text

The library comes in handy all cases where document or PDF generation is needed, such as PDF invoice generation for an e-commerce app, or to serve the user with a filled application form in Docx format.

With our document processing library you can quickly access each element in a given document, modify, remove it or add a new one. Additionally, the generated content can be saved as a stream, file, or send to the client browser.

In this blog post, we will familiarize you with the RadWordsProcessing structure and the features it provides.

WordsProcessing Structure

The document model of RadWordsProcessing for Xamarin includes all necessary elements and features of a document, including:
  • Sections that can be customized using the properties exposed by the corresponding class. Headers, footers, and watermarks for a section can be customized as well.
  • Paragraphs whose properties and methods enable you to change its collection of inlines and appearance.
  • Tables equipped with an API, allowing you to easily insert, edit, and remove from the document. Their rows, cells, appearance, and content are also flexible, with various changes and manipulations possible.
  • Inlines, including Runs, Images, as well as inline and floating images, Fields, Breaks, Bookmarks, Hyperlinks and Tab stops.
  • Styles: the documents processing model includes a repository of Style objects containing sets of character, paragraph or table style properties. Additionally, you can create custom styles and use them throughout the document.

Supported Formats

In addition to the smooth creation, modification, import and export of files, RadWordsProcessing gives you the ability to convert between variety of formats - convert Docx to PDF or HTML to Docx in your Xamarin apps. The currently supported formats are Docx, RTF, HTML, PDF and plain text.

The processing library doesn’t need any external dependencies in order to convert documents from/to the supported formats - the document model is UI-independent and can be used on both server and client-side.

Features

In addition to being able to create, modify, and export a document, RadWordsProcessing has some additional sweet features, including:
  • Inline & floating images: support for inline & floating images for the most popular formats, including support for working with image properties such as Source, Size, Flip, Rotation, AspectRatio, etc.
  • Bookmarks & hyperlinks: enable your users to easily navigate a document with bookmarks and hyperlinks
  • Watermarks: identify document status or mark it as confidential with watermarks
  • Create documents in different file formats, for example:

    generate documents
private RadFlowDocument CreateDocument()
{
RadFlowDocument document = new RadFlowDocument();
RadFlowDocumentEditor editor = new RadFlowDocumentEditor(document);
editor.ParagraphFormatting.TextAlignment.LocalValue = Alignment.Justified;
editor.InsertLine("Dear Telerik User,");
editor.InsertText("We’re happy to introduce the new Telerik RadWordsProcessing component for Xamarin Forms. High performance library that enables you to read, write and manipulate documents in DOCX, RTF and plain text format. The document model is independent from UI and ");
Run run = editor.InsertText("does not require");
run.Underline.Pattern = UnderlinePattern.Single;
editor.InsertLine(" Microsoft Office.");
editor.InsertText("The current community preview version comes with full rich-text capabilities including ");
editor.InsertText("bold, ").FontWeight = FontWeights.Bold;
editor.InsertText("italic, ").FontStyle = FontStyles.Italic;
editor.InsertText("underline,").Underline.Pattern = UnderlinePattern.Single;
editor.InsertText(" font sizes and ").FontSize = 20;
editor.InsertText("colors ").ForegroundColor = GreenColor;
editor.InsertLine("as well as text alignment and indentation. Other options include tables, hyperlinks, inline and floating images. Even more sweetness is added by the built-in styles and themes.");
editor.InsertText("Here at Telerik we strive to provide the best services possible and fulfill all needs you as a customer may have. We would appreciate any feedback you send our way through the ");
editor.InsertHyperlink("public forums", "http://www.telerik.com/forums", false, "Telerik Forums");
editor.InsertLine(" or support ticketing system.");
editor.InsertLine("We hope you’ll enjoy RadWordsProcessing as much as we do. Happy coding!");
editor.InsertParagraph();
editor.InsertText("Kind regards,");
return document;
}
  • Easily Find all instances of a string And Replace it with another through the ReplaceText() method of the RadFlowDocumentEditor.
private void ReplaceText()
{
if (string.IsNullOrEmpty(this.findWhat))
{
return;
}
RadFlowDocumentEditor editor = new RadFlowDocumentEditor(this.replacedDocument);
if (this.useRegex)
{
Regex oldTextRegex = new Regex(this.findWhat);
editor.ReplaceText(oldTextRegex, this.replaceWith);
}
else
{
editor.ReplaceText(this.findWhat, this.replaceWith, this.matchCase, this.matchWholeWord);
}
}

The full demo project can be found here.

  • Insert Documents at a Specific Position using the InsertDocument() method.
  • Merge two RadFlowDocument instance using the Merge() method.
RadFlowDocument target = new RadFlowDocument();
RadFlowDocument source = new RadFlowDocument();
//...
// target will contain merged content and styles.
target.Merge(source);
Here the document you wish to add content to is called target and the document from which you wish to take the content is called source.
Export documents in different formats:
export documents

Tell Us What You Think

Have we caught your interest with the new RadWordsProcessing library for Xamarin.Forms and its features? You can find various demos of the processing library in our Telerik UI for Xamarin Demo application.
I hope that this information will get you started with the processing library— as always, any feedback on it is highly appreciated. If you have any ideas for features, not hesitate to share this information with us on our Telerik Document Processing Feedback Portal.
If this is the first time you're hearing about Telerik UI for Xamarin, you can find more information about it on our website or dive right into a free 30-day trial today.

Happy coding with our controls!

Marin Bratanov / 2019-12-23 4 years ago / 未收藏/ Telerik Blogs发送到 kindle

The WASM Flavor

Before I add the links to the code examples, I need to mention the Client-side (WASM) flavor. At the time of writing, there seems to be no standard or recommended way to set the thread culture and to localize such an app.
Don’t despair, it is not yet official, and besides - there are some sample solutions online (such as using JS Interop to get the current browser culture while initializing the app and to set the thread culture) and I will leave the exact architecture choice to you. We just have to wait and see what the future will bring.
What pertains to the examples we prepared is that while in a server-side Blazor app .resx files will work out-of-the-box, this is not the case with a WASM app. So, we prepared an example that lets you reuse .resx files in a client-side (WASM) Blazor app as well.

Finally, Code Time

Here are the links you need:

What’s Next

Localization is only one part of globalizing an app, and using date and number format strings to match the culture of the user is also very important. The user would like to see their own currency marker, the decimal separators they are used to, the date formats, month and day names they know. And they will demand it from your app, so we will provide this for you. We are working on format support as I am writing this post, and it will be available in our next release – 2.5.0. Stay tuned!

Download Telerik UI for Blazor 2.4.0

See how much better your app will look and how much happier your users will be once you enable them to select their preferred language. Download the latest 2.4.0 version of Telerik UI for Blazor Native Components from the Telerik UI for Blazor page.
Happy Blazor Coding!
Rahul / 2019-12-15 4 years ago / 未收藏/ tecadmin发送到 kindle
This tutorial will help you to log all the disconnected remote sessions on the Windows system. This tutorial has been tested with Windows Server 2019. Setup First of all, open the ‘Group Policy Editor‘ on your server. Start run window by pressing “Win + R” and type gpedit.msc on run window. Then navigate to the […]
The post Automatically Log off Disconnected User Sessions on Windows appeared first on TecAdmin.
2019-12-23 4 years ago / 未收藏/ cddual发送到 kindle
2019.12.23
我国近日颁布《关于减轻中小学教师负担进一步营造教育教学良好环境的若干意见》。《意见》指出,有关部门要精简涉及中小学教师的各类报表填写,杜绝重复上报各种数据,充分利用人工智能等先进技术,提升教育管理工作的信息化水平。
China has issued a guideline to reduce the burden of teachers at primary and secondary schools, and create a good environment for education. The guideline asks relevant authorities to simplify the data reporting from primary and secondary schools, prevent repeated reports, and improve the management of databases with advanced technologies including artificial intelligence.

2019年4月18日,教师谭湘丽带领学生做心理测试游戏。(图片来源:新华社)
 
【知识点】
中共中央办公厅、国务院办公厅近日印发《关于减轻中小学教师负担进一步营造教育教学良好环境的若干意见》(以下简称《意见》)。《意见》指出,对中小学校和教师的督查检查评比考核事项在现有基础上减少50%以上;不得一味要求学校和教师填表格报材料;避免安排中小学教师参加无关培训活动。
据了解,国务院教育督导委员会将把减轻中小学教师负担工作纳入对省级政府履行教育职责的督导中。省级教育督导部门将把减轻中小学教师负担工作作为教育督导和开学检查的重要内容。省级党委和政府将根据《意见》精神,列出具体减负清单,扎实推进减轻中小学教师负担工作取得实效。


【重要讲话】
全党全社会要弘扬尊师重教的社会风尚,努力提高教师政治地位、社会地位、职业地位,让广大教师享有应有的社会声望,在教书育人岗位上为党和人民事业作出新的更大的贡献。
The CPC and society should promote the social norms of respecting teachers and valuing education, and enhance the political, social, and professional status of teachers so that they can enjoy their due social prestige and make greater contributions to the cause of the Party and the people by imparting knowledge and educating people.
——2018年9月10日,习近平出席全国教育大会并发表重要讲话。

 
【相关词汇】
教育惩戒
school discipline

校园安全
campus safety

师德师风
professional ethics of teachers

2019-12-23 4 years ago / 未收藏/ cddual发送到 kindle
2019.12.23
2019年,有很多理由让你“丧”,但是也有很多正能量的事情让你重拾对这个世界的信心。即将过去的这一年其实也有不少让人欣慰的好消息!
 
 
 
马拉维的女酋长上位后,宣布1500多桩童婚无效,并立法禁止童婚,让年轻女孩回校上学。
 
 
世界上幸存的最后两头北部白犀牛的七个蛋被成功孵化,或许可以挽救这个物种。
 
 
瑞典推出了一项伟大举措:每当献血者的血液拯救一条生命时,都会给献血者发一条短信。
 
 
亚马逊部落和石油公司打官司胜诉,阻止他们在亚马逊雨林中开采石油。
 
 
印度的这名男子每天种一棵树,坚持了35年,长成的森林比美国中央公园面积还大。
 
 
挪威决定不在罗弗敦群岛上开采石油(价值约530亿欧元)以保护其生态系统。
 
1 2 3 4 Next   >>|
2019-12-23 4 years ago / 未收藏/ cddual发送到 kindle
2019.12.23
2020年“美国小姐”选美比赛日前落幕,24岁的美女博士卡米尔·施里尔凭借“硬核”才艺展示摘得桂冠。她是一名在读的药剂学博士。在才艺表演环节,施里尔身穿白色实验服,现场进行了一次化学实验。
截图来自每日邮报网站
A Virginian biochemist has been named winner of Miss America 2020 after performing a live science experiment that defied stereotypes of the contest.
近日,2020年“美国小姐”桂冠花落弗吉尼亚的一名生化学家,她颠覆了传统比赛模式,在舞台上展示了一项科学实验。
stereotype [ˈsteriətaɪp]:n.陈腔滥调,老套;成见
 
截图来自每日邮报网站
Camille Schrier defeated 50 women to take the crown at Thursday's final in Uncasville, Connecticut.
上周四(12月19日),在康涅狄格州昂卡斯维尔举行的决赛中,卡米尔·施里尔击败另外50名佳丽,夺得冠军。
Wearing a lab coat, the 24-year-old impressed judges with a chemistry demonstration in the talent show.
这位24岁的选手身穿实验服,在才艺表演中现场完成了一次化学实验,给评委们留下了深刻的印象。
Ms Schrier won a $50,000 scholarship and a one-year role as Miss America.
最终,施里尔当选2020年“美国小姐”,并赢得了5万美元(约合人民币35万元)的奖学金。
In her acceptance speech, Ms Schrier said she hoped to "break stereotypes about what it means to be a Miss America in 2020".
施里尔在获奖感言中说,希望“打破人们对‘美国小姐’的刻板印象”。
Ms Schrier has two undergraduate science degrees and is studying a doctorate in pharmacy at Virginia Commonwealth University.
施里尔女士拥有两个理学学士学位,目前正在弗吉尼亚联邦大学攻读药剂学博士学位。
She reportedly told the celebrity panel, comprised of Kelly Rowland, Queer Eye's Karamo Brown and Superstore actress Lauren Ash, that "Miss America is someone who needs to educate".
据报道,她告诉名人评审小组说,“美国小姐是一个需要对人们有教育作用的人”。凯利·罗兰、出演《粉雄救兵》的卡拉莫·布朗和《超级商店》的女演员劳伦·阿什都是评审小组成员。
 
截图来自每日邮报网站
In her role as Miss America, Ms Schrier will spend a year advocating for Mind Your Meds, a drug safety and prevention programme.
作为“美国小姐”, 施里尔将用一年的时间来宣传“关注药物”活动,该活动致力于倡导药物安全和防止滥用。
Ms Schrier's victory is expected to be viewed as another progressive step away from Miss America's traditional beauty contest format.
施里尔的胜利将被视为“美国小姐”传统选美比赛模式的又一进步。
 
截图来自每日邮报网站
Since 2018 the competition has attempted to re-brand its image, scrapping its swimwear segment and appearance-based judging criteria.
自2018年以来,该比赛试图重塑其形象,放弃泳装部分和基于外观的评判标准。
Instead, contenders showcase their talents and are interviewed about their passion, intelligence and understanding of the Miss America role.
取而代之的是,各位参赛选手展示才能,接受采访,谈论她们热爱的事情、智慧和对美国小姐这一角色的理解。
"We will no longer judge our candidates on their outwards, physical appearance. That's huge," said former Miss America winner Gretchen Carlson, who announced the reforms in 2018.
“我们将不再以貌取人,这是巨大的变化”,前“美国小姐”冠军格雷琴·卡尔森在2018年宣布了这些改革。
Organisers say the changes have encouraged more young women to participate.
组织者表示,这些变化鼓励了更多的年轻女性参加选美比赛。
 
截图来自每日邮报网站
Earlier this year Ms Schrier, a self-confessed "quirky scientist", told the BBC she wanted to "break people's stereotypes" of those competing for the Miss America title.
今年早些时候,自称“古怪科学家”的施里尔女士告诉英国广播公司,她想要“打破人们对参选‘美国小姐’的女士们的刻板印象”。
She said her science experiment - the catalytic decomposition of hydrogen peroxide - was a "big factor" in her winning Miss Virginia in June this year.
她说她的科学实验——过氧化氢的催化分解——是她今年6月赢得“维吉尼亚小姐”的一个“重要因素”。
While acknowledging there was still "controversy" over these types of competitions, Ms Schrier said Miss America has "re-branded" and was being more "progressive" by focusing more on women's achievements than appearances.
虽然承认这类比赛仍存在“争议”,但施里尔表示,“美国小姐”已经“重新包装”,更加“进步”,更多地关注女性的成就,而不是外表。
 
英文来源:BBC
翻译&编辑:yaning
2019-12-23 4 years ago / 未收藏/ cddual发送到 kindle
2019.12.23
国家语言资源监测与研究中心、商务印书馆等联合主办的“汉语盘点2019”揭晓仪式12月20日举行。
“Stability” was chosen as the hottest Chinese character related to domestic topics this year, while the phrase that swept the country was “me and my motherland”.
“稳”、“我和我的祖国”分别当选年度国内字、国内词。

 
The hottest character related to international topics was “difficulty”, and the hottest phrase related to international topics in 2019 was “trade frictions”.
“难”、“贸易摩擦”分别当选年度国际字、国际词。

 
“汉语盘点”活动至今已举办十四年,旨在“用一个字、一个词描述当年的中国与世界”,鼓励全民用语言记录生活,描述中国视野下的社会变迁和世界万象。
The campaign received thousands of suggestions from netizens nationwide since it started on Nov 20.
11月20日,“汉语盘点2019”活动启动以来,收到了网友提交的数千个字词提名。

Many of the submissions related to domestic and international issues, but the dominant influencing factor was the 70th anniversary of the founding of the People’s Republic of China in October.
很多提名的字词都与国内、国际事件相关,不过其中一个主要的影响因素是10月份中华人民共和国成立70周年。


仪式现场还发布了2019年度十大网络用语、十大流行语、十大新词语。

2019年度十大流行语
我和我的祖国 me and my motherland
金色十年 golden decade
学习强国 study to make our country stronger
中美经贸磋商 China-US trade talks
最美奋斗者 the most outstanding contributors
硬核 hardcore
垃圾分类 garbage sorting
先行示范区 pilot demonstration area
基层减负年 the year to reduce burdens on community-level officials
我太南了 What am I supposed to do?
 

2019年度十大新词语


夜经济 nighttime economy
5G元年 epoch-making year for 5G
极限施压 exert maximum pressure
止暴制乱 bring violence and chaos to an end
接诉即办 handling a complaint upon receipt
夸夸群 praise group
基层减负年 the year to reduce burdens on community-level officials
冰墩墩/雪容融 Bing Dwen Dwen/Shuey Rhon Rhon
杀猪盘 pig-butchering scam/romance scam
乡字号/土字号 bucolic brands/agriculture business names
 

2019年度十大网络用语


不忘初心 remain true to the original aspiration
道路千万条,安全第一条 On countless roads ahead, safety comes first.
柠檬精 green with envy
好嗨哟 be so high/get really high
是个狼人 someone is a badass/crackerjack
雨女无瓜 none of your business
硬核 hardcore
996 the 996 work schedule
14亿护旗手 1.4 billion guardians of the national flag
断舍离 simplifying life

参考来源:教育部官网、央视新闻

(中国日报网英语点津 Helen)
2019-12-23 4 years ago / 未收藏/ cddual发送到 kindle
2019.12.23
This photo shows high-sugar food and drinks. [Photo/IC]
>Sugar causes depression
研究称吃甜食加重抑郁

If you've been feeling especially blue, a new study suggests getting those visions of sugar plums out of your head.
一项新研究认为,如果你觉得特别难过,那就别再想着糖果了。

Researchers at the University of Kansas say that the added sugars found in virtually all holiday sweets can induce inflammatory and neurobiological processes that are connected to depression and negative feelings.
堪萨斯大学的研究人员称,几乎所有人们在节假日里吃掉的糖果中都含有不健康的添加糖,这些糖会引发炎症,还会诱发与抑郁和消极情绪相关的神经生物学过程。

Seasonal-depression and winter-depression symptoms will push individuals to eat more sweets.
季节性抑郁症和冬季抑郁症的症状都会使人去吃更多的甜食。

This will, in turn, only create a vicious cycle of feeling down and eating sweets to feel better, only to end up feeling even worse.
而这么做只会造成一个恶性循环:心情低落,吃甜食感觉好些,最终却令人心情更糟。


Researchers said the inflammation caused by excess sugar is the number one contributor to depressive thoughts.
研究人员表示,过多糖分引发的炎症是导致抑郁心绪的首要因素。

They recommend non-processed foods, particularly those rich in plant-based and Omega-3 fatty acids, in order to combat depression on a dietary level.
他们建议人们吃未加工的食品,尤其是那些富含植物脂肪酸以及欧米伽-3脂肪酸的食物,从而从饮食上对抗抑郁症。

 
[Photo/Pexels]
>Smith most common last name
英语国家最大姓:史密斯

"Smith" is still the most commonly used surname in every English-speaking country around the globe, a study has found.
一项研究发现,"史密斯"仍然是全球所有英语国家最常用的姓氏。

Lending firm NetCredit has researched the world's most common family names by country by charting its usage in a series of maps.
信贷公司NetCredit研究了各国最常见的姓氏,并将其使用情况绘制在多张地图上。

The maps show that Smith is still the most frequently used surname in the US, the UK, Canada, Australia and New Zealand.
这些地图显示,史密斯仍然是美国、英国、加拿大、澳大利亚和新西兰最常用的姓氏。

The name "Smith" originates as an Anglo-Saxon term for a metal worker or blacksmith.
"史密斯"起源于盎格鲁-撒克逊人对金属工人或铁匠的称呼。

Some reports put the very first use of surnames as early as 2852 BC in China.
有报道称,中国是最早使用姓氏的国家,大约在公元前2852年开始使用。

In China, one in every 13 people have "Wang" as their second name.
在中国,每13个人中就有一个人姓"王"。

In South Korea, one in five are named "Kim", and in Vietnam, 25% of people are named "Nguyen".
在韩国,1/5的人姓"金",而在越南,25%的人姓"阮"。

The most prevalent surname in South America is "Gonzalez" which derives itself from a Spanish name, originating in the German word for "battle".
南美洲最常见的姓氏是"冈萨雷斯",源于西班牙姓氏,这个词起源于德语中表示"战斗"的单词。

 
Men on construction site during daytime [Photo/Pexels]
>First 3D-printed neighborhood
首个3D打印社区将问世

A giant 3D printer built two houses in an impoverished, rural part of Mexico last week, breaking ground on what will be the first 3D-printed neighborhood in the world.
上周,一台巨型3D打印机在墨西哥一处贫困的农村地区建造了两栋房屋,接下来世界上第一个3D打印社区将在这里破土动工。

The poor families live in a seismic zone that's prone to flooding in the state of Tabasco, Mexico.
一些贫穷的家庭居住在墨西哥塔巴斯科州容易发生洪水的地震带中。

Building something that will withstand an earthquake and keep them dry during heavy rains was a key consideration when it came to the design.
因此建造既经得起地震,又能在大雨中保持干燥的房屋是设计时关键的考虑因素。

A nonprofit called New Story paired up with a construction technology company that developed Vulcan II, the 3D-printing robotics being used on the project.
非营利组织"新故事"和一家建筑技术公司展开合作,后者开发了用于该项目的名为"Vulcan II"的3D打印机器人。

The 33-foot printer pipes out a concrete mix that hardens when it dries, building the walls one layer at a time.
这款3D打印机长33英尺(约合10米),可以用管道做出混凝土混合物,混合物在干燥后会变硬,每次可建造一层墙壁。

It takes several days to build two houses at the same time.
该打印机可以在几天内同时建起两栋房屋。

The concrete mix is sturdier than traditional concrete. The foundation is reinforced to withstand seismic activity.
这种混凝土混合物要比传统的混凝土更加结实,地基经加固后可以承受住地震活动。

Developers hope to build 50 new houses by the end of 2020, replacing structures residents built themselves out of wood, metal and whatever materials they could afford.
开发商希望在2020年底前3D打印出50栋新房,以取代居民们用木头、金属和其它任何他们买得起的材料自主建造的房屋。

 
Costa Venezia cruise ship [Photo/SIPA]
>Air quality worst on cruise
邮轮是空气污染重灾区

The level of pollution on some cruise ship is worse than in the world's most polluted cities like Delhi, an investigation on Britain's biggest crew operator P&O Cruises has found.
一项针对英国最大的邮轮运营公司P&O游轮的调查发现,某些邮轮上空气污染的程度比德里等世界上污染最严重的城市还要严重。

The investigation found one cruise ship can emit as much particulate matter as 1m cars in a day.
调查发现,一艘游轮一天排放的微粒物相当于100万辆汽车的排放量。

The air on the deck downwind of the ship's funnels had 84,000 particulates per cu cm, according to data from particle counter.
来自微粒计数器的数据显示,在甲板上烟囱背风处的位置,微粒物含量为每立方厘米8.4万个。

Directly next to the funnels on the deck, the numbers rocketed to 144,000 with a peak at 226,000.
在甲板上紧挨着烟囱的位置,微粒物含量飙升至每立方厘米14.4万个,峰值达每立方厘米22.6万个。

Passengers are likely to be breathing some of these particulates, which are harmful for health and the environment.
乘客很可能吸入部分这些有害健康和环境的微粒物。

"Short term exposure can cause increasing respiratory symptoms. People who are asthmatic for example, that might give them a wheeze. Similarly for people with cardiovascular disease," an expert said.
有专家称,短时间暴露在污染空气中会导致呼吸道症状加剧。例如患哮喘的人,就会变得气喘吁吁。患心血管疾病的人也会出现类似症状。

 
Find more audio news on the China Daily app.


2019-12-23 4 years ago / 未收藏/ cddual发送到 kindle
2019.12.23
话说,转眼就到年底,时光悄悄溜走,2019年也只剩下几天了。
不少小伙伴们开始扪心自问:这一年我究竟做了什么?我是不是又进步了?接下来的2020年flag还要立起来~~
最近,#2017到2019的对比#挑战突然火了!
玩儿法很简单,左手放出2017年的照片,右手晒出2019年的照片,两张放在一起的对比照,大家一目了然~
正当大家都在感叹时光流逝之时,#2017和2019网络流行语对比#也随之火上了热搜。2017年,“奔三”只是一个“扎心了”的话题;2019年,“奔三”真的进入倒计时。
如今,一波新鲜流行语已上线,两年前你最爱用的话,如今着实有了年代感。新词替旧词,换成英文你都会说吗?
扎心了
2017,扎心扎到心坎上,这种被刺痛的感觉,英文可以用sting,例:His words stung her.(他的话刺痛了她。)“扎心了,老铁”英文可以说:My heart was pricked, Laotie.当然,表示“使痛心”、“使受伤”,形容被别人深深伤害到,还可以用大家最熟悉的hurt。表示“触及要害”、“刺到痛处”,还可以用cut sb to the quick来表达,如:Your heartless comments cut me to the quick.(你那番无心的话深深刺痛到了我。)
 
太难了
2019,我太难了!生活中处处可能会碰上难处,英文up against something表示碰上(麻烦),如:I am up against some serious problems.(我碰上一些很难办的事儿。)面对压力山大的挑战,或许也会疲惫不堪,十分糟糕,英文就是be the worse for wear。如:You were the worse for wear last night.(你昨晚看上去整个人都不好了。)而表示自己被难倒了,还可以说be stumped,如:I'm completely stumped.(我完全被难倒了。)
打call
2017,大家还在疯狂打call(beat a call),原本它表示为了台上爱豆呼喊、挥动荧光棒的喝彩,到生活中泛指对某人的喜爱与应援,打call引申为“为……欢呼”,英文还可以说to cheer for/on someone。
 
夸夸群
2019, 神仙操作“夸夸群”,在群里,无论发什么都会收到别人的夸奖和赞扬,心情不好的时候也能收到安慰。为周围的人送上赞美之词,英文可以选用out of sight形容特别棒,实在是牛得没边儿了。例:You are out of sight!(你太牛了!)而be (really) something也可以用来表示让人刮目相看,了不起。例:Imagine winning an Olympic medal—now that would be something.(想象一下,赢得奥运奖牌——那可是了不起啊。)
2017,一言不合就开怼,2019,盘就是了~

“怼”用来表示故意找茬、反抗、反对等意思。英文可以说pick on sb,例如:Why are you picking on me?(你干吗怼我?)这种言语上的攻击,英文还可以说lash out、bash或者slam。
 

2019,说到“盘”这个词,大家都会想到“盘核桃”、“盘保健球”这个动作,英文可以用rotate来表示。而如果既想表达“盘”这个动作,又想显示出“喜爱”心情,stroke也是一个不错的选择,如:She gave the cat a stroke.(她抚摸了一下猫咪。)有的时候,越盘越上瘾,以至于无法自拔,英文还可以说,be crazy about sth, be fascinated by sth或者be/get addicted to sth。
尬聊
2017,你或许会陷入尬聊(awkward chat),说到“尴尬”,大家都会想到embarrass,意思是“使尴尬”,例:I was embarrassed by his comments about my clothes.(他对我衣服的那番评论很让我尴尬。)形容词embarrassing表示“令人尴尬的”。名词embarrassment意为“尴尬;窘迫”。除了embarrass, egg on one's face可以表示“丢脸”、“难堪”;not know where to put yourself指的是觉得手足无措,不知该怎样是好;而make sb look bad也是表达让人看起来很糟糕、难堪。
 
雨女无瓜
2019,一句“雨女无瓜”,拒绝所有无效社交。None of one's business这个短语指的是“不关某人的事”,另一个短语mind one’s own business也就是“管好自己的事,别多管闲事”;表示“没有权利过问或做某事”,英文还可以说have no business doing sth。口语中表示“多管闲事”还可以说:stick one’s oar in sth或者poke/stick one’s nose into sth。Never you mind则表示不关你的事,用不着你多问。
戏精
2017,受不了“戏精”,drama queen指的是为一点儿小事却反应夸张,“小题大做的人”。Comedian“喜剧演员”还有一种反讽的含义,指那些自认为自己很搞笑,其实并不搞笑的人。Bighead意思是“自命不凡的人”,例:He's always boasting. He's such a bighead!(他老是吹牛。真是自负的家伙!)
 
杠精
2019,受不了“杠精”,英文里“杠精”也可以说argumentative person。抬杠还可用argue for the sake of arguing来表示。
惊不惊喜,意不意外
2017,小伙伴们嘴边挂着太多惊喜与意外,英文除了surprise,还可以用Shock, horror!来表示好怕怕啊~说这话时你其实一点儿也不怕,只不过假装因为看到听到某件事儿感到惊呆。而Don't believe it英文你也可以用来直白地表示很惊喜很意外,哎,简直不能信啊……
 
OMG,买它
2019,来不及想太多,买它!又到了剁手的时刻,“花大钱”英文可以说blow big bucks on;pay good money也是表示“花好多钱、一大笔钱”。Throw money around,四处扔钱,看来是钱没有花到该花的地方,意思是花钱不走心,大手大脚。例:He lost his job, but still seems to have plenty of money to throw around.(他丢了工作,可是看上去还是有好多钱可以随便花。)
油腻
2017,可劲嘲讽油腻的中年大叔,“油腻”英文可以说oily,比如油性皮肤(oily skin)。此外,greasy也可以形容油腻。不过greasy或者oily只表达“油多”,缺失了“使人不想吃”的意思。Greasy或者oily还有一种解释:very friendly and polite in a way that is unpleasant and not sincere,也就是油滑,虚伪,跟油腻不太一样。所以,准确一些的译法或许是unpleasantly greasy,也就是油得令人不舒服。
 
令人头秃
2019,自己已经感觉到渐渐消失的发际线,英文hairless表示“无毛的”、“头秃的”。而“脱发”则可以说hair loss。有些人发量本来就少,英文可以用thin来形容头发稀疏。“头秃”还可以直接说thin on top或者bald,“地中海”的英文还可以说vertex baldness。
你的良心不会痛吗?
2017,看别人秀恩爱,想问:你的良心不会痛吗?Conscience指的是良心,道德心,而英文里表示良心会痛不一定要提到conscience。Guilty其实更能反映出内心的罪恶感,因为除了表示“有罪的”,它还可以指“内疚”。而我们熟悉的ashamed表示“惭愧”,cringe则有“觉得难为情”、“自责”之意。
 
我酸了
2019,身边的朋友一个个结婚生子,单身的我们酸了酸了……俗话说“吃不到葡萄说葡萄酸”,英文里也可以用“酸葡萄”sour grapes来表达这样的心理。说到“酸”英文里还是这个词很常用,be jealous of sb/sth表示“嫉妒某人或某物”,而out of jealousy则表示“出于嫉妒”。Green-eyed或者green with envy也可以用来表示羡慕嫉妒恨。
2017,我们一起撸起袖子加油干(we roll up our sleeves and work with added energy),2019,我们仍是那个怀揣着梦想的追梦人(dream chaser)!
 
(来源:21世纪英文报;综合来源:新华社、人民日报、央视新闻、新浪微博、China Daily)
 
2019-12-22 4 years ago / 未收藏/ cddual发送到 kindle
2019.12.22
On Thursday, the forthcoming animated film JiangZiya: Legend of Deification, the follow-up of this year's highest-grossing hit Ne Zha, released its first trailer. As the latest installment of the so-called "Fengshen Cinematic Universe", the film has stirred a sensation online.
12月12日,接棒本年度票房最高爆款《哪吒》的动画片《姜子牙》终于露出了真面目。一段短短的预告片,很快把这部“封神宇宙”中的动画新作送上了热搜。
The film is adapted from the Ming Dynasty (1368-1644) novel Fengshen Yanyi (Investiture of the Gods). Jiang Ziya is depicted as a 72-year-old man who decides to assist the ruler of Xiqi kingdom to turn over the tyranny of Emperor Zhou. But in the trailer of the new film, Jiang looks much younger. He looks like a middle-aged man who insists on following his heart despite having a complex past.
按照明代小说《封神演义》中的描述,姜子牙出山助西岐伐纣时,已是72岁须发皆白的老翁。但在预告片里,姜子牙抵达颜值新巅峰,看上去更像一个历经沧桑但初心不悔的帅大叔。
Earlier this week, director Wuershan's live-action epic Fengshen Trilogy also announced the cast of two new roles, respectively actor Chen Kun as Yuanshi Tianzu and actress Yuan Quan as Queen Jiang. Before that, several major roles were also revealed, such as actor Huang Bo as Jiang Ziya, Kris Phillips as King Yinshou.
而本周伊始,乌尔善导演的真人巨制《封神三部曲》也放出了新消息:陈坤会演“元始天尊”,袁泉饰“姜王后”。这是继黄渤演姜子牙、费翔演商王殷寿等被宣布之后,最新获宣的两个新角色。
Besides, actor Liuxiao Lingtong will reportedly join hands with actor Ma Dehua to reprise their most classic roles — the Monkey King and Zhu Bajie — in the new movie Journey to the West. They will lend their faces for the motion-capture cinematography.
本月初,六小龄童也传出新消息,要携手“二师兄”马德华,以“动作捕捉”的表演方式复出大银幕,出演《西游记真假美猴王》。
Currently, China has the world's most theater screens and is the world's second-largest movie market in terms of annual box office receipts. The rise of domestic film industry means the fantastic spectacles fictionalized in China's most acclaimed literature works can be brought to big screen.
当中国银幕数位居全球第一,中国也成为全球第二大票房市场之时,中国电影工业的进步,也让中国文学史上瑰丽奇幻的史诗级想象有了成为活生生的画面的可能。
Interestingly, when China's film and TV industry was just taking off, the two novels were adapted to TV series in the late 1980s and early 1990s. But the special effects weren’t that good because the budgets were limited.
虽然在中国影视产业刚刚起步时,已经有了经典的央视86版《西游记》和90版《封神榜》,但毕竟当时制作经费有限,特效效果没有那么令人满意。
With the expansion of special effect sector in domestic film industry, insiders are now more confident to build a cinematic franchise to gather the most influential characters in Chinese mythologies. It is also believed that only Chinese filmmakers can convey the cultural spirit and capture the aesthetic nuance rooted from their own civilization.
但现在中国特效工业已经起步,也让业内对建立“中国神话电影宇宙”充满信心。毕竟,中国人的文化遗产,恐怕还是中国人自己才能拍出东方韵味和自己独有的文化精神。
记者:徐帆
来源:中国日报双语新闻微信


2019-12-22 4 years ago / 未收藏/ cddual发送到 kindle
2019.12.22
国务院扶贫办表示,预计2019年我国减贫人口将超1000万。到今年年底,我国95%以上的贫困人口可以脱贫,90%以上的贫困县可以摘帽。
China is expected to lift over 10 million people out of poverty by the end of 2019. More than 95 percent of China's poor population is expected to shake off poverty and over 90 percent of poor counties are expected to be removed from the poverty list by the end of this year, according to the State Council Leading Group Office of Poverty Alleviation and Development.

2019年12月12日,村民在广西玉林市福绵区沙田镇苏立村百香果种植基地采摘百香果。(图片来源:新华社)
 
【知识点】
以现行标准衡量,1978年末我国农村贫困发生率高达97.5%,农村贫困人口7.7亿人,而截至2018年底,只剩下1660万人未脱贫。2013—2018年6年来,农村累计减贫8239万人,连续6年平均每年减贫1300多万人。
世界银行发布的一份报告称,中国在快速经济增长和减少贫困方面取得了“史无前例的成就”。不少国外专家发出了“中国为什么能”的感叹。
明年是脱贫攻坚战的收官之年,要确保脱贫攻坚任务如期全面完成,集中兵力打好深度贫困歼灭战。下一步,要加快“三区三州”等深度贫困地区脱贫;建立防贫监测预警机制和稳定脱贫长效机制,巩固脱贫成果防止返贫;深化东西部扶贫协作和定点扶贫,助力西部地区产业发展;组织开展脱贫攻坚“回头看”工作,查遗补漏,及时发现和解决问题。


【重要讲话】
农村1000多万贫困人口的脱贫任务要如期完成,还得咬定目标使劲干。
To achieve our task of lifting another 10 million-plus rural residents out of poverty as planned, we shall remain focused and work hard on this.
——2018年12月31日,习近平发表2019年新年贺词

 
脱贫致富终究要靠贫困群众用自己的辛勤劳动来实现。没有比人更高的山,没有比脚更长的路。要重视发挥广大基层干部群众的首创精神,让他们的心热起来、行动起来,靠辛勤劳动改变贫困落后面貌。
To eliminate poverty, the impoverished should rely on their own hard work. There is no mountain top we cannot reach; there is no voyage without a final destination. We should rally the initiative of grassroots officials and people in poverty-stricken areas, and encourage them to act with passion and fight poverty through hard work.
——2015年11月27日至28日,习近平在中央扶贫开发工作会议上的讲话

 
【相关词汇】
“两不愁三保障”:农村贫困人口不愁吃、不愁穿,义务教育、基本医疗、住房安全有保障
Rural poor people are free from worries over food and clothing and have access to compulsory education, basic medical services and safe housing.

消除绝对贫困
eliminate absolute poverty

Paul W. Frields / 2019-12-23 4 years ago / 未收藏/ fedoramagazine发送到 kindle
The end of the year is a perfect time to look back on some of the Magazine’s most popular articles of 2019. One of the Fedora operating systems’s many strong points is its wide array of tools for system administrators. As your skills progress, you’ll find that the Fedora OS has even more to offer. […]
2019-12-23 4 years ago / 未收藏/ guanzhi发送到 kindle
市立大学人文学教授库格麦斯已经是第二次结婚了,跟头一次一样,目前的婚姻生活也不如意。他的妻子达芙妮·库格麦斯是个笨头笨脑的人,另外还有和前妻弗萝所生的两个呆儿子。因为要支付赡养费和儿子的抚养费,他已是焦头烂额。
“我原先就知道事情会变得这么糟糕吗?”有一天,库格麦斯向他的精神分析医生哀叹道,“达芙妮以前还是有希望的,谁能想到她会放任自流,像个浮水气球一样膨胀起来?另外,她以前还有点儿钱,图这点不能算是个跟她结婚的好理由,可是就凭我这样的谋生头脑,那也没什么坏处。你明白我的意思吗?”
库格麦斯头顶已秃,身上的汗毛茂盛得像头熊,可他壮心未已。
“我需要再找个女人,”他又说,“我需要外遇。可能我看上去不是那种人,可我是个需要浪漫的人。我需要温柔,需要调情。我的青春一去不复返了,所以要趁还没变得太晚之前,我想在威尼斯谈恋爱,想在‘二十一’餐厅里互相说些俏皮话,想喝着红葡萄酒在烛光下羞答答地对视。你明白我在说什么吗?”
曼德尔医生在椅子上换了个坐姿说:“外遇解决不了任何问题,你太不现实了,你的问题要深得多。”
“而且这次外遇一定得谨慎进行。”库格麦斯接着说,“我负担不起再离次婚,达芙妮会把我整得很惨。”
“库格麦斯先生——”
“但不是跟市立大学的任何人,因为达芙妮也在那儿工作。里面的教工没一个有什么刺激劲儿,倒是有些学生……”
“库格麦斯先生——”
“帮帮我吧。昨天晚上我做了个梦,梦到我提着野餐篮蹦跳着穿过一块草地,篮子上面写着‘选择’,接着我发现篮子上有个洞。”
“库格麦斯先生,你要是付诸行动,那就最糟糕不过了。你一定得只是在这儿把感情表达出来,我们一起进行分析。你已经治疗了这么长时间,应该晓得没有一夜之间治好病这种事儿。我毕竟是个精神分析医生,不是魔术师。”
“那么也许我需要一个魔术师。”库格麦斯说着从椅子上站起来,就这样终止了对自己的心理治疗。
又过了两周,当库格麦斯和达芙妮像两件旧家具一样百无聊赖地待在公寓里时,电话响了。
“我来接。”库格麦斯说,“喂?”
“库格麦斯吗?”一个声音说,“库格麦斯,我是帕斯基。”
“谁?”
“帕斯基,或者说叫‘了不起的帕斯基’,听说过吗?”
“对不起,你说什么?”
“我听说你为了能给自己的生活带来点儿新鲜感,正在市里到处找一位魔术师?是吗?”
“嘘——”库格麦斯悄声说,“别挂电话,帕斯基,你从哪儿打的电话?”
第二天下午很早的时候,库格麦斯到了位于布鲁克林布什威克区的一幢破破烂烂的公寓楼前。爬上三段楼梯后,在昏暗的走廊里,他眯着眼睛找到要找的那户并按响了门铃。我会后悔的,他心里想。
过了几秒钟,开门迎接他的是个矮而瘦削、脸色苍白的男人。
“你就是‘伟人帕斯基’吗?”
“是‘了不起的帕斯基’。你要喝杯茶吗?”
“不喝,我想要浪漫,想要音乐,想要爱情和美貌。”
“可是不喝茶,是吗?真奇怪。那也好,你坐吧。”
帕斯基到了里屋,库格麦斯听到搬动箱子和家具的声音。帕斯基出来时,他面前推了个大物件,下面安有吱吱嘎嘎作响的滚轮。他把顶上放着的几块绸制大手帕取下,吹走一些灰尘。那是个看上去不值什么钱的中式橱柜,油漆得很差。
“帕斯基,”库格麦斯问道,“这是什么骗人的玩意儿?”
“注意了,”帕斯基说,“这可是件好东西,是我去年为皮西厄斯骑士会预订的一次演出研制的,可是后来又取消了。钻进去吧。”
“怎么着,然后你就可以满满地插上剑什么的?”
“你看到剑了吗?”
库格麦斯扮了个鬼脸,嘴里咕哝着钻进橱柜。就在他面前,有块没油漆过的夹板,上面用胶水粘了两颗丑陋的仿宝石,没法不看到。“简直是开玩笑。”他说。
“挺不错的玩笑呢。喂,关键是这样,你在橱柜里,我随便把什么小说扔里面,关上橱柜门,然后敲三次,你就会发现自己一下子进到那本书里。”
库格麦斯不相信地扮了个鬼脸。
“宝石的作用,”帕斯基说,“那是我伸向上帝的手。还不单单是小说,一个短篇,一部戏剧,一首诗,都可以。你可以见到由全世界最杰出的作家创造出来的任何一个女人,想见谁都可以。你可以一直看下去,直到找到最称心的。觉得差不多的时候你就喊一声,我会让你一眨眼就回来。”
“帕斯基,你是不是得了什么病?”
“我在跟你实打实地说。”帕斯基说。
库格麦斯仍有怀疑。“你是在跟我说,你这个自己做的破箱子能像你说的那样,带我去一趟?”
“二十块钱。”
库格麦斯伸手掏钱包。“看到我才相信。”他说。
帕斯基把钞票装进裤子口袋,转身向书柜走去。
“你想见到谁?嘉莉妹妹?海斯特·白兰?奥菲莉娅?也许是索尔·贝娄笔下的谁?嗨,坦波尔·德里克怎么样?不过对你这样年纪的男人来说,她可不好对付。”
“法国的,我想跟个法国情人有外遇。”
“娜娜怎么样?”
“我不想非得花钱不可。”
“《战争与和平》里的娜塔莎呢?”
“我说过要法国的。我想到了!爱玛·包法利怎么样?听起来最理想了。”
“没问题,库格麦斯,你觉得差不多的时候就喊一声。”帕斯基把福楼拜那本小说的平装本扔了进去。
“你肯定这东西安全吗?”帕斯基关橱柜门时,库格麦斯问道。
“安全,这个破世界上还有什么是安全的?”帕斯基在橱柜上敲了三声,然后猛地打开柜门。
库格麦斯不见了,同时,他出现在包法利夫妇的卧室里。在他面前,是个漂亮的女人,背对着他一个人站在那儿,正在叠几样床上用品。不敢相信啊,库格麦斯盯着包法利医生这位迷人的妻子心里想,不可思议,我到了这儿,那就是她。
爱玛吃惊地转过身来。“天哪,您吓了我一跳。”她说,“您是谁?”她用的是平装书上那种翻译过来的标准英语。
真是太棒了,他心想,接着就意识到她在跟他说话,就说:“对不起,我是西德尼·库格麦斯,市立大学的,人文学教授,CCNY,知道吗?在曼哈顿上城。我——哦,我的天!”
爱玛·包法利轻浮地笑了,她说:“您想喝点儿什么?要么,来杯葡萄酒好吗?”
她真漂亮,库格麦斯心想,跟和我同床共寝的老没劲儿比起来,真是一个天上,一个地下!他突然感到一阵冲动,想把这个美人揽入怀内,并告诉她他一辈子梦寐以求的,就是她那种女人。
“好吧,来点儿葡萄酒,”他声音沙哑地说,“白的,不,红的,不,白的,喝白葡萄酒吧。”
“夏尔今天出去了。”爱玛说,话里带着挑逗性的暗示。
喝完酒,他们去风景优美的法国乡间散步。“我一直梦想一个神秘的陌生人会现身,把我从这种单调乏味的粗俗乡村生活中拯救出去。”爱玛说着抓紧了他的手。他俩经过一个小教堂。“我喜欢你的穿着,”她低声说,“我在这儿从来没见到过,很……很时髦。”
“这叫休闲装,”他语气浪漫地说,“是削价货。”突然,他吻了她。接下来的一个钟头里,他们一起躺在树下低声交谈,并用眼睛进行意蕴深长的对话。后来库格麦斯站起身,他刚刚想起要和达芙妮在布鲁明代尔商店碰头。“我得走了,”他告诉她,“可是别担心,我还会回来。”
“希望你会。”爱玛说。
他热情地和她拥抱,然后两人走回包法利家。他用双手捧着爱玛的脸,再次亲吻了她,接着喊道:“好了,帕斯基!我必须在三点半赶到布鲁明代尔商店。”
只听得砰的一声,库格麦斯又回到了布鲁克林。
“怎么样?我骗你了吗?”
“哎,帕斯基,我和我那位冤家说好要在列克星顿大道上见面,现在已经晚了。什么时候我能再去那儿?明天可以吗?”
“我很乐意帮你,带二十块钱就行了。还有,别告诉任何人。”
“可不是吗,我还要去给鲁帕特·默多克打个电话呢。”
库格麦斯打了辆的士,向着市内疾驰而去。他感到心花怒放。我在恋爱,他想,我有了个了不起的秘密。他没意识到就在此时,在全国各地许多间教室,学生正跟他们的老师说:“第一百页上出现的是谁?一个秃顶的犹太人亲包法利夫人?”南达科他州塞诺克斯福斯市的一个老师叹了口气,心想,天哪,这些小孩儿,真是稀奇古怪,他们的脑袋瓜里都在想什么呀!
库格麦斯气喘吁吁地赶到布鲁明代尔商店时,达芙妮正在卫浴用品部。“你去哪儿了?”她不耐烦地问道,“已经四点半了。”
“路上堵车。”库格麦斯说。
第二天,库格麦斯又去找帕斯基,几分钟后就通过魔法到了永镇。见到他,爱玛的兴奋之情溢于言表。他俩在一起度过了几小时,一边欢笑,一边聊他们的不同经历。库格麦斯走之前,他们做了爱。“天哪,我在跟包法利夫人做爱!”库格麦斯低声对自己说,“就是我,大一的时候英语还不及格呢。”
一个月又一个月过去了,库格麦斯去找了帕斯基很多次,和爱玛·包法利的关系发展到了如胶似漆的程度。“你要确保我每次都出现在书里第一百二十页之前的地方,”有一天,库格麦斯对魔术师说,“我必须在她跟那个叫罗多尔夫的角色勾搭上之前和她见面。”
“为什么?”帕斯基问,“你不能胜他一筹吗?”
“胜他一筹,说得容易。他是个有地产的贵族,那些家伙除了调情和骑马,就没别的事情可干。照我看,他无非是《妇女每日穿着》上刊登的那种人,梳着赫尔穆特·布朗热式发型。可是在爱玛看来,他魅力无穷。”
“她丈夫一点儿也没怀疑?”
“他根本没能力怀疑。他是个死气沉沉的小医生,热情劲儿早过去几十年了。到了十点钟他就准备睡觉,而她才刚开始活出味道呢。噢,那就……回头见吧。”
库格麦斯又一次钻进了橱柜,马上就到了永镇上的包法利家。“你好吗,小乖乖?”他对爱玛说。
“噢,库格麦斯,”爱玛叹了口气说,“我忍得真是太多了。昨天晚餐时,那位活宝先生吃着甜点就睡着了。我正跟他热情万分地说起巴黎的马克西姆餐厅和芭蕾舞,突然听到他打起了呼噜。”
“没关系,亲爱的,我在这儿。”库格麦斯拥抱着她说。他闻着爱玛的法国香水味,把鼻子埋进了她的秀发。我得到了,他想,我已经受苦受够了,我看精神分析医生花的钱已经够多。我一直寻找,直到精疲力竭。她年轻而性感,而我出现在这儿,在莱昂之后的几页,刚好在罗多尔夫出现之前。通过出现在恰当的章节,我真是如鱼得水啊。
一点儿没错,爱玛和库格麦斯一样快乐。她一直渴望刺激,他向她讲述的关于百老汇生活、开快车、好莱坞及电视明星等等,也让这位法国美人向往不已。
“再跟我说说O·J·辛普森的事吧。”那天晚上,他们散步经过布尼齐安神甫主持的教堂时,她恳求道。
“还能怎么说呢?他可真是了不起,创造了所有跑动带球的记录,那动作就甭提了,别人根本碰不到他。”
“还有奥斯卡奖呢?”爱玛充满憧憬地说,“我要能得到一座,死也值了。”
“你首先要得到提名。”
“我知道,你解释过了,但是我相信我会演戏。当然,我得上一两节课,也许跟斯特拉斯特伯格学。然后如果找对了经纪人——”
“再说吧,回头再说吧,我会跟帕斯基讲的。”
那天晚上,在安全回到帕斯基的公寓后,库格麦斯提出想让爱玛来纽约这个大城市看看。
“让我想想看,”帕斯基说,“也许我能办到,比这更奇怪的都发生过呢。”当然,他们都想不出还有什么比这更奇怪。
“见鬼,你一天到晚去哪儿了?”那天晚上回到家里的时间很晚,达芙妮·库格麦斯向丈夫咆哮道,“你是不是在哪儿养了个骚货?”
“对,没错,我就是那种人。”库格麦斯无精打彩地说,“我跟莱奥那多·波普金在一起,我们讨论了波兰的社会主义农业。你也知道波普金,他在这方面是怪才一个。”
“那你最近可是太不正常了,”达芙妮说,“神不守舍。别忘了我爸爸的生日,星期六,记住了?”
“噢,当然,当然。”库格麦斯一边走向浴室一边说。
“我的全家人都会在那儿,能见到那对双胞胎,还有海米什堂弟。你应该对海米什堂弟更客气一点——他喜欢你。”
“没错,那对双胞胎。”库格麦斯说着关上了浴室门,也把他妻子的声音关在了门外。他靠在门上深吸一口气。他告诉自己,再过几个小时,他会又到了永镇,去跟他所爱的人在一起。如果一切顺利,这次他会把爱玛带回来。
第二天下午三点十五分,帕斯基再次施展魔法。库格麦斯出现在爱玛面前,满脸微笑和渴望。他俩和永镇的税务官比内在一起待了几小时,然后就坐上包法利家的马车。他们遵从帕斯基的指示,紧紧抱在一起并闭上眼睛数了十下。他们再次睁开眼睛时,马车正驶到广场酒店的侧门并停了下来,库格麦斯当天已经乐观地在那儿订了个套间。
“我太喜欢了!跟我想像的完全一样!”爱玛说着在卧室里快乐地打着旋,从窗户检视着这座城市。“那是FAO施瓦茨玩具商店,那是中央公园。雪莉画廊在哪儿?噢,那儿——我看见了,太神圣了。”
床上放着几个哈斯顿和圣罗兰服装的包装盒,爱玛打开其中一个,拿起一条黑丝绒裤子往她线条完美的身上比。
“这条家常裤是拉尔夫·劳伦设计的。”库格麦斯说,“你看上去容光焕发。过来,亲爱的,来亲一下。”
“我从来没这样快活过!”爱玛站在镜子前尖叫着,“我们上街去吧,我想看《龙套一族》和古根海姆博物馆,还有你挂在嘴边的杰克·尼科尔森,有没有他演的电影正在放?”
“我怎么也想不通,”斯坦福大学的一位教授说,“先是冒出来一个叫库格麦斯的角色,现在她又从书里失踪了。嗯,我想名著的特点就是你可以把它读上一千遍,仍然次次能够读到新东西吧。”
这对情人过了个愉快之极的周末。库格麦斯告诉达芙妮他要去波士顿参加一个研讨会,星期一才回来。他和爱玛尽情享受每一刻,一起看电影,在唐人街吃饭,在迪斯科舞厅里玩两个钟头,在床上看电视剧。星期天,他们一直睡到中午才起来,然后去了曼哈顿的苏豪区,盯着看出入伊琳餐厅的名流。星期天晚上,他们在酒店的套间里品尝鱼子酱,喝香槟酒,一直聊到黎明。到了早晨,他们打的去帕斯基的公寓。在的士上,库格麦斯心想,这真是够忙碌的,但是值得。我不能太频繁带她来这儿,但是偶尔一次是对永镇生活的调剂,令人向往。
在帕斯基家,爱玛钻进了橱柜,把装着新衣服的盒子整齐地堆在她周围。她情意绵绵地吻着库格麦斯。“下次去我家。”她眨了一下眼睛说。帕斯基在橱柜上敲了三次,但是没动静。
“唔。”帕斯基抓着头。他又敲了几次,魔法还是不灵。“一定是哪儿出毛病了。”他咕哝着说。
“帕斯基,你开玩笑!”库格麦斯叫了起来,“怎么会不灵了呢?”
“别急,别急。爱玛,你还在里面吗?”
“对。”
帕斯基又敲了几次——这次重了点儿。
“我还在这儿,帕斯基。”
“我知道,亲爱的,你坐好。”
“帕斯基,我们必须把她送回去。”库格麦斯悄声说,“我是有妇之夫,再过三个钟头我还要去上课。除了谨慎地来一次外遇,别的我可什么也没想过啊。”
“我不明白,”帕斯基嘀咕道,“这个小把戏可是够灵的呀。”
可他一筹莫展。“我需要一小段时间,”他对库格麦斯说,“要把它拆开看看。回头我给你打电话。”
库格麦斯把爱玛塞进一辆的士,又把她带回广场酒店,自己差点儿没来得及去上课。他整天都在打电话,给帕斯基打,也给他的情人打。魔术师告诉他可能还需要几天,才能找到毛病的根子在哪儿。
“研讨会开得怎么样?”那天晚上达芙妮问他。
“不错,不错。”他说着想点一根烟,却点着了有过滤嘴的那头。
“怎么了?你紧张得像只猫似的。”
“我?哈,真好笑,我平静得像是夏天的夜晚呢。我出去散散步。”他溜出家门,叫了辆的士,急忙赶到广场酒店。
“这样太不好了,”爱玛说,“夏尔会想我的。”
“忍一下吧。”库格麦斯说。他脸色苍白,满头大汗。他再次吻了她,然后冲出去坐电梯下楼。在广场酒店的大堂,他在投币电话里向帕斯基哇哇叫,刚好在午夜前才赶回家。
“据波普金说,从一九七一年以来,克拉科夫的大麦价格从来不像现在这样稳定。”他对达芙妮说。钻进被窝时,他脸上还挤出一点笑容。
整整一周就这样过去了。
星期五晚上,库格麦斯告诉达芙妮他要去参加另外一个研讨会,这次是在西罗古斯。他急忙又赶去广场酒店,但是第二个周末跟第一个完全不一样了。“把我送回小说里,要么跟我结婚。”爱玛告诉库格麦斯,“另外,我想找个工作,或者去上学,天天看电视烦透了。”
“好,那我们就有钱花了。”库格麦斯说,“你花在送餐服务上的钱是你体重的两倍。”
“昨天我在中央公园里认识了一个外百老汇的制作人,他说我可能适合在他制作的一部戏里演出。”
“哪来的小丑?”库格麦斯问道。
“他不是个小丑,他感觉敏锐,心肠好,还招人喜欢。叫杰夫,忘了姓什么。他快得托妮奖了。”
那天下午晚些时候,库格麦斯醉醺醺地去了帕斯基家。
“别急,”帕斯基说,“要不你会得上冠心病。”
“别急,你还跟我说别急。我在酒店房间里藏了个虚构人物,还有,我觉得我老婆在请私人侦探跟踪我。”
“好了,好了,我知道有麻烦。”帕斯基钻到橱柜下面,开始用一个大扳手砸不知什么东西。
“我像一头野兽,”库格麦斯又说,“偷偷摸摸地在市里跑来跑去。爱玛跟我互相很看不顺眼,还不说酒店费用像国防预算那样吓人。”
“我又能怎么办呢?这是魔法的世界,”帕斯基说,“微妙得很。”
“微妙个屁,我可是把香槟酒啊什么的好东西全往微妙这个小耗子的嘴里倒,她还要买衣服,还被社区剧院录取了,突然又需要拍专业摄影照片。还有呢,帕斯基,费维什·科普凯恩德教授,就是教比较文学的,他一直嫉妒我,认出来在福楼拜的小说里偶尔出现的人物是我,威胁要去跟达芙妮说。我想像到不可收拾和付赡养费,还有坐监。因为我和包法利夫人私通,我老婆会把我搞成个叫花子。”
“你想让我怎么说?我现在白天黑夜都在修理它。至于你个人的焦虑,我是帮不上忙了。我是个魔术师,不是个精神分析医生。”
到了星期天下午,爱玛把自己反锁在浴室里面,对库格麦斯的恳求充耳不闻。库格麦斯看着窗户外面的沃尔曼溜冰场,他想到了自杀。真糟糕这层楼不高,他心想,要不然现在我就那么干了。也许,我跑到欧洲重新开始生活……也许我可以把这个故事卖给《国际先驱论坛报》,就像那些年轻女孩子经常会干的那样。
电话铃响了,库格麦斯机械地把话筒拿到耳朵边。
“把她领来吧,”帕斯基说,“我想我已经把它搞好了。”
库格麦斯心头一阵狂喜。“你是说真的?”他说,“你把它修好了?”
“毛病出在传输上,具体也说不清。”
“帕斯基,你是个天才。我们一分钟内就到你那儿,要不了一分钟。”
这对情人又急急忙忙赶到魔术师的公寓,爱玛·包法利再次带着她的一盒盒服装钻进橱柜。这一次,他们没有吻别。帕斯基关上柜门,深吸一口气,在柜子上敲了三下,只听到令人放心的砰的一声。帕斯基往里看,已经人去柜空,包法利夫人又回到了小说里。库格麦斯如释重负地长出一口气,抓着魔术师的手一阵猛握。
“结束了,”他说,“我吃一堑,长一智,再也不会对我老婆不忠了,我发誓。”他再次抓住帕斯基的手一阵猛握,在心里记着要送给他一条领带。
又过了三周,在一个十分宜人的春日傍晚将尽时分,帕斯基听到有人按门铃,就去开了门。是库格麦斯,他的脸上有种羞怯的表情。
“说吧,库格麦斯,”魔术师说道,“这次想去哪儿?”
“就这一次,”库格麦斯,“天气这么好,我的青春一去不复返了。哎,你有没有看过《波特诺的怨诉》,还记得里面那个‘猴子’吗?”
“价钱现在是二十五块,因为生活费用在上涨,不过鉴于我给你带来过麻烦,第一次给你免费。”
“你是个好人。”库格麦斯说。他一边往橱柜里钻,一边梳理着仅存的几根头发。“这东西还好用吧?”
“希望吧,不过自从发生上次那件不愉快的事情以来,我就没怎么试过。”
“性和浪漫,”库格麦斯在柜子里面说,“我们之所以追求漂亮脸蛋,为的就是这两样啊。”
帕斯基扔进一本《波特诺的怨诉》,然后在柜子上敲了三下。但这次没听到砰的一声,而是传来一声沉闷的爆炸声,接着是噼里啪拉的声音,然后火花四溅。帕斯基往后跳了一步,由于心脏病发作,他马上倒地身亡。橱柜熊熊燃烧起来,到最后,整幢楼都被烧掉了。
库格麦斯对这场大祸茫然不知,他自己也遇到了麻烦。他没有一下子进入《波特诺的怨诉》,而是进入了一本名为《西班牙语补习》的旧课本。他正在一片荒凉而多岩的地方奔跑着逃命,“tener”(意为“拥有”)——一个巨大而多毛的不规则动词——正甩开细长的双腿,将他紧紧追赶。


2019-12-22 4 years ago / 未收藏/ guanzhi发送到 kindle
“看,有人给你送礼物来了。”走出房门来迎接他的妻子,在说“你回来啦”之前,突然对丈夫这么说。弯腰脱鞋的丈夫发现鞋箱上放着一盆花。
“噢,这花很漂亮。好像是洋兰。哪里来的?”
丈夫回答道,心里却突然涌起一种不祥之感。
“不知道。我出去买东西时,花店送来的。我不在,所以寄放在邻居家里了。”
他避开妻子的目光,仔细端详这盆花。细竹支撑着的修长纤弱的茎上开着三朵淡紫色的花。在另一棵茎的顶端,有两个胖乎乎的暗紫色的花蕾。几片干燥厚重的叶子耷拉在盆边上,整棵花赤裸裸地挺立着,上面好像只有花和蕾。
“上面还附着一张卡片。”
妻子说着,拿出一张卡片。上面写着:祝贺你的生日。现在我仍然爱你。
为了不叫妻子发现他脸色的变化,他弯腰面对着鞋。
“这是谁送来的呢?”
“……哎呀,我也想不起来。”
“这就怪了。”
妻子扔下蹲在那里不起来的丈夫,咚咚咚向厨房走去。
这个混蛋!丈夫心里骂道,我的苦衷她一点也不理解。一股怒火在他的心里燃烧。
妻子打开了一瓶莆萄酒,为他祝贺生日。但飞来的那盆花,却使这个夜晚罩上了一层阴影。伺候孩子睡觉后,两个人在起居室里相对而坐,丈夫总是躲开妻子的目光。
“这样干坐着实在无聊。你好好想想,就一点想不起来?”
妻子还是穷追不舍。在妻子没完没了的追问下,他哼哼唧唧地说,也许是过去的女友心血来潮送来的生日礼物吧。
“什么样的女朋友,我怎么没听你说过?”
妻子突然板起脸来。
“你别急,不是你所想象的那种关系。”
“你知道我是怎样想的?”
看见妻子的眼睛里一下子涌满了泪,丈夫狼狈不堪,手足无措。这下子糟了,我不应该瞒着她。都是那花引起的麻烦,我马上把它扔了。当他满腹懊恼地站起来时,泪流满面的妻子轻轻地说:
“我不是开玩笑,你,已经不认识我的字了……”


Sergey Kargopolov / 2019-12-23 4 years ago / 未收藏/ appsdeveloperblog发送到 kindle
In this tutorial, you will learn how to add JWT support to your Spring Boot application and also how to add and validate custom JWT Claims. In this tutorial, I am going to use a very simple Spring Boot application generated with Spring Tools. To learn how to create a very simple Spring Boot application…
The post Add and Validate Custom Claims in JWT appeared first on Apps Developer Blog.
2019-12-23 4 years ago / 未收藏/ bingenwallpaper发送到 kindle
20191222-Seventeen arches at sunset
When it's winter solstice time in the Northern Hemisphere, the setting sun shines under the Seventeen-Arch Bridge of the Summer Palace in Beijing, causing this romantic glow. In the 18th century, during the reign of the Qing dynasty of China, Emperor Qianlong ordered the construction of this 1.1 square mile collection of gardens, lakes, and various structures including temples and small palaces. Today it's one of Beijing's premier attractions and will be crowded today with visitors headed to the bridge in time for sunset.
2019-12-22 4 years ago / 未收藏/ bingenwallpaper发送到 kindle
20191221-You've never seen anything like this
This macrophotograph of a snowflake shows the classic, six-sided structure that we've all come to associate with this tiny winter marvel. Until the advent of macro- and micro-photography in the late 1800s, it was impossible to study the structure of snowflakes—they melted too quickly to be accurately sketched under a microscope. Enter Wilson 'Snowflake' Bentley.
2019-12-23 4 years ago / 未收藏/ FEX 百度 Web 前端研发部发送到 kindle
作者:exialym

深阅读

V8 release v8.0
https://v8.dev/blog/v8-release-80
Will we have a party? Will we ship a new compiler? Will we skip versions 8 and 9 and just stay at an eternal V8 version X? Finally, after over 10 years of work, on our 100th blog post, we’re pleased to announce our newest branch, V8 version 8.0 V8, and we can finally answer that question: It’s bug fixes and performance improvements.

WebAssembly becomes a W3C Recommendation
https://www.w3.org/2019/12/pressrelease-wasm-rec.html.en
WebAssembly is a safe, portable, low-level format designed for efficient execution and compact representation of code on modern processors including in a web browser. At its core, WebAssembly is a virtual instruction set architecture that enables high-performance applications on the Web, and can be employed in many other environments. There are multiple implementations of WebAssembly, including browsers and stand-alone systems. WebAssembly can be used for applications like video and audio codecs, graphics and 3D, multi-media and games, cryptographic computations or portable language implementations.

Rethinking Atomic React: an interpretation of Brad Frost’s Atomic Design to use in React projects
https://cheesecakelabs.com/blog/rethinking-atomic-design-react-projects/
Atomic Design is a very widespread topic nowadays within the front-end community, but while looking at several projects, I realized that there is still a lot of personal opinion on its implementation, and here I list some points that caught my attention throughout the years. This article is not a tutorial on how to create your applications but it is intended to share my interpretation on how to best use this amazing methodology.

ECharts 技术发展简史
https://mp.weixin.qq.com/s/CSM7bcd5mevvgi1Jo3zp8Q
ECharts,一个纯 Javascript 的图表库,可以流畅的运行在 PC 和移动设备上,兼容当前绝大部分浏览器(IE8/9/10/11,Chrome,Firefox,Safari等),底层依赖轻量级的 Canvas 类库 ZRender,提供直观,生动,可交互,可高度个性化定制的数据可视化图表。2019年12月7号在上海由ECharts核心团队成员发起的专场线下交流会上,由@沈毅分享了《ECharts 技术发展简史》。

AntV 架构演进-G2 篇
https://www.yuque.com/antv/blog/it4eag
G2 自 2014 年以来已经发展到 4.0,回顾过去,我们发现每一个版本都是一笔记忆,当我们队每个版本的架构进行梳理,分析每个版本背后的思考时,这些记忆就转换成了财富,我从下面这些阶段谈起:G2 之前的图表;v0.1 - v1.0 成型;G2 v2.0 完善图形语法;G2 v3.0 通用场景的支持;G2 v4.0 交互语法和多维分析。

CSS flex属性深入理解
https://www.zhangxinxu.com/wordpress/2019/12/css-flex-deep/
CSS flex属性还是很难理解的,但是flex布局要想玩得溜溜溜,这一关必须得过,来来来,一起看看究竟是什么意思,如何更容易理解与记忆。

聊聊AIOps落地监控报警的应对之策
https://mp.weixin.qq.com/s?__biz=MzUyMzA3MTY1NA==&mid=2247485402&idx=1&sn=6b6e7235eb7f45e6eb10287bccafcc1b
监控报警是故障发现的重要一环,也是百度在AIOps的最早切入方向之一,目前百度AIOps在监控报警方面已经有两个场景取得突出效果:智能异常检测和智能报警合并。如何支撑AIOps算法在监控报警系统的快速落地并产生业务价值,这对监控报警架构提出了很大的挑战!在上篇《AIOps对监控报警架构的挑战》文章中,我们介绍了监控报警系统在故障处理流程中的位置(故障发现和故障通告),并且分析了AIOps对监控报警架构的三个挑战。在本篇,我们将详细介绍应对这三个挑战的方案:为了应对挑战一,我们研发了策略运行平台,让AIOps算法迭代找到飞一般的感觉。为了应对挑战二,我们提出了基于状态机的事件管理引擎,让事件管理so easy。为了应对挑战三,我们设计了灵活的报警合并方案,让值班工程师彻底跟报警风暴say bye bye。下面我们来详细看下这三个方案的实现细节。

State of Storybook 2019
https://medium.com/storybookjs/state-of-storybook-2019-b6a91ac79b74
Breakout year for Community, Product, and Ecosystem

Looking into Svelte 3
https://medium.com/javascript-in-plain-english/looking-into-svelte-3-b3d0b43cd794
Quickstart by creating a retro board in Svelte from scratch

Angular 9.0.0 and Ivy improvements
https://ionicframework.com/blog/angular-9-0-0-and-ivy-improvements/
One of the biggest updates in the pipeline right now is the upcoming release of Angular 9.0.0. Currently in RC, Angular 9.0 will hopefully ship early next year. But this release includes so much that I thought it required a post to highlight some of the most important updates.

What Is JavaScript Made Of?
https://overreacted.io/what-is-javascript-made-of/
Over the years, I’ve formed a mental model of JavaScript that gave me confidence. Here, I’m sharing a very compressed version of it. It’s structured like a glossary, with each topic getting a few sentences. As you read through this post, try to mentally keep score about how confident you feel about each topic. I won’t judge you if quite a few of them are a miss! At the end of this post, there is something that might help in that case.

Monitor Frontend Performance With User-Centric Performance Metrics in New Relic
https://blog.newrelic.com/product-news/monitor-frontend-performance-with-user-centric-performance-metrics/
Further, interactivity metrics help you measure and improve customer experience by showing:

  • How quickly your users can interact with visual content after your site renders it
  • How quickly your users get a response after interacting with visual content
  • How code-level elements of your site affect user interaction across device type, browser, or browser version
Building an Awesome UI Component Library in 2020
https://codeburst.io/building-an-awesome-ui-component-library-in-2020-a85cb8bec20
How to build your team’s next-gen component library in bit.dev, with real React component examples!

How Shopify Manages API Versioning and Breaking Changes
https://engineering.shopify.com/blogs/engineering/shopify-manages-api-versioning-breaking-changes
At Shopify, we have tens of thousands of partners building on our APIs that depend on us to ensure our merchants can run their businesses every day. In April of this year, we released the first official version of our API. All consumers of our APIs require stability and predictability and our API versioning scheme at Shopify allows us to continue to develop the platform while providing apps with stable API behavior and predictable timelines for adopting changes.

Evaluating BBRv2 on the Dropbox Edge Network
https://blogs.dropbox.com/tech/2019/12/evaluating-bbrv2-on-the-dropbox-edge-network/
Overall, BBRv2 is a great improvement over the BBRv1 and indeed seems way closer to being a drop-in replacement for Reno/CUBIC in cases where one needs slightly higher bandwidth. Adding experimental ECN support to that and we can even see a drop-in replacement for Data Center TCP (DCTCP). *Minus the 0.0001% of outliers with a >60% packet loss.

The On-Device Machine Learning Behind Recorder
https://ai.googleblog.com/2019/12/the-on-device-machine-learning-behind.html
We launched Recorder, a new kind of audio recording app for Pixel phones that leverages recent developments in on-device machine learning (ML) to transcribe conversations, to detect and identify the type of audio recorded (from broad categories like music or speech to particular sounds, such as applause, laughter and whistling), and to index recordings so users can quickly find and extract segments of interest. All of these features run entirely on-device, without the need for an internet connection.

Deterministic Aperture: A distributed, load balancing algorithm
https://blog.twitter.com/engineering/en_us/topics/infrastructure/2019/daperture-load-balancer.html
In this blog post, we will walk through a new client-side load balancing technique we’ve developed and deployed widely at Twitter which has allowed our microservice architecture to efficiently scale clusters to thousands of instances. We call this new technique deterministic aperture and it’s available within Finagle, Twitter’s protocol-agnostic remote procedure call (RPC) framework.

An Update on CDNJS
https://blog.cloudflare.com/an-update-on-cdnjs/
When you loaded this blog, a file was delivered to your browser called jquery-3.2.1.min.js. jQuery is a library which makes it easier to build websites, and was at one point included on as many as 74.1% of all websites. A full eighteen million sites include jQuery and other libraries using one of the most popular tools on Earth: CDNJS. Beginning about a month ago Cloudflare began to take a more active role in the operation of CDNJS. This post is here to tell you more about CDNJS’ history and explain why we are helping to manage CDNJS.

This Page is Designed to Last - A Manifesto for Preserving Content on the Web
https://jeffhuang.com/designed_to_last/
My proposal is seven unconventional guidelines in how we handle websites designed to be informative, to make them easy to maintain and preserve. The guiding intention is that the maintainer will try to keep the website up for at least 10 years, maybe even 20 or 30 years. These are not controversial views necessarily, but are aspirations that are not mainstream—a manifesto for a long-lasting website.

新鲜货

The State of JavaScript 2019
https://2019.stateofjs.com/
And so after over 21,717 respondents took this year’s survey we had to dig up our components and charts, curse us-from-a-year-ago for writing such crappy code, and get to work digging through the data.

The future of merge requests: Real-time collaboration
https://about.gitlab.com/blog/2019/12/19/future-merge-requests-realtime-collab/
We want to hear your thoughts on the future of merge requests and code review.

Chrome 80, Content Indexing, ES Modules and More
https://blog.chromium.org/2019/12/chrome-80-content-indexing-es-modules.html
Unless otherwise noted, changes described below apply to the newest Chrome beta channel release for Android, Chrome OS, Linux, macOS, and Windows. Learn more about the features listed here through the provided links or from the list on ChromeStatus.com.

Why we forked Firefox and not Chromium
https://0x65.dev/blog/2019-12-17/why-we-forked-firefox-and-not-chromium.html
In 2015 Cliqz released its own desktop web browser, forked from Firefox. Up to that point, we had distributed our search and privacy products as a single Firefox addon. In this article we cover why we thought it necessary to create our own browser, why we chose Firefox, and why we think it was the right choice. It’s bug fixes and performance improvements.

Tesseract.js
https://tesseract.projectnaptha.com/
Tesseract.js is a pure Javascript port of the popular Tesseract OCR engine. This library supports more than 100 languages, automatic text orientation and script detection, a simple interface for reading paragraph, word, and character bounding boxes. Tesseract.js can run either in a browser and on a server with NodeJS.

Relay V8.0
https://github.com/facebook/relay/releases/tag/v8.0.0
Relay is a JavaScript framework for building data-driven React applications.

A-Frame v1.0.0 - WebXR Support, AR Mode
https://aframe.io/blog/aframe-v1.0.0/
With the help of a community of hundreds of thousands of developers over the years, we’re releasing A-Frame v1.0.0 to support the coming out of the WebXR spec which has been under discussion for the past several years. The upgrade to A-Frame v1 and beyond will become necessary on more and more browsers as they deprecate WebVR and only support the WebXR specification.

Sarus - A JavaScript library for WebSockets
https://sarus.anephenix.com/
Engineered to handle unexpected socket closures.

Alpine.js
https://github.com/alpinejs/alpine
Alpine.js offers you the reactive and declarative nature of big frameworks like Vue or React at a much lower cost. You get to keep your DOM, and sprinkle in behavior as you see fit.

Hammerspoon – macOS automation with Lua
https://www.hammerspoon.org/
This is a tool for powerful automation of OS X. At its core, Hammerspoon is just a bridge between the operating system and a Lua scripting engine. What gives Hammerspoon its power is a set of extensions that expose specific pieces of system functionality, to the user. You can write Lua code that interacts with OS X APIs for applications, windows, mouse pointers, filesystem objects, audio devices, batteries, screens, low-level keyboard/mouse events, clipboards, location services, wifi, and more.

Database Internals
https://www.databass.dev/
The book consists of two parts: Storage Engines and Distributed Systems since that’s where most of the differences between the vast majority of databases is coming from. In Storage Engines, we start with taxonomy and terminology, then explore In-Place Update storage and discuss several B-Tree variants and their structure. Then we talk about binary data formats and file organization and explore the ways to compose efficient on-disk structures. In Distributed Systems, we start with basic concepts such as processes and links and start building more complex communication patterns.

Monica - Open source personal CRM
https://www.monicahq.com/
Monica helps you organize the social interactions with your loved ones.

Redis 6 RC1 is out today
http://antirez.com/news/131
Redis 6 does not bring just ACLs and SSL, it is the largest release of Redis ever as far as I can tell, and the one where the biggest amount of people participated. So, let’s start with credits. Who made Redis 6? This is the list of contributors by commits (it’s a terrible metric, but the one I can easily generate), having at least two commits, and excluding merge commits. Also note that the number of commits in my case may be inflated a lot by the fact that I fix many small stuff here and there constantly.

文言 wenyan-lang
http://wenyan-lang.lingdong.works
文言文編程語言。A programming language for the ancient Chinese.

Announcing Rust 1.40.0
https://blog.rust-lang.org/2019/12/19/Rust-1.40.0.html
The highlights of Rust 1.40.0 include #[non_exhaustive] and improvements to macros!() and #[attribute]s. Finally, borrow-check migration warnings have become hard errors in Rust 2015.

设计

Design APIs: The Evolution of Design Systems
https://matthewstrom.com/writing/design-apis/
Design systems enable designers and developers to quickly create quality software on a massive scale. As the needs of software-driven businesses grow even larger, design systems are evolving — they are beginning to look and work like APIs.

Six Questions to Structure Great Projects
https://medium.com/facebook-design/six-questions-to-structure-great-projects-24a557cb11ab
A clarifying rubric to help teams align around new ideas. Before green-lighting any project, there are six questions your team should be able to answer: What’s the context? What’s the “people problem”? What’s our causal hypothesis? What’s our project plan? How will we measure success? What will we do next?

Your Bot Will Fail
https://medium.com/microsoft-design/your-bot-will-fail-8fa00ac89592
Balancing the real with the ideal in conversational design.

UI/UX Design Trends for 2020
https://uxplanet.org/ui-ux-design-trends-for-2020-f6512b5eb3ac
Twenty design trends for everyone in the world of user interface and experience to watch(out) for in twenty-twenty.

Quantitative Data Tools For UX Designers
https://www.smashingmagazine.com/2019/12/quantitative-data-tools-uxdesigners/
Data analysis, tools and workflow are very helpful to UX designers in a data-driven world, especially for those working on visualizations or data products.

产品及其它

张一鸣:人才不是核心竞争力,机制才是!
https://mp.weixin.qq.com/s/PnjLPyAf2ICtlhO7VWROYg
“在我看来,创业其实同时在做两个产品,一个是为用户提供服务的产品,另外一个就是公司,而CEO是公司这个产品的产品经理。”这绝非一个好做的产品。伴随飞速增长的不仅是流量、收入与估值,还包括执行团队规模:如今已近5万人的队伍。大量公司会在团队快速扩张时,遭遇文化稀释和管理困局。怎么让一个组织,而不仅仅是一款产品具有持续的竞争力,尤其是在这个组织飞速变大之时?本文试着给大家些许答案。

一个有趣的问题:中文BBS的遗产被谁继承了?
https://mp.weixin.qq.com/s/r-vqBySyZIelLaZ8GdkrRQ
中文互联网不会再有一款真正意义上的“BBS应用”了,但是BBS的遗产将被彻底吃下、吸收,以某种前人难以想象的方式,以所有人都认不出来的方式,生动活泼地存在下去。

聪明人的10个工程思维
https://mp.weixin.qq.com/s?__biz=MzUyMDQ5NzI5Mg==&mid=2247514326&idx=1&sn=94fb6f6e4a2b9d22f83bc561ee51120d
创业成功最关键的个人要素是什么?你可能会说,这类问题怎么会有标准答案呢?首先当然是要运气好,肯干,愿意冒险。从鸡汤的角度说,需要有愿景,要追随内心的梦想;从鸡血的角度说,需要有执行力,百折不挠。从商学院的角度说,需要商业模式;从硅谷的角度说,需要精益创业。……我认为,如果你想靠创业发大财,最关键的个人要素是:工程思维。事实上,这是地球上那些最聪明的家伙正在运用的关键思维。本文将阐述如下观点:工程思维是创业方面最基本的、最普适的、最科学的利器。

Silas / 2019-12-23 4 years ago / 未收藏/ 有赞技术团队发送到 kindle
前言:有赞移动技术沙龙刚过去不久,相信很多同学对题为《有赞Android秒级编译优化实践》的分享还记忆犹新,分享中提到了全量编译提效与增量编译提效两种方案。本期我为大家详细介绍下基于EnjoyDependence的全量编译提效方案。

项目背景:

经过多年的发展,有赞零售Android项目代码已经达到45W+的规模(Phone&Pad),其中Kotlin代码占比33%左右,在如此大规模的代码量下,编译逐步成为我们项目加速的桎梏(PC配置:MacBook Pro i5-8G;时间:全量15min+),严重影响了我们的开发效率。为了彻底解决编译慢这一业内难题,我们今年下半年基于已有的组件化工程,展开了编译提效的项目,EnjoyDependence就诞生于这个阶段。

编译提速目标:

  1. 全量编译从15min+降至3min内
  2. 低侵入性,尽量不改造工程结构,保证工程稳定
  3. 方案稳定可靠,不能影响业务同学的开发效率
  4. 易于扩展,可以灵活对接各种已有系统
  5. 方便管理,尽可能保证低廉的学习理解成本,方便大家上手

全量编译提效核心——EnjoyDependence

简介:狭义上EnjoyDependence是集依赖管理、构建发布、编译耗时统计等功能的Gradle插件。

lex / 2019-12-23 4 years ago / 未收藏/ SRE WEEKLY发送到 kindle
A message from our sponsor, VictorOps: Ever find yourself asking, “How do I write Ansible playbooks for new Terraform servers?” Well, this new walkthrough from Splunk + VictorOps has your answer. https://go.victorops.com/sreweekly-ansible-playbooks-with-terraform Articles The Gamma Knife model of incidents Domino model, Swiss Cheese model, stand aside. The Gamma Knife model is a nifty analogy for […]
Chris Pederick / 2019-12-23 4 years ago / 未收藏/ chrispederick发送到 kindle
Beach reflection
Connor Tumbleson / 2019-12-23 4 years ago / 未收藏/ Connor Tumbleson发送到 kindle
Ever since we put a man on the moon, how often do you pay attention to the current Space progress?
山青咏芝 / 2019-12-23 4 years ago / 未收藏/ strengthen发送到 kindle
【摘要】★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:为敢(WeiGanTechnologies)➤个人域名:https://www.zengqiang.org➤GitHub地址:https://github.com/strengthen/LeetCode➤原 阅读全文
左岸 / 2019-12-23 4 years ago / 未收藏/ 左岸读书发送到 kindle
文/小泷包 小时候不理解的事情,长大了渐渐发现无非就是这样。 小的时候因为讨厌而讨厌,因为害怕而害怕,因为想要 […]
2019-12-23 4 years ago / 未收藏/ guardian发送到 kindle
2019-12-22T13:33:59Z
Protesters march in Paris, France
Emmanuel Macron is to give up his own generous presidential pension in an attempt to calm anger over politicians’ privileges, as French transport strikes caused chaos for Christmas travellers.
With rail strikes continuing into their third week and travellers scrambling to get to home for the holidays, Macron, who turned 42 this weekend, made the symbolic move to become the first president in more than 50 years to give up the automatic pension of more than €6,000 a month that all French leaders receive after leaving office, regardless of age or wealth.
Macron’s office added that he would not take his seat in France’s constitutional court, where former presidents are members for life and receive a monthly allowance of €13,500.
“This is about being exemplary and coherent,” an Élysée official said.
The pro-business Macron is aware that the strikes over his planned pensions changes have increasingly focused on him personally, with caricatures and banners at street demonstrations depicting him as king.
Emmanuel Macron
Emmanuel Macron’s move was criticised by the left as a ‘PR stunt’. Photograph: Sia Kambou/AFP/Getty Images
Just as the gilets jaunes anti-government protesters earlier this year complained about Macron building a swimming pool at a presidential summer retreat, and expensively upgrading Élysée carpets and crockery, banners at pensions protests mocked him and and other former presidents for their luxury lifestyles and generous settlements once they leave office.
The prime minister, Édouard Philippe, acknowledged recently that French people believed elected officials were privileged, promising that “in the new universal [pensions] system, they will be treated like any other French person”.
Le Parisien newspaper hailed Macron’s decision to forfeit his presidential pension as “breaking with a dusty tradition of republican monarchy”.
The left criticised it as a PR stunt, with the Communists saying Macron, a former investment banker, had made millions in the private sector, so had a rare luxury of being able to give up his presidential pension.
Despite the strikes, the government insists it will plough ahead to unify the French pensions system, arguing that getting rid of the 42 special regimes for sectors ranging from rail and energy workers to lawyers and Paris Opera staff is crucial to keep the system financially viable as the population ages.
While some unions support a single system, almost all reject any tweaks relating to age. There is union anger over government plans to introduce a “pivot age” – keeping the legal retirement age at 62 but insisting on an additional two years’ of work until 64 to get a full pension.
With strikes set to continue into the new year, the pensions stand-off has become a personal issue for Macron who had promised to deliver the biggest “transformation” of the French social model and welfare system since the postwar era.
The very essence of Macron’s political identity is at stake. He has always said he preferred to make structural changes to the French model amid unpopularity than back down in the face of street protests. Failure to deliver the reforms would dent Macron’s firm support base of about 25%, which is crucial for any re-election bid in 2022. However, further negotiations with unions will be needed to agree any changes.
Travellers across France scrambled to begin their Christmas getaways amid train cancellations and packed roads, with the train operator SNCF warning the traffic would be “severely disrupted” over the festive period.
Despite calls by some unions to suspend strikes during the festive season, rail workers’ groups continued stoppages. The protest is also affecting businesses, particularly retail, with industry associations reporting declines in turnover of 30% to 60% compared with last year. An Ifop poll showed 51% of French people either supported or sympathised with the strikes, slightly lower than the previous week.
Macron urged the strikers to ease up for Christmas, saying they should embrace a “spirit of responsibility”. On a visit to Ivory Coast, he said: “Strike action is justifiable and protected by the constitution, but I think there are moments in a nation’s life when it is good to observe a truce out of respect for families and family life.”
Macron used his west Africa trip to focus instead on foreign policy and France-Africa relations. He said “colonialism was a grave mistake” and called for “turning the page on the past”.


2019-12-23 4 years ago / 未收藏/ guardian发送到 kindle
2019-12-22T19:47:20Z
Donald Trump in West Palm Beach, Florida on 21 December.
As Republicans and Democrats continue to squabble on the terms of Trump’s impeachment trial in the Senate, newly-released government emails show that it took only about 90 minutes after Donald Trump’s infamous July telephone call to Ukrainian president Volodymyr Zelenskiy for White House officials to order the Pentagon to freeze military funding for Ukraine.
The correspondence, published by the Center for Public Integrity, appears to show that Trump appointees acted quickly after the call, behind the scenes, to block Ukrainian aid from the Pentagon.
The emails appear to confirm that the aid was held up soon after Trump pressured Zelenskiy on the call to “do us a favor, though”. That request prompted an American whistleblower who heard details about the call to complain, which in September triggered the entire impeachment inquiry.
Worried intelligence experts assessed that the president was pressing a foreign leader to investigate Trump’s US political rivals, specifically 2020 candidate Joe Biden, in return for vital military assistance for Ukraine’s armed resistance to Russian aggression on its border. Critics of the president have called that a threat to US national security and the integrity of US elections.
There were calls from prominent Democratic senators Chuck Schumer and Amy Klobuchar this weekend for the author of the reported emails, Mike Duffey, associate director for national security programs at the Office of Budget Management, to testify in the Senate during Trump’s expected impeachment trial next year.
While House Democrats and Senate Republicans are this weekend in a standoff over terms for the next steps of the impeachment process, Klobuchar said on Sunday in CNN’s State of the Union politics news show that “I think there will be an agreement on this and the trial will go forward.”
A July email from Duffey to Pentagon officials, said: “Based on guidance I have received and in light of the administration’s plan to review assistance to Ukraine, including the Ukraine Security Assistance Initiative, please hold off on any additional DoD obligations of these funds, pending direction from that process.”
Klobuchar then read a portion of Duffey’s email out on air, while denouncing Trump’s efforts to block witnesses from testifying in the impeachment investigation so far and in the expected Senate trial, which has led to a charge of obstruction of Congress against him.
She said: “We just found out this weekend that someone who works for Mick Mulvaney, Mike Duffey, he sent an email 90 minutes after the president made the critical call to the Ukraine president … we have it here, we just found it,.”
She read out that Duffey’s email continued: “Given the sensitive nature of the request, I appreciate your keeping that information closely held to those who need to know to execute direction.”
Mulvaney is Trump’s acting chief of staff and someone whom Democrats are fiercely keen to testify to Congress. He confirmed in a White House press briefing in October that Trump had demanded a quid pro quo in his contacts with Zelenskiy, though he urged the media and critics to “get over it”.
Schumer tweeted that the email was “all the more reason why we need Duffey and others to testify in a Senate trial”.
The new documents “reveal how quickly the White House moved to cut off
military aid to Ukraine,” Susan Smith Richardson, chief executive
officer of the Center for Public Integrity, said in a statement.

On Sunday, Marc Short, chief of staff to vice-president Mike Pence told NBC’s Meet the Press that the timing of the email from Duffey was just a coincidence.
This prompted scathing comments on Twitter from legal scholar Lawrence Tribe, and George Conway, Trump critic and husband of White House counselor Kellyanne Conway.
The impeachment process is currently in limbo after articles of impeachment – effectively congressional charges against the president – were agreed last week in the House but have not yet been officially delivered to the Senate as required by protocol.
In a showdown with the Republican-controlled Senate, House speaker Nancy Pelosi, a Democrat from California, has delayed the handover of the articles saying she wants an agreement on a fair trial and the calling of witnesses.
Asked by CNN host Dana Bash on Sunday if the Senate trial could go ahead without an agreement to call witnesses, Klobuchar said she thought matters would be resolved and “the trial will go forward”.
She added: “What is shocking to me is that right now despite the president claiming innocence and saying he wants to present witnesses, he is the one blocking witnesses.”
Trump has refused to allow senior White House and administration officials, including his Mulvaney and former national security adviser John Bolton, to testify to the impeachment inquiry or cooperate with producing documents. That has led to one of the two articles of impeachment against Trump being obstruction of Congress. The other is abuse of power, over his dealings with Ukraine.
Senate majority leader Mitch McConnell has said he wants the Senate to consider the case without hearing from new witnesses.
In remarks on Sunday talk shows, Marc Short, chief of staff to Vice-President Mike Pence, indicated the White House was on board with McConnell’s goal of a speedy trial.
“The American people are tired of this sham,” Short said on NBC’s Meet the Press.
After the Duffey emails were released, Rachel Semmel, a spokeswoman for the Office of Budget Management, which was included in the email chain, told the New York Times: “It’s reckless to tie the hold of funds to the phone call.”
OMB officials informed other agencies about a Ukraine aid freeze on 18 July, but the first official action to withhold Pentagon funds came on 25 July.
The timeline of events that day show Trump and Zelenskiy’s call lasted from 9.03am to 9.33am and then the email from Duffey is time-stamped 11.04am.
2019-12-23 4 years ago / 未收藏/ guardian发送到 kindle
2019-12-22T15:34:42Z
Condition on the Greek islands for migrants have been called ‘catastrophic’.
The loss of family reunion rights for unaccompanied asylum-seeking children will leave them with “no options” except taking dangerous routes and using smugglers, charities in France and Greece are warning.
The prime minister, Boris Johnson, faced criticism after he told parliament he had dropped a promise to replace the EU law that allows child refugees stranded in Europe to reunite with family members in the UK after Brexit.
Clare Moseley, the director of Care4Calais, said the news was devastating for those working with young asylum-seekers.
“I’m so shocked. This will have a massive impact. Just yesterday I was with an Afghan boy whose mother is dead, his father is in the UK, there is nowhere else that boy should be. The idea that he won’t have that right to join his father at all is horrific. How could anyone think there is anywhere else that boy should be?
“We have many very young Afghan boys here. Some are under 13. Nearly all the young people here are trying to reach family in the UK. That is the only reason anyone would put up with the horrible conditions here.”
Authorities in Dunkirk and Calais have recently used force to keep the area clear of tents, leaving nowhere for migrants to shelter. Hundreds, including children, sleep rough with no toilets or running water.
Bastien Roland, a lawer supporting young asylum seekers in northern France, said family reunion was a way to persuade vulnerable children to enter the official asylum system.
“We have very young children here in Calais who we worry are being exploited by adults. The family reunion law offers a chance to persuade a young person who is sleeping outside to enter the official system. It is a really key tool to get them out of the jungle. Without family reunion children will put themselves in danger. They will risk injury and death to get to the UK. ”
On Europe’s southern border, in Greece, frontline workers say there needs to be far larger numbers of children brought to the UK.
Sandy Protogerou, the head of the charity Safe Passage in Greece said: “There is no space for these children in Greece, the situation here is horrible. We just recently found a Syrian boy who was trying to go through the Greek asylum system in order to apply to join his brother in the UK. Instead he had been placed in a terrible place, like detention, with really bad conditions.
“Without family reunion there will be no option for a boy like him. He comes from a war zone so he would have no option but to use smugglers to keep moving forward to find his brother.
Even for children who do not have family in other EU countries, she says, “staying here in Greece isn’t proper, there is no space, conditions are horrible, too many minors have arrived over the summer”.
Conditions on the Greek islands for migrants are reported to be catastrophic, as rising numbers of arrivals have put pressure on already overcrowded camps.
More than 50,000 migrants arrived in Greece by sea over 2019. About 17,000 were under 18, 2,600 of them travelling without adults.
On a visit to the Greek islands last month, the UN high commissioner for refugees, Filippo Grandi, called for Europe to do more, particularly for unaccompanied minors.
“Europe has to get its act together,” he said after visiting Lesbos. “[It] has to have a new system that is based on sharing, responsibility sharing. There is a children on-the-move emergency in this country that needs to be tackled.”
The Home Office admitted in September that family reunion would end immediately if there was a no-deal Brexit. But until the latest announcement, campaigners believed that the EU law would be replaced during negotiations for a post-Brexit Britain.
A petition started by two former volunteers from Calais asking the Home Office to save family reunion has gained an extra 25,000 signatures since Johnson made the announcement on Thursday.
There are now 125,000 signatures on the petition , which was started in September after the Guardian reported that family reunion would end if there was a no-deal Brexit.

2019-12-23 4 years ago / 未收藏/ guardian发送到 kindle
2019-12-22T16:32:01Z
Fallon Sherrock celebrates after winning to go into the 3rd round during day nine of the William Hill World Championships at Alexandra Palace.
Fallon Sherrock has said she is overwhelmed by the “incredible” response to her historic victories at the PDC World Darts Championship, as Billie Jean King led a wave of congratulations from across the globe.
Sherrock became the first woman to defeat a male darts player in the sport’s best-known competition last week and repeated the feat by beating one of the world’s top-ranked players on Saturday.
The 25-year-old, who only took up the sport when she was 17 after ditching her career as a professional hairdresser, said she was struggling to grasp the wave of adulation coming her way.
“It hasn’t all sunk in yet,” she told BBC Radio 5 Live on Sunday. “I haven’t had time to process the first game let alone this game. Then the fact that all these people are tweeting me, especially Billie Jean King. Oh my god, I never thought someone like that would be contacting me or congratulating me or anything like that.
“I mean, this is me. I’m just a normal person. This stuff doesn’t happen to me, but it’s incredible and I’m loving it.”
King, the former world No 1 tennis player, posted her congratulations on Twitter along with the hashtag “#Gamechanger”. Television presenters Gabby Logan, Jacqui Oatley and Piers Morgan were also among those paying tribute to the Milton Keynes-born star.
Sherrock’s journey from professional hairdresser to darts history-maker is an incredible one. Viewed as a well-regarded player for a number of years, she has emerged as a stand-out player in 2019 and cemented her status at the PDC World Championships in the past week.
She has spoken about receiving merciless abuse on social media when she suffered a kidney condition following childbirth in 2014, which sometimes caused her face to swell. Sherrock has also called for women to be given more opportunity to play against men, saying there was no reason why the male-dominated sport should remain so: “Us women, we can beat these men. We just need more opportunities to prove ourselves.”
In her nerveless first-round victory over Ted Evetts, she became the first woman to beat a man in the competition, going on to stun Alexandra Palace a second time by defeating 11th-seed Mensur Suljovic to progress to the last 32 on Saturday.
Sherrock, whose walk-on music for her history-making win was Katy Perry, said she hoped to go on and win the tournament: “Why not? The possibilities are endless now.”
2019-12-22T22:49:32Z
Destroyed home at Woodside, outside Adelaide


2019-12-23 4 years ago / 未收藏/ guardian发送到 kindle
2019-12-22T17:53:16Z
Sultan Qaboos bin Said has acted as an honest broker among nations in the Middle East and beyond.
Elaborate discussions are under way in the Omani court about a potential successor to Sultan Qaboos bin Said, who has ruled the Arab nation for nearly 50 years but whose longterm illness has been worsening.
The succession process involves the opening of sealed letters in the court in Muscat identifying the sultan’s choice of successor, if the court cannot agree among itself.
The sultan, one of the mainstays of Middle East politics for the past four decades, returned a week ago from Belgium where his treatment for a reoccurrence of cancer of the colon he has suffered from for four years was cut short. He had been expected to stay until the end of January.
A former British protectorate in the Arabian Peninsula, Oman has been ruled by Qaboos ever since a bloodless coup in 1970 enacted with the help of Britain. He has travelled abroad for medical reasons at least twice since 2014.
Qaboos has no children and has not publicly appointed a successor but he secretly recorded his choice in a sealed envelope addressed to the royal family council. The Al Said dynasty has ruled Oman since the mid-18th century.
Article 6 of the sultanate’s basic law says the royal family should choose a new sultan within three days of the position falling vacant. Should they fail to reach an agreement, the nation’s defence council, the head of the supreme court and the heads of the two chambers of the consultative council would together open the first envelope containing Qaboos’s choice and then enthrone the person he designated.
The purpose of keeping his preferred successor’s name secret is to ensure the authority of the sultan during his lifetime.
There are said to be two sealed envelopes, with the second held in a different royal palace in the southern port city of Salalah. Reports circulating at the weekend suggested that the second duplicate letter is being readied to be taken to Muscat in preparation for opening because of the seriousness of the sultan’s condition and the unresolved disputes within the council.
Most reports said it will contain the same name and that it exists only in case the first envelope cannot be found or if the first letter needs authenticating.
Under Qaboos, who was educated in Britain, Oman has maintained a largely neutral role and acted as a mediator in the region, notably between Iran and the US over the proposed nuclear deal eventually signed in 2015.
It has also been the site of talks between Saudi Arabia and the Houthi rebels from Yemen. Oman’s policy of “friend to all and enemy to none” has allowed the country to develop economically. It has not taken sides in the two-year dispute within the Gulf Cooperation Council between the United Arab Emirates, Bahrain and Saudi Arabia on the one hand and Qatar on the other.




2019-12-23 4 years ago / 未收藏/ guardian发送到 kindle
2019-12-22T15:29:21Z
A view of a fallen palm tree on the road in Valencia, Spain.
The death toll from storms that have battered Spain, Portugal and France rose to nine on Sunday as the region braced for more violent winds and heavy rain.
Storms Elsa and Fabien have flooded rivers, brought down power lines, uprooted trees and disrupted rail and air travel across the region, leaving more than 118,000 households without electricity.
Two people have died in Portugal and seven have been killed in Spain, the worst affected country, after a fisherman was swept off rocks into the sea in Catalonia.
The local government said three police officers and another fisherman had to be rescued after they tried to save the man in the resort town of Sant Feliu de Guíxols, 70 miles (100km) north-east of Barcelona.
Sea foam covers the streets in Bayona, Galicia.
Sea foam covers the streets in Bayona, Galicia. Photograph: SXENICK/EPA
The deaths in Spain include a South Korean woman killed by debris falling from a building in Madrid and a Dutch man who drowned windsurfing in rough weather off the Andalusian coast.
As a weakened Storm Elsa moved over Britain on Saturday, Storm Fabien quickly moved in, bringing winds of 170km/h to Galicia in north-western Spain, forcing the cancellation of 14 flights.
Eight thousand households in Galicia were without power due to damage caused to power lines by the wind, local officials said.
Eight Madrid city parks were shut on Saturday because of the strong winds. However, Spanish officials said on Sunday that Fabien was moving away quickly.
Parks and cemeteries were also closed in Bordeaux, in south-western France, while the Arlette Gruss circus, which had set up in a big tent in the city’s main square, cancelled three performances.
France’s weather office placed 15 regions in the south-west of the country on orange alert on Saturday, as the storm battered its Atlantic coast.
A closed pier in the city of Andernos, France, on the morning after Storm Fabien.
A closed pier in the city of Andernos, France, on the morning after Storm Fabien. Photograph: Nicolas Tucat/AFP via Getty Images
The winds were as fast as 148km/h at Socoa, in the south-west, near the border with Spain. On the north-west coast of Brittany winds reached up to 120km/h. The region was still being buffeted by gusts of up to 90km/h on Sunday.
Across south-western France, violent winds left 110,000 households without electricity, officials said on Sunday.
France’s SNCF rail network cancelled services between Bordeaux, Toulouse and Hendaye in the south-west because of the likelihood of winds blowing trees on to the line.
Officials on the French Mediterranean island of Corsica closed all the island’s airports because of the approaching storm. Ferry services to the mainland were also suspended.
2019-12-23 4 years ago / 未收藏/ guardian发送到 kindle
2019-12-22T15:25:22Z
Boris Johnson and Evgeny Lebedev
It was the equivalent of a V-sign cheerfully flashed at his critics. The day after his landslide election victory, Boris Johnson and his girlfriend Carrie Symonds dropped into a caviar-fuelled Christmas party in London hosted by former KGB agent Alexander Lebedev and his son Evgeny.
During the campaign Johnson had stubbornly refused to publish the Russia report, written by the last parliament’s intelligence and security committee.
Its contents have still not been revealed. But it is understood to examine the extent of Moscow influence on British politics – and the way in which the Russian elite has established a powerful lobby in the UK through lavish expenditure and networking.
Boris Johnson and Carrie Symonds attend Evgeny Lebedev’s Christmas party a day after Johnson’s landslide election win.
Boris Johnson and Carrie Symonds attend Evgeny Lebedev’s Christmas party a day after Johnson’s landslide election win. Photograph: GORC/GC Images
The Lebedev family insist that they are merely entrepreneurs and media proprietors. Nonetheless, the party – held to celebrate Alexander’s 60th birthday – was a practical demonstration of how extensive their connections are, stretching all the way to Downing Street.
Johnson was one of several top politicians who turned up to the event, held at the Lebedev family’s £6m stuccoed mansion overlooking Regent’s Park. Guests included David and Samantha Cameron and George Osborne, now a Lebedev employee as Evening Standard editor. From the Blairite left, Peter Mandelson and Tristram Hunt were also in attendance.
Others invited to the vodka and caviar birthday party included Mick Jagger, Princess Eugenie, the actors Matt Smith and Rosamund Pike, and the model Lily Cole. Comedians Eddie Izzard and David Baddiel, the artist Grayson Perry and sculptor Antony Gormley were also there. Missing this year was the mayor of London, Sadiq Khan.
Johnson and Evgeny Lebedev at a charity event in 2009.
Johnson and Evgeny Lebedev at a charity event in 2009. Photograph: David M Benett/Getty Images
As grand as the list was, the newly elected prime minister’s presence – the night after his election win – was eye-catching. Johnson has long been a celebrity as well as a politician – but for a leader who promised to lead a “people’s government”, to rub shoulders with such an exclusive elite as one of his first public acts after victory might seem a little incongruous.
Jeremy Corbyn was invited, but didn’t attend. (The invite, for the record, shows a youthful Alexander holding a toddler Evgeny.) In the Labour leader’s absence the Lebedevs hired a Corbyn impersonator, who mingled with guests as they sipped Ruinart champagne and tucked into plates of caviar. There was also a Johnson lookalike, perhaps hired just in case the real prime minister didn’t show up.
Johnson’s decision to call in on the Lebedevs might be explained as an act of political homage. The Lebedevs own the Evening Standard. Over the summer the newspaper endorsed Johnson to succeed Theresa May. It backed him again last month as the best candidate to be prime minister, an opinion at odds with the left-leaning, Remain-backing Londoners who form its target audience.
Alexander Lebedev
Johnson went to the champagne-fuelled Christmas party in London on 13 December hosted by former KGB agent Alexander Lebedev. Photograph: Alexander Nemenov/AFP via Getty Images
What lies behind the prime minister’s reluctance to publish the Russia report has yet to be explained. He has dismissed the suggestion of Kremlin interference in the EU referendum vote. And yet on a busy day laced with history, Johnson chose to celebrate with a foreign intelligence officer with the rank of a lieutenant colonel, who graduated from the KGB’s Red Banner Institute.
As a young spy Lebedev served with the KGB’s first directorate. During the late Thatcher years he worked at the Soviet embassy in London, not far from the office in Kensington High Street where the Standard is based today. In the 1990s Lebedev senior swapped cold war spying for banking. In 2009 he bought a majority share in the Standard.
That complicated history has never put Johnson off spending time with the family. As London mayor, Johnson made repeated trips to the Lebedevs’ luxurious palazzo in Ronti, Italy, and to Evgeny’s parties in London in 2010. He was one of an array of celebrities and actors who flew out to the villa for lavish weekends, complete with a Michelin-starred private chef and a dressing-up box filed with exotic costumes. Evgeny’s pet wolves – one of them called Boris – would pad around as the great and the good enjoyed themselves.
Sometimes Johnson took his wife Marina Wheeler, from whom he is now divorcing. But in April 2018, at the height of the crisis over the poisoning of Russian former spy Sergei Skripal, Johnson flew to Perugia on his own. He was foreign secretary. In a highly unusual move, he left his security detail behind. The following morning he was spotted queuing at the airport for a flight home, looking crumpled and hungover.
Johnson, then foreign secretary, on his way back from a Lebedev party in a converted castle near Perugia in Italy
Johnson, then foreign secretary, on his way back from a Lebedev party in a converted castle near Perugia in Italy. Photograph: Supplied
What was the purpose of the trip? Johnson won’t say. In September, Alexander Lebedev told the Guardian he wasn’t at the palazzo. Two months later, speaking at the Moscow launch of his memoir, Hunt the Banker, Lebedev said he did go to the villa/. He didn’t meet Johnson there, he said, instead merely calling in to wish his son a happy birthday.
Guests portray Evgeny as a needy and even lonely figure, craving companionship, and happiest in his Italian home, away from the burdens of being a media owner. They suggest he doesn’t really get politics per se. But, they add, he enjoys the superficial side of mingling with powerful individuals.
Johnson, then foreign secretary, on his way back from a Lebedev party in a converted castle near Perugia, in Italy, spotted looking crumpled and hungover.
Johnson, then foreign secretary, on his way back from a Lebedev party in a converted castle near Perugia, in Italy, spotted looking crumpled and hungover. Photograph: Supplied
Newly arrived visitors are invited to make a Russian-style toast to Evgeny and to perform a ‘turn’ of some kind – a sketch or speech. A lot of drinking goes on, with vodka shots and a mood described by one guest as debauched. Invitees are sometimes stuck there for days, waiting for a lift back on Lebedev’s private jet.
Whether in London or Ronti, Lebedevs’ parties bring together two groups who rarely mingle: celebrities and politicians. One is glamorous, the other not so much; both are curious about each other. The mutual attraction partly explains why so many high-profile individuals accept Lebedev’s invitations.
But others say there is more. One celebrity who has attended the Lebedev parties in the past now declines, arguing “all of these parties seem like covers for meetings” where people can discreetly connect with politicians under the the guise of the event.
Johnson socialises with Evgeny Lebedev at a party along with his sister Rachel Johnson.
Johnson socialises with Evgeny Lebedev at a party along with his sister Rachel Johnson. Photograph: Alan Davidson/REX/Shutterstock
“Evgeny sends cars and flies people around on private jets, he makes people feel important, and always serves tons and tons of caviar, which people love. And there’s usually a photographer there ensuring attendees will get a bit of press,” the celebrity added.
Regular attendees at the Lebedev parties have included the actors Ian McKellen, Keira Knightley, Joan Collins and Ralph Fiennes. One non-actor celebrity – who declined to be named – received an invite out of the blue. When he climbed on board Evgeny’s plane he found that he and the other guests had one thing in common: they were all famous.
Evgeny Lebedev and Alexander Lebedev at the launch of Alexander’s book Hunt the Banker: The Confessions Of A Russian Ex-Oligarch
Evgeny Lebedev and Alexander Lebedev at the launch of Alexander’s book Hunt the Banker: The Confessions Of A Russian Ex-Oligarch. Photograph: David M Benett/Dave Benett/Getty Images
Evgeny’s A-list contacts book is in part due to the Standard’s theatre awards, which have run since 1955 and which he now co-hosts. He also enlists his journalists to pull in big names. His former fixers have included Sarah Sands, who edited the Standard and is now editor of BBC Radio 4’s Today programme, and Amol Rajan, the former Independent editor who is now the BBC’s media editor.
In Russia, Alexander Lebedev depicts himself as a semi-oppositionist and victim of official persecution. His bank has been raided, and he has supported the independent newspaper Novaya Gazeta.
In reality, however, he is on warm terms with the Kremlin. In 2014 he publicly supported Vladimir Putin’s annexation of Crimea, where Lebedev owns a hotel complex in the seaside resort of Alushta. In 2017 he staged a media symposium there. It was arranged – he told Russian state TV – to correct a false impression of Crimea put out by a “biased” western media.
Lebedev shared a panel at the conference with Maria Zakharova, Russia’s foreign ministry spokesperson. She was a guest at his recent Moscow book launch. Zakharova is a hardline exponent of Kremlin policy; last year she denied Russian involvement in the novichok poisoning of Sergei Skripal in Salisbury and suggested that Britain’s spy agencies had kidnapped him.
The Lebedevs say that their forays into the British media are altruistic, and have nothing to do with Moscow’s darker global agenda. Whatever their motives for bringing them together, their remarkably diverse collection of friends – from the British prime minister to senior Putin functionaries – add up to an environment in which gossip, intelligence and information can flow as freely as the champagne.
Alexander Lebedev was contacted for comment.
2019-12-23 4 years ago / 未收藏/ guardian发送到 kindle
2019-12-22T19:28:29Z
Chicago Police Department confirmed that 13 people had been shot on Sunday morning.
A party in Chicago held to memorialize someone who was killed earlier in the year turned into a scene of chaos and violence early Sunday when two gunmen opened fire, picking off partygoers as they fled the scene.
The Chicago Police Department confirmed on Sunday morning that 13 people were wounded by gunfire, with the incident unfolding at multiple locations.
Four are in critical condition in hospital, the rest are in stable condition, CPD’s Fred Waller said in a press briefing. The ages of the victims range from 16 to 48, he said.
Two people were in custody and being questioned on Sunday morning. Police said the incident appeared to be personal, not gang-related as first reported. One of two arrested was apprehended with a weapon, Waller said, while the other was wounded. Officers recovered a revolver.
“There was a dispute where shots were fired inside,” Waller said. “The people started to spill out, and as they spilled out more shots were fired. So we have about three (shooting) scenes.”
Waller said the party was a memorial for someone who had been killed in April. He described a “chaotic” scene but said officers arrived within minutes and were assisting the wounded before ambulances arrived. Some of the outside shooting was caught on police surveillance video, he said.
The shooting stemmed from a dispute at a the party being held to honor the life of a man killed in April, Waller added. “Definitely there were two different shooters. It looked like they were just shooting randomly at people as they exited the party.”
According to police, around 12.35am ShotSpotter technology alerted officers of shots fired in the 5700 block of South May Street in Englewood, a crime-troubled neighborhood about 10 miles from the heart of downtown Chicago.
According to local media reports, the incident occurred at a party in honor of Lonell Irvin, a 22-year-old man fatally shot during an attempted carjacking earlier this year.
Witnesses said shots were fired in the house. As partygoers began to flee, a shooter was seen on video firing more shots. A second shooter was seen firing a weapon.
“From outside, definitely there were two different shooters,” Waller said. “It looked like they were just shooting randomly at people as they exited the party.”

Local resident Terrence Daniely told WLS Chicago: “I was awoken by the sound of, seemed like 4, 5, or 6 gunshots and it seemed pretty loud, so it seemed kind of close.”

He added: “I heard the sounds of people screaming and running from a party on the street,” he said. “The police response was amazing, it seemed like there were 100 police officers running to the scene.”
The Rev Michael Pfleger, pastor of nearby Saint Sabina Church, said that guns are too often used to express “pain, anger and frustration”.
“They were gathered to remember someone else who was Killed,” Pfleger posted on Facebook.
He continued: “So this is how we remember??? When do we end this GUN MADNESS? Anywhere else in the Country this is a Mass Shooting, in Chicago it’s just Another Shooting.”
According to The Chicago Tribune, 2,594 people have been shot in the city this year, which includes deaths and woundings. That is 248 fewer than 2018 and is the third straight year of significant decline in the numbers, with the improvement being felt across the city, including in high-crime areas.
Police have credited Chicago’s drop in crime to the use of technology used to predict where shootings might occur, while experts also credit anti-violence programs that offer jobs and gang conflict mediation.
But homicide totals are still significantly higher for Chicago than comparable metropolitan areas, such as New York or Los Angeles.
In New York, figures compiled by police officials show the city has recorded 249 murders between 1 January and 6 October , up just over 1% from 246 murders in the same period in 2018.
  • This article was modified on 22 December to reflect more accurately the shooting statistics for Chicago.
2019-12-23 4 years ago / 未收藏/ guardian发送到 kindle
2019-12-22T18:11:56Z
Emanuel Ungaro
Emanuel Ungaro, the French fashion designer who described himself as a sensual obsessive, has died in Paris at the age of 86.
Ungaro was a giant of old-school fashion, once memorably declaring: “One should not wear a dress, one should live in it.”
He retired in 2004 and died on Saturday after two years in a weakened state of health, his family said.
Ungaro was born in Aix-en-Province in southern France in 1933 to a family of Italian immigrants. He moved to Paris when he was 22 and learned his trade under the tutelage of the great Spanish couturier Cristóbal Balanciaga.
Balanciaga was a genius, Ungaro once told the Guardian. He “never talked. You had to learn and take from it the sense of quality. He was a very alone, very silent man”.
Ungaro went on to launch his own label, and presented his first collection in 1965. The show was restricted to daywear because his studio in the 17th arrondissement was not big enough for anything else.
He built a reputation for his use of bright colour and mixed prints, creating clothes that had a sensuous old-school glamour. Along with Yves Saint Laurent, Ungaro is seen as someone who helped establish ready-to-wear as an alternative to couture.
His friend and biographer, Christine Orban, once wrote that with Ungaro “sensuality is everywhere”.
“Emanuel … will create a garment too beautiful to be torn off, but clever enough to suggest to take it off with tenderness,” she wrote.
In 2001, Ungaro was the first fashion designer to be invited to address the Oxford Union.
When asked how important he thought it was to communicate femininity through clothes, he quoted Jean-Paul Sartre and Pablo Picasso, saying: “There are no rules and we have no one answer. You have to be your own critical mirror.”
Ungaro built a fashion empire that has since extended into perfumes, shoes, furniture, candles and glasses. His label was bought in 1996 by the Ferragamo family.
He officially retired in 2004 complaining the world of haute couture no longer matched “the expectations of today’s women”. He leaves a wife and a daughter.
2019-12-23 4 years ago / 未收藏/ gdoodle发送到 kindle
Doodle Type : animated
No matter how you choose to celebrate, ‘tis the season to enjoy the holiday festivities during the most wonderful time of the year!
Happy holidays!
 

This day in history

Happy Holidays from Google - 2010
Happy Holidays from Google 2008 - 3
Holidays 2016 (Day 1)
Holidays 2018 (Northern Hemisphere Day 1)
Holidays 2016 (Day 1) Warm Climates
Holidays 2015 (Day 1)
Holidays 2018 (Day 1)
Holidays 2018 (Southern Hemisphere Day 1)
Holidays 2014 (Day 1)
Happy Holidays from Google 2002 - 1
Happy Holidays 2011
Happy Holidays from Google 2006 - 3
Happy Holidays from Google 2009 - 3
Happy Holidays from Google 2005 - 4
Happy Holidays from Google 2003 - 2
Happy Holidays from Google 2007 - 3
Happy Holidays from Google 2004 - 4

This Doodle's Reach

Argentina Australia Austria Bolivia Brazil Bulgaria Canada Chile Colombia Costa Rica Croatia Czechia Dominican Republic Ecuador El Salvador Estonia Finland France Germany Greece Guatemala Honduras Hungary Iceland Italy Japan Lithuania Mexico Netherlands New Zealand Nicaragua Panama Paraguay Peru Philippines Portugal Puerto Rico Romania Singapore Slovakia Slovenia Spain Switzerland U.S. Virgin Islands Ukraine United States Uruguay Venezuela Vietnam
2019-12-22 4 years ago / 未收藏/ gdoodle发送到 kindle
Doodle Type : simple

This day in history

Happy Holidays from Google 2003 - 1
Happy Holidays from Google 2000
Teresa Carreño’s 165th Birthday
Happy Holidays from Google 2007 - 2
Mother's Day 2018 (Indonesia)
Herman Potočnik's 120th Birthday
Happy Holidays from Google 2004 - 3
Happy Holidays from Google 2006 - 2
Happy Holidays from Google 2009 - 2
Happy Holidays from Google 2001 - 3
Winter Solstice 2015 (Northern Hemisphere)
Mother's Day 2015 (Indonesia)
Mother's Day 2016 (Indonesia)
Mother's Day 2014 (Indonesia)
Marko Vovchok’s (Mariya Vilinska) 183rd Birthday
Summer Solstice 2015 (Southern Hemisphere)
Happy Holidays from Google 2005 - 3
Teacher's Day 2016 (Israel)
Srinivasa Ramanujan's 125th Birthday
Happy Holidays from Google 2008 - 2

This Doodle's Reach

Albania Armenia Austria Azerbaijan Belarus Belgium Bosnia & Herzegovina Bulgaria Canada Colombia Costa Rica Croatia Czechia Denmark El Salvador Estonia Finland France Georgia Germany Greece Guatemala Honduras Hong Kong Hungary Iceland India Ireland Israel Italy Japan Kazakhstan Kyrgyzstan Latvia Lithuania Luxembourg Mexico Netherlands Nicaragua North Macedonia Norway Panama Poland Portugal Romania Serbia Singapore Slovakia Slovenia Spain Sweden Switzerland Turkey Turkmenistan Ukraine United Kingdom United States Uzbekistan Venezuela Vietnam
2019-12-22 4 years ago / 未收藏/ gdoodle发送到 kindle
Doodle Type : interactive

This day in history

Happy Holidays from Google 2003 - 1
Happy Holidays from Google 2000
Teresa Carreño’s 165th Birthday
Happy Holidays from Google 2007 - 2
Mother's Day 2018 (Indonesia)
Herman Potočnik's 120th Birthday
Happy Holidays from Google 2004 - 3
Happy Holidays from Google 2006 - 2
Happy Holidays from Google 2009 - 2
Happy Holidays from Google 2001 - 3
Winter Solstice 2015 (Northern Hemisphere)
Mother's Day 2015 (Indonesia)
Mother's Day 2016 (Indonesia)
Mother's Day 2014 (Indonesia)
Marko Vovchok’s (Mariya Vilinska) 183rd Birthday
Summer Solstice 2015 (Southern Hemisphere)
Happy Holidays from Google 2005 - 3
Teacher's Day 2016 (Israel)
Srinivasa Ramanujan's 125th Birthday
Happy Holidays from Google 2008 - 2

This Doodle's Reach

Indonesia


2019-12-22 4 years ago / 未收藏/ gdoodle发送到 kindle
Doodle Type : simple

This day in history

Happy Holidays from Google 2003 - 1
Happy Holidays from Google 2000
Teresa Carreño’s 165th Birthday
Happy Holidays from Google 2007 - 2
Mother's Day 2018 (Indonesia)
Herman Potočnik's 120th Birthday
Happy Holidays from Google 2004 - 3
Happy Holidays from Google 2006 - 2
Happy Holidays from Google 2009 - 2
Happy Holidays from Google 2001 - 3
Winter Solstice 2015 (Northern Hemisphere)
Mother's Day 2015 (Indonesia)
Mother's Day 2016 (Indonesia)
Mother's Day 2014 (Indonesia)
Marko Vovchok’s (Mariya Vilinska) 183rd Birthday
Summer Solstice 2015 (Southern Hemisphere)
Happy Holidays from Google 2005 - 3
Teacher's Day 2016 (Israel)
Srinivasa Ramanujan's 125th Birthday
Happy Holidays from Google 2008 - 2

This Doodle's Reach

Argentina Bolivia Brazil Chile Paraguay Peru South Africa Uruguay


2019-12-22 4 years ago / 未收藏/ mobilesite发送到 kindle
人生海海
“人都是起起落落,失意时给别人捧捧场,得意时听听别人的掌声。人生海海,又何必在意一时沉浮。”
——傅首尔
该书作者:麦家

第一章

前山海一样大,丛山峻岭,像凝固的浪花,一浪赶一浪,波澜壮阔。

春暖花开时节,嫩绿的叶苗像一支秘密部队,从条纹状的树皮下钻出,便一发不可收拾,发疯似的向天空和枝丫争抢地盘;要不了几天,扇形的树叶密密麻麻,隐起枝丫,遮天蔽日,挡风避雨,召集全村的麻雀都来过夜。

秋末冬初,风是染料,把碧绿的树叶子一层层染,最后染成黄铜色。

每到夏天,村子像得了疾病,把人折磨得死去活来。首先是忙,田地要劳作,畜生要侍候,屋漏要补,洪水要防,阴沟要通,茅坑要清,牛栏、猪圈、鸡窠、鸭棚、兔窝里的牲畜都来添乱,一堆事,像疹子一样发出来,日子再长也不够用。

每到夏天,村子像剥了壳的馊粽子,黏糊糊又臭烘烘的,人总忙叨叨的,各路虫豸也总不安生:苍蝇、蚊子、蟋蟀、萤火虫、壁虎、蚂蟥、蚂蚁、蜻蜓、蚂蚱、蜈蚣、毒蛇、蜥蜴、毛毛虫,四面八方冒出来,寻死觅活扎进人堆,加到我们生活里,给我们添乱、生事、生病,等着冬天来收拾。

【笔记】
卖家的描写颇有特点:
一是把通常我们认为静态的东西写出形象感和动态感。比如把山写得“一浪赶一浪,波澜壮阔”;又比如把“嫩绿的叶苗”写成“秘密部队”的一系列动作;而把“碧绿的叶子一层层染”,则把秋风写活了。
二是有不一样的大视角。比如把夏天的各种看似毫不相干的活计、各种虫豸一起组织起来,让琐碎不再,取而代之的是一种跳出来的整体感、新视角。作者是站在一个更高的维度上来写夏天的。
三是形象贴切中有一种俏皮。就像这里写银杏叶子“召集全村的麻雀都来过夜”,又写夏天“一堆事,像疹子一样发出来,日子再长也不够用”,像乡里乡亲拉家常时的抱怨,字里行间依稀带着几分可爱。
爷爷讲:“绰号是人脸上的疤,难看。但没绰号,像部队里的小战士,没职务,再好看也是没人看的,没斤量的。

我和表哥亲眼看见的,他满脸满嘴乌黑涂鸦的烟灰,像活鬼,哭得跟杀猪似的响,声音里掺进血,四面溅,惊得树上的鸟儿都逃进山,真正可怕!

一般一个大村庄总搭配一个木匠和一个铁匠,候鸟一样,贴着季节来去。

每到夏天,在萤火虫漫天飞的夜晚,在臭气熏天的天井或弄堂里,爷爷总是吃着烟,扇着篾扇,跟我和表哥讲这些那个。讲起这些那个,爷爷像老天爷,天上的仙,地下的鬼,人间的理,世间的道,什么都知道,讲不完。

讲着看着,月亮升起来了,村子安静下来,蛐蛐在石头缝里㘗㘗叫,水牛在栏里噗噗喷气,壁虎在墙壁上画画,老鼠在谷仓里唱歌,猫头鹰在后山竹林里哭泣。爷爷讲,它们前世都是人,作了孽才伏了法,转世做不成人,做了蛇虫百兽。

第二章

但群众一边斗争他,一边又巴结讨好他,谁家生什么事,村里出什么乱子,都会去找他商量。即使我爷爷,平时很讨厌他跟我父亲搅在一起,但只要家里遇到什么要紧事,照样要去请他拿主意,好像他才是真正的巫头,天下事都知晓。

村里无人不知晓,太监家有两只猫,一只全黑,一只全白,都跟小豹子一样,腰身长长的,头圆圆的,走路一脚是一脚,慢腾腾,雅致得很。我经常看见他用香皂给猫洗澡,用长柄木梳给它们梳毛,从头梳到脚,用金子小剪刀给它们剪趾甲,剪完又用砂纸磨。最气人的是,还专门给它们买上好的鲞吃!我父母从来没有对我这么好过,我吃过的鲞还没有他家猫多。我宁愿做他家的猫。我敢说,这也是我身边所有小孩子的想法。

农药在小爷爷肚皮里像灶火一样熊熊燃烧,要不是太监——不,必须尊称上校——及时赶来,一定会把他烧死。我亲眼看见,上校是怎么把小爷爷肚皮里的熊熊大火浇灭的,他先是往小爷爷嘴巴里塞进一块肥皂,灌他吞下去;然后扒掉他裤子,把他头朝地吊起来;然后又用打农药的喷壶往小爷爷屁洞里注水。农药壶有一个喷头,通过控制压力杆,可以把农药喷上树,射得比屋檐高。上校把喷头塞进小爷爷屁洞里,按住,一边拉压力杆,把满满一壶水都压进他屁洞里。这一定是痛的,小爷爷啊呀啊呀叫,叫着叫着,水从嘴巴哗哗吐出来。这水比屙出来的屎还要臭,熏得上校睁不开眼。上校睁开眼,对小爷爷儿子讲:“你爹死不了啦,给我去烧面吧。”这是老规矩,上校救活谁,谁家要烧碗肉丝面给他吃。有这样的老规矩,指明他不是第一次这样救人,只是我是第一次看到。这年我十一岁,已经跑得比爷爷快,所以爷爷派我去叫上校,要不我也看不到。

“喏,给你,不就是几块钱的事嘛,值得用性命去抵。世上命最值钱,我被人骂成太监都照样活着,你死什么死,轮不上。”

那天晚上,我第一次看到上校的眼睛,果然是明明亮亮的,比洁白的月光还要亮,一点不像个祟的鬼,像个英雄,堂亮得很。这是我重要的一个经历,我开始对上校生出好感,他救了小爷爷的命,也救了自己在我心目中的形象。我像被他吸着似的,跟着他出门,目送他远去,皎洁的月光披在他身上,照得他隐隐生辉。他走路的样子横竖不像太监,倒真是有些大军官的威风头,大踏步,高抬手,腰笔直,脚生风,一步是一步,昂首挺胸,雄赳赳,气昂昂,怎么看也不像裤裆里缺了东西。

秋天到了,柿子树叶开始变色,发黄,发褐,脱落,原来青绿扁圆的柿子也开始变色变样,变得发黄,泛红,赤红,红得火辣辣的,变得圆滚滚的,像一盏盏小红灯笼。灯笼密密匝匝的,挂满枝枝丫丫、节头梢头,远看整棵树像着火似的。这时,收获开始了,树上摘柿子、板栗、猕猴桃、酸勾子,地里刨红薯、洋芋、花生,水下挖藕、摸蚌。这是一年中最好的季节,不仅因为有收获,也因为风和日丽,天高气爽,可以出门远行。

小爷爷低头讲:“别把死挂在嘴上,我是死过的人,那罪不是人受的。

爷爷像一棵盘根错节、枝繁叶茂的老榕树,上遮天下盖地,里三层外三层,天打雷劈都不怕,怎么会怕小爷爷莫须有的风雪预报?总之,爷爷活成一个老埠头,你要改变他是很难的,不像我。我像三月里的桃树,一夜之间变成一幅画、一本诗,花枝招展,灿烂得连自己都认不得。

【笔记】
写爷爷的顽固,很寻常。但写“我像三月里的桃树,一夜之间变成一幅画、一本诗,花枝招展,灿烂得连自己都认不得”,我以为是特别新鲜的。年轻人对新事物、新观点的接受能力与老爷爷的顽固,两相对照,形成鲜明的对比。

第三章

凡是鼻子灵的人都有体验,上校家经常烧好吃的,尽管他家厨房深在院子里,看不见窗洞,但浓郁的香气会飞的,从锅铁里钻出,从窗洞里飘出,随风飘散,像春天的燕子在逼仄的弄堂里上下翻飞。

香气驱散了空气里的污秽,像给空气撒了一层金,像闪闪金光点亮了人眼睛一样,拉长了人的鼻子。

【笔记】
这里写香气“像春天的燕子在逼仄的弄堂里上下翻飞”、“拉长了人的鼻子”,都把本来无形的香气写得生动有形。
爷爷讲:“百草不如一木,百闻不如一见。”

爷爷告诉过我,上校生来就是个怪胎,胎位不正,又是头胎,他妈鬼哭狼嚎了两天,血流了一脚桶都没把他生出来,最后靠观德寺的和尚送的半枝人参,给她补足一口气才把他生下来。事后她去庙里谢和尚,和尚讲是观世音显灵救他们母子的,一句话叫她一辈子迷信观音菩萨。她把观音像请到家,供在堂前,天天烧香敬拜,求菩萨再显灵,给她添丁。菩萨不灵,求不到,她去庙里跟和尚哭,和尚对她讲,人要知足,不要占了前山还要后山,她是信的。后来丈夫死于非命,她又去寺里找和尚哭,和尚告诉她,要没有菩萨保佑,死的是她儿子,老子是替儿子死的,不幸中有大幸,她也是信的。再后来,听说儿子丢了宝贝疙瘩——那时老保长恨死她儿子,大肆散布谣言,村里连只狗都刮到风声——她又去对和尚哭,和尚劝她,这叫大难不死必有后福,她又是信的。总之,和尚讲什么她都信,从头信到脚,信到死。

爷爷讲:“这老娘们,和尚送她一口气,她还给菩萨一生世,实诚得不像人,像菩萨下凡,所以叫活观音。”

我后来觉得听他讲故事才是真正的“揩油”,比吃肉还过瘾。只是,这样的时节像蹄髈一样,并不多见。

必须是老太婆去普陀山的时候,也必须是上校吃足酒、人高兴的时候,他的故事才会一个劲地从嘴里噼噼啪啪出来,像酒气一样关不住。那时候他必是满脸通红,两只眼珠像电珠一样亮,手里夹着香烟,脚下盘着两只猫。空气里弥漫着烟雾和酒气,猫被呛得喵喵叫,他也不管。那时候他什么都不管,只管抽烟、喝茶、打饱嗝、讲故事。

我最欢喜听他讲故事,他闯过世界,跑过码头,谈起天来天很大,讲起地来地很广,北京上海,天南海北,火车坦克,飞机大炮,有的是稀奇古怪、奇花异草。民国哪一年,我在哪里做什么,有一天发生了一件什么事……他总是这样讲故事,有时间有地点,有人物有事情,情节起伏,波波折折,听起来津津有味,诱得蟋蟀都闭拢嘴不叫,默默流口水。

【笔记】
不管是上校母亲的“从头信到脚,信到死”,还是上校的“谈起天来天很大,讲起地来地很广”,讲起故事来“情节起伏,波波折折,听起来津津有味,诱得蟋蟀都闭拢嘴不叫,默默流口水”,读来都是跃然纸上的。
原来鬼子坦克开进一片原始荆棘林,毁了几十万只马蜂的老巢,那些马蜂都成了精,个头有蝗虫的大,数量也有蝗虫的多,散在空中,遮天蔽日,嗡嗡声连成一片,像沉闷的雷声在山坡上翻滚,卷起一阵风,吹得尘土飞扬。

那些马蜂如有灵性,知道是鬼子作了恶,要报仇,纷纷朝他们身上扑,肉里蜇,前仆后继,奋不顾身。鬼子虽有钢炮坦克,但在无数不要命的马蜂的疯狂围攻追击下,逃无可逃之路,躲无可躲之处,一个个在地上翻转打滚,痛哭嚎叫,最后无一幸存,尸陈遍野,尸体一个个又红又肿,像煺了毛吹了气的死猪。

另一个故事则让我暗暗发誓,长大一定要去上海看看,那个高楼啊,那个电车啊,那个轮船啊,那个霓虹灯啊,那个花园公园啊,那个十里洋场啊,那个花花世界啊,像在天上,像从头到脚都镀了金,连脚指头也不省略。

【笔记】
读到这里,我就想,当老师的,如果要点燃孩子的火焰,就要唤起他们的向往。尤其是对于山村的孩子,如果能给他们讲一讲“那个高楼啊,那个电车啊,那个轮船啊,那个霓虹灯啊,那个花园公园啊,那个十里洋场啊”,应该是有吸引力的。
以开诊所作掩护,埋名隐姓,杀奸除鬼,刺探情报,过上一种恐怖又滑稽的生活:一边纸醉金迷,一边随时丢命。

吃着烟,喝着茶,打着饱嗝,喷着熏人的酒气,有时吊着故事主角的家乡口音,连声带色,自问自答,是上校讲故事的特点,成套路了。这不,他又开始老一套,拖着四川话的腔调,抛出一堆问号:

四川人开口离不开‘咋子’和‘要得’,咋子标致的人咋子要当尼姑?标致的人当婊子才要得是吧?当婊子也比当尼姑要得是吧?再讲,哑巴咋子识得了字?她识得字指明她不是天生的哑巴是吧?那她又是咋子成哑巴的呢?是病还是灾?是祸还是殃?到底是咋子了呢?”

给我印象深的还有一个故事,说的是民国三十二年,他在上海的五个手下的一个,那个会讲鸟语的家伙,被汪精卫的特务重金收买,把他一组人都卖个光。特务全城捕杀他们,死两个,逃两个,抓一个。抓的就是他,被敌人从电车上抓走,后来关押在湖州长兴山里的一个战俘营里劳改,四五百人,天天挖煤。一次山体塌方,把一百多人堵在坑道里,大家拼命救,几百人昼夜不停挖塌方。但塌方面积太大,十多天都挖不通,就泄了气,放弃营救——因为救出来也是死人,不划算。

上校讲:“只有一个人不放弃,一个江苏常熟人,四十多岁,入狱前在上海十六铺码头当搬运工,壮实得像一头牛。他有两个儿子,老大二十一岁,跟他在码头上做工,小儿子十七岁,做母亲的帮工,在乡镇上盘了一爿杂货店,卖油盐酱醋。常熟就是沙家浜的地方,是新四军经常出没的地盘。新四军也要吃饭,常来店里买东西,一来二往,把小儿子发展了,当了交通员,经常往上海跑,传情报,采购药品、枪械、弹药什么的。后来老小把老大也发展了,兄弟俩你来我往,成了新四军一条活络的交通线。”

那次塌方,父亲和上校是一个班的,躲过一劫,但兄弟俩都在里面。“这简直要了当爹的命!”上校讲,“从发生塌方后,十来天他就没出过坑道,人家换班他不换,累了就睡在坑道里,饿了就啃个馒头,谁歇个手他就跟人下跪,求人别歇。他总是一边挖着一边讲着同一句话——你们把我儿子救出来后我就做你们的孙子,你们要我做什么都是我的命。讲过千遍万遍,喉咙哑了还在讲。只要是人,长心眼的,听了看了他这可怜的样子,都情愿替他卖力卖命。”

可塌方是个无底洞,几百人轮流挖了十多天,都卖了命的,就是买不来里面人的命。眼看过了救命时间,狱头放弃营救,要大家去上班,只有他不放弃,白天被押去上班,夜里一个人去挖塌方。大家劝他算了,救出来也是死人,别把自己的命也搭进去。他呜呜叫,你不知道他在讲什么,因为喉咙已经着地哑掉,发不出声。但看他的空床铺,你知道他谁的话都没听进去,他的被窝成了老鼠窝。他本是搬运工,一个壮汉子,胸脯厚实得子弹打不穿,却眼看着一天天瘦下去。像日子是一把刀,在一刻不停削他、刮他、放他血水,血肉一层层剥下来,干下去,枯得像个鬼。

一天夜里有人打架受伤,上校去给人包扎,老远看见一个人在腊月的寒冷里踉跄着往坑道晃去。天已经黑透,只能看清一团黑影子,看不清模样,但上校知道他是谁——可怜的父亲!这些天他曾多次这样见过他,在黑夜的寒风里独孤孤一人往黑洞里奔走,但现在不是在走,而是在跌跌撞撞,一步三晃,几步一跤,像吃醉酒,糊涂得手脚不分,连走带爬的。夜里睡觉时,上校眼前老是浮现这身影,心里很难过,想他可能是腿脚有伤。他带上药水和几个冷馒头去看他,也想劝他回来歇一夜。去了发现,他已死在坑道里,半道上,离塌方还有一个几十米的弯道。他已经爬了几十米,几十米的坑道都是他爬的手印子、吐的饭菜,最后死的样子也是趴着的,保留着往前爬的姿势。上校讲:“我想他一定是想跟两个儿子死得近一些,就想把他抱到塌方段去葬。他本是那么壮实,大冬天,穿着棉袄棉裤,看上去还是很大块头,像你(父亲)。我以为要花好大力气才抱得起他,可一抱发现轻得像个孩子,像你(我)。我知道他已经很瘦,可想不到会瘦成这样子,完全只剩下一把骨头,骨头好像也枯了,朽了,轻飘飘的。我本来是鼓足力气抱他的,反而被这个轻压垮了,哭了。我前半辈子都在跟死人打交道,战场上手术台上死人见得多,从没哪个人的死让我这么伤心。我一路抱着他都在哭,葬他时也在哭,哭得喘不过气来,现在想起来都难过。”

战争年代,伤员多数是枪伤、刀伤,头破、肚皮烂、断脚、缺胳膊;军医多数是外科医生,擅长开刀、缝针、取子弹、接骨头、包肚皮这些血淋淋的手术。平时不打仗,医院清风雅静,清闲得很,前线一开战,伤病员一车车运来,军医累死都忙不过来。有些伤员伤势太重,生死难料,军医懒得管,怕忙碌一阵白忙乎,耽误时间。他们被丢在走道上,困在担架上,呼天求地,鬼哭狼嚎,有的受不了痛撞墙寻了死。医生见怪不怪,心肠铁硬,把他们当死人看,从他们面前匆匆过往,连给个口头安慰的工夫和心情都没有。他养伤了几个月,见的多了,胆子也大了,偷偷把那些被军医丢在走廊上的垂死伤员当活人救,练技术。反正救不了也没人追究,救活了是天上丢馅饼。就这样,他拿起手术刀,私设手术台,偷偷当起军医。几回下来居然救活几人,一下在医院出名,医院就留他当了正式军医。

第四章

什么是战争?就是活一天算一天,一天等于一生世,得空就要快活,及时行乐,死了不冤。

所以战争间隙,别人都去吃喝嫖赌找快活,他不这样,他埋头苦练本领,练枪法,练刺杀,练埋伏。他有自己的看法,做木工手艺就是生意,上战场本领就是性命,练好本领就是保护性命。他想到做到,仗打一路,他练了一路本领,也捡了一路性命。眼看战友死的死,伤的伤,他毫发不损,靠的就是有过硬本领,能打会躲。他枪法准到什么程度?你放飞手上的鸽子,他同时装子弹打,十枪九中。有这身本事战场上早迟要当英雄,

爷爷讲:“你看,他现在还养猫,不吸教训,不回头。他这人就这样,骨头太硬,心气太傲,仗着聪明能干,由着性子活,对老天爷也不肯低头。这样不好的,人啊,心头一定要有个怕,有个躲。世间很大,天外有天,山外有山,不能太任着性子,该低头时要低头,该认错时要认错。”

俗话讲不怕老只怕小,小鬼作恶老鬼哭。你不晓得,我早晓得,城里被这些小鬼搅翻了天,每天江面上都浮出无名死尸。这些小子心还没有长圆,做事没轻重,还是避一避好。

第八章

爷爷讲,大多数蚊虫到寒露节气就要死掉,寒露寒露,蚊虫无路,指的就是这意思。但叮过人、吃过人血的蚊虫,精气足,头脑灵,变得聪明,到了寒露时节会寻个暖和的地方做窝,睡大觉,养精蓄锐。这样就可以熬过三九严寒,死不了,变成蚊虫精,来年继续作威作福。我想,我和矮脚虎今天至少让几十只蚊虫都变成了蚊虫精,明年说不定还要再来吃我们的血。

第九章

爷爷讲,富春江里有大鱼,民国一十二年,富春江爆发百年不遇的洪水,村子里水深一米多,可以撑船;洪水退走时,他在我家楼梯下逮到一条七十八斤重的大白条鱼,那鱼立起来比我奶奶还高,躺在地上一身白亮,把整个灶屋都照亮。但这是一个阴谋,不等家里人把鱼吃完,我奶奶的寿命已经走完。爷爷讲,这鱼是阴府派出的考官,专门来考他的!他考败了,吃了鱼,丢了奶奶。从那以后,他在前堂摆设香炉、烛台、关公像,祭祖拜神,消灾辟邪,直到红卫兵把这些法器抄走。后来我家的日子越过越晦气,惹出一堆事,可能跟这个有一定关系吧。

【笔记】
像蚊子成精、吃了鱼丢了奶奶这样的奇怪事,小时也听说一些,看来农村都一样,因为不知道,无从知晓,想找个说法,都有人讲述着奇怪的、带有迷信色彩的事情。
这是我在村里最后一次见到他,月光下,他面色是那么苍白凄冷,神情是那样惊慌迷离,步履是那么沉重拖沓,腰杆是那么佝偻,耷拉的头垂得似乎要掉下来,整个人像团奄奄一息的炭火,和我印象中的他完全不是同个人——像白天和黑夜的不同,像活人和死鬼的不同,像清泉和污水的不同。

第十章

老保长哈哈笑:“提到死,现在排第一的不是你,当然更不是我,我是无论如何要排在你之后的。为什么?因为你满肚肠心思,心思多了,寿命就少了,这是阎罗王定的规矩,反不了的。”

父亲默不作声,摸出两根烟,递给爷爷一根。爷爷掏出火柴,先点了父亲的,再点自己的,然后两人边抽边走,回屋里去,黑暗中显得越发亲密,像一对难兄难弟。没想到一场来势汹汹的干架最后是这么友好收场,我看着他们愈来愈黑远的背影,心里甜滋滋的。天依旧黑乎乎的,我心里却暖洋洋的亮堂,像爷爷划亮的火柴旺在我心头。

【笔记】
小孩子总是希望家庭和睦的,大人高兴,小孩子也高兴,别以为他们不懂。他们其实知冷知热。
爷爷讲过,村子的一年四季,像人的一辈子,春天像少小孩子,看上去五颜六色,生龙活虎,朝气蓬勃,实际上好看不中用,开花不结果,馋死人(春天经常饿死人);夏天像大小伙子,热度高,精气旺,力(热)气日日长,蛇虫夜夜生,农忙双抢(结婚生子),手忙脚乱,累死人;秋天像精壮汉子,人到中年,成熟了,沉淀了,五谷丰登,六畜兴旺,天高云淡,不冷不热,爽死人;冬天像死老头子,寒气一团团冒,衣服一件件添,出门缩脖子,回家守床板,闷死人。

这里有些是双关语,明的一层意思,暗底一层含义。有些含义好理解,++比如“夏天热度高”,既是指天气的热度,也是指人的热情,热火朝天的生活,狗都闲不住;有些含义却不大好理解,比如“夏天蛇虫夜夜生”,既是指大自然里的毒蛇害虫,也是指人口舌上的是是非非。夏天日长夜短,暑热难当,人都不爱待在家里,要出门,三五成群,四六抱团,散在弄堂里,聚在祠堂门口,吃饭、纳凉、打牌、下棋、看戏、闲聊天、拉家常,是制谣传谣、传播小道消息的好时节。去年夏天,上校失踪后,整个村子都在谈论他,真真假假,犄角旮旯都在浅吟低唱,蘑菇一样的,见风就长。他在“蛇虫夜夜生”的盛夏出事,注定是要被人大张旗鼓地嚼舌,嚼得遍体鳞伤。然后到秋天,盛况逐渐收敛,一路下滑,到冬天滑入谷底。翻过年,只是零星有人提起,提了就提了,气泡一样,风一吹就破了:因为终归是老故事,陈芝麻烂谷子,不可能出现风声四起的老行情。要出现老行情,必须冒出新东西,比如上校被捕了,审出案情真相了;或者小瞎子开口讲话了,揭开一堆秘密真相,等等。新东西迟迟不涌现,上校自然而然在离我们远去,这是大势所趋。++

【笔记】
这里对于季节、人的活动与人事之间关系的描摹,是非常透彻的。说人们口舌是非,“真真假假,犄角旮旯都在浅吟低唱,蘑菇一样的,见风就长”,再合适不过。
爷爷讲,七月半,鬼一半:一半是活人的,一半是死人的。

按习俗,这一天活人要祭祀死人,做好发糕和桂花饼去祠堂的荫堂敬拜祖宗阴人。荫堂是阴曹地府和阳世人间接头的地方,接口,通道,平时不大有人去的,瘆人,但这天你又是必须去的,不去就是不敬祖宗,搞不好要被恶鬼缠住,更瘆人。一般人家都要派人去,去的多为老人、孩子、妇女。中午去的人最多,最热闹,繁碌时荫堂里都是人,像筷筒里的筷子一样插满,济济一堂,嗡嗡嘤嘤的,就有一种危险似的,像祖宗马上要破壁而出,房子马上会塌掉。

爷爷专挑中午去,带着我,去凑热闹。祠堂门口人来人往,空气里全是桂花和米酒的香气,浓厚得醉人

第十一章

开学那天,我瑟缩着,拖沓着,几次拿起书包又放下,迈不开脚步。我怕同学瞎说八道……++同学是最爱瞎说八道的,无风三尺浪,见风就是雨,口无遮拦,舌头子尖,而且专挑你痛处捅,抓你小辫子,揪你烂尾巴,你哪里痛他们往哪里捅,朝你伤口上撒盐。++

【笔记】
都说童言无忌。确实,这个年龄段的孩子,很容易有一拨孩子,“专挑你痛处捅,抓你小辫子,揪你烂尾巴,你哪里痛他们往哪里捅,朝你伤口上撒盐”,分布得轻重和是非。

第十二章

大街上人多车挤,铺一层潮汐一样的市声,稀里哗啦的,穿来梭去的,是乱的,又是不乱的;两边橱窗一律亮堂,从吃喝到穿戴、到日用,一应俱全,招摇得搔首弄姿的,像是等你去拿,又是碰不着的,因为有玻璃隔着。玻璃,这么多玻璃!灯光,这么多灯光!像是全世界的玻璃和灯光都被集合到这儿,老保长来不及看,眼前和心里是一团乱,是碎掉的感觉。

因为村里有老虎山(后山),爷爷教过我许多跟老虎有关的成语俗语,比如初生牛犊不怕虎;虎毒不食子;将门出虎子;前怕狼后怕虎;一山不容二虎;有胆子摸老虎屁股;老虎嘴里讨不到肉吃;山中无老虎,猴子称大王;兵马不离阵,虎狼不离山;打虎要打头,杀鸡要割喉;人到四十五,正如出山虎;凤凰落架不如鸡,猛虎下山被犬欺;深山藏虎豹,乱世出英雄。龙要下海,打虎要上山;不入虎穴,焉得虎子;明知山有虎,偏向虎山行,等等,一大堆。

第十三章

老保长为自己的一番苦心失效而失望,毅然起身走。“法子就是你咽下去!”他边走边骂,“++你这人就是自私,总想着要体面,把面子当命根子。他妈的,面子顶个屁用!我当初像狗一样活着,人家太监现在也是一只丧家之犬,小瞎子是废物一个,屙屎连屁股都不会擦,不都照样活着。照你这样想,我们都该去死,就你一个人活着。++”

【笔记】
这一顿骂死要面子的人颇为贴切。
这天我懂了一个新道理:++人和兽之间,只隔着一团愤怒++,像生死之间只隔着一层纸——后面这话当然是爷爷讲的。

瘌痢头。

第十四章

上校聪明绝顶,怎么可能不懂这道理?他就藏在大陈村,和老母亲一起落脚在当地一个老庙里,庙里的大和尚是他母亲在普陀山修行时相识的。大和尚背上长一个瘤子,活的,年年在长个儿,已经大得像一只老太婆的瘪奶子,耷拉下来,走路晃荡晃荡的。天大地大,上校哪儿不去,偏投奔这儿,正是得知这情况,他可以帮大和尚驱病消灾,建立交情,然后留下来。 这里,我们的公安管不到,大街上没有通缉他的头像,没人知晓他是罪犯。一年多来,他天天晨早傍晚扫地,白天夜里陪母亲念经,念经的水平已追上大和尚。他甚至已经学会一口地道的萧山话,剃一个光头,穿一身僧服,没人看得清他的来历,也没人去看去想。

爷爷讲:“年轻人容易心碎,老人容易嘴碎。”

父亲本是闷葫芦一个,心思重,嘴巴紧,从此变得更闷,几乎不跟人言语,只跟猫讲话。

人们爱听瞎话,不爱听真话,正如大家互相不叫名字,爱叫绰号一样。

【笔记】
作者写出了人性深处的东西。
鸡奸犯的事害得我们一家人难受死,像得了某种丢人的暗病,说不清道不白:说是越描越黑,沉默不说是承认事实。我因此自卑得不行,像身后拖着一根大尾巴,时刻怕同学来揪、来踩。

爷爷给我备一把三角刀,专门用来对付可能出现的坏蛋,保护我和全家尊严。现在尾巴叫这公告彻底割断,我因羞耻而担惊受怕的日子从此一去不复返啦!

【笔记】
人对于面子、舆论的在意,超出一般人的想象。因为痛恨别人做了错事,就有人暗地里对当事人的孙子扔砖头,丝毫不顾及会不会砸死人。当事人为了保护孙子和全家人的尊严,也给孙子备上一把三角刀。如此种种,本来是关乎道德对错的事情,却上升到了你死我亡的程度。让人唏嘘!
村里有人就拿捡来的子弹壳用锉刀磨一眼孔,做哨子,吹出来的哨音尖锋得很,吓麻雀贼灵光。

爷爷讲,麻雀灰不溜秋,一副贼相,贪吃,是农民的天敌,赶不尽,杀不绝;燕子一身漆黑,一副忠诚相,是农民的长工,所以家家户户留它们在屋檐下作窠。自古,远亲不如近邻,近邻不如长工,所以对长工是要待好的。

自贴出公告后,好似公安局在我们村里凿通一个窗洞,风来雨来,不时传来上校一缕缕音讯,众说纷纭的,如一锅热粥,四处冒泡,稀里糊涂,见不着个底,你不知道信谁不信谁。一种说法,上校骨头刚硬,在铁皮牢屋里被连吊几夜,肋排骨被打断几根,就是死不开口,宁死不屈。一种说法,上校当过军统特务,有本事对付公安,轻松耍花招,把公安蒙在鼓里,根本没挨打。一种说法,公安从省里请来专家,专家带来药,药无色无味,掺进白开水,上校喝下去,不过十分钟换一个人,问什么讲什么,一五一十全交代。种种说法都有人信,也有人不信,没威信。对上校肚皮上的字也是这样。

爷爷讲,++酒鬼嗓门大,死鬼乌珠大++。这话一点不假。

第十五章

时值古历十月,蛇虫百豸死掉的死掉,躲掉的躲掉,销声匿迹,夜深人静。++当老保长闭口时,我听得见月光在屋顶上走动的声音,它们赶着黑暗,走入天井,爬上墙,天井变得更大,也更静了++。

爷爷讲:“月光爬上墙,人爬上床。

【笔记】
“听得见月光在屋顶上走动的声音,它们赶着黑暗,走入天井,爬上墙”,这写得很妙,以动写静,显得更静了。
你看他不停地把一个个老故事颠三倒四地讲,以为他早已倾家荡产,想不到还埋着这么大一个金矿。我无法想象一个整天酒醉糊涂的人是靠什么锁住这个金矿的,正如无法想象一个老酒鬼守着一缸老酒不喝一口。这个事实让我对老保长肃然起敬,我觉得我们所有人都应该尊敬他。

月光在老保长不语时显得更亮,好像沉默真的是金子,可以发光,照亮月光。老保长讲故事有门道的,每讲到关键处,总要停下来喝水,重新点一支烟。这是吊人胃口,也是为了把故事讲出门道:好像讲不下去,其实是要个停顿,摆个样子而已。

一路上,四方瞅见磕头烧纸钱的人,街头巷尾,香火缭绕,鬼影幢幢。十月半是又一个鬼节,俗称下元节,是三大鬼节的收官之节。这个日子上路,老保长心头多少有些不祥的预兆。

火车一路北上,也是一路停。一半是临时停,停下来都是一件事:查证件,抓汉奸。这年月,汉奸不是关在监牢里就是逃在路上,火车人多,好掩护,是汉奸逃跑的首选路线。老保长手头有一本证件,是姜太公给他备的,蓝面子,黑印章,有见官高一级的权威。坐他对面的是个书生模样的中年人,戴眼镜,穿长衫,言少笑多,待人彬彬有礼。首次查证件,他顺便刮了一眼老保长证件,然后便对老保长恭恭敬敬,给他递烟买包子,跟勤务兵似的。车上有不少军人,士兵军官,三五成群,吆三喝四,把自己当战斗英雄,把布衣百姓当鬼子,手下败将,想训斥就训斥,要座位就得让,横行霸道。书生悄悄对老保长讲,中国要有这么多战斗英雄,日本佬该早滚蛋了。

盘缠,证件,照片,是寻不着人的。++寻人得靠人,当地人,地头蛇。爷爷讲:“强龙不压地头蛇,天大地大地头蛇大。”++

姜太公在上海是一条暗龙,地头蛇,而各地的暗龙、地头蛇是响应的,如官官相护,青帮黑路私通一起一个样。临行前,姜太公交给老保长三封信,密封,编了号:1、2、3,张三李四,单位地址,一一写明,让他依次去寻人。运气好,三人中必有一主认他这个“娘舅”,帮他去寻见可能落难的“外甥”。

至此至时老保长恍然有悟,姜太公为什么有那么多忌惮和禁令,因为这年月汉奸实在太多啦,当汉奸实在太容易啦,上校被大汉奸包养,罪名上已是汉奸,谁敢保证他实际里不曾失过节?失过节,她周折此事便是自取其辱。

月光爬在墙上,久了,累了,都从墙上下来,匍匐在天井里,把灰白的地砖照得冒出冷气。

第十六章

惊蛰不动土,春分不上山。清明吃青果,冬至吃白饼。立夏小满足,大雪兆丰年。鲤鱼跳龙门,雷公进屋门。朝霞不出门,晚霞行千里。

这些都是爷爷讲的,跟我讲,跟表哥讲,有时也跟非亲非故的人讲。有一回,我看到他在路上拦下我的几个同学,考他们:“你讲,为什么惊蛰不能动土?”

谁知道呢?谁也不想知道。你不想知道他也会告知你:“因为惊蛰是蛇虫百豸苏醒的节气,地里土里都窠着各种幼虫胎卵,娇气得很,动了土就要了它们命了。哪怕害虫也是性命,要让它们投胎活一世,不能叫它们投不了胎,死在胎盘里,这是做人的起码。”

【笔记】
老一辈的一些人,就是这样对古训痴迷,显得神经兮兮的。见多了你才会明白,大千世界,无奇不有,这只是迂一点罢了,这又有什么不可以呢?所以说,要见多识广,才能心胸开阔,才不至于因别人的一点出格举动就显得不可接受,强烈反对,针锋相对。其实,那都是见识短浅的狭隘表现。
老保长不止一次讲过,如果道理可以当钞票用,我们家笃定是全村最富裕的人家。我们家并不富裕,这话是带点笑弄爷爷的意思。爷爷不听见则罢,听见一定要顶他,有时讲:“如果做人不讲道理,吃再多的饭都是白吃,穿金丝绸缎也是马戏团里的猴子。”

大冬天,村子里是不大生事情的,精壮劳力大多被派去江北修水利,老人妇女大多待在家里,生火盆取暖,给孩子纳鞋底、做新鞋,只有小孩子在外头乱窜,在干涸的溪床里翻开石头抓冻僵的泥鳅螃蟹,刨开洞穴捉黄鼠狼和冬眠的蛇。

时近年关,村子里又闹热起来,最热闹的当然是春节头几天,家家户户都忙着拜年,走亲戚,迎亲戚,大人都在酒桌上,小孩都在数压岁钱。一般这种热闹要到正月初十才会冷下来,但这年春节一场大雪提前让热闹冷清下来。

【笔记】
大致写出了农村的迹象,现在也差不多是这个样子。
气愤让我变成了废物。

爷爷也是,自挨老保长打骂后,一直呆若木鸡,傻愣着,既不还嘴骂也不叫苦申辩,好像老保长事先给他灌过迷魂药,他神志不清了,体面不要了,道理丢完了,成了个十足的糊涂蛋、可怜虫。我既觉得有些可怜爷爷,又觉得这里面可能有什么古怪:兴许是爷爷有错在先,他认错了。这么想着我心里少了气愤,多了紧张,怕他有错。不一会儿,父亲回来,像老保长刚才一样,也是一脸杀气,一声不响地走到爷爷面前,像刚才对老保长一样,一把揪住爷爷胸口,推他到板壁前,抵住,恶声恶气地责问爷爷:“你给我讲实话,是不是你向公安揭发了上校?是不是?讲实话!讲啊!”爷爷,你开口啊,不是的,你又不知道上校躲在那里,没人跟你讲过啊;爷爷,你快否认啊,你是冤枉的;爷爷,你一向懂得做人道德的,你不可能干这种缺德事;爷爷,你快讲啊,大声讲出来。可爷爷一言不发,一声不吭,只闭了眼,流出两行泪,虫一样爬着,鼻涕也流出来。看着这样子,我心都碎掉了。我号啕大哭,像爷爷死了。这个该死的下午,天地是雪白,可人是污黑的,坏人打好人,儿子骂老子,天理皇道塌下来,压得我窒息,心里眼前一团黑,恨不得哭死。

人就这样世故,你好给你锦上添花,不好给你雪上加霜。

【笔记】
“人就这样世故,你好给你锦上添花,不好给你雪上加霜。”多么痛的领悟!所以啊,人生在世,还是要靠自己。春风得意时马蹄也急,墙倒时众人都推,人性如此,赶上了也不必埋怨。毕竟大家都是吃五谷杂粮的,怎么能要求大家都有高尚的道德,跟菩萨神仙一样?抱着这样的心态,才能活得明白。否则,难免一辈子活在怨恨里——那是自己跟自己较劲,跟自己对天真与倔强较劲!
我回家必须经过七阿太小店,然后进入祠堂弄。祠堂高,弄堂长,天空狭长一条,天色更加阴沉。++正常,我一分钟可以走完这条弄堂,我已经走过十六年,无数次,但这天下午的一分钟差点成了我一辈子:一块断头砖从祠堂窗口飞出,无声地冲着我坠落,擦着我背脊滑下,砸碎在地上。我只受皮伤,擦出一道血印子,但如果我晚半步,就是一辈子,比爷爷先死。++

++这天我终于明白,爷爷为什么从那天起不再出门,他像只老鼠一样,宁愿去猪圈里待着也不迈出大门一步。因为他知道,出门必定会有更多的窗户飞出断砖碎瓦,你无法寻出谁是凶手,凶手是风,是猫,是老鼠。我甚至怀疑小爷爷都可能这样作证,只有耶稣知晓他们在撒谎。++

但耶稣又会原谅他们的。

家像敌人的碉堡,有人无数次在心里想把它炸毁

死是如此活的、真的、近的,看得见,摸得着,像我养的兔子,就在我身边,我生活里。

我不怕死,我才十六岁,怕父亲打,怕母亲骂,对死是一点不怕的。但爱我的人怕!你别以为,我活得不如上校家的两只猫,就没人爱了。爷爷讲:“所有父母都爱自己的孩子,像所有树都爱阳光一样。”

第二天,父亲通知我别去上学,别出门;如果出门,必须由大哥陪着。他自己倒是夹着油布伞,带着干粮,冒着漫天的雪珠子出门了。他好几天才回来,然后第二天大清早又领着我出门,先乘船,后乘车,不知要去一个什么地方。我不知道这是一次漫长的离别,加上是大清早,我瞌睡蒙眬的,没有和爷爷告别。大哥送我到公路上,母亲送我到镇上,船埠头,抱着我咽咽哭一通,向老天求平安。母亲对我哭诉着:“你一定要平安,一定要回来看我。”

一切都是命,这话爷爷以往多回讲过。那天,我十分后悔离家时没有和爷爷告个别,我猜他一定为我的无情无义伤心死了。这大概是他的命,对我好言好待十六年,却没有得到我一分钟的话别。

爷爷讲过,一分钟的和好抵得过一辈子的仇恨,我和他正好反过来。

第十七章

报纸上说,民航飞机是最安全的,因为所有核心机部都有双份,有预备。

当然遇到恐怖分子预备再多也没用,只有预备死。恐怖分子是当今人类的肿瘤——这也是报纸上说的。

城市有多大多美,我就有多小多丑,小得连名字都没有,大白天不敢上街,听到警笛就发抖。

偷渡客都这样,像阴沟里的老鼠,只能苟且活着,能找到一条阴沟卖命就是最好的活路。

报纸上说,人要学会放下,放下是一种饶人的善良,也是饶过自己的智慧。我这一生许多事都放下了,但有些事又怎么放得下?我在鞋厂给皮鞋钉了六年扣子,深知一个道理,扣子不是鞋带,可以脱下,扣子钉上去后就跟鞋子长在一起,脱不下的,脱下皮子就坏了。有些事长进血肉里,只有死才能放下。

一九九一年,我还没做生意,挣钱难,为了攒足一张机票钱,我得熬五六年时间,像养大一个孩子一样难。我说过,那时回国是伤筋动骨,但只要伤得起,不是粉身碎骨,我是不会放弃的。我已经等了二十二年,每天我用回忆抵抗漫无边际的思念,用当牛作马的辛劳编织回来的梦。

一切都是为了回来!像一个人不能把自己拎起来一样,我放不下回来的念想。一定意义上说,我活着就是为了回来。

谢天谢地,我总算等到了这一天:用二十二年等的一天!记得那天从售票台接过机票的一刻起,我的心就开始怦怦跳,像接到手的是一张生死命状,激动,紧张,害怕,兴奋,太多的情绪,太乱的心思,一路上我都天昏地黑的。等踏进家门,我一下咚地跪在地上,像这套纸票(我订的是中转往返票,便宜)有千斤重量,我负重竭尽全力挺一路,到家再也挺不住,累垮了。现在想起这些,我依然感到膝盖发胀,眼前浮现出妻子用手轻轻抹去我脸上泪水的情景,仿佛发生在昨天。

人活一世,总要经历很多事,有些事情像空气,随风飘散,不留痕迹;有些事情像水印子,留得了一时留不久;而有些事情则像木刻,刻上去了,消不失的。我觉得自己经历的一些事,像烙铁烙穿肉、伤到筋的疤,不但消不失,还会在阴雨天隐隐疼。

++哪里埋着你亲人的尸骨,哪里就是你的故乡++。

一九九一年,我行囊空空、疲惫不堪地回到家乡时,后山的老虎背上已多出三座我亲人的坟墓:一座是爷爷,一座是母亲,一座是我二哥——如果嫂子也算亲人,就有四座,是我未曾谋过面的二嫂。我在一个阴雨绵绵的春日(这是航线淡季)的下午小心翼翼地走进睽违二十二年的老宅时,父亲正落寞地坐在我和爷爷曾经睡觉的东厢房门前的躺椅上,一边抽着烟,一边看着屋檐水滴答在天井里结满污垢的青石板上。他把我当作走错门的人,抬头看我一眼,又低头抽烟,问我:“你找谁?”我叫一声爹,报出自己小名。他像只有二十二个小时没有看到我,没有些许激动——也许是怕激动,也许是要给我腾出时间,认识一下这不堪的老屋,目光自下而上、自外向里无精打采地睃视着,好像在告诉这些老墙、老门、老楼板:有故人回来了。屋子里弥漫着一股发霉酸腐的浊气,门楣上、楼板下、屋檐下、角落里,挂满蒙尘的蜘蛛网;几张条凳、竹椅横七竖八地散乱在前堂;堂前正壁贴着我熟悉的毛主席像,已经脱落一只角;阁几上灰扑扑的,像父亲抽了几年的烟灰都撒在上面,并被摊匀。屋里唯一干净的是那张我从前做作业的八仙桌,即使在昏暗的光线下依然泛出丝丝红光。我以为父亲痴呆了,数着他脸上一条条狂野、黝黑的皱纹。父亲瘦成了一把骨头,手背的青筋有他指头间夹的纸烟一样粗。足足过了一分钟,我再次叫他一声爹,报明身份。他丢掉烟头,看着烟火在雨丝里慢慢熄灭,终于开口:“你爷爷死了。”这我早就猜到,从几方面都猜得到。爷爷是那么要面子的人,当初一个鸡奸犯的假传闻都差点把他送进鬼门,何况后来全村人包括父亲集体公然向他发难谴责,他哪受得了?报纸上说,智者可以从过去摸到未来的痕迹。我不是智者,也从爷爷犯的错误中听到了他死亡的脚步声:一步之遥,触手可及。此外,我出去后每年都给家里写一封信,从没有收到一封回信。头些年我还苦等回信,后来根本不等了,写信只是告诉他们我还活着。家里只有爷爷能写信,他要活着一定会给我回信,哪怕明知第二天要死,也会给我写好回信。后来父亲告诉我,在我走后没几天,还没等到上校的申明告示,爷爷已经把命交给他的裤带,在猪圈里上吊了。上校的申明起的作用,也许只是没人去刨他坟墓。老保长一再公开扬言,爷爷没资格葬在村里任何一寸土里,他应该碎尸万段,喂豺狼吃。何况,爷爷要在世,已是年近百岁的老寿星,一个背负骂名、胆战心惊的人无论如何是享不到这福寿的。过一会儿,父亲又说:“你妈也死了。”这我从刚才看到的屋子的凄凉景象中猜得到的。++母亲要在世,这些灰尘蜘蛛网不可能这么耀武扬威在我面前。母亲是天底下最勤劳的人,屋子在她手里,哪怕是猪圈,地上的垃圾也不会过夜,板壁上、楼板下的灰网不会过月,如今它们成年不败的威风,显然是母亲入土化为尘灰的证据。我问父亲母亲是哪年走的,怎么走的——我希望是自然走的,不是自杀,也不是他害。父亲不理我,继续说:“你二哥也不在了,比你妈先走。”++

二哥是病死的,白血病。这对一个漆匠来说也许是职业病,但父亲不这么认为,理由是镇上漆匠多了去,只他一人得这怪病。我们三兄弟,二哥最像父亲,不爱说话,绰号叫铁疙瘩,心思被铁包着。所以父亲有理由认为,二哥是郁闷死的。父亲说二哥:“他就像被老婆戴了绿帽子,整天愁眉不展,闷声不响。他是被自己憋死的,也是我们逼死的,老辈子作了孽,他是替死鬼。”这是父亲后来说的,那天他像口丧钟似的,只报丧,++报了二哥,又报二嫂:“你二嫂也死了,比你二哥先死。”++二哥的性格不讨人喜,三十岁没女朋友。三十二岁,在父母亲极力张罗下,花钱从贵州买了个媳妇,年纪相差十岁。不知是语言不通的缘故,还是年龄相差大的原因,两人结了婚像结了冤,二哥经常不回家,回家就吵架。他宁愿跟家具、漆桶待在一起也不爱回家,像配给家具似的。据说二嫂到死也没学会我们这边话,因为二哥老不着家,没人跟她说话。因为语言不通,两人吵架经常动手,摔家伙,砸东西。一次,二哥下手重了,一巴掌打掉她一颗门牙,然后摔门走掉。二嫂哭了一夜,凌晨喝下一瓶农药。这是他们婚后第三年,遗下一个儿子,当天晚上我就跟他睡在一张床上,十一岁,长得一点不像二哥,在读小学。据说学习成绩很好,门门功课全班第一,这也不像二哥。二哥是反过来,门门功课班上倒数第一,所以早早退学,去学手艺。

** 父亲说完二嫂的死,我已经被死人包围,心里已胆怯得发抖,不敢去想大哥。看屋里这惨恻的败象,也不像有大哥大嫂鲜活的样子——当然可能根本就没有大嫂这个人。我问大哥是不是也走了,结果父亲说:“是走了,但人还在。”顿了顿,又说:“幸亏走,否则也活不成,你也一样。”++我一下泪满眼眶,好像在战场上,全军覆没,终于保下来一个也终于保住了我千辛万苦回来的价值。++父亲这样子,哪是欢迎我回来的样子——对他,我回来的价值是个负数,他巴不得我别回来呢。后来他告诉我,所以这么多年没让人给我回一封信,就是不想让多一个人知道我还活着。他怕死神恶鬼对着地址去寻我,追杀我。他已经认定,这村子是克我们一家的,他怕我回来,沾了晦气,活不成。**

大哥是去了秦坞,一个偏僻小山村,做了倒插门女婿。在生死面前他躲过一劫,但在荣辱面前,丢尽了脸面。长兄如父,再穷困潦倒的人家也不会把长子拱手出让,这是一个破掉底线的苟且,形同卖国求荣,卖淫求生。这是生不如死,是跪下来讨饶,趴下来偷生。我忽然明白,即使村里人已原谅我们家,但我们家却无法原谅自己,甘愿认罚赎罪。爷爷寻死是认罚,大哥认辱是认罚,二哥年纪轻轻抱病而死和我奔波在逃命路上,亡命天涯,又何尝不是认罚?

这一切,都叫我想起那次漫长的海上逃生之旅。那时我天天做着死的打算,夜夜做着死的噩梦,当终于上岸时,年少的我已变得像一个老人一样懂得感天谢地。

我和一群九死一生的同伴一起跪在码头上,一下下地磕头,引来一群海鸥好奇。它们从高空俯冲下来,翅膀扑扑响着盘旋在我们头顶,嘎嘎叫,仿佛我们在抢吃它们的盘中餐而破口大骂——我们的样子确实像鸡在啄食。

报纸上说,生活不是你活过的样子,而是你记住的样子。

毫无疑问,任何人要来住,都得拿出至少几天时间来收拾、清理大量时间残留的大量垃圾废物。说它是废墟也不为过,所有木头都朽烂,所有铁件都锈蚀,砖墙上长满青苔和各种虫卵,屋顶瓦楞间长出小树。这是一个被冷酷的时间无情啄烂的躯体,父亲大概至少几年没来看过它,他保留的也许是十年前的印象。也许,他认为鬼是怕鬼的,我住在鬼屋里可以借鬼杀鬼,保全自己。

他说会告诉我的,但不是在这里。他要求我马上回去收拾那边房间。他怕在这里对我多语,更怕我晚上住在这里。他慌张地睃视着四周,仿佛四周的鬼在偷听偷看我们。他心里已全是鬼。他自己也许并不怕这些鬼,是在替我怕。我告诉他,若真有鬼,我宁愿被自己家里的鬼所害,也不愿被上校屋里的那些野鬼所害。

第二天清早,我去镇上++请++了香火、冥钱,然后直奔后山老虎背上,给爷爷、母亲、二哥、二嫂四座新坟上坟。

胡司令——父亲叫他小胡子——坐在最左边,他已提拔到县革委会宣传部当什么股长,这天主要负责喊口号。他带着革命热情和个人感情工作,口号喊得特别响亮起劲,带表演性,有煽动性,把台下群众的革命热情一再激发出来,上校人没出来,礼堂里已经山呼海啸的杀声阵阵。

那天是大晴天,五月,天已经热了,上校只穿一件衬衫单裤,整个人轻薄得发飘,要不是被公安架着,后来又掀起的喊口号的热浪都可能把他卷走。

女人这次回来,随身带着一张结婚证明书,她要嫁给上校,一辈子照顾他,请求村里给上校出同样一份证明。村里又开会,征求大家意见。哪有反对的道理?都同意。于是便去镇上办手续,拍照片,就是我看到的那张照片。这年冬天,上校母亲刑满释放回家——这也是女人带上校回来的目的,算好时间的,专门等老人家出狱回家。老人家本来身体就差,在监狱里受累吃苦三年,身体差到底,走一步停三秒,吃饭要吐,只能喝粥,怎么看都像一支风中残烛。女人一边照顾一个病得下不了床的老人,一边照顾一个像小孩子一样懵懂无知的大人,比男人辛苦,比任何女人周到。在她的悉心照顾下,两个病人活得体体面面,一点不受罪。

父亲说:“村里人都说,上校妈一辈子拜观音菩萨,真的拜到一个观音菩萨。”

村里人都叫她“小观音”,也把她当观音菩萨待,她也像观音菩萨一样待全村老小。后来我听村里好多人谈起她,都说天底下这样的女人找不出第二个。

一年多后,上校母亲被一口粥呛死,她以嘹亮悲怆的哭声给老人家送终,哭声像鸽子的哨音一样,泣着血,盘在空中,照亮夜空,把村里所有女人的泪腺激活。后来送葬,她一手死死扶着棺材,一路洒着同样泣血奔泪的恸哭,把村里所有男人的泪腺也激活。所有跟我回忆上校母亲出丧那天情景的人,没有一个不带着迷离的神情,噙着泪,一种无法慰藉的悲伤像岁月一样抹不去。父亲说:“上校身边有这样的女人,这屋子的风水笃定是好的。”

这也是父亲所以要安排我到这儿来谈话,包括让我来这儿住的缘故,他认定我们家里有鬼,这儿笃定没鬼。这儿只有观音菩萨,两个女人都是观音菩萨,一老一小。做完婆婆“七七”后,女人把上校屋里的东西分好,能带的带上,不能带的都分给村里需要的人,然后领着上校和两只猫回她老家去了。猫是畜生,不知人间沧桑,只是年迈得走不动了,要用篮子拎着。上校体力还是好的,猫对他的感情也是好的,甚至更好,因为朝夕相处,相濡以沫一样的。父亲说:“两只猫在他手上拎着,像他人一样老实听话,他们就这样走了。”++村里出动几百人,男女老少,成群结队,送他们到富春江边,船埠头。船在汽笛声中离开码头,女人对着送行的村民长跪不起,抹着泪,上校像孩子随母亲一样,跟着跪下来,那情景把几百人都感动哭了。几百人哭的场面能感动所有人和所有时间,父亲在回忆中依然禁不住滚出泪花。父亲说:“从那以后我再没见过他们,我不想把身上晦气传给他们。”我想去看看上校和这天底最好的女人。父亲给我地址,是女人亲手写在一页作业本的纸上的。我看地址居然在上海青浦朱家角镇,是我返程去上海虹桥机场必须要经过的,更加坚定了我要去见他们的决心。++

第十八章

邻近村庄,我知道它为什么叫桑村,村子被大片光秃秃的桑树包围。尚是早春,桑树一个绿芽也没有,但都被修剪过,像一条流水线上下来的产品,全一个样,低矮,整齐,一畦畦,放眼望去,让人想到一列列被剃光了头、整装待发的士兵,在沉默中等待冲锋

这儿是一望无际的平原,人工开凿的河流,笔直,水面波澜不惊,两岸,裸露的土地黑得冒油。走进村子,房子一律青砖黛瓦,伞形屋顶,两层楼,带后院,像马德里的某些社区,统一规划建造的。

司机是本村的,一个毛头小伙子,我给他看女人和上校的婚照——我要送给他们,物归原主——虽然是快二十年前的照片,他居然一眼认出来,然后熟门熟路,直接把我送到他们家门口,并告诉我,这家男人精神有毛病。但同时也夸这家女人是个大好人,对自己有神经病的男人温柔体贴,照顾周到,对村民温良谦让。摩托车停在门口,他未经我许可,径直朝屋里大喊一声:“郎中奶奶,来客人了。”

不论从哪个方面看,这是一个被生活榨干的人,和我在照片上见到的人完全不一样。

出去头几年,尤其是头一年,我信写得勤,几乎月月写,写信是我用回忆抵抗不可遣散的孤独的唯一方式。后来因为老收不到回信,也是因为有了自己的生活,才写得少,越来越少,最后守住一年一封的底线。那些信,头几年的信,都是她读给父亲听的,所以她了解我不少情况。

“这么说,”她依然握着我手,开朗地笑道,“我们是老朋友了,我看了你那么多信。”生活把她榨干了,但她依然保留着乐观、热情,甚至不乏幽默。她手劲也不小,紧紧握着我手,我感觉得到。

一个是老态毕现却沉稳自如,一个是鹤发童颜害羞胆怯,两个人都远远走出了照片,走出了我的想象。

报纸上说,生活是部压榨机,把人榨成了渣子,但人本身是压榨机中的头号零件。

林阿姨告诉我,作为医生她知道,像上校这种在极端刺激下犯的疯病,只要得到及时治疗完全可以痊愈。但她在半年多后才得知情况,带他去求医,已经错过最佳治疗时间,结果就成现在这样,废了。她给我打一个比方:“像你手上挨一刀,哪怕断了筋骨,只要及时找到好的医生治疗完全治得好,留一道疤而已。但错过时间,伤口烂到骨髓里,只有截肢,不截肢最后会把你烂死。你该知道,他父亲就是这么烂死的。”

是的,我知道。我也知道,这是一种伤害性治疗,断臂求生。上校最后进行的就是这种治疗,把他正常的智力像截肢一样截掉,以抑制他的疯病。他现在的智力只有七八岁孩子的水准,而且是受过惊吓的孩子,特别怕见生人、大人。她建议我把他当小孩子看待,跟他亲热,带他玩,他会很快接受我的。我那时已有两个孩子,一儿一女,大的十岁,小的正好七岁。当我把他当我七岁的女儿待时,果然我们相处得很好,我说什么他都爱听,我问什么他都会讲,完全幼稚、天真、透明。我给他讲故事,他坐得老老实实的,跟他下跳棋,他比我儿子还那个来劲。他嘴上喊林阿姨叫老伴,实际上把她当母亲。

报纸上说的,当一个人心怀悲悯时就不会去索取,悲悯是清空欲望的删除键。

我心里都是上校的前世今生,都是悲伤,都是眼泪,都是苦涩。我预计,我出去后一定会找个地方痛哭一场。

这个摊在宽广的平原上的村庄和我的家乡完全不一样,它有一种开放和现代性,道路宽敞,房屋整齐,沿路有路灯、行道树,家家户户门前有花草,楼上有阳台,窗户挂着窗帘;有人手挽手在马路上散步,不时有自行车从我身边骑过,或者迎面而来:他们对我这个外乡人毫无兴趣,没有人对我投一瞥好奇的目光。二十二年后的我回到村里时,对任何人来说都是陌生人,我深有体会,当我在自己村庄的弄堂里行走时,我身上被多少好奇的目光抚摸过。这儿对陌生人毫无兴趣,它已经半城市化,在工厂里打工的大多是外省人。

报纸上说,中国自实行改革开放政策后焕发出了勃勃生机,从城市到乡村,从吃穿住行到思想观念,都发生了翻天覆地的变化。这一点,现在的我最有发言权,即使近些年我几乎一半时间在国内,因为有另一半的衬托、国外的比对,我照样时常生出惊异的目光、欣喜的心情。

多年以后,++年龄和成功赠予我豁达和宽容之心++,让我和命运达成谅解协议,对小瞎子生出同情心;一年又一年,同情心像树的年轮一样长,最后长成善心义举,真心帮助过他。但在一九九一年,我对他只有恨,恨之入骨!

【笔记】
年龄和成功赠予人豁达和宽容之心。
即便回到马德里,我依然把恨留在村里,咒他快死。

印象很深,就在这个夜晚,我在上校的玩具间,在林阿姨给我临时铺的地铺上,上校阵阵如雷的鼾声令我辗转反侧,我在不眠的镜子里清晰地看到自己两个相杀的形象:一个是为上校的可怜悲悲切切,虚弱得无力闭上眼睛;一个是为小瞎子的可恨咬牙切齿,愤怒得可以拔刀杀人。

当月亮升起后,上校的鼾声像怕光似的一下沉落下去,沉得无声无息,随后我听到林阿姨轻微的呼吸声。她的呼吸声凌乱无序,让我想到她脸上的皱纹。++黎明时,东边天空中布满酒渣色的云层,我不知道它在天亮后是白云还是乌云++。

【笔记】
人生无常,谁又知道前面的路上是白云还是乌云?
她说她是在前线医院里学会抽烟的,那时经常有缺胳膊断手的伤兵,他们苦闷,要抽烟,烟瘾大,自己没手,抽不来,都靠她喂他们抽,就这么不知不觉自己也上了瘾,像传染的。

我知道,抽烟可以一定程度地缓解人的焦虑。我也知道,是照顾上校的烦心把她的烟瘾又唤醒了。不是说久病床前无孝子嘛,还有什么比长年累月对付一个七老八十的小孩子更让人焦虑烦心的?她却不这么看,她说照顾上校让她感到无比安心,累是累,但累得有劲,有寄托,心里踏实。

++在乡下,人心像日常生活一样粗糙简单,黑白分明,分辨不了黑白交织出来的复杂图案和色彩。++

++爷爷就是例子,一错百错,一落千丈,死有余孽++。她怕自己成为我爷爷的复制品,甘愿人无端猜测,莫名礼拜。

【笔记】
“人心日常生活一样粗糙简单,黑白分明,分辨不了黑白交织出来的复杂图案和色彩。”
是的。乌合之众之下,最缺乏的品质之一就是一分为二的客观。
++但战争一下把我们家毁汰了,阿爸、姆妈、二哥、姐姐,四个人在同一个时间被鬼子飞机炸死的炸死,淹死的淹死。当时我们一家人在同一艘船上,准备逃难,去南浔,阿爸在那边有朋友。其实待在家里反而没事,你看这房子,不是好好的?++这是命,不能回头说的。阿爸和二哥当场炸死,姆妈和姐是淹死的,她们和我都不会游水,只有大哥会,逃了命。我不知是怎么逃的命,反正等我有意识时已躺在河边,不知是谁把我救上岸的。这是我的命,命运等着我来吃一生世的苦。

我们回到村里,投靠阿爸的大兄弟。大阿叔人是好的,但大阿婶待人刻薄,经常饭桌上拉脸色,甩风凉话。大哥正处在青春期,吃不下冷脸色,一气之下翻了脸。好在住房、蚕房和桑田都在,生活设备也不缺,大哥也能养蚕,我也能照顾自己,可以凑合过日子。家里有盒粉笔,不知从哪儿来的

大哥每天在蚕房的竹柱上画一个叉,每次画时都对我讲:你快懂事,等你懂事了我就去当兵,杀鬼子报仇。画了一年半多,蚕房里的叉叉比蚕蛹还要多,一天早上我发现他房间空了,只留下一封信和一点钱,告诉我他走了,让我照顾好自己。我心里早有准备,并不意外和害怕。

两个月后,我收到大哥从长沙寄来的一封信,告诉我他已经加入薛岳将军的部队,在训练做机枪手。以后三年多我再没有收到他一丝音讯,收到时已是死讯,他已在一年前的长沙保卫战中牺牲,是邻村一个同他一起参军的人带回来的消息。那时我虽然才十二岁,但比二十岁的人都能干,洗衣、烧饭、养蚕、缫丝、纺线,样样能干。我得知大哥牺牲后,也开始在蚕房里画叉,每天画一个。我想大哥用粉笔画,丢了命,我改用刀刻,用剪刀。

我参军只为报仇,报不成仇,一家人白死了,我活着也是白活。当时我十五岁,已觉得活着没意思。这八年,我是靠仇恨养大的,仇恨死了,我活路也断了。

那天夜里,人家唱歌唱哑了喉咙,我痛哭哭瞎了眼睛,两只眼珠子肿得要从眶里脱出来。

报纸上说,++心有雷霆面若静湖,这是生命的厚度,是沧桑堆积起来的。++

我惊诧她在说这种杀人强奸的事时依然声色不动的平静,像在说抽丝剥蚕的平常事。

倒是隔壁上校,鼾声一阵阵的,时而高亢欢快,时而悲切沉吟,像在梦中历尽悲欢离合。

因为是逃走的,自然不敢回村里,怕被追杀。她漂在上海城里,颠沛流离,做过各类苦工,就是不敢去医院找工作,怕仇家顺藤摸瓜找到她。她吃得起苦,但能吃苦的人实在多,满大街都是跟她抢饭碗的人,竞争激烈,生计总出问题,最后还是斗胆去医院做护士。毕竟学过的,也毕竟是有门槛的活,专业的事,抢的人少,总算安耽下来,过了将近两年太平生活。

我见多了死人,家里人都死了,我对死不怕。

卡车连夜出城,往南京方向开,一路经过多座军营,每进一个兵营放下几人,多则五六个,少则三四个。我在第三天下午和其他四人被一起被丢在镇江郊外,金山寺附近,长江边的一座兵营。后来知道,这是一支舰艇部队,兵营不大,但房子一色是青砖或红砖房,看上去结实牢固,和我们一路上进的几座兵营不一样。这里明显好,以前有些兵营破破烂烂的,像野鸡部队。我庆幸自己被分到一座好的兵营,一路上的恐惧受到安慰,捡了便宜似的,对不明不白被抓来当兵的屈辱反而放下了。

我们五人被安排在同一个房间,四张铁床,上下铺。房间里基本生活设备都有,墙上贴着电影海报,桌上有女人专用的小圆镜、粉盒,甚至箱子里还有不少女人内衣内裤什么的,好像这些人刚死去。其实她们是逃走的。街上四处贴着传单,解放军要打过江来,当国民党死路一条,她们逃去寻活路了,我们是来抵死的。我们也想逃,但兵营里加满岗哨,夜里探照灯雪亮,扫来扫去,逃路堵死,大家只有等死。当天晚上我们各人领到一套军装和白大褂,有人说这是我们的寿衣。死归死,累归累,死是以后的事,累是眼前的事,颠簸一路,累得要死,躺下就睡着,跟死一样。

半夜里有人嘭嘭嘭敲门,说有急救手术,要我们出两人去配合。三轮摩托停在门口,引擎响着,看样子是很紧急。我和另一人去,坐上摩托,两分钟就到。手术室在一楼,我们进去,看到地上、手术台上、医生白大褂上全是血,像刚杀完猪。伤员死猪一样躺着,无声无息,奄奄一息。医生背影高大,手里捧着一堆肠子,翻着,动作麻利,在找创口。我们哪见过这场面,同去的人当场啊啊地呕吐,引得医生回头看。他戴口罩、头罩、手套,只露出一双眼睛:即使在雪亮的手术灯下,这眼睛依然放出亮光,像两只通电的电珠。他朝我甩头叫我快去帮他。我赤手空拳上去,他又朝我甩头,示意我戴口罩、头罩、手套。东西都是齐备的,就在旁边推车的托盘里。我全套戴好,配合他挖开肚皮,把更多的肠子拎起来。他很快找到创口,夸我手气好。然后三个多小时,他埋头操作各种手术器具,我负责递接。经常递错,他也不骂人,只说一个字:错。天毛毛亮时,手术终于结束,我替他摘去头罩、口罩、手套,脱下血淋淋的白大褂,看到他脸色苍白,面容僵硬,是一副累极的样 子。他没穿制服,白大褂里面是一件脱壳绒衣,大概跟我一样是临时从床上拉来的。绒衣洗过多次,黄色褪成灰色,看上去土相。他吩咐我一番护理的要求,叼着一根烟走了。我回头收拾手术台时才发现,整套手术器具,剪刀、镊子、切刀、缝针,大大小小,都是金子打的,刚才太紧张,没注意到。

上午九点多钟,他来病房查房,穿一套带上校军衔的制服,刚睡过觉,脸色红润,和我夜里见的人丝毫搭不上。他倒一眼认出我,问我伤员情况,也问我个人一些情况。知道我是被抓来的壮丁,他似乎猜到我想逃走的心思,劝我别胡思乱想,好好待着。他指着昏睡的

伤员告诉我,他就是逃的下场。原来我们熬了一夜辛苦,救的是一个逃兵,没有功劳,只有苦劳。

护士都逃了,当兵的也要逃,我想这部队必定打不了胜仗。

果然,一个月后,解放军打过江来,整座兵营只冒出几声枪响,解放军就顺顺当当接收了我们

一个月里,我和他没见过几次面,因为逃兵都不敢逃了,没伤员,他是不来医院的。据说他天天在家里养着猫,看着报,吃饭有人送,衣服有人洗,是长官的待遇。++有一次我在营区路上碰到他,他露出一口白牙对我爽朗地笑着,叫我一声名字,并问我,你是这名字吧?我说是的,停下来,等着他再问我话。他却没有下文,径直挺个胸脯,大踏步朝前走去。我听着他洒下一路铿铿的脚步声,像听音乐,心里喜悦,忍不住回头看他,希望他也回头看我。这是我长那么大头一次回头看一个男人。那年我十九岁,他三十一岁。他也是我这辈子唯一这么回头看过的男人。他没有回头,我心里空落落的,像他本来在我心里,就这么走掉了,心里就空了。++

++孩子们都一样,白天天不怕地不怕,夜里却常常为一只吞下大象的蚂蚁吓得要死,惊叫,尿床++。

她过去,像我去看女儿一样,观察一下,摸摸他脸蛋,帮他理理被子——应该是这些吧,反正我是这样的;如果醒了,我会哄一哄,一般哄两下又会睡过去——孩子就这样,睡觉是他们的拿手好戏。

第十九章

刚才说到,解放军顺利接管了我们,据说没费一枪一弹,我们听到的几声枪声是有人自杀,不是抵抗。”她一点不糊涂,不要我任何提示,只靠两口烟的过渡,恢复了淡然的神情,继续机械地往下说——

解放军和国民党军队完全不一样,他们到我们医院,迎面见到女护士,靠边站,等着我们过去再走。第一次这么遇到,吓得我不敢往前走,担心他们要调戏我。后来见多了,就觉得他们是好人。他们对俘虏制定的政策也上好,先谈话,劝你留下来,加入解放军,不愿意的给你发回家路费。找我谈话的人知道我是个孤儿,家里没一个亲人,说:那你没选择,留下来吧,解放军就是你的家,我们都是你的亲人。说得我当场流下眼泪,真像回家一样。

我继续留在医院,并受器重,被提拔当了护士长。解放军真把我当亲人待,对我特别照顾关心。不久上海解放,九月份,我被派去华东医护干部学校学习,就是现在解放军第二军医大学的前身。我学的是麻醉师专业,本来要学两年,后来朝鲜战争爆发,中国派出志愿军抗美援朝,学校发出号召,动员我们去前线保家卫国。很多人报名,我为了获得批准,用血书写申请,写血书的也是第一批被批准的。最后一共批准六十个男生、十二个女生,差不多装满一节火车厢,直接开往前线。

这年年三十,我们是在火车上过的,一路上成千上万的人拥在月台上给我们送饺子,一站站送,哪吃得掉?吃不掉没关系,我们装在网兜里,挂在车窗外冰冻

火车开出济南后等于开进冰箱里,一路都是冰天雪地

开过鸭绿江,那个冷,那个雪,我们南方人想不到的,鼻涕流出来就结冰。天是那么冷,但我们心里热火朝天,一路上唱着歌,跳着舞,根本不像去前线打仗,像从前线凯旋归来。

我们六个同学,四男两女,下车时手上都拎着一网兜冰冻饺子,当天晚上医院会餐,吃的就是我们带去的饺子,大家吃得开心死了。

我说我是谁,他听了不相信似的,对我左看右看,最后说,你怎么胖得像一头过年猪了,可以杀了过大年(元宵)。

那两年,尽管每天出生入死,不死也累得要死,但因为和他在一起,成了我这辈子最开心的时光,我心里越有苦就越是会梦见它。

同样的白炽灯泡,滤掉了苍凉的红光,变更亮了,因为多数工厂停业了,电力足了。她同样的脸,显更大了,因为疲倦爬上去了。疲倦加深了皱纹,下沉了眼袋,拉长了下巴,脸就变大了,更老相了。但她的精神还是好,越发好,记忆清晰,思路活灵,讲得很流畅,或许是美好的回忆在起作用吧。

她把最宝贵的青春和初恋落在朝鲜长津湖边的血土上,这片土地形同她故乡,会魂牵梦绕的,她没收不了,也归还不了。

++初恋的感觉是甜蜜的秘密,是紧张的等待、偷窥,是手不经意中相碰触电的感觉,是炮声轰轰中的害怕和祷告,是午后的阳光在风中行走,是微风吹来了稻花香,是彻夜不眠的累人旅程,是各种复杂幽秘、别出心裁的明测暗探。总之是细腻琐碎的,孤僻,怪异,情乱神迷,神神叨叨++。

【笔记】
看这写法与林徽因的人间四月天挺像。
整个晚上,我第一次出现听力疲劳的感觉,忍不住打断她:“总之你爱上他了。”“是的,”她脱口而出,“我这辈子只对他这么爱过,爱得小心翼翼又天昏地暗。”她又列数种种心花怒放又揪心断肠的细节、事迹,痴迷于逝去的青春和灼伤泪眼的甜滋滋的苦涩中,流连忘返。这是她毕生的辉煌,一生盘根错节的痛的根子,彩虹一样的、惊人的美丽,也是惊鸿一瞥的残酷。

她心里在燃烧,一颗孤寂的心在一往情深。没有人会忘掉自己的宝贝藏在哪里,也没有人会忘掉刺穿自己心的箭。我不忍心再打断她,就让她说个够吧,这不是修养,而是仁慈。

++我不知道具体是从什么时候开始爱上他的,就像我不知道他身上有哪一点是不值得我爱的。我爱他的笑声;我爱他的背影;我爱他抽烟的样子,爱他丢下的烟蒂;我爱他在手术失败后骂娘的愤怒,当然更爱他手术成功后的灿然笑容;我爱他遛猫逗猫的样子,那一定是他最得意开心的时候;我爱他义无反顾奔赴前沿阵地去出诊的英勇,爱他风尘仆仆回来的喜悦和痛苦。我们医院总共有七个外科医生,他去前沿阵地出诊的次数比其他六人加起来还要加倍的多。++

为什么要出诊?因为有些伤员伤势太重,下不了阵地,下来必死在途中。他闻讯后总是对其他医生说,别抢,我去,我要让我的金子(手术器具)多发光。那可能就是去送死,前线的枪炮是不认人的,敌机在空中专门找这种孤单的吉普车,认为里面一定是送情报的人或大首长。

好几次,我随他去前沿出诊,路上遇到敌机扫射,有一次一梭子弹正好钻进我和他肩并肩的夹缝里。我吓得哇哇哭,他笑道,谁说子弹不长眼?子弹知道我们要去救人,打死我俩等于要打死一堆伤兵,它下不了手。

有一次车子被地雷炸翻,滚入山沟里,司机当场牺牲,我下体出血,一只肩膀脱臼,痛得昏过去,他毫发不损。他常说,救人一命胜造七级浮屠,他造的浮屠已上千级,已经在天上,死神够不着他了。

真的,他那么拼命,几十次去前沿阵地救人,身边的人一个个死伤,他最严重的一次只是断过一个脚指头,其他都是擦伤皮肉,跟穿着铁布衫似的。也许这就是福报吧,但他现在这样子哪有福气?我说:“你就是他的福气。”她说:“听下去你就知道我是他什么了。”

他说,可我可能永远不需要妻子。他放开我,指着一旁牺牲的司机说,你看他,需要妻子吗?如果他有妻子,该有多痛苦,一辈子都要痛苦。

我还想说什么,他对我摆手,告诫我别说了。他说,在死者面前说这些是不合适的,对自己也不吉利,我希望你活着回国。至于我嘛,他一边给死者拭去脸上的血污,一边对着天空说,老天知道,我已死过多次,死了也无所谓,多活一天都是赚的。

以后,他再不带我出诊,我把这理解为是对我的爱,是在保护我。尤其是,他每次出去都把他心爱的猫托付给我,就更以为他心里有我。每次,我还他猫时都会塞给他一封信,写的都是我情真意切的感受,浓浓的爱和深深的怕,怕他不回来,怕他受了伤回来。有时我又希望他受了伤回来,当然不是重伤,只是轻伤,这样他可以养伤,我可以照顾他养好伤。他从来不给我回信,一声回音都没有。只有一次,他接过信时突然对我说:你对一个你完全不了解的人谈情说爱,是对自己的不负责任。我用他曾经说过的话说:在前线是最能了解一个人的。

我说我已经在最能了解人的前线和你相处一年多,我很了解你。他说,你的眼睛看不到我的过去。我说我要的是你的以后,不是以前。

我就是这样对他猛冲猛打,什么都不顾忌,狂热,什么姑娘家的矜持、面子、尊严,都放下,只要他一个字:爱!我亲人都死光了,太孤独了,太需要一个人来爱我,而天下哪里去找他这样的好人?英俊、能干、英勇、幽默,只要他答应爱我,我为他死的心都有,不答应我也想死。这种心情你可能很难理解的。

我想我是能理解的,那个孤独,那个渴望,我尝过,就在我出国头几年,那种举目无亲的感觉,那种什么都放得下的悲凉和狠心,像汗毛一样附在我身上,我像熟识老家的弄堂一样熟识。

他知道我住在那里,不顾死活来救我,披一床用水浸过的毛毯冲进大火,大声叫着:小上海!小上海!找我。

自我们在朝鲜见面后他一直这么叫我。他找到我时火已经在烧我辫子梢头,咝咝的声音,像蛇在喷气。

他把我扑在身下,先把我辫子上的火头灭了,然后灭四周的火,最后用湿毛毯裹着把房梁抬起,把我从死神手里夺回来。当时敌机还在轰炸,大家都还在东躲西藏,营救工作其实并没有开始,他完全是冒死来救我的。所以,我后来的命实际是他冒死救来的。

怕天亮后敌机再来轰炸,部队连夜撤离村庄,往山区转移。中途要经过一条溪,我受了伤,小腿撕开一道嘴巴大的口子,刚作包扎,不便下水。他背我过河,刚趴在他背上我便开始哭。五月正是雨季,溪里水满满的,深过膝盖,我哭着,他背着更累,上岸便一屁股坐在地上,大口喘着气。我仍然哭着,哭得稀里哗啦的。人累时容易生气,他突然训我:你哭什么!但马上又安慰我,哭吧,哭吧,死了那么多人,该哭,一边来拉我的手。我紧紧抓住他手,一头扎在他怀里,哭个够。黎明前的黑暗,伸手不见五指,我有种强烈的冲动,希望他吻我。

我说,如果我刚才死了,我在这世上什么也没留下。他说,今天晚上牺牲的人一半都这样,战争就是这么残酷。

我本来是希望他对我说,我至少给他留下了那么多信,我留下了对他的爱。但他没那么说,我只有直接讨。我昂起头,对他说:你给我留个吻吧,这样我死了至少留下了爱,和我给你的那些信是配的。他迟疑一下,低头吻了我。是那种吻,只有仪式,没有欲望。

我想我还是要活下去,我活着,至少可以每年回去给我死光的亲人上个坟。我不为活人活,只为死人活。

我就这么活下来了。

我不死,他在我心里也死不了。一天下午,我把他拦在路上,是要决一死战的意思。我直接问他,你到底要不要娶我?他看我这么决绝的样子,少见地端出一副诚恳的老实相对我说:我的小上海同志,你不了解我,我娶不了你,我这辈子注定是个光棍命。我说,你不娶我我就去死。他有些生气,说我这是在威胁他。他说,我千错万错,至少还救过你命,何必这样?接着又说,我是为你好,你这么年轻干吗要吊死在我这棵死树上?

心死了,人反而不会死了,只是活得像一台机器

中间有一天他把我给他的十几封信还了我,那天,我把那日记本和这些信统统烧掉 ,完了去澡堂好好洗了个澡。我要把自己洗得干干净净,开始迎接新的生活。

他隐隐讳讳地向我透露,他听到一个风声,说我老头子在外面讲,我把身子给了他,可他怀疑我也给过别人,所以跟我绝了交。

“你信吗?世上没有不透风的墙,没有风飘不到的角落。”她端着一双青黑的眼圈——像长期戴眼镜留下的阴影——问我。不等我回答,她又替我回答:“反正我是信的,否则很难理解当初我在无锡军营里的风怎么会吹到上海,后来你老家的风又怎么会吹到这个村庄里。总之是吹来了,并且吹到我耳朵里,说得有名有姓,有经过,有结果,有人甚至连他肚皮上的字都一个不落地告诉了我。”

说到这里,她又低眉轻声地问我:“你知道他肚皮上的字吗?”看我摇头——我选择了摇头——她露出惊异的目光,“怪了,你们傍晚在一起这么长时间他都没给你看?”我说:“他想给我看,但我没看。”

她说:“这就对了。曾经他把那地方当罪恶和耻辱,宁愿杀人放火也不要人看到,要瞒住,现在他把它当宝贝,见到陌生人就要给人看,现宝一样的,我想拦都拦不住,拦他就要哭,你说这人已经变成什么样了。”

冥纸

但首先是他害了我,那个王八蛋。”“谁?”我抬头问,发现她正昂起头,冲着我。“他,那个向我报信的家伙!”她咬牙切齿地说,“那个在澡堂门前碰到我的主任,内科主任。”说着声音又低下去,仿佛怕隔壁老头子听见似的。她看我一眼,接着说:“其实事发半年后,当时我还在部队,这家伙当上副院长后我就怀疑自己被他当枪使了。医院缺个副院长,他和我老头子都是候选人,他资历比我老头子深,可我老头子是英模,当时又在南京干训班学习,他怕被抢去,便耍了这个阴招。”我问:“他怎么知道你们的事?”她说:“这也是那些年我一直在想的。我想不外乎两个原因,一个是老头子确实在外头说过这事,他性格豪爽又爱喝酒,有时失言也不是不可能;另一个是他看见老头子夜里去找过我。”

我说:“以他能把身上的秘密藏一辈子这点看,酒后失言的可能性不大。”她说:“是的,可以前我哪知道这些?何况……”说着停下来,摇着头,似乎是不想说了,又似乎为了隆重推出下面的话,“我希望是我老头子酒后失言,这样我心里要好受些。以前我就是老这么自己骗自己,想不到……”突然刷地挂下两行泪,啜泣说:“我老头子从来没有去找过我。”

我一时没听懂什么意思。她一把拭掉泪,看我一眼说:“那个人根本不是他,我完全冤枉了他!”

第二十章

++报纸上说的,世上只有一种英雄主义,就是在认清了生活真相后依然热爱生活++

当牛作马的生活让我对生活只有恨,没有爱——爱被我恨死了,葬在大海里。

++一个十七岁的乡下傻小子,付得出死的勇气,却拿不出活的底气——当时我连“人生海海”也不知什么意思。她扑哧一下笑了,告诉我这是一句闽南话,是形容人生复杂多变但又不止这意思,它的意思像大海一样宽广,但总的说是教人好好活而不是去死的意思。++

++她说:“如果因为生活苦而去死,轮不到你,我排在你十万八千里前。”++

后来我知道,她家里很惨的,父亲被红卫兵打死,她哥哥去报仇,打死一个红卫兵,自己也被红卫兵打死。红卫兵分两派,一派杀上门,要斩草除根;一派暗中报信,想帮她和母亲逃走。她连夜逃走,母亲死守丈夫和儿子的尸体不肯走,宁死不走,结果受尽折磨,以死求了解脱。她逃回福建老家,东躲西藏,最后走投无路,只好用年轻的身子抵出头费,逃了出来。

++她说:“你不能死,你死了连给我上坟的人都没有,我的亲人都死了。”++

++记住,人生海海,敢死不叫勇气,活着才需要勇气,如果你死了,我在阴间是不会嫁给你的。记得当初你向我求婚时是怎么说的?世上只有一种英雄主义,就是在认清了生活真相后依然热爱生活。++

【笔记】
记住,“生海海,敢死不叫勇气,活着才需要勇气”!
“只有一种英雄主义,就是在认清了生活真相后依然热爱生活”!
她把“你”又改掉,改回原样,然后告诉我,这是一个著名作家说的,叫罗曼·罗兰,她看过他两本书,抄下了他一本子话,其中就有这句话。她说:“你要替我记住这句话,我要不遇到它,你也一定遇不到我,死几回都不够。”

++其实那张报纸上根本没那句话,是她要送我这句话,用报纸的名义说,可以增加它的权威性,反正我也不懂西语。真的,我前妻真的是个好人,就是命苦,像上校。++

听了这情况,父亲眼睛倏地发亮,没有悲伤,只有侥幸的欣然,对我说:“难怪你能活着回来,是她替你死了。”我想说,是我替她在活,但话到嘴边被我咬住,不想说。++父亲的冷漠和自私让我觉得对不起前妻,而我宁可对不起自己也不愿对不起她,她是藏在我心中最深的痛,也是爱,我不许父亲在她面前失礼,给我丢脸++

++多年后,我挣了钱,我把前妻的遗骸带回国,想和我爷爷、母亲他们几位亲人安葬在一起,也是将来和我葬在一起的想法。故土是热的,她孤零零一个人待在国外,太凄冷了,让我心疼。++

我们把铺子从巴塞罗那迁到马德里,已花光所有积蓄,到马德里又没挣到钱,一直做着青黄不接的生意,过着青黄不接的生活。生意是靠妻子撑着的,她去世后,我一个人开不了铺子,租不起房子,只好都退掉,过流浪汉的生活,露宿街头,靠垃圾堆里的过期食物填饱肚子。经常跟垃圾堆打交道,后来我也从垃圾堆里发现挣钱的门道。国外的垃圾堆尤其是富人区的垃圾桶里,经常有一些在穷人眼里值钱的东西,春夏秋冬的衣帽鞋袜,厨房里的锅碗餐具,甚至连收音机、唱片、唱机都有。有富人区必有穷人区,而且穷人总比富人多。

报纸上说,穷人区是大海湾,漫无边际,富人区是小湖泊,一小时可以绕一圈。

++不论春花秋月,白天黑夜,我都随身带着妻子的骨灰,她比任何一个活人都安慰我,给我活下去的力量++

++用她自己的话说:上帝把她的左腿借去了一寸,却赖皮不还她++

大多数人家都在溪对岸,前山脚下,造起新房,老村子成了一个旅游景点,每个周末都开来旅游大巴,带千里万里远的客人来观光,吃土菜,喝米酒;春天看竹笋尖尖破土而出;夏天进山打野猪——有人专门养的野猪;秋天摘野山柿野山枣——对不起,要称斤付钱的;只有冬天村子是安静的,还给本村人。

人生如戏,每一出戏都明里暗地连好的,如果我没有三年流浪汉的垃圾生活,就不可能有后来的垃圾生意;曾经垃圾让我丢尽脸面,如今垃圾加倍地偿还我尊严

他说:“垃圾是个宝啊,你有本事能把你那边垃圾搞到国内,保你发洋财。”他告诉我现在这里所有厂都需要垃圾,他的工作就是把垃圾进行分类,废纸归废纸,金属归金属,塑料归塑料,能当旧货卖的归一类。旧货翻新后可以当商品直接卖,金属卖给冶炼厂,塑料卖给化工厂,废纸留下来打成纸浆,造好纸。总之,都能卖,都是钱,垃圾里藏的是人们美好广阔的前途。

父亲怕我们去老屋,自己却坚守老屋,目的是要把鬼留在自己身边,别去找我们,是甘为我们当替死鬼的意思。他认为这些年我生意能做得这么好,风调雨顺,家里平平安安,靠的是他每天跟鬼死缠烂打,不让鬼出门来找我们。

有一次他跟我悄悄说,我们家里有四个鬼,每一个鬼的长相他都能描述出来,有一个长三只眼,有一个头上长角,有一个长一身白毛白发,有一个有头没脸,只有一头披肩拖地的长发。事隔没几天,他又跟我说,我们家里有三个鬼,全是男鬼,他又要对我描述每一个鬼的长相,被我打断。我说:“上次你说是四个。”他说:“被我搞死了一个。”

爱人是一种像体力一样的能力,有些人天生在这方面肌肉萎缩

回国一般都会顺便回家看看,回去免不了要看到小瞎子,他是村里最早一个“游客”,整天无所事事,东游西逛,逛累了就在祠堂门口待着,看人来人往,看人眼色,等人逗他、怜他。逗他的人不会怜他,怜他的人不会逗他。但对他来说,逗他其实也是怜他,因为太无聊了,无聊到被人奚落、看洋相也是他的乐处

小瞎子能活下来,不冻死,不饿死,全靠他父亲壮烈的死。老瞎子算了一辈子命,真正算清楚的只有自己儿子的命,他知道自己死后儿子废物一个,活不成,要活下去,必须靠村里有人发大慈悲,小慈悲都不行。小慈悲是同情心,是眼里冒出来的,触景生情,有一搭没一搭的,不成流。大慈悲是责任心,是心底长出来的,因缘而生,细水长流。他要给全村人埋下一个缘故,心里种下一份责任,去世前在祠堂门口长跪不起,胸前挂一块牌子,写一段话,见人就说:“全村的父老乡亲,我该死,对上校作了恶,罪该万死。我死了就去天上给你们看门守家,只拜托你们看好我儿子,让他活个天寿。他死了照样去天上给你们看门,守你们家家老少平平安安,发财发福,好运不断。”跪了三天三夜,说了百遍千回。跪得祠堂门口的石狮子的心都嘣嘣跳了,慈悲了,说得荫堂牌位上的列祖列宗都听见了,发话了。

村里一拨拨的人:老人、妇女、村干部、老师,凡是有头脸的人、有知识的人,都去对他应允、许诺,想拉他起身。可就是拉不起,谁都拉不起。他是决心要跪到死为止的,死的姿势都是跪着的,拜着的,磕着头,就这样壮烈地以死相求,以命相托。

正是靠着这个“缘故”的造化,小瞎子才得以杀破各路死神的层层包围,熬过一个个漫长的冬天和黑夜,没有死。死是没有死,但终归是活得苦难,命悬一线,熬着,煎着,挣扎着,随时可能断线、脱底。我后来每次回家,看他越发生不如死的样子,总担心他熬不下去,熬到头了,等不了我下次回来。但他的生命力十分顽强,也许生不如死的生是最富有生命力的,也许老瞎子在保佑他,也许死神也不想接收这种人不人鬼不鬼的活鬼。

【笔记】
本书中有好多出这种场景,令人动容。
++这是我的胜利,我饶过了他,也饶过了自己。++我战胜了几十年没战胜的自己,仿佛经历了一场激烈的鏖战,敌人都死光了,一个不剩,我感到既光荣又孤独,孤独是我的花园。

++人比人气死人,我不跟人比,只跟自己比。报纸上说,幸福是养自己心的,不是养人家眼的。++

三年前他得过一次中风,右手废了,左手认为自己离死期不远,除了学会用瓢羹吃饭外,懒得去学右手的其他手艺,包括洗脸。他的眼睛基本上也昏得什么都看不见,大概只能看见死亡。++他在心甘情愿地等死,但死亡像悬在猪圈椽子上的一张破蜘蛛网,看上去摇摇欲坠,似乎马上要掉落,却总不掉落,甚至挂得越来越牢++。

“让你别来这里,又来了。”每次去看他,父亲总以这句话开头。有一次我曾说:“因为你没死。”他说:“你就当我死了就好了。”++和晚年的父亲相处,让我得出一个结论:世上最无情的是老人,其次是有钱人。老人因为怕死或不怕死而变得无情++,有钱人因为可以用钱买到无情而变得无情。

一年后父亲临终前还在惦记这事,问我:“你打听到西安那个大师了吗?”看我摇头——其实已看不到,只是感觉到我在摇头——他又重复了那句话:“人没双手,就不像个人。”意思很明确,希望我去落实这件事。父亲给我的遗言只有两个,一个就是它:还小瞎子一双手;另一个是把老宅卖掉,卖不掉就拆掉,因为这是个鬼屋,让它见鬼去吧。两个交代根子上是一脉相承的,都是怕鬼来缠我,包括小瞎子以后将变成的鬼。

报纸上说,++多数人说了一辈子话,只有临终遗言才有人听;如果临终遗言都没人听,这人差不多就白活了。++

蚕房里有两排像脚手架一样高的木架子,架着几十个篾编的方匾,每个匾里都躺满淡绿色的蚕宝宝。它们真的是宝宝,娇气得很,冷热不行,要常温——最好是摄氏二十四度,每隔三小时进食一次,夜里也慢怠不得,一夜不进食,第二天只能当鸡食。

进食的桑叶必须鲜嫩,洗干净,当日吃,吃了过夜或不干净的桑叶,蚕宝宝就过不了夜了。因为娇气,养蚕的人必须花足力气,每天日出之前和日落之后两次去采桑叶,夜里至少两次起夜添食,总之要起早摸黑,熬更守夜。一般养这么两架蚕至少得双人,但上校一人比两个人还顶用,还养得好。

阿姨告诉我说:“村里有一半人家养蚕,公认养得最好的是老头子,他养的蚕个大,病少,出匾率高,出丝率也高,卖的价钱也高。”

我问:“有什么窍门吗?”她说:“认真,他像孩子一样认真听话,我教他什么他做什么,决不打折扣。

或许,和正常人相比,上校最大的特点——也是弱点——是不会打折扣,不会偷懒,不会像大人一样算计,甚至也不会疲倦。我曾多次到现场看他干活,那个恪尽职守,那个专注潜心,只有机器才能跟他比。比如采桑叶,人家一把把抓,他一片片摘,老的不要,虫啃过的不要;清洗也是,一片片洗,摸着洗;喂食严格听闹钟的,闹钟一响,拔脚就走;天气热了,他给蚕宝宝扇扇子,一匾匾换着扇;冷了,用报纸糊住四面漏风的竹排缝,用干稻草铺满架子添暖。他可以一个小时一动不动地守着蚕宝宝,也会为几只蚕宝宝的死大把大把地流泪,涕泪滂沱。阿姨告诉我,她曾教过他多种作业:种菜、烧饭、养鸡鸭等,包括养猫,都学不会,唯独养蚕,一教就会,一做就喜欢,一头扎进去,一年比一年得心应手,好像命中注定要来这个以养蚕为业的桑村跟她会合,当养蚕高手;也好像,命中注定他要一辈子在各方面施展才华,哪怕被命运打趴在地,依然要绝地反击,在蚕宝宝面前露一手,正常的大人都不是他对手,像

有一次,我看他一下午都在给蚕宝宝扇风,扇得挥汗如雨的,看得我特别伤感,忍不住去抱住他哭了。他对我嘘一声,说:“别吵,蚕宝宝在睡觉呢。”

报纸上说,++生活是如此令人绝望,但人们兴高采烈地活着。++这说的是晚年的上校吗?我视晚年的上校如父,所以一直坚持去看望他们,尽量奉献一个晚辈的孝心和责任。

说起三十几年前的神医大师,阿姨根本不记得他地址,只记得确有这么个神医。“可神医也续不了自己寿命,”她说,“我记得那时他都已是七老八十,现在该早作古了吧。”

其实,即使人活着,地址记着,该也是寻不着人的,中国现在已没有几个老地址可供人寻的。再说即使人活着,我寻着他,甚至寻着比他更牛的大师神医,我想也还不了小瞎子一双手,多少年前的陈伤旧病,回天比补天还难。常识总比真理知道得多,常识告诉我这是一个荒唐的愿望。

阿姨是医生,比我更确定这件事的荒唐性。“谁要说他能帮你如这个愿,他就不是什么大师,而是大仙、大骗子。”阿姨说,“你父亲老糊涂了,他说这话说明他的智力已经跟我老头子差不多了。”

这回,我告别时上校正在吃午饭,他的饭量比我还大。++阿姨送我到门口,对我苦笑道:“你看他这胃口,我真担心自己活不过他,先走了。”这话像游荡在这屋里的幽灵,每次来我都会冷不丁撞到。每次撞到,我都会看到她被乌云笼罩的脸和被恐惧刺伤的心,有时脸上挂着两行泪,努力地向下蜿蜒——有时我觉得这是两滴血,有时我觉得这就是他们两个人,两个人的生活,活得吃力、孤独、凄苦,凄苦得只有用眼泪来洗掉眼泪,用孤独来驱散孤独。++

++乡亲面前自大不得的,即使你升到月亮上,你的祖宗还在他们脚下。++

但他现在的精神世界是不会空虚的,因为有一堆人围着他,顶着他。他把自己扮成一位出身算命世家、精通阴文的算命先生,跟这人聊生死,跟那人谈得舍,说得头头是道,忙得不亦乐乎。他几乎无时不刻不在网上出没,像雇着几个替身,什么时间都在线上,什么问题都能对答如流。生活摧残了他,让他过着活鬼一样的生活,也让他穿越了生死恐惧和世态炎凉,变得大彻大悟,笑傲江湖。

他在网上人气很高,人缘很好,众星捧月的。他找到了自己的江湖,在虚拟的世界里生龙活虎,活蹦乱跳。

正如报纸上说的:++网络让无数的人在希望中死去,在绝望中诞生。++

老头子,我替你成全了,你就安心走吧,下辈子你就放放心心娶我。”

++死人不怕冷,只怕脏。++

她一遍遍默默又细致地用双手熨着白布,其实是在抚摸上校遗体,是一副舍不得。我注意到她泪水滴下来,滴在白布上,一滴一个印。

++隔壁始终没有动静,阿姨一定是累倒了,睡着了。我想让她多睡一会儿,一直等到八点钟才过去看他们。阿姨确实睡在床上,但样子有些异常,换过衣服:是一套崭新的黑色西服,和上校穿的寿衣一模一样;床头柜上,端端正正放着一页信笺,上面压着一对黄金婚戒;床头柜前,立着原先置于墙角的移动输液架,架上吊着一只最小的药瓶。药瓶滴出的一般总是治病救人的药水,但这回却是夺人命的。一切都是蓄谋已久的,作为一个前麻醉师,阿姨以最专业的方式结束了自己,追随爱人而去。她不能选择和上校同时生,却可以选择同时死。她选择和上校同时死,是为了来生与他同时生吗?++

报纸上说,++没有完美的人生,不完美才是人生++。我哭着,想着,不知道我的哭声能传到多远,能唤来多少阴阳两界的灵和人为他们送行?

【笔记】
全书读下来,读到了许多优美的描写,读到一系列令人动容的故事、道理,也读到许多日常不容易见到的生活侧面,同时读到很多自己童年见闻的投影。
2019-12-23 4 years ago / 未收藏/ xiaozhou发送到 kindle

前言

自从 2013 年入手 HHKB Pro2,以及后续入手 HHKB Pro Type-S以来,HHKB 已经成为我日常工作的主力键盘。尽管后续官方还推出了 HHKB 的蓝牙版本,考虑再三,我也没有继续再入手了。原因是官方的蓝牙版 HHKB 有一个凸起的电池仓,由两节电池进行供电。相比起有线版本,凸起的电池仓有点影响美观。

要是 HHKB 的蓝牙版能通过内置锂电池的方式对键盘进行供电,并去掉那个电池仓的话,那岂不是更好?
事实上,虽然官方并没有推出这样的版本,但是民间已经有很多 DIY 的方案,对有线 HHKB 进行改造了。之前看到过网上的一些方案,感觉并不是很成熟,直到最近一个叫YDKB的改造方案,引起了我的兴趣。
YDKB 是由国人 Yang 开发键盘的主控芯片,其中针对 HHKB 进行蓝牙改造的主控模块,叫做HHKB BLE Mod。通过替换掉 HHKB 官方原厂的主控芯片,你的 HHKB 立马就能从有线变为蓝牙/有线的双模版本,意思是既可以保留之前有线的功能,通过 USB 连接电脑使用,也可以通过蓝牙无线的方式连接电脑使用,所谓一举两得的解决方案。
其实从 10 月下旬,我就购买了 YDKB 的改装套件,包括:一个主控芯片,卖家代焊接 3 个 LED 灯,并买了一块内置锂电池。由于我现在在新加坡,而淘宝并不支持跨国邮寄锂电池,只好先下单将套件邮寄到国内。11 月底刚好回国一趟,然后人肉将 YDKB 的改装套件带了过来……

动手改造

YDKB 的这款HHKB BLE Mod是一个集成度较高的主控模块,所以在安装上没有太大难度。我没有电烙铁,也想偷懒,于是让卖家帮焊上了 LED 灯,有兴趣的同学可以自己 DIY 一下 LED 灯。
HHKB 的背面只有三颗螺丝,拆开后,可以看到内部结构其实也挺简单。

原厂的主控芯片其实没多大体积,我们的目标,就是要拆下那块原厂主控,安装上 YDKB 的主控。

拧下固定主控芯片的螺丝,拔掉数据排线,就可以取下原厂主控了。

接着是安装 YDKB 的主控,用螺丝固定,插上数据排线。电池正刚好可以放在左边空余的位置,注意将电池的线接上主控。卖家还附送了胶条,用来把电池粘在键盘外壳的内壁上。为了不让电池接触到键盘,垫了一层塑料纸。
接下来的过程就简单了,对齐 LED 灯,合上盖子,拧好螺丝。然后拨开那个主控芯片上的开关,不出意外的话,键盘开始工作,并进入蓝牙匹配状态。

在 Mac Book 的蓝牙设置中,找到HHKB BLE并匹配连接,大功告成!
由于是第一次使用,锂电池的电量不一定是满电状态,所以可以接上 USB 给锂电池充电。


从键盘的背后来看,除了能看到 LED 指示灯,其他的样子和改造前无异。另外,开关被隐藏在了之前调整 DIP 开关的盖子里面。这个开关就是整个主控电源的总开关了。之前的 DIP 开关在改造后已经被去掉,因为 YDKB 可以通过刷固件的形式,支持更多的自定义功能,之前官方 DIP 调节键位设置的功能也略显鸡肋。

键位定制

这款 YDKB 主控强大的地方不仅在于支持有线和蓝牙双模,更方便的地方还在于支持更丰富功能的自定义按键和更方便的固件烧写方式,这也是前面提到的完胜官方DIP开关的功能。
YDKB 默认支持 8 层的自定义按键设置,甚至还支持 LED 灯的设置。访问官网 http://ydkb.io,可以对你的 HHKB 每一层按键进行配置。其实对于我来说,两层已经够我用了。

值得一提的亮点还是固件烧写的方式,真的非常方便,通过同时按住左右Shift键和b键,键盘会重启,此时立马再按住Esc键不放,键盘就会进入 U 盘刷机模式。在 Mac 的笔记本下,你会发现文件浏览器里面多出了一个可以移动设备。

从 YDKB 的网站上设置好你的自定义键位,下载得到一个 HHKB_BLE.BIN 文件,替换掉移动设备里面那个文件,然后退出移动设备,这样键盘的固件就算是刷写成功了,不得不说,实在是非常的方便。

蓝牙与续航体验

HHKB 改造成蓝牙无线后,日常工作用来码字和写代码,没有感觉到因为无线而带来的明显延迟。由于没有了连线,键盘的摆放位置变得更加随意起来,可以放在MacBook上,或者桌子上用,甚至是腿上用。众所周知,新款的MacBook去掉了USB接口,之前为了接键盘,还得适配器来转接。现在有了蓝牙,更是方便多了,我甚至更愿意特地把键盘放包里背到任何地方拿出来使用。
此外,内置的锂电池也是我喜欢的一点,没有官方突出的电池仓,也不用换电池,相对来说更加的经济和环保。内置的锂电池是 2500mAh 的型号,经过接近一个月的测试,充满一次电量大概需要几小时,满电估计能续航一个多月左右,续航表现也是令人满意的。
YDKB 还为电池续航提供了一些省电的设计。平日离开的时候,可以通过快捷键让键盘进入低功耗省电模式,重新激活键盘,同时按住F键和J键保持三秒左右,即可重新激活键盘,非常方便。
唯一感觉不足的地方,是电池充满电之后,一直显示电量为 90,而不是 100,这一点也在群里跟作者确认过了,90 即表示满电状态,并不是 100,也许这是对于强迫症的我来说,唯一的一点小缺憾吧,但是完全不影响使用。

小结

不得不说 YDKB 是一款比较完善的 HHKB 蓝牙改造方案,通过改造,让你的 HHKB 键盘同时支持有线和蓝牙的连接方式,实在是让 HHKB 得到了质的飞跃,有兴趣将 HHKB 改造为无线蓝牙的同学可以考虑入手。

相关文章推荐

《码农神器 HHKB Pro2 入手与开箱》
《终极退烧利器 HHKB Pro Type-S 入手与开箱》

<a href="https://linux.cn/" target=_blank>Linux中国</a> / 2019-12-23 4 years ago / 未收藏/ linuxChina发送到 kindle
这有一些建议帮助你的全球化开发团队能够更好地理解你们的讨论并能参与其中。


几周前,我见证了两位同事之间一次有趣的互动,他们分别是 Jason,我们的一位美国员工,和 Raj,一位来自印度的访问工作人员。
Raj 在印度时,他一般会通过电话参加美国中部时间上午 9 点的每日立会,现在他到美国工作了,就可以和组员们坐在同一间会议室里开会了。Jason 拦下了 Raj,说:“Raj 你要去哪?你不是一直和我们开电话会议吗?你突然出现在会议室里我还不太适应。” Raj 听了说,“是这样吗?没问题。”就回到自己工位前准备和以前一样参加电话会议了。
我去找 Raj,问他为什么不去参加每日立会,Raj 说 Jason 让自己给组员们打电话参会,而与此同时,Jason 也在会议室等着 Raj 来参加立会。
到底是哪里出的问题?Jason 明显只是调侃 Raj 终于能来一起开会了,为什么 Raj 没能听懂呢?
Jason 明显是在开玩笑,但 Raj 把它当真了。这就是在两人互相不了解对方文化语境时发生的一个典型误会。
我经常会遇到有人在电子邮件的末尾写“请复原”,最开始我很迷惑,“这有什么需要我复原的内容?”后来我才搞懂,“请复原”其实是“请回复”的意思。
在 Ricardo Fernandez 的TED 演讲“如何管理跨文化团队” 中,他提到了自己与一位南非同事发生的小故事。那位同事用一句“我一会给你打电话。”结束了两人的 IM 会话,Ricardo 回到办公室后就开始等这位同事的电话,十五分钟后他忍不住主动给这位同事打了电话,问他:“你不是说要给我打电话吗?”,这位同事答到:“是啊,我是说以后有机会给你打电话。”这时 Ricardo 才理解那位同事说的“一会”是“以后”的意思。
现在是全球化时代,我们的同事很可能不跟我们面对面接触,甚至不在同一座城市,来自不同的国家。越来越多的技术公司拥有全球化的工作场所,和来自世界各地的员工,他们有着不同的背景和经历。这种多样性使得技术公司能够在这个快速发展的科技大环境下拥有更强的竞争力。
但是这种地域的多样性也会给团队带来挑战。管理和维持高性能的团队发展对于同地协作的团队来说就有着很大难度,对于有着多样背景成员的全球化团队来说,无疑更加困难。成员之间的交流会发生延迟,误解时有发生,成员之间甚至会互相怀疑,这些都会影响着公司的成功。
到底是什么因素让全球化交流间发生误解呢?我们可以参照 Erin Meyer 的书《文化地图》,她在书中将全球文化分为八个类型,其中美国文化被分为低语境文化,与之相对的,日本为高语境文化。
看到这里你可能会问,高、低语境文化到底是什么意思?美国人从小就教育孩子们简洁表达,“直言不讳”是他们的表达准则;另一边,日本人从小学习在高效处理社交线索的同时进行交流,“察言观色”是他们的交流习惯。
大部分亚洲国家的文化都属于高语境文化。作为一个年轻的移民国家,美国毫不意外地拥有着低语境文化。移民来自于世界各地,拥有着不同的文化背景,他们不得不选择简洁而直接的交流方式,这或许就是其拥有低语境文化的原因。

从文化语境的角度与异国同事交流的三个步骤:

怎样面临跨文化交流中遇到的挑战?比如说一位美国人与他的日本同事交流,他更应该注重日本同事的非语言线索,同样的日本同事应当更关注美国人直接表达出的信息。如果你也面临类似的挑战,按照下面这三个步骤做,可以帮助你更有效地和异国同事交流,增进与他们的感情。

认识到文化语境的差异

跨文化交流的第一步是认识到文化差异,跨文化交流从认识其他文化开始。

尊重文化语境的差异

一旦你意识到了文化语境的差异会影响跨文化交流,你要做的就是尊重这些差异。在你遇到一种不同的交流方式时,学会接受差异,学会积极听取他人意见。

调和文化语境的差异

只是认识和尊重差异还远远不够,你还需要学会如何调和这些差异。互相理解和换位思考可以增进差异的调和,你还要学着用它们去提高同事间的交流效率,推动生产力。

( ↓↓ —— 未完 —— ↓↓ )


完美阅读及吐槽,请猛击:https://linux.cn/article-11705-1.html?utm_source=qqmail&utm_medium=qqmail

<a href="https://linux.cn/" target=_blank>Linux中国</a> / 2019-12-22 4 years ago / 未收藏/ linuxChina发送到 kindle
本文是 24 天 Linux 桌面特别系列的一部分。如果你还在怀念 GNOME 2,那么 Mate Linux 桌面将满足你的怀旧情怀。


如果你以前听过这个传闻:当 GNOME3 第一次发布时,很多 GNOME 用户还没有准备好放弃 GNOME 2。Mate(以马黛茶yerba mate植物命名)项目的开始是为了延续 GNOME 2 桌面,刚开始时它使用 GTK 2(GNOME 2 所基于的工具包),然后又合并了 GTK 3。由于 Linux Mint 的简单易用,使得该桌面变得非常流行,并且从那时起,它已经普遍用于 Fedora、Ubuntu、Slackware、Arch 和许多其他 Linux 发行版上。今天,Mate 继续提供一个传统的桌面环境,它的外观和感觉与 GNOME 2 完全一样,使用 GTK 3 工具包。
你可以在你的 Linux 发行版的软件仓库中找到 Mate,也可以下载并安装一个把 Mate 作为默认桌面的发行版。不过,在你这样做之前,请注意为了提供完整的桌面体验,所以许多 Mate 应用程序都是随该桌面一起安装的。如果你运行的是不同的桌面,你可能会发现自己有多余的应用程序(两个 PDF 阅读器、两个媒体播放器、两个文件管理器,等等)。所以如果你只想尝试 Mate 桌面,可以在虚拟机(例如 GNOME box)中安装基于 Mate 的发行版。

Mate 桌面之旅

Mate 项目不仅仅可以让你想起来 GNOME 2;它就是 GNOME 2。如果你是 00 年代中期 Linux 桌面的粉丝,至少,你会从中感受到 Mate 的怀旧情怀。我不是 GNOME 2 的粉丝,我更倾向于使用 KDE,但是有一个地方我无法想象没有 GNOME 2:OpenSolaris。OpenSolaris 项目并没有持续太久,在 Sun Microsystems 被并入 Oracle 之前,Ian Murdock 加入 Sun 时它就显得非常突出,我当时是一个初级的 Solaris 管理员,使用 OpenSolaris 来让自己更多学会那种 Unix 风格。这是我使用过 GNOME 2 的唯一平台(因为我一开始不知道如何更改桌面,后来习惯了它),而今天的 OpenIndiana project 是 OpenSolaris 的社区延续,它通过 Mate 桌面使用 GNOME 2。
Mate on OpenIndiana
Mate 的布局由左上角的三个菜单组成:应用程序、位置和系统。应用程序菜单提供对系统上安装的所有的应用程序启动器的快速访问。位置菜单提供对常用位置(如家目录、网络文件夹等)的快速访问。系统菜单包含全局选项,如关机和睡眠。右上角是一个系统托盘,屏幕底部有一个任务栏和一个虚拟桌面切换栏。
就桌面设计而言,这是一种稍微有点奇怪的配置。它从早期的 Linux 桌面、MacFinder 和 Windows 中借用了一些相同的部分,但是又创建了一个独特的配置,这种配置很直观而有些熟悉。Mate 执意保持这个模型,而这正是它的用户喜欢的地方。

Mate 和开源

Mate 是一个最直接的例子,展示了开源如何使开发人员能够对抗项目生命的终结。从理论上讲,GNOME 2 会被 GNOME 3 所取代,但它依然存在,因为一个开发人员建立了该代码的一个分支并继续发展了下去。它的发展势头越来越庞大,更多的开发人员加入进来,并且这个让用户喜爱的桌面比以往任何时候都要更好。并不是所有的软件都有第二次机会,但是开源永远是一个机会,否则就永远没有机会。
使用和支持开源意味着支持用户和开发人员的自由。而且 Mate 桌面是他们的努力的有力证明。

via: https://opensource.com/article/19/12/mate-linux-desktop
作者:Seth Kenlon 选题:lujun9972 译者:mayunmeiyouming 校对:wxy
本文由 LCTT 原创编译,Linux中国 荣誉推出


完美阅读及吐槽,请猛击:https://linux.cn/article-11703-1.html?utm_source=qqmail&utm_medium=qqmail

<a href="https://linux.cn/" target=_blank>Linux中国</a> / 2019-12-22 4 years ago / 未收藏/ linuxChina发送到 kindle

62 岁的前西门子合同工 David A. Tinley 因为植入逻辑炸弹的阴谋而被判六个月徒刑七千美元罚款。他在今年 7 月认罪。Tinley 为西门子在 Monroeville 的办事处服务了将近 10 年的时间,他曾被要求为公司创建管理订单的电子表格,包括了自定义脚本可以根件据储存在其他文的当前订单去更新文件,让公司自动化库存和订单管理。
他的电子表格正常工作了多年,直到 2014 年开始出现问题。他植入的逻辑炸弹导致了在特定日期后电子表格崩溃。每次崩溃,西门子都会联络他来修复,他会收修复费用。
他的阴谋暴露是因为有一次电子表格崩溃而他不在城里,不得不将电子表格的管理密码交给公司的 IT 工作人员去执行紧急修复。西门子的 IT 雇员发现了逻辑炸弹。  
来源:cnBeta.COM

更多资讯

千余款 App 平均申请 25 项权限 某银行竟需获取性取向
使用银行 App 还需要获取性取向、婚史、好友列表、精准定位信息?近日,据国家网络安全通报中心透露,又有100款违法违规采集个人信息的App被查处整改,光大银行、更美、考拉海购、微店等知名App在列。通告指出,此次集中整治,重点针对无隐私协议、收集使用个人信息范围描述不清、超范围采集个人信息和非必要采集个人信息等情形。
来源:工人日报
详情链接:https://www.dbsec.cn/blog/article/5604.html 

加州新法允许互联网用户删个性数据 影响有多大?
据《财富》网站报道,到 2020 年,许多美国人将得到一个强大工具来保护他们的在线隐私。美国加州出台了一项新法律,将要求企业告诉消费者它们收集了消费者哪些数据,而且消费者可要求删除这些数据。这部被称为《加州消费者隐私法》(CCPA)的法律可能会对网络经济造成严重破坏,因为很多公司——从科技巨头到普通零售商——都依赖定向推送广告。如果人们要求公司删除他们的数据,这些广告的效果就会下滑。
来源:网易科技
详情链接:https://www.dbsec.cn/blog/article/5605.html 

卧底调查身份证买卖 这个暴利黑产手段真的脏
最近,国家新闻出版署发布《关于防止未成年人沉迷网络游戏的通知》,对未成年人游戏在线时间实行严加管控。为了解除游戏时间限制,我的小侄子就打起我的身份证主意来了。小侄子除了对我下手,还特意再网上搜索了【相关信息】。
来源:太平洋电脑网
详情链接:https://www.dbsec.cn/blog/article/5606.html 

个人信息保护法等纳入明年立法规划
10 月 31 日至 11 月 29 日,民法典婚姻家庭编草案通过中国人大网向社会公开征求意见,“共收到 198891 位社会公众网上提出的 237057 条意见和 5635 封群众来信。意见主要集中在完善近亲属的范围、修改可撤销婚姻的撤销机关、进一步完善夫妻共同债务、同性婚姻合法化等方面。
来源:新京报
详情链接:https://www.dbsec.cn/blog/article/5607.html 

(信息来源于网络,安华金和搜集整理)


完美阅读及吐槽,请猛击:https://linux.cn/article-11704-1.html?utm_source=qqmail&utm_medium=qqmail

四月的奥德赛 / 2019-12-14 4 years ago / 未收藏/ azurew发送到 kindle
笔者最近也安装了vSphere 7.0 RC版,Build号为VMware-VMvisor-Installer […]
四月的奥德赛 / 2019-12-14 4 years ago / 未收藏/ azurew发送到 kindle
VMware 7.0 Beta 版 Build号 VMware-VMvisor-Installer-7.0.0 […]
四月的奥德赛 / 2019-12-14 4 years ago / 未收藏/ azurew发送到 kindle
笔者安装好vSphere 7.0 Bet后,调整了硬盘到200G,才显示存储。     可是笔者明明是200G […]
2019-12-23 4 years ago / 未收藏/ Feed for FT中文网 - 双语阅读发送到 kindle

杰米•史密斯悉尼报道
澳大利亚总理斯科特•莫里森(Scott Morrison)为在国家发生致人死亡的丛林大火危机期间在夏威夷度假而致歉,但他坚称,他领导的政府不会在恐慌之下仓促改变其气候政策。
“我明白,人们得知我在和我的家人度假,而与此同时他们的家人正在承受巨大压力,他们会感到不满。”莫里森周日在访问位于悉尼的农村地区消防服务总部时表示,“我为此表示道歉。”
在未对外宣布出国度假之...
Atman Rathod / 2019-12-12 4 years ago / 未收藏/ cmarix发送到 kindle
There are no mobile app development projects that don’t involve at least some kind of risk. But some projects working with more rigorous approaches and concepts are more prone to such risks. Agile app development projects are more exposed to risks than traditional waterfall development approach. Such increased risk factors are often a result from …
Atman Rathod / 2019-12-16 4 years ago / 未收藏/ cmarix发送到 kindle
For the e-commerce stores, holiday months of November and December together generate at least 30% more revenue than all other months combined. This clearly shows why e-commerce stores are up on their toes at the end of the year. This is the most happening time of the year for the e-commerce industry. Naturally, they need …
Atman Rathod / 2019-12-17 4 years ago / 未收藏/ cmarix发送到 kindle
Android apps and the Google Play Store now together make up the most versatile mobile app marketplace with the widest range of apps for every niche, category, and sub-category imaginable. New mobile apps keep pouring on the platform. From startups to the small local businesses to the big brands to the professionals, everyone vies for …
Atman Rathod / 2019-12-18 4 years ago / 未收藏/ cmarix发送到 kindle
It is not surprising that Magento as the leading e-commerce CMS system already powers the vast majority of e-commerce stores and websites across the spectrum. Considering the huge potential and the power-packed features it delivers, it is not surprising that Magento is behind 1% of the web which makes around 1.5 billion websites. The biggest …
Atman Rathod / 2019-12-19 4 years ago / 未收藏/ cmarix发送到 kindle
For developing any web application, performance aka loading speed should get the utmost priority. To boost the loading speed of Angular apps, we have several things at our disposal. From using lazy loading modules to tree-shaking to AoT (ahead-of-time compilation) the use of caching mechanism, an Angular Development Company can try quite a few ways …
cmarix / 2019-12-20 4 years ago / 未收藏/ cmarix发送到 kindle
CMARIX Technolobs has yet again been honored as a “Best Software Development Company (Runner Up)” at GESIA, Vibrant Gujarat Start-Up and Technology Summit 2019 held on Friday, 13th December 2019, Ahmedabad. The GESIA Laurel was awarded by honorable Neelam Rani, IFS (MD, iNDExtB), Hareet Shukla, IAS (Secretary, DST) & Maulik Bhansali (Chairman, GESIA). At VSTS, …
Tony Phillips / 2019-12-23 4 years ago / 未收藏/ starterstory发送到 kindle
Read the full story here.
Nate Lipton / 2019-12-23 4 years ago / 未收藏/ starterstory发送到 kindle
Read the full story here.

【黑果小兵】macOS Catalina 10.15.2 19C57 正式版 with Clover 5100原版镜像[双EFI双平台版]

macOS Catalina正式版

Mac 上你喜爱的一切,都因 macOS Catalina 而变得更精彩。你可以享用两款全新媒体 app;用新的查找 app 定位遗失的 Mac;而你常用的 iPad app 现在也有了 Mac 版。你甚至可以将 iPad 当作第二个显示屏,扩展桌面空间。现在,无论做什么,你将拥有远超以往的体验

Apple Music

• 订阅 Apple Music,在线聆听并下载上千万首歌曲。
• 优化后的资料库井然有序,轻松查找你所有下载的或从 CD 导入的音乐。

Apple 播客

• 通过节目的名称、主题、嘉宾、主持人或内容搜索播客。
• 订阅喜爱的播客,自动接收上新通知。
• 你可以在 Mac 上开始听一集节目,然后随时利用 iPhone、iPad 或 Apple Watch 继续收听。

同步与备份

• 当 iPhone 或 iPad 连接上 Mac 时,可在访达中同步内容,进行备份、更新,或恢复软件。

Mac Catalyst

• 你现在可以在 Mac 上,畅享多款你常用 iPad app 的 Mac 版。

随航Sidecar

• 将你的 iPad 用作 Mac 的第二个显示屏,从而扩展工作空间。
• 可在每个屏幕上各运行一个 app;也可在 iPad 上使用工具和调色板,在 Mac 上进行绘画创作。
• 操作你常用的创意类专业 Mac app 时,以 Apple Pencil 在平板电脑上进行输入。

速绘和标记支持连续互通

• 在 iPad 或 iPhone 上进行速绘,然后将其插入到 Mac 的任何文档中。
• 使用 iPad 或 iPhone 来签署 Mac 上的 PDF 或标记 Mac 上的截屏。

辅助功能

• 利用语音控制功能,通过你的语音全权掌控 Mac。
• 启用悬停文本,可在一个专用窗口里显示高分辨率大字体。
• 当使用两个屏幕时,利用放大显示功能,可将其中一个屏幕上的内容放大,以便阅读。

安全性

• 你所用的 app 均已由 Apple 事先检查是否含有已知恶意软件,让你安心使用。
• 当 app 要求访问数据时,将基于新的数据保护功能进行授权。

查找

• 借助简单易用的新 app,追踪你的设备或寻找亲友。
• 即使设备未连接无线局域网或蜂窝网络,也能帮你定位。
• 可使用激活锁,保护搭载 Apple T2 安全芯片的 Mac 机型。

照片

• 呈现你的精彩照片和回忆,带给你活泼生动、引人入胜的新体验。
• 浏览“回忆影片”,更可编辑影片时长、风格和标题。

Safari 浏览器

• 使用优化的起始页面浏览历史记录、书签、阅读列表、iCloud 标签页中显示的站点,甚至你在信息 app 中收到的链接。
• 使用 Safari 浏览器建议的强密码,有助保护账户安全。

备忘录

• 在画廊视图中,浏览以缩略图呈现的备忘录。
• 尽享更强大的搜索功能,可识别扫描件中的文字和图像中的物体。
• 使用共享文件夹,可分享整个备忘录文件夹。
• 轻松地将核对清单项目重新排序或缩进排版,并自动将已完成的项目移动到底部。

提醒事项

• 使用新的编辑按钮,为提醒事项添加日期、位置、旗标和附件等。
• 智能列表可自动整理并显示临近的提醒事项,方便你及时跟进。

邮件

• 通过邮件主题静音、屏蔽发件人或从商业推广列表中取消订阅,全权掌控你的收件箱。

屏幕使用时间

• 透彻了解在 app 和网站上花费的时间,并设定使用限额。
• 设置家人共享,为家人的设备设定屏幕使用时间。

此更新还包括以下错误修复和改进:

  • 恢复在“照片”的“所有照片”视图中查看文件名的功能
  • 恢复在“照片”的“天”视图中按收藏夹,照片,视频,已编辑和关键字进行过滤的功能
  • 修复了启用重复警报选项时消息仅发送单个通知的问题
  • 解决了导致联系人启动到先前打开的联系人而不是联系人列表的问题
  • 在Apple News中添加了两指轻扫手势以用于向后导航
  • 解决了在文件夹中显示播放列表并在“歌曲”列表中显示新添加的歌曲时在“音乐”应用中可能出现的问题
  • 提高了将iTunes资料库数据库迁移到音乐,播客和电视应用程序的可靠性
  • 修复了电视应用程序的“下载”文件夹中看不到下载标题的问题
更多的信息请移步:这里

镜像靓点:

安装教程简单版

截图:

结合了台式机和笔记本的截屏,仅供参考
1About_10.14.6
21Displays
22Displays
2Video
1Audio
1Audio2
1AudioPatch
2BlueTooth
2BlueTooth2
3Wifi
4Sata
5Ethernet
6USB
6USBFinal
7Light
8Volume
Extensions
PCI_List
4I2C
5TouchPad
18G84

特别感谢

@宪武 提供的hotpatch的全套方法:

10.15.2 19C57 双EFI分区版下载链接

迅雷离线下载:[请点击下载] 感谢@难忘情怀提供下载资源
http下载链接:请点击下载 感谢@难忘情怀提供下载资源
百毒云下载链接: https://pan.baidu.com/s/1LVbWZ-qLd8xQwQCsJct_bA 提取码: rk3d
1
MD5 (macOS Catalina 10.15.2(19C57) Installer for Clover 5100 and WEPE.dmg) = 1b517732c6ab2155f3673aaeb33cd45a
2019-12-23 4 years ago / 未收藏/ pentester发送到 kindle
Hey hackers! These are our favorite resources shared by pentesters and bug hunters last week.
This issue covers the week from 06 to 13 of December.
T5HN84.png

Our favorite 5 hacking items

1. Tutorial of the week

Quality of Life Tips and Tricks - Burp Suite

These tips are very helpful for improving your Burp experience. Some are old news but I’m discovering others for the first time:
  • How to reduce the size of Burp projects for long term storage (Burp project hoarders, hello!)
  • How to leverage Match and Replace for simplifying the use of complex or long test username/passwords (Simple yet genius! Useful especially with mobile tests)
  • How to rearrange Burp Repeater request and response tabs (So useful for taking screenshots for reports!)

2. Tool of the week

Bookmarks

Have you ever used Burp Repeater as a bookmarking feature? I do, and the result is not pretty. Tens of tabs open, which is not practical and slows down Burp.
So, this bookmarking extension can be life-changing. It allows you to save interesting requests/responses, replay requests directly in the extension’s tab, sent it to Repeater/Intruder, and highlight the request in Burp Proxy.

3. Video of the week

Docker For Pentesting And Bug Bounty Hunting & Bug Bounty Toolkit

This is an excellent introduction to Docker. If you are not already using it, you can learn in less than 40 minutes why and how to leverage it for pentest and bug bounty.
An example toolkit is also provided. It basically allows you to customize any Linux distribution by adding tools. The list of tools installed can be modified. This would be a good exercise for practicing with Docker.

4. Tip of the week

Trying to use Masscan through a VPN client? Use -e to specify the interface. Similarly, Nessus won’t scan over a VPN interface unless you set the source_ip setting in the advanced options to your VPN interface’s IP.

Tip added to knowledge base! This is good to know and might save me (and you maybe?) time when using a VPN for either pentest or bug bounty.

5. Non technical item of the week

Learning How to Learn: Powerful mental tools to help you master tough subjects & @knoxxs’s notes

I know someone who can literally learn anything in a very short period of time. I don’t think it is due to an abnormal intelligence or anything, but because of skills like the ability to detect the missing knowledge, where to get it and what to prioritize to get quick results.
These skills can be taught. This free Coursera course is an excellent start. Personally, I’ve added it to my list of online courses to go through in 2020. It explains both theory and practical techniques to improve learning, tackle procrastination, and understand how memory works.

Other amazing things we stumbled upon this week

Videos

Podcasts

Webinars & Webcasts

Conferences

Slides only

Tutorials

Medium to advanced

Beginners corner

Writeups

Challenge writeups

Pentest writeups

Responsible(ish) disclosure writeups

Bug bounty writeups

See more writeups on The list of bug bounty writeups.

Tools

If you don’t have time

More tools, if you have time

Misc. pentest & bug bounty resources

Challenges

Articles

News

Bug bounty & Pentest news

Reports

Vulnerabilities

Breaches & Attacks

Malicious apps/sites

Other news

Non technical

Tweeted this week

We created a collection of our favorite pentest & bug bounty related tweets shared this past week. You’re welcome to read them directly on Twitter: Tweets from 12/06/2019 to 12/13/2019.

Curated by Pentester Land & Sponsored by Intigriti

Have a nice week folks!
If you want to be notified when new articles (including this newsletter) are published, you can subscribe to this blog.
And if you enjoyed reading this, please consider sharing it, leaving a comment, suggestions, questions…

Tim Nolet / 2019-12-17 4 years ago / 未收藏/ checklyhq发送到 kindle
An in depth teardown of hosting, software and other cost at the developing stage of a developer focuses SaaS. And the nuance of it all....
Marcin Wanago / 2019-12-16 4 years ago / 未收藏/ wanago发送到 kindle
The hooks are an exciting addition to React and undoubtedly one that helps us to separate the logic from the template. Doing so makes the above logic more testable. Unfortunately, testing hooks does not prove to be that straightforward. In this article, we look into how we can deal with it using react-hooks-testing-library. Identifying the tricky […]
The post JavaScript testing #5. Testing hooks with react-hooks-testing-library and Redux appeared first on Marcin Wanago Blog - JavaScript, both frontend and backend.
2019-12-23 4 years ago / 未收藏/ zdyxry发送到 kindle

背景

之前已经介绍过 ClusterAPI 及相应实现方式,但是针对使用 ClusterAPI 部署的 K8s 集群社区中一直没有升级方案,其中 vmware 实现了一个简单的升级工具,可以在社区没实现之前提供使用,今天来看下这个工具是如何实现的。

cluster-api-upgrade-tool

项目地址:https://github.com/vmware/cluster-api-upgrade-tool
因为这只是一个单独的工具,因此代码结构比较简单:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
yiran@t480:~/go/src/github.com/vmware/cluster-api-upgrade-tool 
master ✔ $ tree .
.
├── CODE-OF-CONDUCT.md
├── CONTRIBUTING.md
├── Dockerfile
├── go.mod
├── go.sum
├── hack
│   └── tools
│   ├── go.mod
│   ├── go.sum
│   └── main.go
├── LICENSE.txt
├── main.go # 命令行入口
├── Makefile
├── NOTICE.txt
├── pkg
│   ├── internal
│   │   └── kubernetes
│   │   ├── client.go # 获取 client
│   │   └── pod_exec.go
│   ├── logging
│   │   └── logrus_logr.go
│   └── upgrade
│   ├── config.go
│   ├── control_plane.go # 主要升级逻辑
│   └── control_plane_test.go
├── README.md
└── test
└── integration
├── go.mod
├── go.sum
└── main_test.go

升级流程

首先在 main.go 封装了相应的命令行用于升级,获取到相应配置后,开始升级:
1
2
3
4
5
6
upgrader, err := upgrade.NewControlPlaneUpgrader(newLogger(), finalConfig)
if err != nil {
return err
}

return upgrader.Upgrade()
在 control_plane.go 文件中是升级的主要逻辑,所有的步骤都是顺序执行的,摘要下主要的步骤:
1
2
3
4
5
6
7
8
9
10
11
12
13
// Upgrade does the upgrading of the control plane.
func (u *ControlPlaneUpgrader) Upgrade() error {
machines, err := u.listMachines()
...
if err := u.reconcileKubeadmConfigMapAPIEndpoints(); err != nil {
err = u.updateKubeletConfigMapIfNeeded(u.desiredVersion)
err = u.updateKubeletRbacIfNeeded(u.desiredVersion)
if err := u.etcdClusterHealthCheck(15 * time.Second); err != nil {
if err := u.UpdateProviderIDsToNodes(); err != nil {
return updateKubeadmKubernetesVersion(in, "v"+u.desiredVersion.String())
if err := u.updateMachines(machines); err != nil {
if err := u.reconcileKubeadmConfigMapAPIEndpoints(); err != nil {
}
  1. u.listMachines获取 TargetCluster 所有的 ControlPlane 节点
    a. 通过 label 来进行 machine 过滤
    b. 如果 Machine 的 DeletionTimestamp 字段为 0,则追加到列表中
  2. u.reconcileKubeadmConfigMapAPIEndpoints 这里主要是确保对应的 APIEndpoints 节点都在 k8s 集群中,通过对比 nodeList 与 APIEndpoints 来进行过滤
  3. u.updateKubeletConfigMapIfNeeded 在 k8s 中,在 ConfigMap 中是有保存 kubelet 配置信息的,在升级过程中,需要重新创建对应版本的 kubelet 配置信息,这个函数中直接将原版本的 kubelet 复制了一份,创建一份目标版本的 kublet 配置 ConfigMap
  4. u.updateKubeletRbacIfNeeded 创建目标版本的 Role 和 RoleBinding 资源
  5. u.etcdClusterHealthCheck 检查 etcd 集群是否健康,这里主要通过 endpoint health --endpoints 来检查 etcd 是否健康
  6. u.UpdateProviderIDsToNodes 通过 Cluster-API 创建出来的节点,需要设置 node.ProviderID 才可以被 Cluster-API 识别为 running 状态,ProviderId 格式为:vmware://xxxxx ,这里根据 ProviderID 来检测出具体的 ID,并将其作为一个 map 返回
  7. u.updateKubeadmKubernetesVersion 将 kubeadm ConfigMap 中的 kubernetesVersion 字段更新为目标版本,便于后续添加节点时指定的是目标版本
  8. u.updateMachines 在所有配置文件已经准备、更新完成后,开始做整个升级中最重要的部分,节点(Machine)升级,首先遍历所有 Machine 资源,针对每个 Machine,进行如下动作:
    a. u.etcdClusterHealthCheck 检查 etcd 集群是否健康,如果不健康,则退出升级
    b. generateReplacementMachineName 生成替代 Machine 相应配置信息,如 MachineName
    c. u.updateInfrastructureReference 创建替代 Machine 的 Infrastructure Object
    d. u.updateBootstrapConfig 创建替代 Machine 的 Bootstrap Config,因为默认 Cluster-API 使用的 kubeadm Bootstrap,所以这里其实是生成替代 Machine 执行的 kubeadm 配置
    e. u.updateMachine 真正创建替代 Machine 的步骤,创建对应的目标虚拟机,等待目标虚拟机添加到 K8s 集群中且处于 Ready 后,将原虚拟机对应 etcd 节点从 etcd 集群中移除,随后将原虚拟机对应节点从 K8s 集群中移除
  9. u.reconcileKubeadmConfigMapAPIEndpoints 等待所有 Machine 替换完成后,重新配置 kubeadm ConfigMap 保证 kubeadm ConfigMap 中保存所有的 APIEndpoints 信息。

总结

Cluster-API 作为 K8s cluster-lifecycle SIG 的一个还处于 Alpha 阶段的项目,还处于一个快速更新迭代的状态,因此最终升级流程是怎样还不确定,但是从 vmware 的这个工具来看,后续很有可能采用这种方案,毕竟 Cluster-API 不想通过 SSH 这种比较麻(恶)烦(心)的方式对节点进行管理,从 kubeadm Bootstrap 使用 Cloud-init 就可以看出,尤其是当支持多种 Linux 发行版后,可以预想这是一个灾难。因此使用这种节点逐一替换的方式是很方便的,具体实现看 v1alpha3 发布之后社区的讨论结果吧。
吐槽:
Cluster-API 项目在代码中很少使用缩写,带来的好处很明显,易读易懂,上手容易,我自己的项目也一直秉承着这种观点,但是当我看到 reconcileKubeadmConfigMapAPIEndpoints 这种长度的变量名,还是很崩溃的。

参考链接

2019-12-22 4 years ago / 未收藏/ economistp发送到 kindle
FOR SEVERAL years, beginning in the mid-2000s, devotees of Chinese food on America’s east coast obsessed over a mystery: Where was Peter Chang? A prodigiously talented—and peripatetic—chef, Mr Chang bounced around eateries in the south-east. One day diners at a strip-mall restaurant in suburban Richmond or Atlanta might be eating standard egg rolls and orange chicken; the next, their table would be graced by exquisite pieces of aubergine the size of an index finger, greaselessly fried and dusted with cumin, dried chillies and Sichuan peppercorns. Or by a soup made of pickled mustard greens and fresh sea bass, in its way as hauntingly perfect and austere as a Bach cello suite. A few months later, Mr Chang would move on.
He now seems to have settled down, running a string of restaurants bearing his name between Rockville, Maryland, and Virginia Beach. His latest—Q by Peter Chang—in the smart Washington suburb of Bethesda, may be his finest. The space is vast and quasi-industrial, with brushed concrete floors, massive pillars and not a winking dragon in sight. Order a scallion pancake, and what appears is not the typical greasy disc but an airy, volleyball-sized dough sphere. Jade shrimp with crispy rice comes under what looks like an upturned wooden bowl (perhaps, you think, for the shells). On inspection the bowl turns out to be the rice. Thumping through it with a spoon reveals perfectly cooked shrimp floating in shamrock-green sauce.


A tab for two at Q can easily top three figures—several times the outlay on an average Chinese meal. Nor is Mr Chang’s the only such restaurant in the area: like many big American cities, Washington has seen a rise in high-end Chinese cuisine. That is good news, and not just for well-heeled gourmands who can tell shuijiao from shuizhu. The culinary trend is underpinned by two benign social ones. Chinese-Americans are becoming wealthier and more self-confident; and customers are shedding old stereotypes about Chinese food. To put it another way: sometimes a dumpling is more than just a dumpling.

The comfort of strangers

Chinese restaurants began to open in America in the mid-19th century, clustering on the west coast where the first immigrants landed. They mostly served an Americanised version of Cantonese cuisine—chop suey, egg fu yung and the like. In that century and much of the 20th, the immigrants largely came from China’s south-east, mainly Guangdong province.
After the immigration reforms of 1965 removed ethnic quotas that limited non-European inflows, Chinese migrants from other regions started to arrive. Restaurants began calling their food “Hunan” and “Sichuan”, and though it rarely bore much resemblance to what was actually eaten in those regions, it was more diverse and boldly spiced than the sweet, fried stuff that defined the earliest Chinese menus. By the 1990s adventurous diners in cities with sizeable Chinese populations could choose from an array of regional cuisines. A particular favourite was Sichuan food, with its addictively numbing fire (the Sichuan peppercorn has a slightly anaesthetising, tongue-buzzing effect).
Yet over the decades, as Chinese food became ubiquitous, it also—beyond the niche world of connoisseurs—came to be standardised. There are almost three times as many Chinese restaurants in America (41,000) as McDonald’s. Virtually every small town has one and, generally, the menus are consistent: pork dumplings (steamed or fried); the same two soups (hot and sour, wonton); stir-fries listed by main ingredient, with a pepper icon or star indicating a meagre trace of chilli-flakes. Dishes over $10 are grouped under “chef’s specials”. There are modest variations: in Boston, takeaways often come with bread and feature a dark, molasses-sweetened sauce; a Chinese-Latino creole cuisine developed in upper Manhattan. But mostly you can, as at McDonald’s, order the same thing in Minneapolis as in Fort Lauderdale.
Until recently, the prices varied as little as the menus—and they were low. Eddie Huang, a Taiwanese-American restaurateur turned author and presenter, recounts how his newly arrived father kept his prices down because “immigrants can’t sell anything full-price in America.”
That, in truth, was a consoling simplification. Americans have traditionally been willing to pay through the nose at French or Italian joints (where, in fact, Latinos often do most of the cooking). And every city has its pricey sushi bars and exorbitant tapas restaurants (tapas, as one joke goes, is Spanish for “$96 and still hungry”).
But Mr Huang is right that Americans have long expected Chinese food to be cheap and filling. One step up from the urban takeaway, with its fluorescent lighting and chipped formica counter, is the strip-mall bistro with its imposing red doors and fake lions standing guard—sufficiently exotic to be special, but still affordable enough for a family to visit once a week when nobody feels like cooking.

American dreams

Even the superior outlets were cheap for what they served (and often still are). Consider the hand-ripped noodles with lamb at Xi‘an Famous Foods in lower Manhattan. A tangle of long noodles, each about the width of Elvis Costello’s ties in the late 1970s, is tossed with curls of braised lamb and a complex, incendiary sauce laced with cumin and chillies—all for just over $10, a fraction of the price of comparably accomplished dishes at smart restaurants nearby. True, Xi‘an Famous Foods has no waiters (diners carry their plates on plastic trays to bench seating). But its noodles are handmade, and the lamb dish may be the single best thing to eat in New York at any price.
But now things are changing. Mr Huang sells deliciously pillowy stuffed buns in New York and Los Angeles for $5.50 each—or, as he puts it, “full fucking price”—and encourages other immigrants not to undervalue their work. Restaurants in Q’s bracket are cropping up not just in America’s Chinatowns but in the suburbs, where Chinese immigrants and their families have settled, following the classic strivers’ path. The median income of Chinese-Americans’ households is nearly 30% higher than the average. They are more than twice as likely as other Americans to have an advanced degree.
Chef’s special relationship
Meanwhile, although racism persists, the pervasive discrimination of earlier ages has waned. Witness the presidential campaign of Andrew Yang, in which his ethnicity has scarcely been mentioned. Since the Chinese-American population is six times as big as 40 years ago, Americans overall are much more familiar with Chinese people and their cooking. All of which means that, in your correspondent’s fairly extensive experience, the new fancy breed of Chinese restaurants draws a heartening mix of Chinese and non-Chinese diners.
Not everyone is enticed. The same cult of authenticity which decrees that good tacos only come from trucks posits that the best Chinese food is found in humble settings. That is as inaccurate as the snobbery that Mr Huang decries. Chinese chefs are as ambitious as any others; a bowl of noodle soup no more stands for all of Chinese cuisine than a slice of pizza does for Italian.
In any case, authenticity is a slippery commodity. Recipes constantly evolve as people move and mingle. The chillies now considered essential to Sichuan dishes were actually brought to China by Iberian traders in the late 16th century. Hot dogs were originally German, pizza Neapolitan, bagels Polish—but now they are all American, and like America, infinitely varied.
The goat ribs at Duck, Duck Goat, in Chicago’s trendy meatpacking district, are more Chinese-ish than Chinese. So is the place itself—headed by a non-Chinese chef and kitschily decorated with paper lanterns and bright red walls. The ribs come as a mesh of burnished meat stilettos with a wonderful chew, the sweetness of the glaze giving way to the goat’s irresistible gaminess. They spark fights over who gets the last one. They are as inauthentic, and as imaginative and lovingly created, as Mr Chang’s scallion dough sphere—and as delicious, which in the end, is what counts.
This article appeared in the Books and arts section of the print edition under the headline "A bao in every steamer"
2019-12-22 4 years ago / 未收藏/ economistp发送到 kindle
HALFWAY BETWEEN Moscow and the Pacific Ocean sits the city of Krasnoyarsk. At its heart, on the banks of the mighty Yenisei, stands a brutalist building encased in granite. Built in 1987, it was the last of the Lenin Museums that the Soviet Union bestowed on deserving provincial cities to showcase the achievements of socialism. It is now an art museum. And when night falls, giant letters are projected onto one of its stark walls: SVOBODA.
Svoboda, or freedom, is not the first thing which springs to Western minds at the mention of Siberia; the vast region is more readily associated with fetters, exile and suffering. Nor is it a word much associated with present-day Russia. But it is a word that fits.


The projection on the side of the Krasnoyarsk museum is at least as much about geography as politics: a tribute to Siberia’s limitless expanse, its high skies and rivers that flow so fast and so deep that their water will steam rather than freeze. It is a historical statement, too—Siberia has been seen for centuries, by visitors and inhabitants alike, as a place of freedom. But by the same measure it is also an ironic one: Siberia was a place of punishment and exile long before the Soviet Gulag.
Inside the museum you will find a lot more irony. An artistic movement called “Siberian ironic conceptualism” is well represented. “Irony and self-irony is a mode of survival in Siberia,” says Vyacheslav Mizin, an artist from Novosibirsk. He and his partner, who style themselves “The Blue Noses”, produce pieces which populate the Siberian landscape with American rock stars, poking fun at state propaganda and liberal fetishes alike. If you are incapable of irony, Mr Mizin says, “you turn beastly. The harder the conditions, the more you need it.” Whether the conditions are climatic, political or spiritual goes unsaid.
This Siberian school is less intellectual than the conceptual art you find in Moscow. It is more coarsely grotesque and openly mocking. It embodies a Siberian belief held far beyond the world of art galleries: that Siberia is both the essence of Russia and separate from it.
The movement’s most famous piece is called “United States of Siberia”. In the early 2010s Damir Muratov, an artist from Siberia’s ancient capital, Tobolsk, some 1,500km west of Krasnoyarsk, took an old wooden door and painted it with green and white horizontal stripes, a field of snowflakes in the top left corner. It was a homage to the American painter Jasper Johns, who in the 1950s first posed the question of whether a painting of a flag was something different from the flag itself—and if so, what, if anything, such somethings symbolised.
Mr Muratov’s painting was similarly not a flag. It did not represent a country—merely suggested one—and it did not fly free in the wind. For Mr Muratov, the wind is the essence of a flag. “The most important thing is the movement of air,” he says. “Where there is a wind, there is a flag.” But because the windless wooden painting still looks like a flag, it is clearly asking to be taken as a symbol: of a non-state, of artistic freedom, of an anarchy free from any authority other than the endless horizons of the Taiga forests and the patterns of falling snow.

New worlds

The symmetries between Siberia and North America date back centuries. Russia’s colonisation of Siberia began in the reign of Ivan the Terrible—roughly at the same time as Elizabethan England began to explore its new world. Siberia’s Walter Raleigh was an audacious Cossack called Yermak. Previously a raider on the Volga river, he was hired by the Stroganov family in a bid to expand its fur trade by taking on the Khanate of Sibir. In 1582, accompanied by about 800 men, he crossed the Urals and established a foothold in Siberia. He drowned three years later, but Russia’s expansion continued at a breakneck speed, pushing aside Mongolian Buryats, Turkic Yakuts, Samoyedic Nenets and other indigenous, or at least established, peoples. By 1648 the territory under their sway stretched all the way to the port of Okhotsk on the Pacific coast.
Much less is known of Yermak than of, say, Raleigh or Sir Francis Drake. But a lack of detailed biography is no obstacle to becoming a folk hero. In the romantic mythology of the 19th century he came to embody the energy and enterprise of the free settlers who had moved east into a land where serfdom was never imposed, fighting, mixing and assimilating with those who the cossacks had displaced as they did so. As Nikolay Yadrintsev, a 19th century historian who did much to create the region’s founding myths, wrote: “Siberia, in its origin, is a product of an independent, rather than state-driven, movement and of the creative forces of the people…that was later hijacked and regimented by the state.”
Regimented—and exploited. Its claim on the lands taken by Yermak and his Cossacks transformed Muscovy, a second-tier duchy, into the world’s largest continental empire. Over the following centuries the bounty of Siberia sustained the Russian, Soviet and post-Soviet empires. A source of valuable furs and salt in the 17th century, precious metals and gold in the 19th century, and oil and gas in 20th century, the vastness of Siberia was to Russia something akin to what the west was to America. It felt similar, too. “My God, how far removed life here is from Russia,” Anton Chekhov wrote as he travelled across Siberia. “I really felt I wasn’t in Russia at all, but somewhere in Patagonia or Texas.”
A key difference, though, is that Russia has not moved beyond the extraction of riches from these empty lands. Up to three-quarters of what the country exports comes from Siberia. The rents extracted from these various trades still allow Russia to deal with economic crises without modernising its economy or renouncing autocracy and state monopoly, just as they did in centuries past.
In a book published in 2003, Clifford Gaddy and Fiona Hill (more noted, recently, for her testimony to Congress during the hearings on the impeachment of President Donald Trump) call this “The Siberian Curse”. Siberia’s size, its extreme climate and its misdevelopment by Russian rulers, they argue, hold all of Russia back. “In essence,” they sum up, “to become competitive economically and to achieve sustainable growth, Russia needs to ‘shrink’. It must contract not its territory, but its economic geography.”
The trade across the Urals was not all one way: in return for its resources, Siberia was sent criminals, prostitutes, dissidents and revolutionaries. In “The House of the Dead”, a history of Siberian exile, Daniel Beer notes that “the metaphors changed over time, but the basic conviction remained that Siberia was a receptacle for the empire’s own disorder.”
This was meant to cleanse Russia, not change Siberia. But some exiles could not help but bring change—none more so than the Decembrists. Young men who had been greeted as liberators across Europe during the Napoleonic wars, they had returned home to Russia infused with the ideals of liberty, nationhood and republicanism. On December 26th 1825 they mounted an armed revolt in St Petersburg. It failed. Five were hanged; 121 were sent east.
Instead of oblivion, they found hope. Nikolai Basargin, a 26-year-old Decembrist, wrote in his diaries, “The further we travelled into Siberia, the more fetching it seemed in my eyes. The common people seemed freer, more lively and more educated than our Russian peasants, especially the serfs.” The locals were, he thought, rather like Americans. “There is no doubt,” Basargin wrote, “that Siberia would stand its own in comparison to the American States, this young republic whose rapid growth in material and political significance is so striking both in terms of its attitude to dignity and to human rights”.

A spring from December

The legacy of the Decembrists survived the fall of the Russian empire and the Bolshevik revolution. In the 1970s one Decembrist family home in Irkutsk became an atmospheric museum and a place of pilgrimage for the Soviet intelligentsia. In an act of dignity and defiance, a Siberian publishing house brought out a series of Decembrist memoirs. If you see that legacy in Mr Muratov’s snowflakes and stripes, too, you might not be wrong.
When, in 1861, serfdom was abolished in the rest of Russia, millions of the newly free but landless flocked there, assisted by the Russian state. The Trans-Siberian railway, second only to St Petersburg itself as a tsarist imposition of modernity on the landscape, spread them between newly thriving cities and settlements in between. The elegant classical architecture of 19th-century Irkutsk and the delicate ornamentation of wooden Art Nouveau houses in Tomsk still survive among grey Soviet apartment blocks and eclectic post-Soviet monstrosities, testament to the tastes, money and energy that turned frontier forts into prosperous and cultured cities.
Rich as it was in resources and talents, Siberia lacked a wide range of freedoms. It saw itself as a colony and did not like that status, especially when the word was preceded by the modifier “penal”. Siberian intellectuals, students and journalists fostered a new regionalism. They did not want independence. They wanted Siberia to live up to its potential as the best part of Russia—as what Russia might become. As Yadrintsev argued in “Siberia as a Colony”: “The views and horizons of Russian people widen along with the Russian border…Whatever the history of this land, it cannot be deprived of its future.” America was, once again, the model.
On July 4th 1918, in the aftermath of the Bolshevik revolution, Siberia claimed its sovereignty. It could not hold it, and eventually fell to the Bolsheviks, many of whom had spent time imprisoned in the region. They went on to do unto others as had been done unto them on an industrial scale. Tsarist-era exile settlements were turned into slave-labour camps.
Yet even as the country descended into Stalin’s great terror, Siberia retained its romantic allure; its sense of being a place of shelter. Osip Mandelstam, one of Russia’s most significant 20th century poets, who would soon perish in the Gulag, described this paradox in 1931. In Vladimir Nabokov’s translation:
On my shoulders there pounces the wolfhound age,
but no wolf by blood am I;
better, like a fur cap, thrust me into the sleeve
of the warmly fur-coated Siberian steppes,
...
Lead me into the night where the Enisey flows,
and the pine reaches up to the star,
because no wolf by blood am I,
and injustice has twisted my mouth.


After Stalin’s death in 1953 new hope arose. Novosibirsk, about 800km west of Krasnoyarsk, was chosen as the site for a new, scientific town, or Akademgorodok, that would accelerate the Soviet Union into the communist future. It brought together, among others, nuclear physicists working on thermonuclear fusion and linguists considering how to communicate with aliens in the cosmic future Yuri Gagarin had opened up.
Other futurisms were less welcome. The 1970s saw huge new industrial projects in the region carried out under the aegis of the communist youth league. One of Siberia’s earliest chroniclers had rejoiced that “the air above is cheerful”. Not once the smelters started. The air was poisoned and the rivers dammed, engulfing whole villages. People were enraged and the 19th-century notion of a separate identity resurfaced, notably in the work of the Siberian “village writers”. In 1987 Irkutsk, the city where Alexander Kolchak, the Imperial Army admiral recognised by many countries as Russia’s head of state, was executed in 1920, staged the first mass anti-government demonstration in the history of the Soviet Union. It was aimed at a barbaric plan to dump waste from a paper-processing plant on the shores of Lake Baikal into the river which supplied the city’s water.
When the Soviet empire finally collapsed, Boris Yeltsin, Russia’s new president, promised the country’s regions “as much sovereignty as they could swallow.” Siberia was to get 10% of all the revenues raised from its natural resources. The region’s affinity for the wild West returned with a vengeance as oligarchs, local criminals and chancers tussled for dominance. Tomsk, home to a university that had served as an intellectual hub for regionalism in the 19th century, grew into one of Russia’s most politically vibrant cities, with a critical, independent television channel—TV2—and competitive politics.
All this stacked up against Tomsk when President Vladimir Putin started to consolidate his power. Mikhail Khodorkovsky, the boss of Yukos, Siberia’s largest company, was imprisoned. So was the mayor of Tomsk, his fate a warning to uppity regional politicians. Yukos was dismembered. TV2 was taken off the air in 2014.

The monstration mash-up

In 2004, the year of Mr Khodorkovsky’s first trial, Artem Loskutov, an artist, saw a Soviet-style May Day procession in Novosibirsk in which workers marched under the portraits of their factory bosses and logos of their produce. A poster for a strip-club painted in the style of Great Patriotic War propaganda demanded “capitulation” from its clients and promised a “victorious” shot of vodka. “The whole thing was absurd,” Mr Loskutov recalls. Enthused and amused, he and his friends joined in, carrying posters with slogans such as “Something like this” and “Oh!”. Thus did Novosibirsk’s “Contemporary Art Terrorism” group come into being, deconstructing a demonstration until it was just, as they put it, a “monstration” instead.
At first it was just playful. But as Russia descended deeper into authoritarianism—and with it, state intervention in art—the monstrations grew in size and in substance. “Don’t teach us how to live, or we shall teach you” read their main slogan in 2008. Two years later: “If everyone starts walking like this, what kind of anarchy will it be?!” Other cities started to copy Novosibirsk’s example. Mr Muratov began to paint his “flag”.
Responding to the everyday surrealism of Putin’s Russia, Vasily Slonov, an ironic conceptualist from Krasnsoyarsk (whose heavy-metal crown, poking fun at Western pop culture, is pictured), inverted one of the slogans from Paris in 1968: “Be impossible, demand reality.” Before an exhibition in Moscow in 2018, he displayed a toy bear carrying the slogan in Red Square. The bear was subsequently detained by the police, and has not been heard from since.
In a small, packed Novosibirsk bar ironically (of course) decked out as a Soviet-era pivnaya (beer hole), what strikes you about such artists and their intellectual partners in the resurgence of Siberian regionalism is their self-confidence. They do not debate whether Russia belongs in Europe or in Asia or whether it could ever become a “normal” country—the sort of questions fretted over in trendy Moscow cafés. As far as they are concerned, theirs is a normal country: one called Siberia which is populated by “spontaneous Eurasians”, people who listen to their own common sense rather than the agenda pushed by the Kremlin or Moscow liberals.
When, after the invasion of 2014, Russia’s state propaganda whipped up patriotic hysteria under the slogan “Crimea is ours”, the monstrators responded with “Hell is ours”. When the Kremlin demanded the federalisation of Ukraine, the artists called a march for the federalisation of Siberia and “the creation of the Siberian republic within the Russian Federation”. Its slogan was “Stop feeding Moscow”. The Russian authorities banned the march and blocked the internet page that advertised it. Predictably, this generated a far greater resonance from the media than the march itself would probably have done: conceptual politics born from conceptual art, and all the more powerful for it.
“All Siberian cities have different problems, but they have a common grievance against Moscow,” explains Mikhail Rozhansky, a historian and sociologist in Irkutsk. Yet while on paper Siberia is no different from any other Russian region, in reality it has retained some autonomy. And the harder the Kremlin tries to unify the country, the stronger the sense of separateness becomes. It is perhaps simply a function of size. In a land this large, people rely on themselves and each other; they do not have high expectations of any politician and reject authority as a matter of principle. This gives them the country’s strongest streak of Russia’s most distinctive contribution to political discourse: anarchy.
In 1898 Prince Peter Kropotkin, the father of anarcho-communism, wrote that in Siberia he “understood that the administrative machine [of the state] can do no good for people…In Siberia I lost any faith in state discipline and was ready to become an anarchist”. In the last days of the Soviet Union Grazhdanskaya Oborona (Civil Defence), an iconic punk band from the Siberian city of Omsk, inspired their fans by singing: “Kill the state within yourself” and “Our truth, our faith, our deed is anarchy”.
Protest is currently easier in Siberia than in the rest of Russia, and politics freer, too. For the most part this liberty is exercised only locally. But in 2019 Alexander Gabyshev took it on himself to expand it in a very Siberian way. Though some indigenous Siberians converted to Christianity after the Russians arrived, and some practice Buddhism, some still follow Shamanism. Mr Gabyshev styles himself a shaman warrior. In the spring he set off from his native Yakutia dragging a cart, a dozen followers in his wake. His destination was Moscow, “the heart of evil”; his goal was to exorcise the dark forces embodied by Mr Putin by lighting a fire in Red Square and performing a ritual with a tambourine.
As he went along, his following grew, both on the road and online. “From now on Putin is not a law to you. Live freely. That is the law,” he preached—part Kropotkin, part Aleister Crowley, an occultist. Then one night a SWAT team descended on his camp and packed the shaman back on a plane to Yakutia. There he was briefly incarcerated in a psychiatric ward before being ordered not to leave Yakutia again. Amnesty International declared him a prisoner of conscience.
Mr Gabyshev compares himself to a caterpillar which “knows that what will come out of this cocoon will be faster, stronger and wiser.” The idea of a shaman liberating Russia has a surrealism all of its own.

A valour undreamed of

But he is not the only one to believe that a cleansing can come from the east to the west. In 2013 Vladislav Inozemtsev, a liberal economist, and Valery Zubov, a former governor of the Krasnoyarsk region, wrote a response to “The Siberian Curse” called “The Siberian Blessing”. They argue that, “in a vast and over-centralised country such as Russia, [modernisation] cannot come from the centre, because the centre is the main beneficiary of the rent-seeking system.” Siberia—“the awakening colony that frees itself”—is not an eastern province of Russia. Rather Moscow is a city west of Siberia in dire need of reform and anarchy, cheerful skies and irony, and perhaps a touch of shamanism, too.
As Sergei Kovalevksy, the curator of the museum in Krasnoyarsk, puts it, “In Siberia anything is possible.”
A similar thought occurred to Chekhov when he stopped in Krasnoyarsk. “On the Volga a man started with valour and ended with a moan, which is called a song. On the Yenisei, life started with a moan and will end with valour of a kind we can’t even dream of. This is what I thought standing on the bank of the wide Yenisei: what a full, clever and brave life will light these banks with time!”
Most of the Krasnoyarsk that Chekhov would have seen in the 1890s is long gone. But something of that thought remains, projected every night onto a building that was once Lenin’s Museum.
This article appeared in the Christmas Specials section of the print edition under the headline "The ironies of freedom"
2019-12-22 4 years ago / 未收藏/ economistp发送到 kindle
THE CONSERVATIVE PARTY has been in the business of winning elections since the 1830s. In the 19th century it vied with the Liberals as Britain’s dominant political party, but it was the Liberals who eventually found themselves beached on the shores of modernity. In the 20th century the Conservatives held office for longer than any other party. In the 21st century they are on course to hold power, either in their own right or as the dominant partner in a coalition, for 14 of the first 24 years. Not bad for an outfit that John Stuart Mill dismissed as “the stupid party”.
To be sure, the Tories have had more than their fair share of Chris Grayling-style dunces and time-servers. They have also suffered long periods in the wilderness, particularly after the repeal of the Corn Laws in 1846 and during their long flirtation with imperial preference after 1906. During Tony Blair’s ascendancy the Conservatives were so enfeebled that Geoffrey Wheatcroft wrote a book entitled “The Strange Death of Tory England”, a deliberate echo of George Dangerfield’s rather more enduring “The Strange Death of Liberal England” (1935). But unlike the Liberals, the Conservative Party has always managed to revitalise itself.


Evelyn Waugh once complained that the Tories had never succeeded in turning the clock back for a single minute. But this is exactly why they have been so successful. The party has demonstrated a genius for anticipating what Harold Macmillan once called “the winds of change”, and harnessing those winds to its own purposes.
In the 1840s Robert Peel recognised the rise of industrial capitalism and championed the repeal of the Corn Laws, which had kept the price of grain unreasonably high. This split the party but allowed it to incorporate the new “men of business” in the longer term. In the second half of the 19th century, Benjamin Disraeli and Lord Salisbury recognised not only that democracy was the coming thing but also that, thanks to the conservative instincts of the middle and working classes, it could be used to extend rather than undermine the party’s power. In the 1970s Margaret Thatcher reached the future first in recognising that the post-war consensus was about to give way to a new world of free markets, privatisation and what Peregrine Worsthorne, an old-school Tory, called “get your snouts in the trough with the rest of us” Conservatism.
The Tories have three other great weapons in their arsenal. The first is highlighted in the title of one of the best books on the party, John Ramsden’s “An Appetite for Power”. The Conservatives have always been quick to dump people or principles when they become obstacles to the successful pursuit of power. Theresa May immediately sacked her two chief advisers, Fiona Hill and Nick Timothy, after the party’s poor performance in 2017, whereas Jeremy Corbyn is still clinging on to Karie Murphy and Seumas Milne after Labour’s devastating failure last week.
The second is patriotism. The Tories have always played this card better than any other party, whether in the form of imperialism in the 1870s or retaking the Falkland islands in the 1980s. They have been much aided in this by those radical intellectuals who admire any institution or cause so long as it is not British.
No one should underestimate the party’s third weapon: jollity. The Conservatives have always been the party of “champagne and women and bridge”, to borrow a phrase from Hilaire Belloc, whereas the Liberals and Labour have been the parties of vegetarianism, book clubs and meetings. Conservatives are never happier than when mocking the left for its earnestness.
Boris Johnson fits perfectly into this great Tory tradition. He was one of the first members of his political generation to spot the rising tide of nationalist populism and recognise that it was about to reshape the global landscape. This earned him the hatred of the metropolitan class into which he was born, which is convinced that the future lies with multilateral institutions and globalisation. But it put him at the front of Britain’s Eurosceptic movement, which could have degenerated into a narrow faction under Sir William Cash or a noisy fringe under Nigel Farage, but which entered the Tory mainstream because of Mr Johnson.
He succeeded in this where Mrs May failed because he possessed the other great Tory weapons. He has been willing to sacrifice anything in the pursuit of office. Beneath the bumbling exterior lies a ruthless, power-seeking machine. His withdrawal of the whip from 21 colleagues (some of them close friends) in September made Macmillan’s “night of the long knives” in 1962 look tame. Mr Johnson has never missed an opportunity to wave the flag—even when it has made him look absurd, as when he got stuck on a zip-wire clutching two little Union Jacks. Predictably, the left has played into his hands. Some Remainers have gone out of their way to give the benefit of every doubt to the EU, and Mr Corbyn has devoted his life to supporting anti-Western causes.
Above all, Mr Johnson has embraced the women-and-champagne side of Toryism, if not the bridge. He made his career as a Eurosceptic not by agonising about sovereignty but by making fun of the EU’s (imagined) imperial ambitions to regulate the shape of bananas or the size of condoms. He cracked jokes that were calculated to rile the guardians of political correctness as much as to delight the masses (post-mortems on the election have underestimated the role of these guardians in turning working-class voters against Labour).
The hunt is on to discover the meaning of Johnsonism. How will he flesh out the sketchy promises in his manifesto? What can he do for working-class voters in Blyth Valley? How will he reconcile the free-marketeer and big-government factions of his party? The best way to answer these questions is not just to engage in the British version of Kremlinogy by interrogating every ministerial leak. It is also to study the long history of a party that Mr Johnson now leads with such a resounding mandate.

This article appeared in the Britain section of the print edition under the headline "An appetite for power"
2019-12-22 4 years ago / 未收藏/ economistp发送到 kindle
THE INTENDED destination is not in doubt. Fully 97.7% of voters on the Pacific island of Bougainville opted for independence from Papua New Guinea (PNG) in a referendum held in late November and early December. But how—and how fast—the island will get there remains far from clear. The outcome of the poll is not binding on the government, which inherited Bougainville, part of the archipelago that includes the Solomon Islands, as a quirk of colonial map-making. There will now be a lengthy consultation between the island’s autonomous administration and the national authorities. The ultimate say rests with PNG’s parliament.
There are many reasons for delay. Bertie Ahern, a former Irish prime minister who oversaw the referendum, says the island’s 300,000-odd people are not ready for independence. James Marape, PNG’s prime minister, argues that Bougainville’s economy is too weak and has promised greater spending on infrastructure. The region’s main powers, Australia and New Zealand, fear the creation of a mendicant state on their doorsteps, susceptible, in particular, to Chinese bribes and blandishments. Nearly all of Bougainville’s revenue comes either from the central government or foreign aid.


That was not always the case. Bougainville once boasted the third-largest copper mine in the world. It delivered close to half of PNG’s export revenues in the 1970s. But arguments about the distribution of revenue and jobs from the Panguna mine sparked an insurgency in the late 1980s, which forced the mine to close. PNG’s armed forces struggled to establish control over the island’s mountainous terrain and hostile population. They withdrew in 1990, and blockaded the island by sea instead. When PNG hired mercenaries from a firm called Sandline International to restore order, its own soldiers mutinied, prompting the government of the day to fall and Australia and New Zealand to step in to broker a peace deal.

The agreement, signed in 2001, promised a referendum on independence by 2020 and self-government in the meantime. But the mine did not re-open, leaving the autonomous administration starved of cash. Other big mines and oil- and gasfields were developed on the mainland, diminishing the central government’s incentive to make autonomy work. National leaders’ main concern these days is that Bougainville might inspire other secessionist rebellions, given PNG’s diversity (its 8.5m citizens speak 839 languages), poverty, isolating terrain and dire infrastructure.
The leader of the autonomous government on Bougainville, John Momis, once supported greater autonomy within PNG—the other option on the ballot in the referendum. But the stinginess of PNG’s fiscal transfers and its broader neglect of Bougainville drove him and other voters towards independence instead. Few islanders have confidence in Mr Marape’s promise to fix these problems, having heard such pledges before.
In fact, there is a risk of lack of leadership on both sides. Mr Momis is 81 and must step down by June because of term limits. He has no obvious successor. Bougainville’s people, having voted so emphatically for independence, presumably expect speedy change. The politicians seem unlikely to gratify their desires. The chances of further discord are high.
This article appeared in the Asia section of the print edition under the headline "The 20-year itch"
krunalPatel / 2019-12-23 4 years ago / 未收藏/ icetutor发送到 kindle
This is just to satisfy my own curiosity. Is there an implementation of this: float InvSqrt (float x) { float xhalf = 0.5f*x; int i = *(int*)&x; i = 0x5f3759df - (i>>1); x = *(float*)&i; x = x*(1.5f - xhalf*x*x); return x; } in Rust? If it exists, post the code. I tried it and […]
krunalPatel / 2019-12-23 4 years ago / 未收藏/ icetutor发送到 kindle
I installed vapor via homebrew and then immediately wanted to jump into a project by executing vapor new Hello but then got the following message back in the terminal: dyld: Library not loaded: /usr/local/opt/openssl/lib/libssl.1.0.0.dylib Referenced from: /usr/local/bin/vapor Reason: image not found zsh: abort vapor new Hello I tried some approaches to fix this like uninstalling and reinstalling […]
krunalPatel / 2019-12-23 4 years ago / 未收藏/ icetutor发送到 kindle
Currently, I have upgraded version of Django 2.0.6 to 3.0 and suddenly after calling python manage.py shell command got this error: ImportError: cannot import name ‘six’ from ‘django.utils’ (/path-to-project/project/venv/lib/python3.7/site-packages/django/utils/init.py) Full trace: Traceback (most recent call last): File "manage.py", line 13, in <module> execute_from_command_line(sys.argv) File "/path-to-project/project/venv/lib/python3.7/site-packages/django/core/management/__init__.py", line 401, in execute_from_command_line utility.execute() File "/path-to-project/project/venv/lib/python3.7/site-packages/django/core/management/__init__.py", line 377, in execute django.setup() […]
krunalPatel / 2019-12-23 4 years ago / 未收藏/ icetutor发送到 kindle
I created a project with vue-cli 4.1.1, and after running yarn serve, i got the following error Error: [BABEL] C:devvuestudysrcmain.js: Cannot find module './src/data' (While processing: "C:\dev\vuestudy\node_modules\@vue\cli-plugin-babel\preset.js") Any tips on this? Thanks in advance. UPDATE: Kousha and Dave are right,after waiting for the core-js package update, I created a new project, and now it’s working.
krunalPatel / 2019-12-23 4 years ago / 未收藏/ icetutor发送到 kindle
I’m prettye sure that this question has come up at some point, but sadly I haven’t been able to find an explanation. I’m wondering what the error “cannot borrow as mutable while also borrowed as immutable” means in the following case: let mut v: Vec<usize> = vec![1,2,3,4,5]; v[v[1]] = 999; After some digging I found […]
krunalPatel / 2019-12-23 4 years ago / 未收藏/ icetutor发送到 kindle
I’m trying to run npm start but giving me error “An unhandled exception occurred: Could not find module “@angular-devkit/build-angular” I also tried to install it by using npm install @angular-devkit/build-angular but still getting me an error. Error “npm WARN deprecated core-jsMilan Dhameliya.6.11: core-js@&