IT牛人博客聚合网站 发现IT技术最优秀的内容, 寻找IT技术的价值 http://www.udpwork.com/ zh_CN http://www.udpwork.com/about hourly 1 Sun, 22 Oct 2017 09:10:06 +0800 <![CDATA[BGFX 的一个 lua 封装库]]> http://www.udpwork.com/item/16467.html http://www.udpwork.com/item/16467.html#reviews Sat, 21 Oct 2017 20:14:49 +0800 云风 http://www.udpwork.com/item/16467.html 前两年有同学给我推荐了BGFX这个库,第一眼被它吸引是它的口号:"Bring Your Own Engine/Framework" style rendering library 。这动不动就说自己是 3d engine 的时代,好好做好一个渲染库,仅仅做好渲染库,是多难得的一件事情。

今年国庆节的时候,偶然间我又翻到这个仓库,居然作者一直在更新。坚持了五年,一直在维护这么个小玩意,让我对这个项目多了点信心。节后我饶有兴趣的研究了一下它的代码。

现在我觉得,这个库的设计思想非常对我的胃口,核心部分几乎没有多余的东西:数据计算、平台 API 支持、数据持久化格式支持、等等都没有放在核心部分。它仅仅只做了一件事:把不同平台的图形 API :Direct X 、OpenGL 等等整合为一套统一的接口,方便在此基础上开发跨平台的 3d 图形程序。不同平台的 3d api 的差异,正是 3d 游戏开发中最脏最累的活了。

虽然 BGFX 已经有人写了 lua binding 库,但我觉得不太合我意:封装的不是很 lua 化,就是简单的 C api 包装,而且 api 覆盖也不全面。

我花了半个月的时间,重新制作了一套 lua 封装。

因为 BGFX 仅仅是渲染库,并不负责创建窗口,获取输入消息。所以我另外整合了 iup 作为窗口框架的支持。因为 iup 已经有很好的原生界面的支持,我在改写 BGFX 自带的 examples 时,就放弃了使用原本例子中用到的 imgui ,转而直接使用 iup 。

在逐个改写 BGFX 自带的例子的过程中,我了解了 bgfx 的架构和 api 设计思路,结合我对 lua 的使用经验,封装为类似但不完全一致的 lua api 。

比如原本 bgfx 的 C/C++ 接口用VertexDecl::beginVertexDecl::addVertexDecl::end等一组 api 来构建顶点结构。这是因为 C/C++ 语言本身不太适合描述这类数据。但 lua 有很灵活的数据结构支持,故而只需要一个 api 就可以搞定。

又比如 C/C++ 中用位操作组合预定义的宏来组合整数 flags 是一种常见手法,但是在 lua 中即不那么直观、也未必高效。换成字符串描述就会更好。

C++ 可以重载同名函数、可以把函数调用参数的最后几个设置默认值;而 Lua 则更加灵活,只要类型不同,默认值不一定限制在调用参数的最后几个。

Lua 有一些缺陷:不方便直接操作内存。这回导致直接翻译那些 BGFX 中和内存地址有关,例如更新动态 buffer 这种 API 便不适用了,需要找到合适的方法来封装。

我在做这项封装工作的流程中采用的是挑选有代表性的 example 翻译成 lua 版本。在人工转译的过程中,会发现依赖 BGFX 的 C/C++ API ,就随之实现这些 API 的 lua 封装版本。所以这个封装库是随着转译 examples 逐渐增加而逐步完善的。

我目前的工作环境是 windows 10 64bit ,使用 mingw64 编译器。虽然理论上这是一个跨平台库,我是用的 iup 也是跨平台的。但是目前我尚未有精力去折腾多平台的构建脚本。

有兴趣玩一下,却搞不定编译的同学可以直接下载我编译好的动态库版本。只需要写 lua 脚本就可以试用了。github 仓库在这里。 我在release页面上传了我自己编译好的二进制文件。

注意:直接行对行改写的 lua 版本的 example 有时会很低效,尤其是需要做大量数学运算的,例如 02 metaballs 这个例子就比 C 版本慢很多。如果需要兼顾效率,需要重新按 lua 的风格重新组织代码,并把重度计算的地方用 C 写一个库去计算供 lua 调用。

不过大多数 example 运行起来会发现性能和 C/C++ 版本相差无几,完全可以实用。对于做原型,尝试新的图形算法来说,lua 带来的开发便利性无可比拟。

]]>
前两年有同学给我推荐了BGFX这个库,第一眼被它吸引是它的口号:"Bring Your Own Engine/Framework" style rendering library 。这动不动就说自己是 3d engine 的时代,好好做好一个渲染库,仅仅做好渲染库,是多难得的一件事情。

今年国庆节的时候,偶然间我又翻到这个仓库,居然作者一直在更新。坚持了五年,一直在维护这么个小玩意,让我对这个项目多了点信心。节后我饶有兴趣的研究了一下它的代码。

现在我觉得,这个库的设计思想非常对我的胃口,核心部分几乎没有多余的东西:数据计算、平台 API 支持、数据持久化格式支持、等等都没有放在核心部分。它仅仅只做了一件事:把不同平台的图形 API :Direct X 、OpenGL 等等整合为一套统一的接口,方便在此基础上开发跨平台的 3d 图形程序。不同平台的 3d api 的差异,正是 3d 游戏开发中最脏最累的活了。

虽然 BGFX 已经有人写了 lua binding 库,但我觉得不太合我意:封装的不是很 lua 化,就是简单的 C api 包装,而且 api 覆盖也不全面。

我花了半个月的时间,重新制作了一套 lua 封装。

因为 BGFX 仅仅是渲染库,并不负责创建窗口,获取输入消息。所以我另外整合了 iup 作为窗口框架的支持。因为 iup 已经有很好的原生界面的支持,我在改写 BGFX 自带的 examples 时,就放弃了使用原本例子中用到的 imgui ,转而直接使用 iup 。

在逐个改写 BGFX 自带的例子的过程中,我了解了 bgfx 的架构和 api 设计思路,结合我对 lua 的使用经验,封装为类似但不完全一致的 lua api 。

比如原本 bgfx 的 C/C++ 接口用VertexDecl::beginVertexDecl::addVertexDecl::end等一组 api 来构建顶点结构。这是因为 C/C++ 语言本身不太适合描述这类数据。但 lua 有很灵活的数据结构支持,故而只需要一个 api 就可以搞定。

又比如 C/C++ 中用位操作组合预定义的宏来组合整数 flags 是一种常见手法,但是在 lua 中即不那么直观、也未必高效。换成字符串描述就会更好。

C++ 可以重载同名函数、可以把函数调用参数的最后几个设置默认值;而 Lua 则更加灵活,只要类型不同,默认值不一定限制在调用参数的最后几个。

Lua 有一些缺陷:不方便直接操作内存。这回导致直接翻译那些 BGFX 中和内存地址有关,例如更新动态 buffer 这种 API 便不适用了,需要找到合适的方法来封装。

我在做这项封装工作的流程中采用的是挑选有代表性的 example 翻译成 lua 版本。在人工转译的过程中,会发现依赖 BGFX 的 C/C++ API ,就随之实现这些 API 的 lua 封装版本。所以这个封装库是随着转译 examples 逐渐增加而逐步完善的。

我目前的工作环境是 windows 10 64bit ,使用 mingw64 编译器。虽然理论上这是一个跨平台库,我是用的 iup 也是跨平台的。但是目前我尚未有精力去折腾多平台的构建脚本。

有兴趣玩一下,却搞不定编译的同学可以直接下载我编译好的动态库版本。只需要写 lua 脚本就可以试用了。github 仓库在这里。 我在release页面上传了我自己编译好的二进制文件。

注意:直接行对行改写的 lua 版本的 example 有时会很低效,尤其是需要做大量数学运算的,例如 02 metaballs 这个例子就比 C 版本慢很多。如果需要兼顾效率,需要重新按 lua 的风格重新组织代码,并把重度计算的地方用 C 写一个库去计算供 lua 调用。

不过大多数 example 运行起来会发现性能和 C/C++ 版本相差无几,完全可以实用。对于做原型,尝试新的图形算法来说,lua 带来的开发便利性无可比拟。

]]>
0
<![CDATA[解析贴吧体验升级背后的故事I:用户洞察与交互升级]]> http://www.udpwork.com/item/16466.html http://www.udpwork.com/item/16466.html#reviews Fri, 20 Oct 2017 11:10:15 +0800 UXC http://www.udpwork.com/item/16466.html        随着产品的发展和用户群体的演变,用户对产品的认知也在发生变化,当用户认知和产品形象发生偏差时,体验升级就势在必行了。而用户对产品的认知大概体现在视觉感受(品牌形象)和使用感受(交互操作)上。对此我们从用户出发寻找设计上的突破口。

一、洞察用户让设计有的放矢

       视觉感受对于大多数人是一种难以言说的东西,难以通过简单的方式得到答案。面对这个问题,我们选择迎难而上,通过精巧的实验、可视化的方式来解决,邀请用户与设计师一起,参与到品牌升级中。以达到体验升级的同时,使贴吧品牌形象与用户的理解、期望更加贴合。

1. 关键词探索与确定

       邀请用户参与深度访谈,聆听真实的声音,了解他们眼中的贴吧,他们对贴吧有哪些期待,并由此构成,原始的贴吧品牌形象关键词库:       收集到大量的原始关键词后,如何有效地确定在用户心中,哪些关键词与贴吧的关系更为密切,成为了破题的关键点。在这里我们引入了內隐联想测试的范式,通过同时记录用户对关键词与贴吧关联的评分、以及做出评分的反应时,甄选出最能代表贴吧的品牌关键词,补充以在访谈中用户表示强期待的「简洁」,形成了如下五大核心关键词。

 

2. 衍生关键词发散

       确认核心关键词后,便需要建立与关键词相对应的图片素材库,如果通过关键词直接收集,易导致图片素材同质化过高的问题。因此在准备图片素材库前,我们组织了一场设计师为主力的头脑风暴,分别针对五大核心关键词从视觉、心境、无话的角度进行发散,并根据发散后的衍生关键词建立了多维度的视觉素材库。

3. 视觉风格偏好测试

       最后邀请用户参与测试,从素材库中选出最贴近他们对关键词理解的图片,并配合定性访谈了解偏好背后的原因,最终分析提取出不同关键词的情绪板,如下所示:

       在这五个关键词中,多元化、开放、有趣是用户对贴吧的基础感知,年轻是对贴吧的心理投射,而简洁是目前用户对贴吧的主要期待,因此根据用户的认知,可在贴吧品牌形象升级中的不同场景做到有相应的侧重。

二、提炼核心行为路径引导交互框架升级

       用户研究通过情绪板挖掘出目前用户对贴吧的主要期望——简洁。然而简洁对用户来说,是一种整体感受,这种感受也贯彻整个贴吧的框架、流程、细节。因此在交互层面,我们从框架到细节逐级进行了轻量化改造。

       移动端的交互框架趋于成熟,但却不能套用,产品的框架要基于用户的“核心行为路径”,换句话说用户在产品中做什么决定了产品的形态。对于贴吧来说,用户的核心行为路径比较清晰,主要是获取信息的路径和输出信息的路径。接下来我们对此进行了分析,并制定了轻量化改造策略。

1 、获取信息的路径分析及框架设计

       路径分析: 用户在贴吧主要通过看贴获取信息。升级前用户除了从首页推荐看贴,只能进吧。路径如下:点击进吧Tab—点击吧—点击贴子—看贴。想要切换吧只能退出当前吧重新选择。如果是新用户,只能先关注吧才能进吧。

       定位问题: 一、对于老用户来说,进吧、切换吧的成本高,吧与吧之间的壁垒大,内容比较封闭。二、对新用户不友好,关注吧、进吧的路径太长,流失较大。

       策略和目标: 优质内容显性化,降低用户获取内容的成本。贴吧在产品层面已经做了很多针对性工作,如首页推荐、智能FRS页等,但产品策略只有配合合理的交互框架才能行之有效。因此,新框架要具备以方便吧与吧之间的切换、进吧路径更短,更容易获取优质内容的特点。

2 、输出信息的行为分析及框架设计

       路径分析: UGC内容是社区类产品最重要的内容来源之一。和PGC用户有目的有策略的发布内容不同,UGC用户发布的以瞬时的、碎片化的内容居多。升级前,用户这样发贴:点击进吧tab——选择吧——点击发贴——选择发贴类型——编辑内容。对于新用户来说,学习成本很高。

       定位问题: 和竞品相比,贴吧发布内容路径较长,就像流程中有多个漏斗,降低了到达率。

       策略和目标: 发布入口前置,简化发贴流程,降低用户发贴难度。贴吧用户基于兴趣聚集,用户对吧的认知根深蒂固。既然吧不能消失,我们便将复杂性适当转移,并通过策略降低复杂性。将复杂的选吧的流程后置,让用户快速找到发布入口,避免创作热情消失。在选吧时,通过算法推荐适合发布到的吧,降低用户选吧成本。

       基于以上两个核心行为路径的分析,我们确定了贴吧的新框架:推荐内容+吧tab切换(内容显性化,用户可以调整吧顺序)发布入口提前到主Tab(入口前置,选吧流程后置)。

       框架层面核心路径的优化拉近了用户与内容之间的距离,为内容生成和内容消费搭好了桥梁。这是UGC社区运转的基础,接下来需要考虑的就是用户与内容之间,以及用户与用户之间产生关联的场景,也就是聚焦到社区内的互动。

 

三、拆解用户行为引导交互细节创新

       在贴吧,传统互动文化就是盖楼(回贴)。用户通过盖楼实现与内容、人的互动交流,通过文字或表情来表达自己的观点态度。但回贴行为本身有一定门槛,操作成本较高,不利于内容消费用户向活跃用户的转化。所以数据上能看到贴吧中内容消费的用户数量远远大于参与回复的用户数量,而给用户提供更加轻量化的互动方式,可能会增加用户参与社区互动的意愿和行为。在这个假设下,我们开始在互动层面的探索和尝试。

1 、目标期望

       在轻量化的互动方式中,点赞似乎成了社交产品的标配,一个点击就能承载很多社交情绪,用户的接受度也更高。因此我们也加入了融合贴吧文化的点赞,以示友好又无需多言,降低用户互动门槛,还能通过点赞为内容沉淀带来一些利好。除了基本的点赞玩法外,我们还尝试做了长按赞的彩蛋,对于尝鲜用户希望有一些有趣的反馈。

2 、设计过程

       点赞的设计过程可以大致拆分为两个阶段:操作与反馈。

操作 ——侧重操作的轻量高效

       单击和双击: 在操作环节中,单击点赞作为表态方式也契合轻量高效的目标。 其次,贴吧做为一个较为丰富的产品,点赞的引入必然需要同步到其他的内容层面。在直播和视频中,我们也希望能够让用户在观看过程中快速、轻量的表达态度,因此加入了双击的交互方式来实现点赞。而从产品整体性方面考量,也要考虑互动方式的一致性,所以在贴子中也同步了双击的点赞效果。

       长按: 我们把长按触发的表态作为彩蛋,用户可以选择怒赞或怒踩,更丰富的表达自我。在设计长按表态时,我们希望让用户轻松快速的选择表态,同时让表达变得更有趣,并针对这一构想开展设计。

       设计过程中产出了不同方案,例如长按后通过滑动选择,类似Pinterest的图片长按操作;长按后触发面板后面板常驻类似Facebook的Reaction。

在此过程中,我们又衍生出了不同的思考:

  • 点踩的情况相对于点赞来说较为低频
  • 在产品策略上也是更希望鼓励用户点赞,沉淀内容

       因此我们做了一些调整:用户在长按过程中默认选择怒赞。长按过程中通过动效告诉用户此时正在选择怒赞;若用户想选择怒踩,则只需向下滑动,选择怒踩。整个表态的过程又进一步简化,也满足了我们之前轻量表达的设计思路,最终选择了此方案。

反馈 ——重点落在对用户情绪给予充足的反馈

       在操作后的反馈环节方面,我们希望通过一些有趣的设计引导用户行为。例如,在贴子无人点赞时,用户首次点赞给予用户有趣的视觉反馈作为用户首赞的鼓励。

       同时,用户给自己的贴子点赞我们也认为是一个有趣的心理活动,在设计中加入了滑稽笑脸与用户情感进行互动。

       而在长按选择表态后的设计上,希望给用户更强的氛围感受,因此在用户表态后设计了对应的动画效果作为反馈,让整个表态过程显得更加有趣。考虑到用户既然选择了这种高成本的长按,应该是出于内心一定程度的感情诉求,也希望通过这种方式激励用户行为循环,让用户不断的玩下去。

       通过行为的拆解细化,来思考不同阶段的设计着重点让整个流程更加完善, 最终确定了整套点赞设计方案。此外,我们还对发贴、回贴、查看消息等用户行为进行了拆解细化,让体验升级渗透到贴吧的每个细微之处。

       贴吧体验升级是一个贯彻品牌形象和交互操作的整体改造过程。以上总结主要包含了从用户研究获得设计目标到完成交互升级的过程,贴吧品牌形象的升级会将在后续的文章中分享。

]]>
       随着产品的发展和用户群体的演变,用户对产品的认知也在发生变化,当用户认知和产品形象发生偏差时,体验升级就势在必行了。而用户对产品的认知大概体现在视觉感受(品牌形象)和使用感受(交互操作)上。对此我们从用户出发寻找设计上的突破口。

一、洞察用户让设计有的放矢

       视觉感受对于大多数人是一种难以言说的东西,难以通过简单的方式得到答案。面对这个问题,我们选择迎难而上,通过精巧的实验、可视化的方式来解决,邀请用户与设计师一起,参与到品牌升级中。以达到体验升级的同时,使贴吧品牌形象与用户的理解、期望更加贴合。

1. 关键词探索与确定

       邀请用户参与深度访谈,聆听真实的声音,了解他们眼中的贴吧,他们对贴吧有哪些期待,并由此构成,原始的贴吧品牌形象关键词库:       收集到大量的原始关键词后,如何有效地确定在用户心中,哪些关键词与贴吧的关系更为密切,成为了破题的关键点。在这里我们引入了內隐联想测试的范式,通过同时记录用户对关键词与贴吧关联的评分、以及做出评分的反应时,甄选出最能代表贴吧的品牌关键词,补充以在访谈中用户表示强期待的「简洁」,形成了如下五大核心关键词。

 

2. 衍生关键词发散

       确认核心关键词后,便需要建立与关键词相对应的图片素材库,如果通过关键词直接收集,易导致图片素材同质化过高的问题。因此在准备图片素材库前,我们组织了一场设计师为主力的头脑风暴,分别针对五大核心关键词从视觉、心境、无话的角度进行发散,并根据发散后的衍生关键词建立了多维度的视觉素材库。

3. 视觉风格偏好测试

       最后邀请用户参与测试,从素材库中选出最贴近他们对关键词理解的图片,并配合定性访谈了解偏好背后的原因,最终分析提取出不同关键词的情绪板,如下所示:

       在这五个关键词中,多元化、开放、有趣是用户对贴吧的基础感知,年轻是对贴吧的心理投射,而简洁是目前用户对贴吧的主要期待,因此根据用户的认知,可在贴吧品牌形象升级中的不同场景做到有相应的侧重。

二、提炼核心行为路径引导交互框架升级

       用户研究通过情绪板挖掘出目前用户对贴吧的主要期望——简洁。然而简洁对用户来说,是一种整体感受,这种感受也贯彻整个贴吧的框架、流程、细节。因此在交互层面,我们从框架到细节逐级进行了轻量化改造。

       移动端的交互框架趋于成熟,但却不能套用,产品的框架要基于用户的“核心行为路径”,换句话说用户在产品中做什么决定了产品的形态。对于贴吧来说,用户的核心行为路径比较清晰,主要是获取信息的路径和输出信息的路径。接下来我们对此进行了分析,并制定了轻量化改造策略。

1 、获取信息的路径分析及框架设计

       路径分析: 用户在贴吧主要通过看贴获取信息。升级前用户除了从首页推荐看贴,只能进吧。路径如下:点击进吧Tab—点击吧—点击贴子—看贴。想要切换吧只能退出当前吧重新选择。如果是新用户,只能先关注吧才能进吧。

       定位问题: 一、对于老用户来说,进吧、切换吧的成本高,吧与吧之间的壁垒大,内容比较封闭。二、对新用户不友好,关注吧、进吧的路径太长,流失较大。

       策略和目标: 优质内容显性化,降低用户获取内容的成本。贴吧在产品层面已经做了很多针对性工作,如首页推荐、智能FRS页等,但产品策略只有配合合理的交互框架才能行之有效。因此,新框架要具备以方便吧与吧之间的切换、进吧路径更短,更容易获取优质内容的特点。

2 、输出信息的行为分析及框架设计

       路径分析: UGC内容是社区类产品最重要的内容来源之一。和PGC用户有目的有策略的发布内容不同,UGC用户发布的以瞬时的、碎片化的内容居多。升级前,用户这样发贴:点击进吧tab——选择吧——点击发贴——选择发贴类型——编辑内容。对于新用户来说,学习成本很高。

       定位问题: 和竞品相比,贴吧发布内容路径较长,就像流程中有多个漏斗,降低了到达率。

       策略和目标: 发布入口前置,简化发贴流程,降低用户发贴难度。贴吧用户基于兴趣聚集,用户对吧的认知根深蒂固。既然吧不能消失,我们便将复杂性适当转移,并通过策略降低复杂性。将复杂的选吧的流程后置,让用户快速找到发布入口,避免创作热情消失。在选吧时,通过算法推荐适合发布到的吧,降低用户选吧成本。

       基于以上两个核心行为路径的分析,我们确定了贴吧的新框架:推荐内容+吧tab切换(内容显性化,用户可以调整吧顺序)发布入口提前到主Tab(入口前置,选吧流程后置)。

       框架层面核心路径的优化拉近了用户与内容之间的距离,为内容生成和内容消费搭好了桥梁。这是UGC社区运转的基础,接下来需要考虑的就是用户与内容之间,以及用户与用户之间产生关联的场景,也就是聚焦到社区内的互动。

 

三、拆解用户行为引导交互细节创新

       在贴吧,传统互动文化就是盖楼(回贴)。用户通过盖楼实现与内容、人的互动交流,通过文字或表情来表达自己的观点态度。但回贴行为本身有一定门槛,操作成本较高,不利于内容消费用户向活跃用户的转化。所以数据上能看到贴吧中内容消费的用户数量远远大于参与回复的用户数量,而给用户提供更加轻量化的互动方式,可能会增加用户参与社区互动的意愿和行为。在这个假设下,我们开始在互动层面的探索和尝试。

1 、目标期望

       在轻量化的互动方式中,点赞似乎成了社交产品的标配,一个点击就能承载很多社交情绪,用户的接受度也更高。因此我们也加入了融合贴吧文化的点赞,以示友好又无需多言,降低用户互动门槛,还能通过点赞为内容沉淀带来一些利好。除了基本的点赞玩法外,我们还尝试做了长按赞的彩蛋,对于尝鲜用户希望有一些有趣的反馈。

2 、设计过程

       点赞的设计过程可以大致拆分为两个阶段:操作与反馈。

操作 ——侧重操作的轻量高效

       单击和双击: 在操作环节中,单击点赞作为表态方式也契合轻量高效的目标。 其次,贴吧做为一个较为丰富的产品,点赞的引入必然需要同步到其他的内容层面。在直播和视频中,我们也希望能够让用户在观看过程中快速、轻量的表达态度,因此加入了双击的交互方式来实现点赞。而从产品整体性方面考量,也要考虑互动方式的一致性,所以在贴子中也同步了双击的点赞效果。

       长按: 我们把长按触发的表态作为彩蛋,用户可以选择怒赞或怒踩,更丰富的表达自我。在设计长按表态时,我们希望让用户轻松快速的选择表态,同时让表达变得更有趣,并针对这一构想开展设计。

       设计过程中产出了不同方案,例如长按后通过滑动选择,类似Pinterest的图片长按操作;长按后触发面板后面板常驻类似Facebook的Reaction。

在此过程中,我们又衍生出了不同的思考:

  • 点踩的情况相对于点赞来说较为低频
  • 在产品策略上也是更希望鼓励用户点赞,沉淀内容

       因此我们做了一些调整:用户在长按过程中默认选择怒赞。长按过程中通过动效告诉用户此时正在选择怒赞;若用户想选择怒踩,则只需向下滑动,选择怒踩。整个表态的过程又进一步简化,也满足了我们之前轻量表达的设计思路,最终选择了此方案。

反馈 ——重点落在对用户情绪给予充足的反馈

       在操作后的反馈环节方面,我们希望通过一些有趣的设计引导用户行为。例如,在贴子无人点赞时,用户首次点赞给予用户有趣的视觉反馈作为用户首赞的鼓励。

       同时,用户给自己的贴子点赞我们也认为是一个有趣的心理活动,在设计中加入了滑稽笑脸与用户情感进行互动。

       而在长按选择表态后的设计上,希望给用户更强的氛围感受,因此在用户表态后设计了对应的动画效果作为反馈,让整个表态过程显得更加有趣。考虑到用户既然选择了这种高成本的长按,应该是出于内心一定程度的感情诉求,也希望通过这种方式激励用户行为循环,让用户不断的玩下去。

       通过行为的拆解细化,来思考不同阶段的设计着重点让整个流程更加完善, 最终确定了整套点赞设计方案。此外,我们还对发贴、回贴、查看消息等用户行为进行了拆解细化,让体验升级渗透到贴吧的每个细微之处。

       贴吧体验升级是一个贯彻品牌形象和交互操作的整体改造过程。以上总结主要包含了从用户研究获得设计目标到完成交互升级的过程,贴吧品牌形象的升级会将在后续的文章中分享。

]]>
0
<![CDATA[iPhone X 下关闭 UIScrollView 自动调整 ContentInset]]> http://www.udpwork.com/item/16465.html http://www.udpwork.com/item/16465.html#reviews Thu, 19 Oct 2017 22:30:53 +0800 图拉鼎 http://www.udpwork.com/item/16465.html 项目开发了三分之一,才想到在 iPhone X 上去跑一跑,一跑吓一跳,各种 UI 上的布局错误。本着尽快将问题消灭在萌芽阶段,于是接下去决定使用 iPhone X 作为默认模拟器去开发。本篇介绍修复一则非常常见的,即 UIScrollView 的 ContentInset 不对的问题。

问题是这样的

App 在 iPhone X 上模拟器打开,中间空了一大块(不重要的部分已经打码),这是怎么回事?仔细一看,我中间的 UICollectionView(即 UIScrollView)的 ContentInset 不对了,中间空着一大块。

App on iPhone X

奇怪了,虽然我已经关掉了 UIViewController 的automaticallyAdjustsScrollViewInsets了,为什么它还是被调整了呢?

新增 UIScrollViewContentInsetAdjustmentBehavior

原来,在 iOS 11 上,UIViewController 的automaticallyAdjustsScrollViewInsets已经 deprecated 了。UIScrollView 自动调整ContentInset的行为接下去是自己管理了,而不是让 UIViewController 来管理了。

从 iOS 11 开始,我们可以通过将 UIScrollView 的contentInsetAdjustmentBehavior这个新增UIScrollViewContentInsetAdjustmentBehavior属性,设置成.never,来关掉对它的自动调整。当然了,这个值默认是.automatic,也就是会根据 NavigationBar 和 TabBar 来自动调整。

于是乎,我们的项目多了一句这样的代码(除非你的项目是 iOS 11 Only 的,你可以不用这么写):

if #available(iOS 11.0, *) {
        self.collectionView.contentInsetAdjustmentBehavior = .never
}

更好的 Storyboard 方式

如果你的项目用了较多的地方需要去针对 iOS 11 设置这个属性,你会发现到处是 available 判断真是太糟心了,能不能有方便的方式?

当然有。如果你用 Storyboard 的话,直接给 UIScrollView 的那个属性,设置 Never 就好了,不用写 iOS 版本判断,只要保证 Storyboard 的最小 iOS 部署目标正确即可。

iPhone X Adjust UIScrollView content

小结

不得不说 iPhone X 是至今为止对 iOS App 的排版布局破坏性最大的一个版本,接下去我应该还会分享好几篇相关适配文章。

本站架设于Linode 东京机房

]]>
项目开发了三分之一,才想到在 iPhone X 上去跑一跑,一跑吓一跳,各种 UI 上的布局错误。本着尽快将问题消灭在萌芽阶段,于是接下去决定使用 iPhone X 作为默认模拟器去开发。本篇介绍修复一则非常常见的,即 UIScrollView 的 ContentInset 不对的问题。

问题是这样的

App 在 iPhone X 上模拟器打开,中间空了一大块(不重要的部分已经打码),这是怎么回事?仔细一看,我中间的 UICollectionView(即 UIScrollView)的 ContentInset 不对了,中间空着一大块。

App on iPhone X

奇怪了,虽然我已经关掉了 UIViewController 的automaticallyAdjustsScrollViewInsets了,为什么它还是被调整了呢?

新增 UIScrollViewContentInsetAdjustmentBehavior

原来,在 iOS 11 上,UIViewController 的automaticallyAdjustsScrollViewInsets已经 deprecated 了。UIScrollView 自动调整ContentInset的行为接下去是自己管理了,而不是让 UIViewController 来管理了。

从 iOS 11 开始,我们可以通过将 UIScrollView 的contentInsetAdjustmentBehavior这个新增UIScrollViewContentInsetAdjustmentBehavior属性,设置成.never,来关掉对它的自动调整。当然了,这个值默认是.automatic,也就是会根据 NavigationBar 和 TabBar 来自动调整。

于是乎,我们的项目多了一句这样的代码(除非你的项目是 iOS 11 Only 的,你可以不用这么写):

if #available(iOS 11.0, *) {
        self.collectionView.contentInsetAdjustmentBehavior = .never
}

更好的 Storyboard 方式

如果你的项目用了较多的地方需要去针对 iOS 11 设置这个属性,你会发现到处是 available 判断真是太糟心了,能不能有方便的方式?

当然有。如果你用 Storyboard 的话,直接给 UIScrollView 的那个属性,设置 Never 就好了,不用写 iOS 版本判断,只要保证 Storyboard 的最小 iOS 部署目标正确即可。

iPhone X Adjust UIScrollView content

小结

不得不说 iPhone X 是至今为止对 iOS App 的排版布局破坏性最大的一个版本,接下去我应该还会分享好几篇相关适配文章。

本站架设于Linode 东京机房

]]>
0
<![CDATA[母婴人群画像与母婴消费洞察]]> http://www.udpwork.com/item/16464.html http://www.udpwork.com/item/16464.html#reviews Wed, 18 Oct 2017 15:31:11 +0800 UXC http://www.udpwork.com/item/16464.html 犹太人有句名言:“挣女人和孩子的钱是最容易的!”那么瞄准“女人+孩子”的母婴产业,是否真的前景光明呢?

 

一、母婴行业现状如何?

 

全面二孩新政带来新一波生育高峰

2011年11月,中国各地全面实施双独二孩政策;2013年12月,中国实施单独二孩政策;2015年10月,十八届五中全会公报提出实施全面二孩政策。随着国家二孩政策的逐步放开,2011年到2012年、2013年到2014年、2015年到2016年我国人口出现了较明显的增长;特别是2015年全面二孩政策的提出,再加上生肖的影响,2015年到2016年出现大幅度人口增长。

 

生育高峰带动母婴行业高速发展

在生育高峰的影响下,母婴行业规模自2014年起增速持续超过15%,2016年突破2万亿。据预测未来几年母婴行业发展前景将持续向好,2017年将达到2.59万亿元,保持15%以上的高增速。

 

如此大好形势下,如何撬动万亿级母婴市场?精准锁定用户是关键。那么母婴人群到底什么样?他们如何进行母婴消费?

带着这些疑问,百度UXC移动用研团队开展了针对母婴用户的专项研究,通过百度移动搜索及宝宝知道客户端发放在线调研问卷,从回收的问卷中筛选出母婴用户(备孕至6岁孩子的父亲或母亲)进行数据分析。

 

二、母婴用户是怎样一群人? 二胎/多胎母婴用户又有哪些特点?

 

母婴人群整体特征

女性占绝大多数;年龄相对年轻、集中在20-34岁;中等教育程度超6成,相比整体网民学历较高;全职带孩子的较多;超过半数家庭月收入在5000元以上;城镇居民超7成;居住区域上三成在华东,近两成在华中;生育一胎为主,二胎/多胎用户有所增长;近7成孩子处于孕育核心阶段(孕中期到孩子3岁);以小家庭自己照顾孩子为主。

 

本研究中引入了TGI指数。为什么引入这样一个指标呢?因为单就目标人群做人口统计学分析得出的结论会较为片面,忽视了总体人群分布的影响。比如:若全体网民中男性较多,那么某个互联网产品男性用户居多的可能性也会较大。所以只有对比总体情况,我们才能更好得出母婴人群的和其他类型产品所不同的分布特征。

而TGI指数正是可以对比总体的有效指标。TGI即Target Group Index(目标群体指数),可反映目标群体在特定研究范围(如地理区域、人口统计领域、媒体受众、产品消费者)内的强势或弱势。计算方法:TGI指数= [目标群体中具有某一特征的群体所占比例/总体中具有相同特征的群体所占比例] × 标准数100。高于100代表在目标群体中的具有某类特征的群体占比高于总体中具有相同特征群体的占比。本研究中将母婴人群数据与2017年1月中国互联网络信息中心发布的《中国互联网络发展状况统计报告》中相关的网民数据进行比对、计算出TGI数值。

 

二胎/多胎母婴用户特征

二胎/多胎母婴用户相比一胎用户年龄更大(更多30岁以上用户)、学历较低(更多中学及以下学历用户)、全职带孩子更多、收入较低(更多收入在5千元以下)、更多居住在华东华中区域。

 

 三、母婴人群在母婴消费上有什么特点?

 

母婴用户是不是真的肯为孩子花钱呢?到底会花多少钱在孩子身上呢?他们又是怎么花这笔钱的呢?

母婴花费占家庭总消费比例

母婴用户还是比较舍得为孩子花钱:超过8成用户在商品上花费占家庭总消费的10%以上,超过5成用户在服务上花费10%以上。对比不同收入的母婴用户发现,低收入用户(收入5000元及以下)在母婴商品和服务上的花费占比最高。这可能是由于低收入用户的收入较低,家庭总消费金额也较低,因此同等金额的母婴花费对低收入用户来说占家庭总消费的比例更大。这也说明母婴用户即使收入低,在母婴消费上的支出却并不含糊。

 

了解母婴商品/服务信息的渠道

购买前,母婴用户主要通过自己搜索、朋友他人推荐 来了解母婴商品/服务信息;而妈妈群、母婴社区APP等线上渠道也受到一定关注。另外,30岁以下的年轻用户 更偏好通过互联网母婴社区APP 来了解;30岁以上的年长用户 则更偏好通过妈妈群 来了解。一胎用户 更偏好通过朋友/达人推荐、互联网母婴社区APP 了解母婴商品/服务信息。这可能是由于一胎用户缺乏孕育经验,没有形成购买决策习惯;更倾向于参考其他人(朋友/达人)推荐的商品/服务。

 

购买母婴商品/服务渠道

购买时,母婴用户主要通过母婴专卖店、百货商场超市等线下渠道 来购买母婴商品/服务。其中一胎用户 则更偏好通过综合类电商、专门的母婴电商、母婴社区/APP、海外购/跨境电商、找他人代购 等新兴的购买渠道;二胎/多胎用户 更偏好通过百货商场、批发市场/杂货店 等传统线下渠道。

挑选母婴商品/服务时关注的因素

挑选商品/服务时,母婴用户重品质、轻价格 :最关注安全健康、质量、实用性 ;高于对价格的关注。但不同生育状况的用户关注的因素略有差异:一胎用户 更注重评价/口碑二胎/多胎用户 更关注价格和实用性。

 

消费最高的项目

对于母婴用户来说,用、吃、穿 最烧钱,毕竟纸尿裤、奶粉、衣服什么的都是宝宝的刚需。不同消费项目的高峰期则有所不同: 的高峰期是孩子6个月到3岁1-6岁0-1岁教育3-6岁护理孕晚期-3个月医疗健康孕期

对于奶粉、纸尿裤、玩具的品牌偏好

对于宝爸宝妈经常要购买的奶粉、纸尿裤、玩具,宝爸宝妈们的品牌偏好有哪些趋势呢?结果显示,用户推荐的奶粉、玩具品牌比较分散 ,推荐最多的品牌仅占2成;纸尿裤品牌则相对集中 ,推荐最多的品牌占近5成。

 

以上就是母婴人群的特点以及他们的母婴消费习惯,你get了吗?

]]>
犹太人有句名言:“挣女人和孩子的钱是最容易的!”那么瞄准“女人+孩子”的母婴产业,是否真的前景光明呢?

 

一、母婴行业现状如何?

 

全面二孩新政带来新一波生育高峰

2011年11月,中国各地全面实施双独二孩政策;2013年12月,中国实施单独二孩政策;2015年10月,十八届五中全会公报提出实施全面二孩政策。随着国家二孩政策的逐步放开,2011年到2012年、2013年到2014年、2015年到2016年我国人口出现了较明显的增长;特别是2015年全面二孩政策的提出,再加上生肖的影响,2015年到2016年出现大幅度人口增长。

 

生育高峰带动母婴行业高速发展

在生育高峰的影响下,母婴行业规模自2014年起增速持续超过15%,2016年突破2万亿。据预测未来几年母婴行业发展前景将持续向好,2017年将达到2.59万亿元,保持15%以上的高增速。

 

如此大好形势下,如何撬动万亿级母婴市场?精准锁定用户是关键。那么母婴人群到底什么样?他们如何进行母婴消费?

带着这些疑问,百度UXC移动用研团队开展了针对母婴用户的专项研究,通过百度移动搜索及宝宝知道客户端发放在线调研问卷,从回收的问卷中筛选出母婴用户(备孕至6岁孩子的父亲或母亲)进行数据分析。

 

二、母婴用户是怎样一群人? 二胎/多胎母婴用户又有哪些特点?

 

母婴人群整体特征

女性占绝大多数;年龄相对年轻、集中在20-34岁;中等教育程度超6成,相比整体网民学历较高;全职带孩子的较多;超过半数家庭月收入在5000元以上;城镇居民超7成;居住区域上三成在华东,近两成在华中;生育一胎为主,二胎/多胎用户有所增长;近7成孩子处于孕育核心阶段(孕中期到孩子3岁);以小家庭自己照顾孩子为主。

 

本研究中引入了TGI指数。为什么引入这样一个指标呢?因为单就目标人群做人口统计学分析得出的结论会较为片面,忽视了总体人群分布的影响。比如:若全体网民中男性较多,那么某个互联网产品男性用户居多的可能性也会较大。所以只有对比总体情况,我们才能更好得出母婴人群的和其他类型产品所不同的分布特征。

而TGI指数正是可以对比总体的有效指标。TGI即Target Group Index(目标群体指数),可反映目标群体在特定研究范围(如地理区域、人口统计领域、媒体受众、产品消费者)内的强势或弱势。计算方法:TGI指数= [目标群体中具有某一特征的群体所占比例/总体中具有相同特征的群体所占比例] × 标准数100。高于100代表在目标群体中的具有某类特征的群体占比高于总体中具有相同特征群体的占比。本研究中将母婴人群数据与2017年1月中国互联网络信息中心发布的《中国互联网络发展状况统计报告》中相关的网民数据进行比对、计算出TGI数值。

 

二胎/多胎母婴用户特征

二胎/多胎母婴用户相比一胎用户年龄更大(更多30岁以上用户)、学历较低(更多中学及以下学历用户)、全职带孩子更多、收入较低(更多收入在5千元以下)、更多居住在华东华中区域。

 

 三、母婴人群在母婴消费上有什么特点?

 

母婴用户是不是真的肯为孩子花钱呢?到底会花多少钱在孩子身上呢?他们又是怎么花这笔钱的呢?

母婴花费占家庭总消费比例

母婴用户还是比较舍得为孩子花钱:超过8成用户在商品上花费占家庭总消费的10%以上,超过5成用户在服务上花费10%以上。对比不同收入的母婴用户发现,低收入用户(收入5000元及以下)在母婴商品和服务上的花费占比最高。这可能是由于低收入用户的收入较低,家庭总消费金额也较低,因此同等金额的母婴花费对低收入用户来说占家庭总消费的比例更大。这也说明母婴用户即使收入低,在母婴消费上的支出却并不含糊。

 

了解母婴商品/服务信息的渠道

购买前,母婴用户主要通过自己搜索、朋友他人推荐 来了解母婴商品/服务信息;而妈妈群、母婴社区APP等线上渠道也受到一定关注。另外,30岁以下的年轻用户 更偏好通过互联网母婴社区APP 来了解;30岁以上的年长用户 则更偏好通过妈妈群 来了解。一胎用户 更偏好通过朋友/达人推荐、互联网母婴社区APP 了解母婴商品/服务信息。这可能是由于一胎用户缺乏孕育经验,没有形成购买决策习惯;更倾向于参考其他人(朋友/达人)推荐的商品/服务。

 

购买母婴商品/服务渠道

购买时,母婴用户主要通过母婴专卖店、百货商场超市等线下渠道 来购买母婴商品/服务。其中一胎用户 则更偏好通过综合类电商、专门的母婴电商、母婴社区/APP、海外购/跨境电商、找他人代购 等新兴的购买渠道;二胎/多胎用户 更偏好通过百货商场、批发市场/杂货店 等传统线下渠道。

挑选母婴商品/服务时关注的因素

挑选商品/服务时,母婴用户重品质、轻价格 :最关注安全健康、质量、实用性 ;高于对价格的关注。但不同生育状况的用户关注的因素略有差异:一胎用户 更注重评价/口碑二胎/多胎用户 更关注价格和实用性。

 

消费最高的项目

对于母婴用户来说,用、吃、穿 最烧钱,毕竟纸尿裤、奶粉、衣服什么的都是宝宝的刚需。不同消费项目的高峰期则有所不同: 的高峰期是孩子6个月到3岁1-6岁0-1岁教育3-6岁护理孕晚期-3个月医疗健康孕期

对于奶粉、纸尿裤、玩具的品牌偏好

对于宝爸宝妈经常要购买的奶粉、纸尿裤、玩具,宝爸宝妈们的品牌偏好有哪些趋势呢?结果显示,用户推荐的奶粉、玩具品牌比较分散 ,推荐最多的品牌仅占2成;纸尿裤品牌则相对集中 ,推荐最多的品牌占近5成。

 

以上就是母婴人群的特点以及他们的母婴消费习惯,你get了吗?

]]>
0
<![CDATA[如何过好这一生]]> http://www.udpwork.com/item/16462.html http://www.udpwork.com/item/16462.html#reviews Tue, 17 Oct 2017 08:00:00 +0800 李忠 http://www.udpwork.com/item/16462.html 这是一个很大的话题,每个人都会有自己的见解。随着年龄的增长,在焦虑感的协迫下,越来越希望尽快找到「正确答案」,来给未来的路指明方向。

怎么算找到呢?一个简单的评判方式是:当听到「xx 在 yy 时间内,通过做 zz 获得了一笔可观的收入」时,内心的波动程度。

对于「如何过好这一生」,我的答案是下面这张图,尽量往「理想模式」靠拢。

几个前提

  • 人的精力有限,过了黄金阶段之后,随着年龄的增长逐渐下滑。
  • 健康是基石,且随着年龄的增长,维护成本逐渐增加。
  • 财富是自由的基础,需要有足够的存量,但不是目的。
  • 精神乐趣最为重要。最高级、最丰富多彩以及维持最为恒久的乐趣是精神思想上的乐趣。

结论推导

  • 早日财务自由。因为年龄增长必然导致精力下降,健康维护成本增加,剩下的时间已然不多,如果再分一点给财富积累,最重要的精神乐趣就享受不了了。
  • 多花时间在精神乐趣上,最重要的事自然应该分配更多的资源。
  • 最合适的途径就是通过大量精神乐趣上的投入,带来财富上的回报。

所以,当你不知道该干什么时,读书和挣钱之间挑一个总是没错的。

]]>
这是一个很大的话题,每个人都会有自己的见解。随着年龄的增长,在焦虑感的协迫下,越来越希望尽快找到「正确答案」,来给未来的路指明方向。

怎么算找到呢?一个简单的评判方式是:当听到「xx 在 yy 时间内,通过做 zz 获得了一笔可观的收入」时,内心的波动程度。

对于「如何过好这一生」,我的答案是下面这张图,尽量往「理想模式」靠拢。

几个前提

  • 人的精力有限,过了黄金阶段之后,随着年龄的增长逐渐下滑。
  • 健康是基石,且随着年龄的增长,维护成本逐渐增加。
  • 财富是自由的基础,需要有足够的存量,但不是目的。
  • 精神乐趣最为重要。最高级、最丰富多彩以及维持最为恒久的乐趣是精神思想上的乐趣。

结论推导

  • 早日财务自由。因为年龄增长必然导致精力下降,健康维护成本增加,剩下的时间已然不多,如果再分一点给财富积累,最重要的精神乐趣就享受不了了。
  • 多花时间在精神乐趣上,最重要的事自然应该分配更多的资源。
  • 最合适的途径就是通过大量精神乐趣上的投入,带来财富上的回报。

所以,当你不知道该干什么时,读书和挣钱之间挑一个总是没错的。

]]>
0
<![CDATA[换头术]]> http://www.udpwork.com/item/16463.html http://www.udpwork.com/item/16463.html#reviews Tue, 17 Oct 2017 07:56:00 +0800 阮一峰 http://www.udpwork.com/item/16463.html 1、

我读过一本医学畅销书《最好的告别》,作者是美国医生葛文德。

他的一个观点,令我印象深刻。他说,医学的进步改变了人们对于死亡的看法。人们不再把死亡当作不可避免的自然结果,而是归因于某种技术失败。 某个治疗步骤出错了,或者技术还不够好,所以病人死了。

越来越多的人相信,死亡的原因是技术缺陷,而不是预料之中的事。死亡证明书的诊断结论,不会写死于老年,总是写着某种最终的近似原因----例如呼吸衰竭,或者心搏停止。

既然死亡是技术失败,而技术问题总可以用更好的技术解决,所以人们逐渐形成一种观念:衰老和死亡只有在反常的情况下才会发生,正常情况下是可以治疗和延迟的。

新闻媒体经常炫耀某个97岁的老人跑马拉松的故事,仿佛类似事例不是生物学奇迹,而是对所有人的合理期待。然后呢?当我们的身体不能满足这种幻觉时,我们就觉得好像需要为此感到惭愧 。

2、

我一直无法忘怀这个观点,技术是否可以阻止死亡?如果技术变得无比先进,人类是否真能将死亡推迟得足够久,活到200岁呢?

我越来越觉得,这是很有可能的。未来人类的寿命也许非常长,远超过自然的生理极限。

延长寿命的关键是什么?我认为主要就是一点:克服器官老化和衰竭,方法就是器官移植。目前,器官移植的成功率正变得越来越高,越来越多的器官可以移植。肺癌就换肺,肝癌就换肝,冠心病就换心,都有办法救回来。

台北市长柯文哲曾经是台大医院的外科权威,在一次演讲中,讲过两个他亲手处理的病例。一个女孩九天没有心跳,全靠体外循环维持生命,最后还是撑到心脏移植,活了过来;另一个病例更厉害,心脏由于严重的细菌感染都烂了,只好拿掉,没心脏撑了16天,心脏移植以后也活了下来。

随着手术技术的成熟、抗排异药物的完善、人造器官的出现,可以想象,未来的器官移植终将像拔牙那样简单易行、安全可靠。

3、

目前为止,只有一个器官,从来没有人尝试过移植,那就是脑袋。

医学上,死亡的定义就是脑死亡。也就是说,如果大脑死了,就算身体的其他部分还活着(心脏还在跳动),这个人也是死了。反过来说,如果其他部分坏死了,但是大脑还有意识,那么这个人就是还活着。

大部分人死的时候,大脑的功能其实都是好的,思维依然敏捷,就是身体的其他部分不行了,导致大脑养分供不上,于是先陷入昏迷,然后再死亡。

如果头部移植可以成功,那么人的寿命就会有本质的提高。躯干不行了,脑袋就移植到另一个躯干上,于是就可以接着活。

4、

头部移植的难度无疑是极高的,血管和神经都要正确连接。一个人的大脑如何指挥另一具身体,没有人知道能不能实现。但是,技术是那么地不可思议,我觉得没有理由怀疑可能性,未来是一定可以做到头部移植。

事实上,1970年就有人尝试,一只猴子的脑袋移植到另一只猴子身上。手术后猴子活了三天,被认为实验成功。

迄今为止,人的大脑移植还从来没有实验过。有一位意大利神经科医生 Sergio Canavero宣称2017年底前,就要完成第一例头部移植手术。他还宣称,已经在一条狗身上实验成功,将脊髓神经跟大脑连接起来,让这条瘫痪的狗重新恢复了行动能力。

他还找到了一位俄国志愿者,此人患有退化性疾病,不能行走,不能照料自己,类似英国物理学家霍金的情况,因此愿意割下自己的脑袋,让医生安装在另一具躯体上。

Canavero 医生声称,手术的第一步将是冰冻大脑和身体,阻止脑细胞死亡。然后切开脖子,将关键的动脉和静脉将连接到管子上。在进行移植之前将切断患者的脊椎。当肌肉和血液供给成功连接之后,病人将昏迷一个月时间来限制新移植头颅的活动,同时将通过电刺激让脊椎新连接得到强化。这位野心勃勃的医生相信,物理疗法将让接受头部移植手术的病人在一年内下床走路。

医学界普遍不相信这个实验,认为这不过是另一场伪科学的闹剧。但是,没有一个科学家说,头部移植是绝对不可能的。

5、

展望未来,几乎可以肯定,人类将不再是纯自然的产物,很可能一部分器官和肢体是自然的,另一部分是人工合成材料。这既是为了替换坏掉的器官,也可能是为了追求更强的功能,比如安装电动的碳纤维假肢,老年人就可以健步如飞,登高山如履平地。

美国发明家、《奇点迫近》的作者、谷歌公司工程总监雷蒙德·库兹维尔(Raymond Kurzweil)说过一句著名的话。

"虽然我像别人一样热爱自己的身体,但是如果我能依靠硅基材料活上200岁,我会毫不犹豫地放弃肉体。"

未来,器官移植和换头术一旦成熟,人的寿命可能会翻倍增加。那时,只要保住脑袋就可以了,其他部分就不太重要了,因为可以换。动画《Futurama》里面,人甚至连躯体都不需要了,就是一个头安装在底座上那样活着。

到了那个地步,人与机器就将合为一体:机器给了人更长的寿命,人给了机器灵魂。

(说明:本文选自我正在写的新书《未来世界的幸存者》,点击这里免费阅读全书。)

(完)

文档信息

]]>
1、

我读过一本医学畅销书《最好的告别》,作者是美国医生葛文德。

他的一个观点,令我印象深刻。他说,医学的进步改变了人们对于死亡的看法。人们不再把死亡当作不可避免的自然结果,而是归因于某种技术失败。 某个治疗步骤出错了,或者技术还不够好,所以病人死了。

越来越多的人相信,死亡的原因是技术缺陷,而不是预料之中的事。死亡证明书的诊断结论,不会写死于老年,总是写着某种最终的近似原因----例如呼吸衰竭,或者心搏停止。

既然死亡是技术失败,而技术问题总可以用更好的技术解决,所以人们逐渐形成一种观念:衰老和死亡只有在反常的情况下才会发生,正常情况下是可以治疗和延迟的。

新闻媒体经常炫耀某个97岁的老人跑马拉松的故事,仿佛类似事例不是生物学奇迹,而是对所有人的合理期待。然后呢?当我们的身体不能满足这种幻觉时,我们就觉得好像需要为此感到惭愧 。

2、

我一直无法忘怀这个观点,技术是否可以阻止死亡?如果技术变得无比先进,人类是否真能将死亡推迟得足够久,活到200岁呢?

我越来越觉得,这是很有可能的。未来人类的寿命也许非常长,远超过自然的生理极限。

延长寿命的关键是什么?我认为主要就是一点:克服器官老化和衰竭,方法就是器官移植。目前,器官移植的成功率正变得越来越高,越来越多的器官可以移植。肺癌就换肺,肝癌就换肝,冠心病就换心,都有办法救回来。

台北市长柯文哲曾经是台大医院的外科权威,在一次演讲中,讲过两个他亲手处理的病例。一个女孩九天没有心跳,全靠体外循环维持生命,最后还是撑到心脏移植,活了过来;另一个病例更厉害,心脏由于严重的细菌感染都烂了,只好拿掉,没心脏撑了16天,心脏移植以后也活了下来。

随着手术技术的成熟、抗排异药物的完善、人造器官的出现,可以想象,未来的器官移植终将像拔牙那样简单易行、安全可靠。

3、

目前为止,只有一个器官,从来没有人尝试过移植,那就是脑袋。

医学上,死亡的定义就是脑死亡。也就是说,如果大脑死了,就算身体的其他部分还活着(心脏还在跳动),这个人也是死了。反过来说,如果其他部分坏死了,但是大脑还有意识,那么这个人就是还活着。

大部分人死的时候,大脑的功能其实都是好的,思维依然敏捷,就是身体的其他部分不行了,导致大脑养分供不上,于是先陷入昏迷,然后再死亡。

如果头部移植可以成功,那么人的寿命就会有本质的提高。躯干不行了,脑袋就移植到另一个躯干上,于是就可以接着活。

4、

头部移植的难度无疑是极高的,血管和神经都要正确连接。一个人的大脑如何指挥另一具身体,没有人知道能不能实现。但是,技术是那么地不可思议,我觉得没有理由怀疑可能性,未来是一定可以做到头部移植。

事实上,1970年就有人尝试,一只猴子的脑袋移植到另一只猴子身上。手术后猴子活了三天,被认为实验成功。

迄今为止,人的大脑移植还从来没有实验过。有一位意大利神经科医生 Sergio Canavero宣称2017年底前,就要完成第一例头部移植手术。他还宣称,已经在一条狗身上实验成功,将脊髓神经跟大脑连接起来,让这条瘫痪的狗重新恢复了行动能力。

他还找到了一位俄国志愿者,此人患有退化性疾病,不能行走,不能照料自己,类似英国物理学家霍金的情况,因此愿意割下自己的脑袋,让医生安装在另一具躯体上。

Canavero 医生声称,手术的第一步将是冰冻大脑和身体,阻止脑细胞死亡。然后切开脖子,将关键的动脉和静脉将连接到管子上。在进行移植之前将切断患者的脊椎。当肌肉和血液供给成功连接之后,病人将昏迷一个月时间来限制新移植头颅的活动,同时将通过电刺激让脊椎新连接得到强化。这位野心勃勃的医生相信,物理疗法将让接受头部移植手术的病人在一年内下床走路。

医学界普遍不相信这个实验,认为这不过是另一场伪科学的闹剧。但是,没有一个科学家说,头部移植是绝对不可能的。

5、

展望未来,几乎可以肯定,人类将不再是纯自然的产物,很可能一部分器官和肢体是自然的,另一部分是人工合成材料。这既是为了替换坏掉的器官,也可能是为了追求更强的功能,比如安装电动的碳纤维假肢,老年人就可以健步如飞,登高山如履平地。

美国发明家、《奇点迫近》的作者、谷歌公司工程总监雷蒙德·库兹维尔(Raymond Kurzweil)说过一句著名的话。

"虽然我像别人一样热爱自己的身体,但是如果我能依靠硅基材料活上200岁,我会毫不犹豫地放弃肉体。"

未来,器官移植和换头术一旦成熟,人的寿命可能会翻倍增加。那时,只要保住脑袋就可以了,其他部分就不太重要了,因为可以换。动画《Futurama》里面,人甚至连躯体都不需要了,就是一个头安装在底座上那样活着。

到了那个地步,人与机器就将合为一体:机器给了人更长的寿命,人给了机器灵魂。

(说明:本文选自我正在写的新书《未来世界的幸存者》,点击这里免费阅读全书。)

(完)

文档信息

]]>
0
<![CDATA[有一家傻坏傻坏的公司 气得让人想笑]]> http://www.udpwork.com/item/16453.html http://www.udpwork.com/item/16453.html#reviews Fri, 13 Oct 2017 16:26:25 +0800 魏武挥 http://www.udpwork.com/item/16453.html

有一种状态叫气哭,对方太坏,自己实在受不了,哭了。

而还有一种状态叫气笑,对方不仅坏,还坏得特别傻。

于是,只好气得哑然失笑。

这么傻冒的人,痛骂ta,总觉得就像正常人骂智力残疾者一样,骂不出口啊!

 

携程APP里卖机票的小心机很多人都知道了。

简单说来,就是在你购买一张机票的时候,会被搭售一些你并不怎么需要的服务,比如机场贵宾休息室、专车券之类。这些搭售不是说不能取消,而是:

1、搭售很隐蔽,一不小心会没看到;

2、要取消的话略复杂,得点来点去;

3、搭售至少有两次,躲过了前一次,后一次依然有可能中招。

几年前,有人写文章吐槽过这件事,最近不知道为什么又被翻了出来,重新传播。

文章里提到携程用这种方式,坑了用户们一百个亿——我觉得脑回路正常的人,都知道这只是一种比方。

携程抓住这个细节,说是造谣。然后就举报,文章被删除了。

然后演员韩雪于10月9日在微博上发难这件事,这位女艺人可是有684万粉丝的主,而且也没说你携程靠这个坑了我们多少多少个亿。

携程于是有点懵逼。

好像是有点懵,因为一直到10日晚上7点半左右,携程通过一家财经媒体发了声明,才算是做了回应。在辩称什么贵宾室专车券是消费升级大背景下所为且可取消之余,重点是对机票销售做了个改进。

所以,还有一种可能:这一天,它大概都花在“改进”产品上了。

 

以下是携程在机票销售上的“改进”,图为我于10日23点48分测试时所截屏。

我选中了这张980块的机票,这里有两个“订”,上面那个附加了38块,还是很明显的搭售。我忽视。选下面那个“普通预订”吧。

神奇的,傻坏傻坏的地方出现了:

这是一个要你等上五秒的广告(我是第二秒截的)!

我当时就在想,以后哪个甲方在这里投广告,都要被用户骂五秒钟傻逼吧?

然后才进入到这个页面,的确,什么接送机券都是默认不勾选的。右上和左下两个价格中间的确只有50块差额——这是机场建设费

也就是说,携程认为,如果你不想被很麻烦地搭售各种其它东西,你就应该很麻烦地看五秒广告。

难道携程的宗旨是:不求最好!但求最麻烦?

坏是坏。

但是不是傻坏傻坏的?

不管你怎么看,我反正是气。。。笑了。

 

携程是一家很奇特的公司。

2000年3月,纳斯达克创下历史高点5048.62之后,一路掉头向下。一直到02年8月,才算见底,整个市场市值蒸发掉7成。虽然见了底,但还是一直萎靡不振,史称“互联网泡沫破灭”。

携程在02年做了一件很奇葩的事:宣布公司改名,叫携程旅行服务公司,不再有网络字样,大概生怕沾上网络就倒霉。03年12月,跑美国去上了市。

奇葩归奇葩,彼时,携程究其实质,还真不是什么网络公司。

携程有两个人数特别多的单元。第一个单元是“地推”,十几年前坐飞机的人都应该在机场里碰到过有人给你携程卡,你拿了卡就算会员了。这些人就是地推,用地推的方式发展会员。

还有一个单元是“呼叫中心”(call center),你做了会员要买机票怎么办呢?什么上网买,那时候并没有什么支付宝微信支付,买机票的方式是:打卡片上的那个电话。

所以梁建章在那个时候说了一句大实话:

公司不需要特别大的网,客人一个电话就过来了。

——摘自中国经济时报采访,02.11.22。

再往后,我们看到机场里塞卡的人就少了。地推部队越来越小,一直到13年携程一次500人裁员,才算基本上消失殆尽。不过呼叫中心依然是存在的,大部分都是客服。很少有用户买机票用电话,倒是买完之后万一有什么需要,会打电话——比如说,在携程上买机票想退改,航空公司是不管的,会让你去找携程。

与同时代的浪狐易、BAT不同,携程真的不是靠互联网业务起家的。地推+呼叫中心,和互联网有什么关系?

人应该属于传统企业转型互联网

 

携程发展到今天,业务线是很庞杂的,但仅就机票销售这一路,携程恐怕即便有优势,优势也在消失中,且,毫无办法。

这其中的原因在于作为平台,它的供给方开始垄断化。

国内现在真正意义上独立经营的航空公司已经非常少了,就我所知(我不是业内人士,可能有误,欢迎指出):国航、东航、南航、海南(大新华)航空算是各自独霸一方的。地方航空公司基本都被收编,比如上海航空就从属于东方航空,虽然你依然会买到上航承运的机票,乘坐机身刷着上海航空字样的飞机,上航空姐制服和东航的都不一样。

做平台有个前提:两边都有一定的量级,比如淘宝,一边是亿当量级的消费者,一边是百万当量级的商家。这两边,对淘宝都缺乏博弈能力,平台才坐的住。

但航空公司垄断化后,携程与它们的博弈能力肯定在降低。

最致命的一击,应该来自于官方的政策。

2015年,国资委要求三大航努力”提直降代”:在三年内实现直销机票占比提升至50%,同时机票代理费在现有基础上下降50%。

来自官方的这种要求,国内几个航空公司当然不敢怠慢。我这种一年要飞几十次的人,已然发现,在大多数情况下,国内航班,航空公司的直接销售(官网或APP),和携程这种OTA销售,没有什么价差可言。

飞的多了,当然会有一些积分,这些积分可以在APP里换机票也可以换礼品,渐渐的,携程已成了我手机中一个死掉的APP。

当然有很多人飞行次数并没有那么高频,下个APP好像太过麻烦,于是我向各位推荐微信公号,南航就属于我这两年坐得较少的航空公司,用他们家公号一样可以完成买票选座。

在我看来,单独销售机票这件事,携程已毫无优势可言,而且不会翻身。

这也就可以一窥携程如此傻坏傻坏的“改进”的背后原因。

 

在PC上,有一种非常流氓的行为,被搜狗王小川大言不惭地称之为“三级火箭”,前阵子据说要准备上市,还拿出来卖弄。

三级火箭的意思就是:你装了我的输入法(一级),就会装我的浏览器(二级),装我的浏览器,就会用我的搜索(三级),搜索当然是有现成盈利模式的。后者通过浏览器默认搜索引擎为搜狗得以实现,那么装了输入法怎么就会装浏览器了呢?

这就是很让人心烦的且不足为道的下三路技巧了。

比如说,在安装输入法这个软件包的时候,默认勾选“安装浏览器”,你要小心翼翼地去掉,才不会中招。

再比如说,在你平常使用的时候,忽然蹦出来一个窗口说你浏览器太慢,要不要改进一下呀?你点确认是安装,你点取消,还是有可能会安装!你非得点这个小窗口右上那个叉,才能躲过去。

类似三级火箭模式,不是搜狗一家,什么360,什么百度,什么腾讯,什么猎豹,都会这么干。有时候还会通过搭载别家软件“悄没声息”地就安装到你的PC里。

你要说没给你选择吧,也不尽然,就像携程这种捆绑销售,足够细心足够耐心,也是躲得过的。

但你说他们不流氓吧?恐怕也未必吧?

我算是用PC也有年头了,躲是能躲过去,但非常烦心,后来一气之下,索性抛弃了windows改投MAC阵营。因为PC上骚扰实在一年比一年猖狂。

我想不明白为啥愈演愈烈,求教一家有类似三级火箭模式的公司的朋友。人一语道破天机。

谁都知道移动才是未来,PC已经没有太大前途,为什么不在最后几年狠捞一把呢?

我豁然开朗。

 

携程对这场危机的处理无疑是很失败的。

但我依然十分同情携程公关团队。

这种产品上的“改进”,公关能起什么作用。

又是出来背锅的。

我几乎从来没在我的文章里用过表情包。

但这一次,我实在忍不住想用一个来刻画公关部兄弟姐妹们的心情:

有一家傻坏傻坏的公司 气得让人想笑,首发于扯氮集

]]>

有一种状态叫气哭,对方太坏,自己实在受不了,哭了。

而还有一种状态叫气笑,对方不仅坏,还坏得特别傻。

于是,只好气得哑然失笑。

这么傻冒的人,痛骂ta,总觉得就像正常人骂智力残疾者一样,骂不出口啊!

 

携程APP里卖机票的小心机很多人都知道了。

简单说来,就是在你购买一张机票的时候,会被搭售一些你并不怎么需要的服务,比如机场贵宾休息室、专车券之类。这些搭售不是说不能取消,而是:

1、搭售很隐蔽,一不小心会没看到;

2、要取消的话略复杂,得点来点去;

3、搭售至少有两次,躲过了前一次,后一次依然有可能中招。

几年前,有人写文章吐槽过这件事,最近不知道为什么又被翻了出来,重新传播。

文章里提到携程用这种方式,坑了用户们一百个亿——我觉得脑回路正常的人,都知道这只是一种比方。

携程抓住这个细节,说是造谣。然后就举报,文章被删除了。

然后演员韩雪于10月9日在微博上发难这件事,这位女艺人可是有684万粉丝的主,而且也没说你携程靠这个坑了我们多少多少个亿。

携程于是有点懵逼。

好像是有点懵,因为一直到10日晚上7点半左右,携程通过一家财经媒体发了声明,才算是做了回应。在辩称什么贵宾室专车券是消费升级大背景下所为且可取消之余,重点是对机票销售做了个改进。

所以,还有一种可能:这一天,它大概都花在“改进”产品上了。

 

以下是携程在机票销售上的“改进”,图为我于10日23点48分测试时所截屏。

我选中了这张980块的机票,这里有两个“订”,上面那个附加了38块,还是很明显的搭售。我忽视。选下面那个“普通预订”吧。

神奇的,傻坏傻坏的地方出现了:

这是一个要你等上五秒的广告(我是第二秒截的)!

我当时就在想,以后哪个甲方在这里投广告,都要被用户骂五秒钟傻逼吧?

然后才进入到这个页面,的确,什么接送机券都是默认不勾选的。右上和左下两个价格中间的确只有50块差额——这是机场建设费

也就是说,携程认为,如果你不想被很麻烦地搭售各种其它东西,你就应该很麻烦地看五秒广告。

难道携程的宗旨是:不求最好!但求最麻烦?

坏是坏。

但是不是傻坏傻坏的?

不管你怎么看,我反正是气。。。笑了。

 

携程是一家很奇特的公司。

2000年3月,纳斯达克创下历史高点5048.62之后,一路掉头向下。一直到02年8月,才算见底,整个市场市值蒸发掉7成。虽然见了底,但还是一直萎靡不振,史称“互联网泡沫破灭”。

携程在02年做了一件很奇葩的事:宣布公司改名,叫携程旅行服务公司,不再有网络字样,大概生怕沾上网络就倒霉。03年12月,跑美国去上了市。

奇葩归奇葩,彼时,携程究其实质,还真不是什么网络公司。

携程有两个人数特别多的单元。第一个单元是“地推”,十几年前坐飞机的人都应该在机场里碰到过有人给你携程卡,你拿了卡就算会员了。这些人就是地推,用地推的方式发展会员。

还有一个单元是“呼叫中心”(call center),你做了会员要买机票怎么办呢?什么上网买,那时候并没有什么支付宝微信支付,买机票的方式是:打卡片上的那个电话。

所以梁建章在那个时候说了一句大实话:

公司不需要特别大的网,客人一个电话就过来了。

——摘自中国经济时报采访,02.11.22。

再往后,我们看到机场里塞卡的人就少了。地推部队越来越小,一直到13年携程一次500人裁员,才算基本上消失殆尽。不过呼叫中心依然是存在的,大部分都是客服。很少有用户买机票用电话,倒是买完之后万一有什么需要,会打电话——比如说,在携程上买机票想退改,航空公司是不管的,会让你去找携程。

与同时代的浪狐易、BAT不同,携程真的不是靠互联网业务起家的。地推+呼叫中心,和互联网有什么关系?

人应该属于传统企业转型互联网

 

携程发展到今天,业务线是很庞杂的,但仅就机票销售这一路,携程恐怕即便有优势,优势也在消失中,且,毫无办法。

这其中的原因在于作为平台,它的供给方开始垄断化。

国内现在真正意义上独立经营的航空公司已经非常少了,就我所知(我不是业内人士,可能有误,欢迎指出):国航、东航、南航、海南(大新华)航空算是各自独霸一方的。地方航空公司基本都被收编,比如上海航空就从属于东方航空,虽然你依然会买到上航承运的机票,乘坐机身刷着上海航空字样的飞机,上航空姐制服和东航的都不一样。

做平台有个前提:两边都有一定的量级,比如淘宝,一边是亿当量级的消费者,一边是百万当量级的商家。这两边,对淘宝都缺乏博弈能力,平台才坐的住。

但航空公司垄断化后,携程与它们的博弈能力肯定在降低。

最致命的一击,应该来自于官方的政策。

2015年,国资委要求三大航努力”提直降代”:在三年内实现直销机票占比提升至50%,同时机票代理费在现有基础上下降50%。

来自官方的这种要求,国内几个航空公司当然不敢怠慢。我这种一年要飞几十次的人,已然发现,在大多数情况下,国内航班,航空公司的直接销售(官网或APP),和携程这种OTA销售,没有什么价差可言。

飞的多了,当然会有一些积分,这些积分可以在APP里换机票也可以换礼品,渐渐的,携程已成了我手机中一个死掉的APP。

当然有很多人飞行次数并没有那么高频,下个APP好像太过麻烦,于是我向各位推荐微信公号,南航就属于我这两年坐得较少的航空公司,用他们家公号一样可以完成买票选座。

在我看来,单独销售机票这件事,携程已毫无优势可言,而且不会翻身。

这也就可以一窥携程如此傻坏傻坏的“改进”的背后原因。

 

在PC上,有一种非常流氓的行为,被搜狗王小川大言不惭地称之为“三级火箭”,前阵子据说要准备上市,还拿出来卖弄。

三级火箭的意思就是:你装了我的输入法(一级),就会装我的浏览器(二级),装我的浏览器,就会用我的搜索(三级),搜索当然是有现成盈利模式的。后者通过浏览器默认搜索引擎为搜狗得以实现,那么装了输入法怎么就会装浏览器了呢?

这就是很让人心烦的且不足为道的下三路技巧了。

比如说,在安装输入法这个软件包的时候,默认勾选“安装浏览器”,你要小心翼翼地去掉,才不会中招。

再比如说,在你平常使用的时候,忽然蹦出来一个窗口说你浏览器太慢,要不要改进一下呀?你点确认是安装,你点取消,还是有可能会安装!你非得点这个小窗口右上那个叉,才能躲过去。

类似三级火箭模式,不是搜狗一家,什么360,什么百度,什么腾讯,什么猎豹,都会这么干。有时候还会通过搭载别家软件“悄没声息”地就安装到你的PC里。

你要说没给你选择吧,也不尽然,就像携程这种捆绑销售,足够细心足够耐心,也是躲得过的。

但你说他们不流氓吧?恐怕也未必吧?

我算是用PC也有年头了,躲是能躲过去,但非常烦心,后来一气之下,索性抛弃了windows改投MAC阵营。因为PC上骚扰实在一年比一年猖狂。

我想不明白为啥愈演愈烈,求教一家有类似三级火箭模式的公司的朋友。人一语道破天机。

谁都知道移动才是未来,PC已经没有太大前途,为什么不在最后几年狠捞一把呢?

我豁然开朗。

 

携程对这场危机的处理无疑是很失败的。

但我依然十分同情携程公关团队。

这种产品上的“改进”,公关能起什么作用。

又是出来背锅的。

我几乎从来没在我的文章里用过表情包。

但这一次,我实在忍不住想用一个来刻画公关部兄弟姐妹们的心情:

有一家傻坏傻坏的公司 气得让人想笑,首发于扯氮集

]]>
0
<![CDATA[[译] Kafka 存储的工作机制]]> http://www.udpwork.com/item/16452.html http://www.udpwork.com/item/16452.html#reviews Thu, 12 Oct 2017 19:40:40 +0800 鸟窝 http://www.udpwork.com/item/16452.html 翻译自 Kafka/Confluent 公司的工程师 Travis Jeffery 的文章:How Kafka’s Storage Internals Work

通过本文我会帮助你理解Kafka是如何存储它的数据的。
对于调优Kafka的性能以及了解broker配置实际是干什么的, 了解Kafka的存储很有用。 我受Kafka的简单性的启发, 用我所学开始实现一个Go的Kafka:jocko

那么, Kafka存储内部是如何工作的呢?

Kafka 的存储单元是分区

分区(partition)是有序的, 新的不可变的消息增加到尾部。一个分区不能扩多个boker,甚至不能跨多个磁盘。

保留策略管理kakfa如何保留消息

你可以指定保留多少数据和多久的数据(大小和时间策略), 之后kakfa会按照顺序清理数据, 不管数据是否已经倍消费。

分区被分割成多个分段

所以Kafka会定期地查找磁盘中需要清理的消息。如果一个分区单一的文件比较打, 操作会很慢,而且容易出错。为了解决这个问题(和其它问题), 分区文件被分割成几个分段(segment)。

当Kafka往分区中写数据时, 它实际是往分段文件中写的。如果这个分段文件达到文件大小的限制,一个新的分段文件会被创建,以后往这个分区上写的数据会写入到这个新的分段文件中。

分段文件使用它们的基偏移量(base offset)作为文件名。 一个分段文件的基偏移量 要大于前一个分段中的偏移量, 小于或者等于本分段文件中的偏移量。

在磁盘中, 每个分区一个文件夹, 包含分段文件, 分段文件由索引文件和log文件两个文件组成。

1234567
$ tree Kafka | head -n 6Kafka├── events-1│ ├── 00000000003064504069.index│ ├── 00000000003064504069.log│ ├── 00000000003065011416.index│ ├── 00000000003065011416.log

分段日志文件存储消息

每个消息包含它的值,偏移量,时间戳,key,消息大小、编解码器器、checksum 以及消息的版本。

磁盘上的数据格式和broker从producer接收到网络消息时一样的, 也和发送给consumer的消息格式一样。这种设计的好处时可以使用零拷贝进行数据传输。

12345
$ bin/Kafka-run-class.sh Kafka.tools.DumpLogSegments --deep-iteration --print-data-log --files /data/Kafka/events-1/00000000003065011416.log | head -n 4Dumping /data/Kafka/appusers-1/00000000003065011416.logStarting offset: 3065011416offset: 3065011416 position: 0 isvalid: true payloadsize: 2820 magic: 1 compresscodec: NoCompressionCodec crc: 811055132 payload: {"name": "Travis", msg: "Hey, what's up?"}offset: 3065011417 position: 1779 isvalid: true payloadsize: 2244 magic: 1 compresscodec: NoCompressionCodec crc: 151590202 payload: {"name": "Wale", msg: "Starving."}

分段索引文件记录消息在日志文件中的位置(偏移量)

分段索引文件记录消息在日志文件中的位置(偏移量)。

索引文件映射到内存中。偏移量的查找使用二分查找, 找到最接近偏移量的位置(小于或者等于目标偏移量)。

索引文件是由8字节的entry组成。4个字节存储相相对偏移量, 4个字节存储位置。相对偏移量加上基偏移量才是实际偏移量,这样只用4个字节久可以了。 例如,假设基偏移量是10000000000000000000,之后的偏移量10000000000000000001、10000000000000000002只需用1、2来表示即可。

Kafka保持压缩的消息

Producer发送的一批消息会呗压缩在一起,作为一个消息的payload发送给broker。和前面的介绍一样,数据保持原样压缩存在磁盘上。

回顾

Now you know how Kafka storage internals work:

现在, 你应该了解了Kafka是如何存储日志(消息)的:

  • 分区时存储的基本单元
  • 分区文件被分割成分段文件
  • 分段文件包含两个文件:索引文件和日志
  • 索引文件记录消息在日志文件中的位置,用来快速查询消息
  • 索引文件中存在的是相对偏移值
  • 压缩的批量消息保持不变存储
  • 磁盘上的文件和收发的消息时一样的

实现Go语言的kafak

我正在用Go实现Kafka。 目前我已经实现了在单个broker上读写分段,正在实现分布式。欢迎贡献。

]]>
翻译自 Kafka/Confluent 公司的工程师 Travis Jeffery 的文章:How Kafka’s Storage Internals Work

通过本文我会帮助你理解Kafka是如何存储它的数据的。
对于调优Kafka的性能以及了解broker配置实际是干什么的, 了解Kafka的存储很有用。 我受Kafka的简单性的启发, 用我所学开始实现一个Go的Kafka:jocko

那么, Kafka存储内部是如何工作的呢?

Kafka 的存储单元是分区

分区(partition)是有序的, 新的不可变的消息增加到尾部。一个分区不能扩多个boker,甚至不能跨多个磁盘。

保留策略管理kakfa如何保留消息

你可以指定保留多少数据和多久的数据(大小和时间策略), 之后kakfa会按照顺序清理数据, 不管数据是否已经倍消费。

分区被分割成多个分段

所以Kafka会定期地查找磁盘中需要清理的消息。如果一个分区单一的文件比较打, 操作会很慢,而且容易出错。为了解决这个问题(和其它问题), 分区文件被分割成几个分段(segment)。

当Kafka往分区中写数据时, 它实际是往分段文件中写的。如果这个分段文件达到文件大小的限制,一个新的分段文件会被创建,以后往这个分区上写的数据会写入到这个新的分段文件中。

分段文件使用它们的基偏移量(base offset)作为文件名。 一个分段文件的基偏移量 要大于前一个分段中的偏移量, 小于或者等于本分段文件中的偏移量。

在磁盘中, 每个分区一个文件夹, 包含分段文件, 分段文件由索引文件和log文件两个文件组成。

1234567
$ tree Kafka | head -n 6Kafka├── events-1│ ├── 00000000003064504069.index│ ├── 00000000003064504069.log│ ├── 00000000003065011416.index│ ├── 00000000003065011416.log

分段日志文件存储消息

每个消息包含它的值,偏移量,时间戳,key,消息大小、编解码器器、checksum 以及消息的版本。

磁盘上的数据格式和broker从producer接收到网络消息时一样的, 也和发送给consumer的消息格式一样。这种设计的好处时可以使用零拷贝进行数据传输。

12345
$ bin/Kafka-run-class.sh Kafka.tools.DumpLogSegments --deep-iteration --print-data-log --files /data/Kafka/events-1/00000000003065011416.log | head -n 4Dumping /data/Kafka/appusers-1/00000000003065011416.logStarting offset: 3065011416offset: 3065011416 position: 0 isvalid: true payloadsize: 2820 magic: 1 compresscodec: NoCompressionCodec crc: 811055132 payload: {"name": "Travis", msg: "Hey, what's up?"}offset: 3065011417 position: 1779 isvalid: true payloadsize: 2244 magic: 1 compresscodec: NoCompressionCodec crc: 151590202 payload: {"name": "Wale", msg: "Starving."}

分段索引文件记录消息在日志文件中的位置(偏移量)

分段索引文件记录消息在日志文件中的位置(偏移量)。

索引文件映射到内存中。偏移量的查找使用二分查找, 找到最接近偏移量的位置(小于或者等于目标偏移量)。

索引文件是由8字节的entry组成。4个字节存储相相对偏移量, 4个字节存储位置。相对偏移量加上基偏移量才是实际偏移量,这样只用4个字节久可以了。 例如,假设基偏移量是10000000000000000000,之后的偏移量10000000000000000001、10000000000000000002只需用1、2来表示即可。

Kafka保持压缩的消息

Producer发送的一批消息会呗压缩在一起,作为一个消息的payload发送给broker。和前面的介绍一样,数据保持原样压缩存在磁盘上。

回顾

Now you know how Kafka storage internals work:

现在, 你应该了解了Kafka是如何存储日志(消息)的:

  • 分区时存储的基本单元
  • 分区文件被分割成分段文件
  • 分段文件包含两个文件:索引文件和日志
  • 索引文件记录消息在日志文件中的位置,用来快速查询消息
  • 索引文件中存在的是相对偏移值
  • 压缩的批量消息保持不变存储
  • 磁盘上的文件和收发的消息时一样的

实现Go语言的kafak

我正在用Go实现Kafka。 目前我已经实现了在单个broker上读写分段,正在实现分布式。欢迎贡献。

]]>
0
<![CDATA[游戏和游戏化(上)]]> http://www.udpwork.com/item/16451.html http://www.udpwork.com/item/16451.html#reviews Wed, 11 Oct 2017 22:02:40 +0800 唐巧 http://www.udpwork.com/item/16451.html 引言

最近看完了两本书:《游戏改变世界》和《游戏化思维》。前者是讲游戏的,后者是讲游戏化的。

《游戏改变世界》的作者简•麦戈尼格尔 (Jane McGonigal) 认为游戏不但好,而且建立了相对于真实社会的一种“平行宇宙”,进入游戏其实就像进入了另一个社会一样。所以本书的英文名更为贴切,叫:《Reality is Broken》。作者从头到尾都在夸游戏中的社会是如何如何好,现实是如何如何让人沮丧。

《游戏化思维》从另外一个角度来讲游戏:主要是将游戏中的一些元素融入到非游戏中,使得人们更加轻松地完成一些现实中的任务。

我们先说游戏吧。

游戏

《游戏改变世界》认为所有游戏都有一个决定性的特征:目标、规则、反馈系统和自愿参与。前三者比较好理解,最后一项自愿参与:要求所有玩游戏的人都了解并愿意接受目标,规则和反馈。这一条其实建立了玩家的安全感,保证玩家把游戏中的高压挑战当作愉快的活动。

《游戏改变世界》将游戏世界对现实世界的改造比喻成“补丁”。在书中,作者一共介绍了 14 个补丁,用于描述游戏世界相对于现实世界的优越性。

但是,我觉得作者的解释过于拖沓,并且没有重点。其实游戏世界相对于现实世界的最最核心的理由是:游戏世界构造了一些机制,使得玩家一直处于一种心流(flow)的状态。所谓的心流,是指游戏构造了一种合适的难度,这种难度使得玩家一直处于自己可以在能力上“够得着”的状态。在书中,这种状态被称作:”个人最优化的障碍”,这种状态可以让人始终产生成就感。

这种持续的成就感很快就会让人上瘾。上瘾之后,游戏会加大难度,使得大家为了再次体会到类似的快感欲罢不能。我们来看几个例子。

俄罗斯方块恐怕是最简单又让人上瘾的游戏了。在游戏的刚开始,一切操作速度都很慢,玩家很容易适应游戏的节奏。然后俄罗斯方块通过各种即时反馈,让人们感觉到”正反馈“。比如随时在增加的分数,消除掉多行的快感,于是人们觉得”有点意思“。但是这种心流状态很快随着熟练就丧失了,于是游戏的难度加大,你不得不集中精力来处理快速下落的方块,最终你输了,虽然有所遗憾,但是你得到了历史最高的分数。于是你顾不得休息,又开始了新一轮的尝试。

最近流行的游戏王者荣耀也是这样。如果你也玩王者荣耀,并且是iOS平台的玩家,建议你尝试拿个Android手机重新玩一次,由于苹果的限制,在王者荣耀中iOS和Android 手机并不能互通角色。然后你就可以重新体验一次游戏的整个生命期。

我玩了大概半年的王者荣耀,一次偶然的机会,我拿起身边的一台 Android 测试机重新登录了一个新号。于是我立马发现了王者荣耀为新手构建了一个异常强大的”个人最优化的障碍”。在我用 Android 重新玩的那几天,王者荣耀为我模拟了一个非常舒服的比赛环境,我在游戏中把把拿 MVP,几乎没有输过。我突然想到,我在 iOS 平台刚开始玩黄忠的时候,基本上每次都赢,但是之后才发现黄忠其实被很多英雄相克,我很好奇,在相当长的几个月中,我都没有遇到过一次黄忠的克星。

所以,我几乎可以肯定的是:王者荣耀为玩家构建了一个相当长时期的保护期。在这个保护期内,你就如同进入了 iOS 的沙盒环境,所有的对手都是为你特别准备的,刚好够让你杀个痛快还有一点挑战。我有一次细心观察了一下,对手有一个走位明显就是机器人。我才一下子明白了,原来这个环境竟然是虚拟的。

所以大家就可以明白了,为什么有那么多妹子喜欢玩王者荣耀,其实游戏玩得好的女生非常少,但是王者荣耀的”个人最优化的障碍”机制,使得很多陪练参与其中,真实的玩家得到了极大地鼓励。当然,你如果一直这么玩,难道水平就不会有涨进吗?答案是肯定的,这就像《异类》中提到的那些成功人士一样让人羡慕。

在《异类》中,作者分析发现那些各种所谓的天才,其实只是比别人更加早完成 10000 小时练习而已,而他们的坚持动力,很大程度上都来自于刚开始比别人稍微强一点点,这稍微一点点的优势,成为了他的激励和成就感来源。这其实就是真实社会的”个人最优化的障碍”。在书中印象最深的是加拿大冰球队队员大多集中在 1 月份的例子:

加拿大冰球队按年龄分组所依据的分界线是 1 月 1 日,即从 1 月 1 日到当年 12 月 31 日之间出生的球员将会被分在同一组。也就是说,一个 1 月 1 日出生的选手,是在跟许多年纪比他小的队友争夺晋级权。

而年龄大几个月而显现的微弱优势,会在孩子的成长过程中不断积累,最终引导孩子走向成功或不成功,自信或不自信的轨道中,其影响会延伸许多年。

社会学家罗伯特·默顿援引《新约·马太福音》,把这种现象叫作 “马太效应”。“凡是有的,还要加给他,叫他有余;没有的,连他所有的,也要夺过来。” 成功者,换句话说,就是获得这些特殊机遇的人,他们因此最终取得了更大的进步。

我们脑洞一下,如果整个人类社会都是虚拟的,然后整个社会为了某个真实玩家而构建”个人最优化的障碍”,例如让你每次努力和别人比赛的时候都能赢。每次成绩都获得家长、同学、老师的肯定。当然,给玩家刻意构造几次挫折和失败也是必要的。然后社会再给这个玩家各种千载难逢的机会。最终这个人从虚拟社会中”毕业“的时候,他会不会在各个需要后天培养的地方都达到我们理想中的标准?我觉得答案是肯定的。

这个设定在各种科幻题材的作品中常常看到。比如经典的电影《黑客帝国》,人类就生存在一个机器构造的虚拟世界(电影中叫Matrix)中。在美剧《Rick and Morty》的第一季第四集,也脑洞了一个完全虚拟的世界(下图)。

需要注意的是,”个人最优化的障碍”并不意味着一定要让玩家通关。在玩家对于游戏熟悉之后,通关并不是最爽的,相反,不能通关反倒是让人欲罢不能的事情。

国内的火爆游戏“天天爱消除”,其实就是让你一遍一遍不能通关,让你产生反复尝试的动力。最终有些人买了道具通关了,其实他的乐趣也结束了。“王者荣耀”也是这样,最终人们会陷在游戏给他的一个几乎不能完成的目标里面不能自拔,但是自己还乐在其中。

其它因素

当然,一款游戏光有”个人最优化的障碍”也是不够的,书中介绍的清晰的目标和规则、即时的反馈、低成本的失败压力、多人协作等元素,也是游戏成功必不可少的环节。

“个人最优化的障碍”的实现相对来说是有很多套路的。但是游戏的目标和规则却是真正比拼创意的事情。很多成功的游戏都开创了一些新的玩法从而成功。“王者荣耀”将 PC 时代的 DOTA 成功简化到手机上,优化掉各种装备购买,补刀等逻辑,这里面肯定花费了大量精力来做可玩性的设计。

单单是一款游戏里面的各种参数值,都会对游戏产生致命性影响。高中时我玩过一个 PC 网游叫“决战”,那款游戏的失败之处就在于法师的数值远高于别的职业,造成游戏完全没有平衡性可言。所以游戏开发里面专门有“数值设定”这个职位,他们的工作就是一遍一遍地调整游戏参数,使得整个世界的平衡性得以保证。

小结

总结一下,一款成功的游戏都有着”个人最优化的障碍”作为基础,在这之上,通过清晰的目标和规则、即时的反馈、低成本的失败压力、社交元素来构建出一个让人上瘾的环境。

]]>
引言

最近看完了两本书:《游戏改变世界》和《游戏化思维》。前者是讲游戏的,后者是讲游戏化的。

《游戏改变世界》的作者简•麦戈尼格尔 (Jane McGonigal) 认为游戏不但好,而且建立了相对于真实社会的一种“平行宇宙”,进入游戏其实就像进入了另一个社会一样。所以本书的英文名更为贴切,叫:《Reality is Broken》。作者从头到尾都在夸游戏中的社会是如何如何好,现实是如何如何让人沮丧。

《游戏化思维》从另外一个角度来讲游戏:主要是将游戏中的一些元素融入到非游戏中,使得人们更加轻松地完成一些现实中的任务。

我们先说游戏吧。

游戏

《游戏改变世界》认为所有游戏都有一个决定性的特征:目标、规则、反馈系统和自愿参与。前三者比较好理解,最后一项自愿参与:要求所有玩游戏的人都了解并愿意接受目标,规则和反馈。这一条其实建立了玩家的安全感,保证玩家把游戏中的高压挑战当作愉快的活动。

《游戏改变世界》将游戏世界对现实世界的改造比喻成“补丁”。在书中,作者一共介绍了 14 个补丁,用于描述游戏世界相对于现实世界的优越性。

但是,我觉得作者的解释过于拖沓,并且没有重点。其实游戏世界相对于现实世界的最最核心的理由是:游戏世界构造了一些机制,使得玩家一直处于一种心流(flow)的状态。所谓的心流,是指游戏构造了一种合适的难度,这种难度使得玩家一直处于自己可以在能力上“够得着”的状态。在书中,这种状态被称作:”个人最优化的障碍”,这种状态可以让人始终产生成就感。

这种持续的成就感很快就会让人上瘾。上瘾之后,游戏会加大难度,使得大家为了再次体会到类似的快感欲罢不能。我们来看几个例子。

俄罗斯方块恐怕是最简单又让人上瘾的游戏了。在游戏的刚开始,一切操作速度都很慢,玩家很容易适应游戏的节奏。然后俄罗斯方块通过各种即时反馈,让人们感觉到”正反馈“。比如随时在增加的分数,消除掉多行的快感,于是人们觉得”有点意思“。但是这种心流状态很快随着熟练就丧失了,于是游戏的难度加大,你不得不集中精力来处理快速下落的方块,最终你输了,虽然有所遗憾,但是你得到了历史最高的分数。于是你顾不得休息,又开始了新一轮的尝试。

最近流行的游戏王者荣耀也是这样。如果你也玩王者荣耀,并且是iOS平台的玩家,建议你尝试拿个Android手机重新玩一次,由于苹果的限制,在王者荣耀中iOS和Android 手机并不能互通角色。然后你就可以重新体验一次游戏的整个生命期。

我玩了大概半年的王者荣耀,一次偶然的机会,我拿起身边的一台 Android 测试机重新登录了一个新号。于是我立马发现了王者荣耀为新手构建了一个异常强大的”个人最优化的障碍”。在我用 Android 重新玩的那几天,王者荣耀为我模拟了一个非常舒服的比赛环境,我在游戏中把把拿 MVP,几乎没有输过。我突然想到,我在 iOS 平台刚开始玩黄忠的时候,基本上每次都赢,但是之后才发现黄忠其实被很多英雄相克,我很好奇,在相当长的几个月中,我都没有遇到过一次黄忠的克星。

所以,我几乎可以肯定的是:王者荣耀为玩家构建了一个相当长时期的保护期。在这个保护期内,你就如同进入了 iOS 的沙盒环境,所有的对手都是为你特别准备的,刚好够让你杀个痛快还有一点挑战。我有一次细心观察了一下,对手有一个走位明显就是机器人。我才一下子明白了,原来这个环境竟然是虚拟的。

所以大家就可以明白了,为什么有那么多妹子喜欢玩王者荣耀,其实游戏玩得好的女生非常少,但是王者荣耀的”个人最优化的障碍”机制,使得很多陪练参与其中,真实的玩家得到了极大地鼓励。当然,你如果一直这么玩,难道水平就不会有涨进吗?答案是肯定的,这就像《异类》中提到的那些成功人士一样让人羡慕。

在《异类》中,作者分析发现那些各种所谓的天才,其实只是比别人更加早完成 10000 小时练习而已,而他们的坚持动力,很大程度上都来自于刚开始比别人稍微强一点点,这稍微一点点的优势,成为了他的激励和成就感来源。这其实就是真实社会的”个人最优化的障碍”。在书中印象最深的是加拿大冰球队队员大多集中在 1 月份的例子:

加拿大冰球队按年龄分组所依据的分界线是 1 月 1 日,即从 1 月 1 日到当年 12 月 31 日之间出生的球员将会被分在同一组。也就是说,一个 1 月 1 日出生的选手,是在跟许多年纪比他小的队友争夺晋级权。

而年龄大几个月而显现的微弱优势,会在孩子的成长过程中不断积累,最终引导孩子走向成功或不成功,自信或不自信的轨道中,其影响会延伸许多年。

社会学家罗伯特·默顿援引《新约·马太福音》,把这种现象叫作 “马太效应”。“凡是有的,还要加给他,叫他有余;没有的,连他所有的,也要夺过来。” 成功者,换句话说,就是获得这些特殊机遇的人,他们因此最终取得了更大的进步。

我们脑洞一下,如果整个人类社会都是虚拟的,然后整个社会为了某个真实玩家而构建”个人最优化的障碍”,例如让你每次努力和别人比赛的时候都能赢。每次成绩都获得家长、同学、老师的肯定。当然,给玩家刻意构造几次挫折和失败也是必要的。然后社会再给这个玩家各种千载难逢的机会。最终这个人从虚拟社会中”毕业“的时候,他会不会在各个需要后天培养的地方都达到我们理想中的标准?我觉得答案是肯定的。

这个设定在各种科幻题材的作品中常常看到。比如经典的电影《黑客帝国》,人类就生存在一个机器构造的虚拟世界(电影中叫Matrix)中。在美剧《Rick and Morty》的第一季第四集,也脑洞了一个完全虚拟的世界(下图)。

需要注意的是,”个人最优化的障碍”并不意味着一定要让玩家通关。在玩家对于游戏熟悉之后,通关并不是最爽的,相反,不能通关反倒是让人欲罢不能的事情。

国内的火爆游戏“天天爱消除”,其实就是让你一遍一遍不能通关,让你产生反复尝试的动力。最终有些人买了道具通关了,其实他的乐趣也结束了。“王者荣耀”也是这样,最终人们会陷在游戏给他的一个几乎不能完成的目标里面不能自拔,但是自己还乐在其中。

其它因素

当然,一款游戏光有”个人最优化的障碍”也是不够的,书中介绍的清晰的目标和规则、即时的反馈、低成本的失败压力、多人协作等元素,也是游戏成功必不可少的环节。

“个人最优化的障碍”的实现相对来说是有很多套路的。但是游戏的目标和规则却是真正比拼创意的事情。很多成功的游戏都开创了一些新的玩法从而成功。“王者荣耀”将 PC 时代的 DOTA 成功简化到手机上,优化掉各种装备购买,补刀等逻辑,这里面肯定花费了大量精力来做可玩性的设计。

单单是一款游戏里面的各种参数值,都会对游戏产生致命性影响。高中时我玩过一个 PC 网游叫“决战”,那款游戏的失败之处就在于法师的数值远高于别的职业,造成游戏完全没有平衡性可言。所以游戏开发里面专门有“数值设定”这个职位,他们的工作就是一遍一遍地调整游戏参数,使得整个世界的平衡性得以保证。

小结

总结一下,一款成功的游戏都有着”个人最优化的障碍”作为基础,在这之上,通过清晰的目标和规则、即时的反馈、低成本的失败压力、社交元素来构建出一个让人上瘾的环境。

]]>
0
<![CDATA[开源许可证教程]]> http://www.udpwork.com/item/16450.html http://www.udpwork.com/item/16450.html#reviews Wed, 11 Oct 2017 20:30:17 +0800 阮一峰 http://www.udpwork.com/item/16450.html 作为一个开发者,如果你打算开源自己的代码,千万不要忘记,选择一种开源许可证(license)。

许多开发者对开源许可证了解很少,不清楚有哪些许可证,应该怎么选择。本文介绍开源许可证的基本知识,主要参考了 OpenSource.com (12)。

一、什么是开源许可证

开源许可证是一种法律许可。通过它,版权拥有人明确允许,用户可以免费地使用、修改、共享版权软件。

版权法默认禁止共享,也就是说,没有许可证的软件,就等同于保留版权,虽然开源了,用户只能看看源码,不能用,一用就会侵犯版权。所以软件开源的话,必须明确地授予用户开源许可证。

二、开源许可证的种类

目前,国际公认的开源许可证共有80多种。它们的共同特征是,都允许用户免费地使用、修改、共享源码,但是都有各自的使用条件。

如果一种开源许可证没有任何使用条件,连保留作者信息都不需要,那么就等同于放弃版权了。这时,软件可以直接声明进入"公共领域"(public domain)。

根据使用条件的不同,开源许可证分成两大类。

  • 宽松式(permissive)许可证
  • Copyleft 许可证

三、宽松式许可证

3.1 特点

宽松式许可证(permissive license)是最基本的类型,对用户几乎没有限制。用户可以修改代码后闭源。

它有三个基本特点。

(1)没有使用限制

用户可以使用代码,做任何想做的事情。

(2)没有担保

不保证代码质量,用户自担风险。

(3)披露要求(notice requirement)

用户必须披露原始作者。

3.2 常见许可证

常见的宽松式许可证有四种。它们都允许用户任意使用代码,区别在于要求用户遵守的条件不同。

(1)BSD(二条款版)

分发软件时,必须保留原始的许可证声明。

(2) BSD(三条款版)

分发软件时,必须保留原始的许可证声明。不得使用原始作者的名字为软件促销。

(3)MIT

分发软件时,必须保留原始的许可证声明,与 BSD(二条款版)基本一致。

(4)Apache 2

分发软件时,必须保留原始的许可证声明。凡是修改过的文件,必须向用户说明该文件修改过;没有修改过的文件,必须保持许可证不变。

四、Copyleft 许可证

4.1 Copyleft 的含义

Copyleft 是理查德·斯托曼发明的一个词,作为 Copyright (版权)的反义词。

Copyright 直译是"复制权",这是版权制度的核心,意为不经许可,用户无权复制。作为反义词,Copyleft 的含义是不经许可,用户可以随意复制。

但是,它带有前提条件,比宽松式许可证的限制要多。

  • 如果分发二进制格式,必须提供源码
  • 修改后的源码,必须与修改前保持许可证一致
  • 不得在原始许可证以外,附加其他限制

上面三个条件的核心就是:修改后的 Copyleft 代码不得闭源。

4.2 常见许可证

常见的 Copyleft 许可证也有四种(对用户的限制从最强到最弱排序)。

(1)Affero GPL (AGPL)

如果云服务(即 SAAS)用到的代码是该许可证,那么云服务的代码也必须开源。

(2)GPL

如果项目包含了 GPL 许可证的代码,那么整个项目都必须使用 GPL 许可证。

(3)LGPL

如果项目采用动态链接调用该许可证的库,项目可以不用开源。

(4)Mozilla(MPL)

只要该许可证的代码在单独的文件中,新增的其他文件可以不用开源。

五、常见问题

本节回答一些开源许可证的常见问题。

5.1 什么叫分发(distribution)?

除了 Affero GPL (AGPL) ,其他许可证都规定只有在"分发"时,才需要遵守许可证。换言之,如果不"分发",就不需要遵守。

简单说,分发就是指将版权作品从一个人转移到另一个人。这意味着,如果你是自己使用,不提供给他人,就没有分发。另外,这里的"人"也指"法人",因此如果使用方是公司,且只在公司内部使用,也不需要遵守许可证。

云服务(SaaS)是否构成"分发"呢?答案是不构成。所以你使用开源软件提供云服务,不必提供源码。但是,Affero GPL (AGPL) 许可证除外,它规定云服务也必须提供源码。

5.2 开源软件的专利如何处理?

某些许可证(Apache 2 和 GPL v3)包含明确的条款,授予用户许可,使用软件所包含的所有专利。

另一些许可证(BSD、MIT 和 GPL v2)根本没提到专利。但是一般认为,它们默认给予用户专利许可,不构成侵犯专利。

总得来说,除非有明确的"保留专利"的条款,使用开源软件都不会构成侵犯专利。

5.3 什么是披露要求?

所有的开源许可证都带有"披露要求"(notice requirement),即要求软件的分发者必须向用户披露,软件里面有开源代码。

一般来说,你只要在软件里面提供完整的原始许可证文本,并且披露原始作者,就满足了"披露要求"。

5.4 GPL 病毒是真的吗?

GPL 许可证规定,只要你的项目包含了 GPL 代码,整个项目就都变成了 GPL。有人把这种传染性比喻成"GPL 病毒"。

很多公司希望避开这个条款,既使用 GPL 软件,又不把自己的专有代码开源。理论上,这是做不到的。因为 GPL 的设计目的,就是为了防止出现这种情况。

但是实际上,不遵守 GPL,最坏情况就是被起诉。如果你向法院表示无法履行 GPL 的条件,法官只会判决你停止使用 GPL 代码(法律上叫做"停止侵害"),而不会强制要求你将源码开源,因为《版权法》里面的"违约救济"没有提到违约者必须开源,只提到可以停止侵害和赔偿损失。

(完)

文档信息

]]>
作为一个开发者,如果你打算开源自己的代码,千万不要忘记,选择一种开源许可证(license)。

许多开发者对开源许可证了解很少,不清楚有哪些许可证,应该怎么选择。本文介绍开源许可证的基本知识,主要参考了 OpenSource.com (12)。

一、什么是开源许可证

开源许可证是一种法律许可。通过它,版权拥有人明确允许,用户可以免费地使用、修改、共享版权软件。

版权法默认禁止共享,也就是说,没有许可证的软件,就等同于保留版权,虽然开源了,用户只能看看源码,不能用,一用就会侵犯版权。所以软件开源的话,必须明确地授予用户开源许可证。

二、开源许可证的种类

目前,国际公认的开源许可证共有80多种。它们的共同特征是,都允许用户免费地使用、修改、共享源码,但是都有各自的使用条件。

如果一种开源许可证没有任何使用条件,连保留作者信息都不需要,那么就等同于放弃版权了。这时,软件可以直接声明进入"公共领域"(public domain)。

根据使用条件的不同,开源许可证分成两大类。

  • 宽松式(permissive)许可证
  • Copyleft 许可证

三、宽松式许可证

3.1 特点

宽松式许可证(permissive license)是最基本的类型,对用户几乎没有限制。用户可以修改代码后闭源。

它有三个基本特点。

(1)没有使用限制

用户可以使用代码,做任何想做的事情。

(2)没有担保

不保证代码质量,用户自担风险。

(3)披露要求(notice requirement)

用户必须披露原始作者。

3.2 常见许可证

常见的宽松式许可证有四种。它们都允许用户任意使用代码,区别在于要求用户遵守的条件不同。

(1)BSD(二条款版)

分发软件时,必须保留原始的许可证声明。

(2) BSD(三条款版)

分发软件时,必须保留原始的许可证声明。不得使用原始作者的名字为软件促销。

(3)MIT

分发软件时,必须保留原始的许可证声明,与 BSD(二条款版)基本一致。

(4)Apache 2

分发软件时,必须保留原始的许可证声明。凡是修改过的文件,必须向用户说明该文件修改过;没有修改过的文件,必须保持许可证不变。

四、Copyleft 许可证

4.1 Copyleft 的含义

Copyleft 是理查德·斯托曼发明的一个词,作为 Copyright (版权)的反义词。

Copyright 直译是"复制权",这是版权制度的核心,意为不经许可,用户无权复制。作为反义词,Copyleft 的含义是不经许可,用户可以随意复制。

但是,它带有前提条件,比宽松式许可证的限制要多。

  • 如果分发二进制格式,必须提供源码
  • 修改后的源码,必须与修改前保持许可证一致
  • 不得在原始许可证以外,附加其他限制

上面三个条件的核心就是:修改后的 Copyleft 代码不得闭源。

4.2 常见许可证

常见的 Copyleft 许可证也有四种(对用户的限制从最强到最弱排序)。

(1)Affero GPL (AGPL)

如果云服务(即 SAAS)用到的代码是该许可证,那么云服务的代码也必须开源。

(2)GPL

如果项目包含了 GPL 许可证的代码,那么整个项目都必须使用 GPL 许可证。

(3)LGPL

如果项目采用动态链接调用该许可证的库,项目可以不用开源。

(4)Mozilla(MPL)

只要该许可证的代码在单独的文件中,新增的其他文件可以不用开源。

五、常见问题

本节回答一些开源许可证的常见问题。

5.1 什么叫分发(distribution)?

除了 Affero GPL (AGPL) ,其他许可证都规定只有在"分发"时,才需要遵守许可证。换言之,如果不"分发",就不需要遵守。

简单说,分发就是指将版权作品从一个人转移到另一个人。这意味着,如果你是自己使用,不提供给他人,就没有分发。另外,这里的"人"也指"法人",因此如果使用方是公司,且只在公司内部使用,也不需要遵守许可证。

云服务(SaaS)是否构成"分发"呢?答案是不构成。所以你使用开源软件提供云服务,不必提供源码。但是,Affero GPL (AGPL) 许可证除外,它规定云服务也必须提供源码。

5.2 开源软件的专利如何处理?

某些许可证(Apache 2 和 GPL v3)包含明确的条款,授予用户许可,使用软件所包含的所有专利。

另一些许可证(BSD、MIT 和 GPL v2)根本没提到专利。但是一般认为,它们默认给予用户专利许可,不构成侵犯专利。

总得来说,除非有明确的"保留专利"的条款,使用开源软件都不会构成侵犯专利。

5.3 什么是披露要求?

所有的开源许可证都带有"披露要求"(notice requirement),即要求软件的分发者必须向用户披露,软件里面有开源代码。

一般来说,你只要在软件里面提供完整的原始许可证文本,并且披露原始作者,就满足了"披露要求"。

5.4 GPL 病毒是真的吗?

GPL 许可证规定,只要你的项目包含了 GPL 代码,整个项目就都变成了 GPL。有人把这种传染性比喻成"GPL 病毒"。

很多公司希望避开这个条款,既使用 GPL 软件,又不把自己的专有代码开源。理论上,这是做不到的。因为 GPL 的设计目的,就是为了防止出现这种情况。

但是实际上,不遵守 GPL,最坏情况就是被起诉。如果你向法院表示无法履行 GPL 的条件,法官只会判决你停止使用 GPL 代码(法律上叫做"停止侵害"),而不会强制要求你将源码开源,因为《版权法》里面的"违约救济"没有提到违约者必须开源,只提到可以停止侵害和赔偿损失。

(完)

文档信息

]]>
0
<![CDATA[GOMAXPROCS需要设置吗?]]> http://www.udpwork.com/item/16449.html http://www.udpwork.com/item/16449.html#reviews Wed, 11 Oct 2017 15:41:59 +0800 鸟窝 http://www.udpwork.com/item/16449.html 自 Go 1.5开始, Go的GOMAXPROCS默认值已经设置为 CPU的核数, 这允许我们的Go程序充分使用机器的每一个CPU,最大程度的提高我们程序的并发性能, 而且,在大部分情况下, 我们并不会去设置这个参数。因为默认值已经足够好了, 以至于fasthttp的作者valyala提议禁止runtime.GOMAXPROCS设置这个数值, 对于所有的case,GOMAXPROCS默认值是否是最好的值呢?

badger的作者Manish Rai Jain就遇到了这样一个问题

Manish Rai Jain 写了一段Go代码, 用来测试Go的读写SSD的性能,看看是否能打到Linux的I/O测试工具fio的读写性能。 使用fio,可以在 AWS(Amazon i3.large instance with NVMe SSD)达到100K IOPS, 但是使用这个Go程序,怎么也无法接近这个IOPS。

他尝试了三个case:

  1. 单个goroutine随机读
  2. 使用一定数量的goroutine随机读
  3. 类似#2,但是使用一个channel

明显#1, #3比不上#2的性能,但是#2怎么也达不到fio的吞吐率。如果大家都使用小于CPU的核数, Go和Fio的吞吐率接近,但是如果把goroutine设置为大于CPU的核数,Fio性能提升明显,直到达到最大的IOPS,但是Go程序却没有显著变化。

12345
$ fio --name=randread --ioengine=psync --iodepth=32 --rw=randread --bs=4k --direct=0 --size=2G --numjobs=16 --runtime=120 --group_reportingGives around 62K, tested via sar -d 1 -p, while$ go build . && GOMAXPROCS=16 ./randread --dir ~/diskfio --jobs 16 --num 2000000 --mode 1Gives around 44K, via sar. Number of cores on my machine are 4.

通过将GOMAXPROCS设置更大的数(64/128, 数倍CPU核数), Go 程序可以取得几乎和Fio一样的吞吐率。

在今年的Gophercon上,Manish Rai Jain碰到了Russ Cox,问到了这个问题:

If File::Read blocks goroutines, which then spawn new OS threads, in a long running job, there should be plenty of OS threads created already, so the random read throughput should increase over time and stabilize to the maximum possible value. But, that's not what I see in my benchmarks.

大意是如果文件读取倍 block, Go会产生新的OS Thread,因为有很多的OS Thread,所以随机读的吞吐率应该也会上升才对,但是实际却不是。

Russ Cox解释说GOMAXPROCS就像一个多路复用器,所以GOMAXPROCS就会是一个瓶颈。

The GOMAXPROCS in a way acts like a multiplexer. From docs, "the GOMAXPROCS variable limits the number of operating system threads that can execute user-level Go code simultaneously." Which basically means, all reads must first be run only via GOMAXPROCS number of goroutines, before switching over to some OS thread (not really a switch, but conceptually speaking). This introduces a bottleneck for throughput.

最后Manish Rai Jain在他的高性能的K/V数据库调大了这个参数

这是一个有趣的讨论,很明显默认的GOMAXPROCS在某些情况下也不是最好的值,特别是在Manish Rai Jain这种写block的情况下,设置GOMAXPROCS更大一些会提高I/O的吞吐率。

]]>
自 Go 1.5开始, Go的GOMAXPROCS默认值已经设置为 CPU的核数, 这允许我们的Go程序充分使用机器的每一个CPU,最大程度的提高我们程序的并发性能, 而且,在大部分情况下, 我们并不会去设置这个参数。因为默认值已经足够好了, 以至于fasthttp的作者valyala提议禁止runtime.GOMAXPROCS设置这个数值, 对于所有的case,GOMAXPROCS默认值是否是最好的值呢?

badger的作者Manish Rai Jain就遇到了这样一个问题

Manish Rai Jain 写了一段Go代码, 用来测试Go的读写SSD的性能,看看是否能打到Linux的I/O测试工具fio的读写性能。 使用fio,可以在 AWS(Amazon i3.large instance with NVMe SSD)达到100K IOPS, 但是使用这个Go程序,怎么也无法接近这个IOPS。

他尝试了三个case:

  1. 单个goroutine随机读
  2. 使用一定数量的goroutine随机读
  3. 类似#2,但是使用一个channel

明显#1, #3比不上#2的性能,但是#2怎么也达不到fio的吞吐率。如果大家都使用小于CPU的核数, Go和Fio的吞吐率接近,但是如果把goroutine设置为大于CPU的核数,Fio性能提升明显,直到达到最大的IOPS,但是Go程序却没有显著变化。

12345
$ fio --name=randread --ioengine=psync --iodepth=32 --rw=randread --bs=4k --direct=0 --size=2G --numjobs=16 --runtime=120 --group_reportingGives around 62K, tested via sar -d 1 -p, while$ go build . && GOMAXPROCS=16 ./randread --dir ~/diskfio --jobs 16 --num 2000000 --mode 1Gives around 44K, via sar. Number of cores on my machine are 4.

通过将GOMAXPROCS设置更大的数(64/128, 数倍CPU核数), Go 程序可以取得几乎和Fio一样的吞吐率。

在今年的Gophercon上,Manish Rai Jain碰到了Russ Cox,问到了这个问题:

If File::Read blocks goroutines, which then spawn new OS threads, in a long running job, there should be plenty of OS threads created already, so the random read throughput should increase over time and stabilize to the maximum possible value. But, that's not what I see in my benchmarks.

大意是如果文件读取倍 block, Go会产生新的OS Thread,因为有很多的OS Thread,所以随机读的吞吐率应该也会上升才对,但是实际却不是。

Russ Cox解释说GOMAXPROCS就像一个多路复用器,所以GOMAXPROCS就会是一个瓶颈。

The GOMAXPROCS in a way acts like a multiplexer. From docs, "the GOMAXPROCS variable limits the number of operating system threads that can execute user-level Go code simultaneously." Which basically means, all reads must first be run only via GOMAXPROCS number of goroutines, before switching over to some OS thread (not really a switch, but conceptually speaking). This introduces a bottleneck for throughput.

最后Manish Rai Jain在他的高性能的K/V数据库调大了这个参数

这是一个有趣的讨论,很明显默认的GOMAXPROCS在某些情况下也不是最好的值,特别是在Manish Rai Jain这种写block的情况下,设置GOMAXPROCS更大一些会提高I/O的吞吐率。

]]>
0
<![CDATA[badger 一个高性能的LSM K/V store]]> http://www.udpwork.com/item/16448.html http://www.udpwork.com/item/16448.html#reviews Wed, 11 Oct 2017 12:17:49 +0800 鸟窝 http://www.udpwork.com/item/16448.html 大家好,给大家介绍一下, 新晋的高性能的 K/V数据库:badger

这是dgraph.io开发的一款基于 log structured merge (LSM) tree 的 key-value 本地数据库, 使用 Go 开发。

事实上,市面上已经有一些知名的基于LSM tree的k/v数据库, 比如leveldbgoleveldbrocksdbboltdb, 可是为什么还要创造新的轮子呢。我们不妨从LSM说起。

LSM Tree

Log-structured merge-tree(简称 LSM tree) 可以追溯到1996年 Patrick O'Neil等人的论文。最简单的LSM tree是两层树状结构C0,C1。 C0比较小,驻留在内存,当C0超过一定的大小, 一些连续的片段会从C0移动到磁盘中的C1中,这是一次merge的过程。在实际的应用中, 一般会分为更多的层级(level), 而层级C0都会驻留在内存中。

2006年, Google发表了它的那篇著名的文章:Bigtable: A Distributed Storage System for Structured Data, 不但催生了 HBase这样的项目的诞生, 而且广泛地引起了大家对 LSM tree这种数据结构重视。

之后, 2007 HBase, 2010年 Cassandra, 2011年 LevelDB, 2013年 RocksDB, 2015年 InfluxDB的 LSM tree引擎等众多的 基于LSM tree的k/v数据库(引擎)诞生。

LevelDB也是由Google的牛人 Jeffrey Dean 和 Sanjay Ghemawat创建的,被多个NoSql数据库用作底层的存储引擎。RocksDBfork自LevelDB,但为多核和SSD做了很多的优化, 增加了一些有用的特性,比如Bloom filters、事务、TTL等,被Facebook、Yahoo!和Linkedin等公司使用。

badger

回到开始的问题, 既然已经有了一些优秀的开源的LSM tree的项目,为什么dgraph还要创建一个新的轮子呢?

答案是:更好的性能

dgraph开发一个新的基于LSM tree的数据库引擎badger是基于这篇论文:WiscKey: Separating Keys from Values
in SSD-conscious Storage
, 这篇论文很新, 也就是去年(2016年)发表的,这篇论文提出了一种新的设计,专门为SSD所优化,将key和value分别存储以减少I/O放大。论文是由斯康辛大学麦迪逊分校的Lanyue Lu等人完成,Lanyue Lu毕业于中国科大,现在就职于Google。论文中提供了一个测试数据,加载数据库要比LevelDB快2.5–111倍,随机lookup要快1.6-14倍。

dgraph实现的这个产品叫做Badger, 对于随机读,Badger至少要比RocksDB快3.5倍,对于值的大小从128B到16KB,数据加载Badger比LevelDB快0.86 - 14倍。

Badger分离的key和value,只有key存在LSM tree中, value存在WAL中,叫做value log。通常情况下,key比较小,所以LSM tree比较小,当获取value值的时候,再从SSD存储中读取。现在的SSD, 比如Samsung 960 Pro,对于4KB的数据块,可以提供44万的读操作/秒,这相当快了。

LSM tree最主要的性能消耗在于 compaction 过程。 在compaction的时候,多个文件需要读进内存,排序,然后再写回。每个文件都固定大小,如果文件中包含value, 文件大小会显著的增加,compaction会更频繁地发生。Badger不存储value,而是存储value的指针, 如果每个键是16, 每个value的指针是16 byte的话,一个64MB的文件就可以存储200万个键值对。

因为Badger不存储value,而是存储value的指针,compaction的时候只移动key和value指针,对于 1KB大小的value和16 byte的key, 写放大为(10*16 + 1024)/(16 + 1024) ~ 1.14。

因为Badger的LSM tree比较小,所以它的层级相对于普通的LSM tree要少,这也意味着查找会更少。例如1KB大小的value, 22byte的key, 7500万条数据的原始大小是 72GB,但是对于Badger的LSM tree来说,只需要1.7G,完全可以放在内存中,这也是Badger的随机读比RocksDB快3.5的原因。

容错

LSM tree将所有的更新写入到内存中的memtable,一旦填满, memtable回替换为immutable memtable,最终回写入到磁盘中的level0中。

如果机器宕机,内存表中的数据就会丢失。k/v数据库一般使用write-ahead log (WAL)来处理这个问题,Badger也一样。Badger会记录memtable的最后一个值的指针,当恢复的时候,它可以replay和重建LSM tree。

文件大小

Badger还使用技术对value值进行压缩,以便是log文件更小。

对于1KB的value,16 byte的key, 7500万条数据,RocksDB的 LSM tree 是 50GB, Badger的 value log文件是74GB(未压缩), LSM tree 是 1.7GB。

和RocksDB的Benchmark比较:

使用

Badger使用起来超级简单, 配置参数页不多,而且提供了默认的配置参数。

下面的代码是读写查和便利的代码,所有的操作都是在事务中完成的, Badger的事物是基于MVCC实现的。

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
package mainimport (	"fmt"	"log"	"github.com/dgraph-io/badger")func main() {	opts := badger.DefaultOptions	opts.Dir = "./data"	opts.ValueDir = "./data"	db, err := badger.Open(&opts)	if err != nil {		log.Fatal(err)	}	defer db.Close()	// set	err = db.Update(func(txn *badger.Txn) error {		err := txn.Set([]byte("answer"), []byte("42"), 0)		return err	})	if err != nil {		panic(err)	}	// get	err = db.View(func(txn *badger.Txn) error {		item, err := txn.Get([]byte("answer"))		if err != nil {			return err		}		val, err := item.Value()		if err != nil {			return err		}		fmt.Printf("The answer is: %s\n", val)		return nil	})	if err != nil {		panic(err)	}	// iterate	err = db.View(func(txn *badger.Txn) error {		opts := badger.DefaultIteratorOptions		opts.PrefetchSize = 10		it := txn.NewIterator(opts)		for it.Rewind(); it.Valid(); it.Next() {			item := it.Item()			k := item.Key()			v, err := item.Value()			if err != nil {				return err			}			fmt.Printf("key=%s, value=%s\n", k, v)		}		return nil	})	if err != nil {		panic(err)	}	// Prefix scans	db.View(func(txn *badger.Txn) error {		it := txn.NewIterator(badger.DefaultIteratorOptions)		prefix := []byte("ans")		for it.Seek(prefix); it.ValidForPrefix(prefix); it.Next() {			item := it.Item()			k := item.Key()			v, err := item.Value()			if err != nil {				return err			}			fmt.Printf("key=%s, value=%s\n", k, v)		}		return nil	})	if err != nil {		panic(err)	}	// iterate keys	err = db.View(func(txn *badger.Txn) error {		opts := badger.DefaultIteratorOptions		opts.PrefetchValues = false		it := txn.NewIterator(opts)		for it.Rewind(); it.Valid(); it.Next() {			item := it.Item()			k := item.Key()			fmt.Printf("key=%s\n", k)		}		return nil	})	// delete	err = db.Update(func(txn *badger.Txn) error {		return txn.Delete([]byte("answer"))	})	if err != nil {		panic(err)	}}

参考文档

  1. https://blog.dgraph.io/post/badger/
  2. https://www.slideshare.net/ssuser7e134a/log-structured-merge-tree
  3. https://blog.dgraph.io/post/badger/
]]>
大家好,给大家介绍一下, 新晋的高性能的 K/V数据库:badger

这是dgraph.io开发的一款基于 log structured merge (LSM) tree 的 key-value 本地数据库, 使用 Go 开发。

事实上,市面上已经有一些知名的基于LSM tree的k/v数据库, 比如leveldbgoleveldbrocksdbboltdb, 可是为什么还要创造新的轮子呢。我们不妨从LSM说起。

LSM Tree

Log-structured merge-tree(简称 LSM tree) 可以追溯到1996年 Patrick O'Neil等人的论文。最简单的LSM tree是两层树状结构C0,C1。 C0比较小,驻留在内存,当C0超过一定的大小, 一些连续的片段会从C0移动到磁盘中的C1中,这是一次merge的过程。在实际的应用中, 一般会分为更多的层级(level), 而层级C0都会驻留在内存中。

2006年, Google发表了它的那篇著名的文章:Bigtable: A Distributed Storage System for Structured Data, 不但催生了 HBase这样的项目的诞生, 而且广泛地引起了大家对 LSM tree这种数据结构重视。

之后, 2007 HBase, 2010年 Cassandra, 2011年 LevelDB, 2013年 RocksDB, 2015年 InfluxDB的 LSM tree引擎等众多的 基于LSM tree的k/v数据库(引擎)诞生。

LevelDB也是由Google的牛人 Jeffrey Dean 和 Sanjay Ghemawat创建的,被多个NoSql数据库用作底层的存储引擎。RocksDBfork自LevelDB,但为多核和SSD做了很多的优化, 增加了一些有用的特性,比如Bloom filters、事务、TTL等,被Facebook、Yahoo!和Linkedin等公司使用。

badger

回到开始的问题, 既然已经有了一些优秀的开源的LSM tree的项目,为什么dgraph还要创建一个新的轮子呢?

答案是:更好的性能

dgraph开发一个新的基于LSM tree的数据库引擎badger是基于这篇论文:WiscKey: Separating Keys from Values
in SSD-conscious Storage
, 这篇论文很新, 也就是去年(2016年)发表的,这篇论文提出了一种新的设计,专门为SSD所优化,将key和value分别存储以减少I/O放大。论文是由斯康辛大学麦迪逊分校的Lanyue Lu等人完成,Lanyue Lu毕业于中国科大,现在就职于Google。论文中提供了一个测试数据,加载数据库要比LevelDB快2.5–111倍,随机lookup要快1.6-14倍。

dgraph实现的这个产品叫做Badger, 对于随机读,Badger至少要比RocksDB快3.5倍,对于值的大小从128B到16KB,数据加载Badger比LevelDB快0.86 - 14倍。

Badger分离的key和value,只有key存在LSM tree中, value存在WAL中,叫做value log。通常情况下,key比较小,所以LSM tree比较小,当获取value值的时候,再从SSD存储中读取。现在的SSD, 比如Samsung 960 Pro,对于4KB的数据块,可以提供44万的读操作/秒,这相当快了。

LSM tree最主要的性能消耗在于 compaction 过程。 在compaction的时候,多个文件需要读进内存,排序,然后再写回。每个文件都固定大小,如果文件中包含value, 文件大小会显著的增加,compaction会更频繁地发生。Badger不存储value,而是存储value的指针, 如果每个键是16, 每个value的指针是16 byte的话,一个64MB的文件就可以存储200万个键值对。

因为Badger不存储value,而是存储value的指针,compaction的时候只移动key和value指针,对于 1KB大小的value和16 byte的key, 写放大为(10*16 + 1024)/(16 + 1024) ~ 1.14。

因为Badger的LSM tree比较小,所以它的层级相对于普通的LSM tree要少,这也意味着查找会更少。例如1KB大小的value, 22byte的key, 7500万条数据的原始大小是 72GB,但是对于Badger的LSM tree来说,只需要1.7G,完全可以放在内存中,这也是Badger的随机读比RocksDB快3.5的原因。

容错

LSM tree将所有的更新写入到内存中的memtable,一旦填满, memtable回替换为immutable memtable,最终回写入到磁盘中的level0中。

如果机器宕机,内存表中的数据就会丢失。k/v数据库一般使用write-ahead log (WAL)来处理这个问题,Badger也一样。Badger会记录memtable的最后一个值的指针,当恢复的时候,它可以replay和重建LSM tree。

文件大小

Badger还使用技术对value值进行压缩,以便是log文件更小。

对于1KB的value,16 byte的key, 7500万条数据,RocksDB的 LSM tree 是 50GB, Badger的 value log文件是74GB(未压缩), LSM tree 是 1.7GB。

和RocksDB的Benchmark比较:

使用

Badger使用起来超级简单, 配置参数页不多,而且提供了默认的配置参数。

下面的代码是读写查和便利的代码,所有的操作都是在事务中完成的, Badger的事物是基于MVCC实现的。

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
package mainimport (	"fmt"	"log"	"github.com/dgraph-io/badger")func main() {	opts := badger.DefaultOptions	opts.Dir = "./data"	opts.ValueDir = "./data"	db, err := badger.Open(&opts)	if err != nil {		log.Fatal(err)	}	defer db.Close()	// set	err = db.Update(func(txn *badger.Txn) error {		err := txn.Set([]byte("answer"), []byte("42"), 0)		return err	})	if err != nil {		panic(err)	}	// get	err = db.View(func(txn *badger.Txn) error {		item, err := txn.Get([]byte("answer"))		if err != nil {			return err		}		val, err := item.Value()		if err != nil {			return err		}		fmt.Printf("The answer is: %s\n", val)		return nil	})	if err != nil {		panic(err)	}	// iterate	err = db.View(func(txn *badger.Txn) error {		opts := badger.DefaultIteratorOptions		opts.PrefetchSize = 10		it := txn.NewIterator(opts)		for it.Rewind(); it.Valid(); it.Next() {			item := it.Item()			k := item.Key()			v, err := item.Value()			if err != nil {				return err			}			fmt.Printf("key=%s, value=%s\n", k, v)		}		return nil	})	if err != nil {		panic(err)	}	// Prefix scans	db.View(func(txn *badger.Txn) error {		it := txn.NewIterator(badger.DefaultIteratorOptions)		prefix := []byte("ans")		for it.Seek(prefix); it.ValidForPrefix(prefix); it.Next() {			item := it.Item()			k := item.Key()			v, err := item.Value()			if err != nil {				return err			}			fmt.Printf("key=%s, value=%s\n", k, v)		}		return nil	})	if err != nil {		panic(err)	}	// iterate keys	err = db.View(func(txn *badger.Txn) error {		opts := badger.DefaultIteratorOptions		opts.PrefetchValues = false		it := txn.NewIterator(opts)		for it.Rewind(); it.Valid(); it.Next() {			item := it.Item()			k := item.Key()			fmt.Printf("key=%s\n", k)		}		return nil	})	// delete	err = db.Update(func(txn *badger.Txn) error {		return txn.Delete([]byte("answer"))	})	if err != nil {		panic(err)	}}

参考文档

  1. https://blog.dgraph.io/post/badger/
  2. https://www.slideshare.net/ssuser7e134a/log-structured-merge-tree
  3. https://blog.dgraph.io/post/badger/
]]>
0
<![CDATA[Convert Shadowsocks into an HTTP proxy]]> http://www.udpwork.com/item/16447.html http://www.udpwork.com/item/16447.html#reviews Tue, 10 Oct 2017 15:35:17 +0800 鸟窝 http://www.udpwork.com/item/16447.html 备用,不解释

socks5 to http proxy

1、 首先安装 polipo, 设置parent proxy to Ss:

123
apt-get install poliposervice polipo stoppolipo socksParentProxy=localhost:1080 &

macosx运行

12
brew install polipopolipo socksParentProxy=localhost:1080 &

设置全局http proxy:

123
http_proxy=http://localhost:8123 apt-get updatehttp_proxy=http://localhost:8123 curl www.google.comhttp_proxy=http://localhost:8123 wget www.google.com

对于git:

12345
git config --global http.proxy 127.0.0.1:8123git clone https://github.com/xxx/xxx.gitgit xxxgit xxxgit config --global --unset-all http.proxy

glide mirror

123456789
$ rm -rf ~/.glide$ mkdir -p ~/.glide$ glide mirror set https://golang.org/x/mobile https://github.com/golang/mobile --vcs git$ glide mirror set https://golang.org/x/crypto https://github.com/golang/crypto --vcs git$ glide mirror set https://golang.org/x/net https://github.com/golang/net --vcs git$ glide mirror set https://golang.org/x/tools https://github.com/golang/tools --vcs git$ glide mirror set https://golang.org/x/text https://github.com/golang/text --vcs git$ glide mirror set https://golang.org/x/image https://github.com/golang/image --vcs git$ glide mirror set https://golang.org/x/sys https://github.com/golang/sys --vcs git
]]>
备用,不解释

socks5 to http proxy

1、 首先安装 polipo, 设置parent proxy to Ss:

123
apt-get install poliposervice polipo stoppolipo socksParentProxy=localhost:1080 &

macosx运行

12
brew install polipopolipo socksParentProxy=localhost:1080 &

设置全局http proxy:

123
http_proxy=http://localhost:8123 apt-get updatehttp_proxy=http://localhost:8123 curl www.google.comhttp_proxy=http://localhost:8123 wget www.google.com

对于git:

12345
git config --global http.proxy 127.0.0.1:8123git clone https://github.com/xxx/xxx.gitgit xxxgit xxxgit config --global --unset-all http.proxy

glide mirror

123456789
$ rm -rf ~/.glide$ mkdir -p ~/.glide$ glide mirror set https://golang.org/x/mobile https://github.com/golang/mobile --vcs git$ glide mirror set https://golang.org/x/crypto https://github.com/golang/crypto --vcs git$ glide mirror set https://golang.org/x/net https://github.com/golang/net --vcs git$ glide mirror set https://golang.org/x/tools https://github.com/golang/tools --vcs git$ glide mirror set https://golang.org/x/text https://github.com/golang/text --vcs git$ glide mirror set https://golang.org/x/image https://github.com/golang/image --vcs git$ glide mirror set https://golang.org/x/sys https://github.com/golang/sys --vcs git
]]>
0
<![CDATA[[转]KV存储的对比]]> http://www.udpwork.com/item/16446.html http://www.udpwork.com/item/16446.html#reviews Tue, 10 Oct 2017 11:39:10 +0800 鸟窝 http://www.udpwork.com/item/16446.html 本文转自饿了么的高级架构师陈东明(cadem)发布于云栖社区的文章:KV存储的对比。作者还整理了另外一篇文章:存储系统的分类,列举了常见的一些kv存储模型和实现。

最近对各种KV存储进行一个比较,从存储引擎到存储引擎的类型,到单机版的kvstore,再到分布式kvstore集群。

存储引擎的类型

类型 全称
btree
LSH Log-Structured Hash Table
LSM Log-Structured Merge Tree
FractalTree 分型树

存储引擎

Welcome to Oschina Tools website

类型 名称 语言 备注 应用在___ dbengine排名 出品
berkeleyDB BTREE, HASH, QUEUE, RECNO C,Java No.9 oracle
Wiredtiger btree, LSM C mongodb No.24 WiredTiger/mongodb
Tokyo Cabinet /Kyoto Cabinet b+tree,hash table nmdb,Kyoto Tycoon NO.27/No.36 FAL Labs
LMDB btree C OpenLDAP symas
BoltDB btree Go LMDB的go版本
leveldb LSM c++ No.15 google
goleveldb LSM Go leveldb的go版本 个人
levigo LSM Go Go wrapper for LevelDB 个人
rocksdb LSM c++,java No.18 facebook
gorocksdb LSM Go Go wrapper for RocksDB 个人
mongo-rocks LSM C++ RocksDB Storage Engine Module for MongoDB mongo partner
bitcast LSH C beansdb,riak basho
PerconaFT FractalTree C++ Mysql存储引擎之TokuDB percona

单机kvstore

类型 名称 采用的存储引擎 语言 出品 主从复制 github star
kv对 nmdb qdbm, berkeley db, tokyo cabinet,tdb C 个人Alberto Bertogli
kv对 memcachedb Berkeley DB C 新浪 yes
kv对 Kyoto Tycoon Kyoto Cabinet C/C++ FAL Labs
结构化kv(redis兼容) ssdb leveddb C/C++ 个人ideawu yes 4k+
结构化kv(redis兼容) ssdb-rocks rocksdb C/C++ 个人ideawu 92
结构化kv(redis兼容) ardb LevelDB, RocksDB, LMDB, WiredTiger C++ 个人yinqiwen yes 800+
结构化kv(redis兼容) (reborndb)qdb Rocksdb and LevelDB Go 个人ngaut 200+
结构化kv(redis兼容) Pika Rocksdb C Qihoo360 yes 900+
结构化kv(redis兼容) LedisDB LevelDB, goleveldb, LMDB, RocksDB, BoltDB or Memory Go 个人SiddonTang 2K+

单机kvstore的分布式代理

名称 语言 存储 出品 应用/githubstar 分片 弹性 备注
Reborndb Go qdb 个人ngaut 200+ yes yes
Netflix Dynomite C redis,memcached Netflix 1.9K+ Dynamo
Codis Go codis-server(定制redis) 社区CodisLabs 5K+
Twenproxy C redis,memcached twitter 6K+

分布式KVstore

类型 名称 语言 出品 dbengine排名/githubstar 存储引擎 社区活跃 特性 备注
kv型 riak erlang basho No.3/2K+ bitcask Dynamo
kv型 beansdb C douban 600+ bitcask模型 近2年没有更新,没有文档 Dynamo
kv型 project Voldmort Java LinkedIn No.29/1.8K+ BDB-JE,MySQL,Read-Only
kv型 Scalris erlang Zuse Institute Berlin No.35/88
kv型 aeospike aespike No.7
kv型 Tair C/C++ alibaba 400+ 自研的fdb 停止更新,没有文档

补充

原文中没有提到的,在这里添加上作为补充。

  • tidb: 国产的分布式NewSQL产品,底层采用tikv, 支持MySQL协议, PingCap出品。
  • badger: 性能优异,特别为SSD优化,仅仅key存储在LSM tree中。
]]>
本文转自饿了么的高级架构师陈东明(cadem)发布于云栖社区的文章:KV存储的对比。作者还整理了另外一篇文章:存储系统的分类,列举了常见的一些kv存储模型和实现。

最近对各种KV存储进行一个比较,从存储引擎到存储引擎的类型,到单机版的kvstore,再到分布式kvstore集群。

存储引擎的类型

类型 全称
btree
LSH Log-Structured Hash Table
LSM Log-Structured Merge Tree
FractalTree 分型树

存储引擎

Welcome to Oschina Tools website

类型 名称 语言 备注 应用在___ dbengine排名 出品
berkeleyDB BTREE, HASH, QUEUE, RECNO C,Java No.9 oracle
Wiredtiger btree, LSM C mongodb No.24 WiredTiger/mongodb
Tokyo Cabinet /Kyoto Cabinet b+tree,hash table nmdb,Kyoto Tycoon NO.27/No.36 FAL Labs
LMDB btree C OpenLDAP symas
BoltDB btree Go LMDB的go版本
leveldb LSM c++ No.15 google
goleveldb LSM Go leveldb的go版本 个人
levigo LSM Go Go wrapper for LevelDB 个人
rocksdb LSM c++,java No.18 facebook
gorocksdb LSM Go Go wrapper for RocksDB 个人
mongo-rocks LSM C++ RocksDB Storage Engine Module for MongoDB mongo partner
bitcast LSH C beansdb,riak basho
PerconaFT FractalTree C++ Mysql存储引擎之TokuDB percona

单机kvstore

类型 名称 采用的存储引擎 语言 出品 主从复制 github star
kv对 nmdb qdbm, berkeley db, tokyo cabinet,tdb C 个人Alberto Bertogli
kv对 memcachedb Berkeley DB C 新浪 yes
kv对 Kyoto Tycoon Kyoto Cabinet C/C++ FAL Labs
结构化kv(redis兼容) ssdb leveddb C/C++ 个人ideawu yes 4k+
结构化kv(redis兼容) ssdb-rocks rocksdb C/C++ 个人ideawu 92
结构化kv(redis兼容) ardb LevelDB, RocksDB, LMDB, WiredTiger C++ 个人yinqiwen yes 800+
结构化kv(redis兼容) (reborndb)qdb Rocksdb and LevelDB Go 个人ngaut 200+
结构化kv(redis兼容) Pika Rocksdb C Qihoo360 yes 900+
结构化kv(redis兼容) LedisDB LevelDB, goleveldb, LMDB, RocksDB, BoltDB or Memory Go 个人SiddonTang 2K+

单机kvstore的分布式代理

名称 语言 存储 出品 应用/githubstar 分片 弹性 备注
Reborndb Go qdb 个人ngaut 200+ yes yes
Netflix Dynomite C redis,memcached Netflix 1.9K+ Dynamo
Codis Go codis-server(定制redis) 社区CodisLabs 5K+
Twenproxy C redis,memcached twitter 6K+

分布式KVstore

类型 名称 语言 出品 dbengine排名/githubstar 存储引擎 社区活跃 特性 备注
kv型 riak erlang basho No.3/2K+ bitcask Dynamo
kv型 beansdb C douban 600+ bitcask模型 近2年没有更新,没有文档 Dynamo
kv型 project Voldmort Java LinkedIn No.29/1.8K+ BDB-JE,MySQL,Read-Only
kv型 Scalris erlang Zuse Institute Berlin No.35/88
kv型 aeospike aespike No.7
kv型 Tair C/C++ alibaba 400+ 自研的fdb 停止更新,没有文档

补充

原文中没有提到的,在这里添加上作为补充。

  • tidb: 国产的分布式NewSQL产品,底层采用tikv, 支持MySQL协议, PingCap出品。
  • badger: 性能优异,特别为SSD优化,仅仅key存储在LSM tree中。
]]>
0
<![CDATA[也许,你并不知道,你也是一个戏精]]> http://www.udpwork.com/item/16445.html http://www.udpwork.com/item/16445.html#reviews Mon, 09 Oct 2017 18:23:43 +0800 魏武挥 http://www.udpwork.com/item/16445.html

连续八天的国庆黄金假期快要结束了。

朋友圈里的摄影大赛也进入尾声。比起前几天的疯狂晒图,今天看着已经不怎么热闹了。

确切地说,这场摄影大赛,还是地理位置大赛。

我微信好友数比较多,所以欧美日东南亚都不稀奇,我还见过非洲的。

大概就是南极北极没见到了。

我曾经在课上开玩笑式地问过学生。

为什么同样是学校食堂,你们从来不标注我交那四五个餐厅,做交换生到了国外大学,就要显示出来呢?有时候甚至不是国外,不过是港台这种海外罢了。

当然,我是知道答案的。

 

我很早以前写过一篇文章,提到过“互联网并不是一个相对现实社会而独立存在的虚拟社会,它恰恰是这个社会的组成部分”。

虚拟不是现实的对立面,而是现实的一个子集。

我们在网上很多举动,并非脱离现实社会而行为,它们大多是我们现实行为的一种延续。

比如,表演。

这并不是什么贬义词,说你是戏精也不是在骂你,因为我也是。

我们日常生活每天都需要表演,有时候甚至是非常刻意非常精致非常有脑洞地在表演。

在戈夫曼的笔下,这叫“自我呈现”。他还专门写了一本书,题为《日常生活的自我呈现》。

这是一本社会学领域相当经典的著作。

英文版1959年付梓的这本书,对互联网社交网络上的“表演”依然有着足够的解释力。

 

一周前过世了一位大佬:纽豪斯。

他是著名杂志《纽约客》的老板,当然,他还有很多其它杂志,比如《Vogue》。

《纽约客》在1993年刊登了一幅漫画。

null

互联网上没人知道你是条狗。

到了2000年,这幅画可能是纽约客史上被重印最多的一则漫画,作者也由此而赚了数万美元。

但很显然,这幅画描绘的是网络的史前时代。

今天,还有人不知道你是条狗么?

如果有需要,人们甚至能知道你这条狗的祖宗八代血统脉络——术语叫“人肉搜索”。

既然各位都知道我是条狗,接下来的问题就变得很重要:

我不能让各位觉得我是条坏狗、烂狗、low狗。

 

2010年,刚刚结束了内测的新浪微博,和我所在的交大媒体与设计学院达成了一个基于数据库的合作:做一份类似白皮书蓝皮书的研究报告。

基于此项研究,微博向我们开放了它的后台数据库。

这使得我对微博在当年4月份时搞的一项活动得以全样数据的观测——在七年后的今天,可以被很不准确地称呼为“微博大数据告诉你所不知道的一件事”。

新浪微博发起了一个带绿丝带的活动,参加者会在自己的微博名字边上多一个绿丝带的符号,以表示ta对彼时青海玉树受灾群众的哀悼之心。截止到某个日子,在活动发起日到该日有登陆的用户中,v字认证用户有51%悬挂了绿丝带,非v用户的比例只有20.5%,而粉丝数排名前2000的大v,比例上升到57%。

我们很难得出结论说,大V比一般的V更有同情心,V比非V用户更有同情心这类不着边际的论断。

我们只能这么说:大V比V,V比普通用户,更愿意让别人知道ta是有同情心的。

这个例子,近乎完美地诠释了戈夫曼的“自我呈现”。

当你一旦“实名”(也就是别人知道你是谁)后,这个所谓虚拟的社交网络,就是你的前台——这个词也是戈夫曼在书中创造的。

 

某年某月某日,我在微博上闲逛,发现到一个特别有趣的。。。姑且称之为小事件。

一位带V且我认识的某知识分子,忽然发出了一条微博:

null

我怀着非常大的恶趣味,立刻截了屏。因为我预判的是——根据我对他的了解,他会飞快删除这条微博。

根据微博附属产品微盘的机制,当你下载一个文件后,它会默认帮你自动发出一条类似上图的微博。一般只有微博素养很高的人,才会很小心地去去除那个自动发微博的勾选框。

正如我所预料的,事主在非常快的时间内,删除了这条微博。但他的粉丝量比较可观,还是在几分钟内引起了二位数的转发和评论。

这是一个“后台”的行为进入到“前台”中,所引起的一点点小尴尬。

一位成年男子,出于种种原因,看一段不雅视频,其实并没有什么。

但后台行为是不能给人看的。

有的人对后台定义比较宽泛,比如如果我要是被自动发了这条微博,我并不觉得这是不可以进入前台的。

有的人对后台则定义非常狭窄。有一个传说,说是宋美龄绝对不允许蒋介石见到她刚醒的样子,一定要梳洗后蒋才能见到她——睡眼惺忪这种事每个人都有,但对自己的丈夫都要定义为“后台”,这算是非常极端的案例了。

 

但人的前台并不是只有一个前台,且在不同的前台,后台定义也不同。

在不同的场景下,我们需要扮演不同的社会角色。

一个医生,在病人面前轻易不能表现出你的病特别得疑难杂症,我也搞不懂,诸如此类的行为话语。但若以进修的身份在参加某个学习或讨论时,面对更高等级的大咖名医,表示我不知道,并没有什么。正相反的是,一副完全了然的神色,未必是对的。

中国有一句古话,”见人说人话,见鬼说鬼话“,话丑理端。

不信你见人说鬼话试试?

微博这个前台,和微信朋友圈这个前台,是不太相同的。因为它的link确认机制不同。

微博上要关注一个人,并不需要后者的同意。但在朋友圈你想关注一个人,后者不通过是不行的。

这意味着,朋友圈这个前台,是当事人事先做过一定的区隔的。

早期的时候,我们更愿意称为”熟人社交“,现在可能也说不上有多熟,但至少,与你建立link的其人其貌,与你建立link的何时何地,大致总是有些数,全然不像微博,你完全有可能不知道follower究竟是谁。

 

我不太记得,即便是在微博鼎盛时——也就是号称围观要改变中国时,假期摄影大赛如火如荼过。

但很显然,这两年朋友圈的摄影大赛,地理位置大赛,一年盛于一年。

我甚至推断,朋友圈是假期旅游的重要推手。

是的,我把朋友圈放在了”因“这个位置上。

因为人是互相影响的。今年你参赛了,我观赛观得心动不已,当然有可能导致下一个假期我也要参赛。

假期里的地理位置大赛,我想没人会否认——即便是参赛者,有”显摆“的意味。

而显摆这件事,对于”熟人“去行为,比对”陌生人“去行为,来得更为合适一些。

或者这么说,微信这个前台,比微博这个前台,更适合去做一些轻度的炫耀型行为。因为大多数人的显摆,也不过就想讨一些”熟人“的赞,并不想引起公众层面的注意。

毕竟不是所有人都是郭美美。

朋友圈对照片、地理位置这些东西,并没有”转发“的机制,这很技巧地达成了参赛者微妙的一种心理:只对一部分人炫耀,收获有限的赞即可。

 

朋友圈的产品设计,说好听叫真是洞察人性,说不好听:这帮产品经理,真是心机boy+girl。

它可能是最好地契合了你我做一个戏精的欲望。

比如对参赛照片点赞这个行为。

当参赛者兴致勃勃地发布了九宫图进行显摆后,ta肯定期望能得到一些回应。如果你和ta的确属于“熟人”,或是后者社会资本比你高的情况下,进行点赞,是符合常理的,也是一种友谊的。。。唔。。。表达?表演?

但如果每个观赛者都如你这么想,而且一不小心,这些观赛者和你也有link,一个烦恼的结果出现了:你只不过是表达一下友谊,但你会不断被被提示其他人也在点赞。

微信的产品经理们,准确地抓捕到了你这个想要表演又不想太过被骚扰的心机。

你现在可以选择“不再提示”了。

多好的一个机制,又可以表达友谊,又可以事了拂衣去,哪管后面点赞滔天。

当然,更有可能的是,微信的产品经理,期望你们能对朋友圈广告多多点赞吧,哈哈哈

 

凡勃伦在他的《有闲阶级》一书中提到:炫耀性有闲。

但今天的炫耀型有闲,很难讲是有闲阶级做的事。至少摄影大赛不是。中产阶级是最苦逼的一个阶层,远远谈不上“有闲”。

能做到人群区隔化的社交网络的出现,使得我们进行炫耀性有闲得以可能。

当然,也要考虑到工具的进步。

包括安卓和苹果在内的智能手机在摄影能力上的大幅提到,也是朋友圈摄影大赛的重要推动力。

30万像素是拍不出美轮美奂的风景照片的,也就谈不上“炫耀”了。

是吧?

 

最后再提一下戈夫曼的这本书。

此书看起来绝不轻松,颇有一种晦涩难懂的感觉。这并不完全是翻译的问题,原著的英文就写得非常繁复。推荐看台湾版的。

台湾桂冠出的版本里,有孙中兴的导读,帮助理解。

孙中兴讲过一个“爱情社会学”,听上去有些无厘头,不过人是正儿八经的哥伦比亚大学博士,台大社会系教授。

渊源上讲,孙教授算是戈夫曼的徒孙吧。

 

—— 首发 扯氮集 ——

版权说明 及 商业合作

作者执教于上海交通大学媒体与设计学院,天奇创投管理合伙人

也许,你并不知道,你也是一个戏精,首发于扯氮集

]]>

连续八天的国庆黄金假期快要结束了。

朋友圈里的摄影大赛也进入尾声。比起前几天的疯狂晒图,今天看着已经不怎么热闹了。

确切地说,这场摄影大赛,还是地理位置大赛。

我微信好友数比较多,所以欧美日东南亚都不稀奇,我还见过非洲的。

大概就是南极北极没见到了。

我曾经在课上开玩笑式地问过学生。

为什么同样是学校食堂,你们从来不标注我交那四五个餐厅,做交换生到了国外大学,就要显示出来呢?有时候甚至不是国外,不过是港台这种海外罢了。

当然,我是知道答案的。

 

我很早以前写过一篇文章,提到过“互联网并不是一个相对现实社会而独立存在的虚拟社会,它恰恰是这个社会的组成部分”。

虚拟不是现实的对立面,而是现实的一个子集。

我们在网上很多举动,并非脱离现实社会而行为,它们大多是我们现实行为的一种延续。

比如,表演。

这并不是什么贬义词,说你是戏精也不是在骂你,因为我也是。

我们日常生活每天都需要表演,有时候甚至是非常刻意非常精致非常有脑洞地在表演。

在戈夫曼的笔下,这叫“自我呈现”。他还专门写了一本书,题为《日常生活的自我呈现》。

这是一本社会学领域相当经典的著作。

英文版1959年付梓的这本书,对互联网社交网络上的“表演”依然有着足够的解释力。

 

一周前过世了一位大佬:纽豪斯。

他是著名杂志《纽约客》的老板,当然,他还有很多其它杂志,比如《Vogue》。

《纽约客》在1993年刊登了一幅漫画。

null

互联网上没人知道你是条狗。

到了2000年,这幅画可能是纽约客史上被重印最多的一则漫画,作者也由此而赚了数万美元。

但很显然,这幅画描绘的是网络的史前时代。

今天,还有人不知道你是条狗么?

如果有需要,人们甚至能知道你这条狗的祖宗八代血统脉络——术语叫“人肉搜索”。

既然各位都知道我是条狗,接下来的问题就变得很重要:

我不能让各位觉得我是条坏狗、烂狗、low狗。

 

2010年,刚刚结束了内测的新浪微博,和我所在的交大媒体与设计学院达成了一个基于数据库的合作:做一份类似白皮书蓝皮书的研究报告。

基于此项研究,微博向我们开放了它的后台数据库。

这使得我对微博在当年4月份时搞的一项活动得以全样数据的观测——在七年后的今天,可以被很不准确地称呼为“微博大数据告诉你所不知道的一件事”。

新浪微博发起了一个带绿丝带的活动,参加者会在自己的微博名字边上多一个绿丝带的符号,以表示ta对彼时青海玉树受灾群众的哀悼之心。截止到某个日子,在活动发起日到该日有登陆的用户中,v字认证用户有51%悬挂了绿丝带,非v用户的比例只有20.5%,而粉丝数排名前2000的大v,比例上升到57%。

我们很难得出结论说,大V比一般的V更有同情心,V比非V用户更有同情心这类不着边际的论断。

我们只能这么说:大V比V,V比普通用户,更愿意让别人知道ta是有同情心的。

这个例子,近乎完美地诠释了戈夫曼的“自我呈现”。

当你一旦“实名”(也就是别人知道你是谁)后,这个所谓虚拟的社交网络,就是你的前台——这个词也是戈夫曼在书中创造的。

 

某年某月某日,我在微博上闲逛,发现到一个特别有趣的。。。姑且称之为小事件。

一位带V且我认识的某知识分子,忽然发出了一条微博:

null

我怀着非常大的恶趣味,立刻截了屏。因为我预判的是——根据我对他的了解,他会飞快删除这条微博。

根据微博附属产品微盘的机制,当你下载一个文件后,它会默认帮你自动发出一条类似上图的微博。一般只有微博素养很高的人,才会很小心地去去除那个自动发微博的勾选框。

正如我所预料的,事主在非常快的时间内,删除了这条微博。但他的粉丝量比较可观,还是在几分钟内引起了二位数的转发和评论。

这是一个“后台”的行为进入到“前台”中,所引起的一点点小尴尬。

一位成年男子,出于种种原因,看一段不雅视频,其实并没有什么。

但后台行为是不能给人看的。

有的人对后台定义比较宽泛,比如如果我要是被自动发了这条微博,我并不觉得这是不可以进入前台的。

有的人对后台则定义非常狭窄。有一个传说,说是宋美龄绝对不允许蒋介石见到她刚醒的样子,一定要梳洗后蒋才能见到她——睡眼惺忪这种事每个人都有,但对自己的丈夫都要定义为“后台”,这算是非常极端的案例了。

 

但人的前台并不是只有一个前台,且在不同的前台,后台定义也不同。

在不同的场景下,我们需要扮演不同的社会角色。

一个医生,在病人面前轻易不能表现出你的病特别得疑难杂症,我也搞不懂,诸如此类的行为话语。但若以进修的身份在参加某个学习或讨论时,面对更高等级的大咖名医,表示我不知道,并没有什么。正相反的是,一副完全了然的神色,未必是对的。

中国有一句古话,”见人说人话,见鬼说鬼话“,话丑理端。

不信你见人说鬼话试试?

微博这个前台,和微信朋友圈这个前台,是不太相同的。因为它的link确认机制不同。

微博上要关注一个人,并不需要后者的同意。但在朋友圈你想关注一个人,后者不通过是不行的。

这意味着,朋友圈这个前台,是当事人事先做过一定的区隔的。

早期的时候,我们更愿意称为”熟人社交“,现在可能也说不上有多熟,但至少,与你建立link的其人其貌,与你建立link的何时何地,大致总是有些数,全然不像微博,你完全有可能不知道follower究竟是谁。

 

我不太记得,即便是在微博鼎盛时——也就是号称围观要改变中国时,假期摄影大赛如火如荼过。

但很显然,这两年朋友圈的摄影大赛,地理位置大赛,一年盛于一年。

我甚至推断,朋友圈是假期旅游的重要推手。

是的,我把朋友圈放在了”因“这个位置上。

因为人是互相影响的。今年你参赛了,我观赛观得心动不已,当然有可能导致下一个假期我也要参赛。

假期里的地理位置大赛,我想没人会否认——即便是参赛者,有”显摆“的意味。

而显摆这件事,对于”熟人“去行为,比对”陌生人“去行为,来得更为合适一些。

或者这么说,微信这个前台,比微博这个前台,更适合去做一些轻度的炫耀型行为。因为大多数人的显摆,也不过就想讨一些”熟人“的赞,并不想引起公众层面的注意。

毕竟不是所有人都是郭美美。

朋友圈对照片、地理位置这些东西,并没有”转发“的机制,这很技巧地达成了参赛者微妙的一种心理:只对一部分人炫耀,收获有限的赞即可。

 

朋友圈的产品设计,说好听叫真是洞察人性,说不好听:这帮产品经理,真是心机boy+girl。

它可能是最好地契合了你我做一个戏精的欲望。

比如对参赛照片点赞这个行为。

当参赛者兴致勃勃地发布了九宫图进行显摆后,ta肯定期望能得到一些回应。如果你和ta的确属于“熟人”,或是后者社会资本比你高的情况下,进行点赞,是符合常理的,也是一种友谊的。。。唔。。。表达?表演?

但如果每个观赛者都如你这么想,而且一不小心,这些观赛者和你也有link,一个烦恼的结果出现了:你只不过是表达一下友谊,但你会不断被被提示其他人也在点赞。

微信的产品经理们,准确地抓捕到了你这个想要表演又不想太过被骚扰的心机。

你现在可以选择“不再提示”了。

多好的一个机制,又可以表达友谊,又可以事了拂衣去,哪管后面点赞滔天。

当然,更有可能的是,微信的产品经理,期望你们能对朋友圈广告多多点赞吧,哈哈哈

 

凡勃伦在他的《有闲阶级》一书中提到:炫耀性有闲。

但今天的炫耀型有闲,很难讲是有闲阶级做的事。至少摄影大赛不是。中产阶级是最苦逼的一个阶层,远远谈不上“有闲”。

能做到人群区隔化的社交网络的出现,使得我们进行炫耀性有闲得以可能。

当然,也要考虑到工具的进步。

包括安卓和苹果在内的智能手机在摄影能力上的大幅提到,也是朋友圈摄影大赛的重要推动力。

30万像素是拍不出美轮美奂的风景照片的,也就谈不上“炫耀”了。

是吧?

 

最后再提一下戈夫曼的这本书。

此书看起来绝不轻松,颇有一种晦涩难懂的感觉。这并不完全是翻译的问题,原著的英文就写得非常繁复。推荐看台湾版的。

台湾桂冠出的版本里,有孙中兴的导读,帮助理解。

孙中兴讲过一个“爱情社会学”,听上去有些无厘头,不过人是正儿八经的哥伦比亚大学博士,台大社会系教授。

渊源上讲,孙教授算是戈夫曼的徒孙吧。

 

—— 首发 扯氮集 ——

版权说明 及 商业合作

作者执教于上海交通大学媒体与设计学院,天奇创投管理合伙人

也许,你并不知道,你也是一个戏精,首发于扯氮集

]]>
0
<![CDATA[爆炸,解体,入侵,你想得到的你想不到的大BUG们 - 旁观者]]> http://www.udpwork.com/item/16444.html http://www.udpwork.com/item/16444.html#reviews Sat, 07 Oct 2017 03:12:00 +0800 旁观者 http://www.udpwork.com/item/16444.html 【摘要】又到了扶额兴叹的节气。继《5年前的今天,一个小小的部署错误,让美股最大交易商坠入深渊》之后,再赠四枚逆天大BUG。阅读全文

]]>
【摘要】又到了扶额兴叹的节气。继《5年前的今天,一个小小的部署错误,让美股最大交易商坠入深渊》之后,再赠四枚逆天大BUG。阅读全文

]]>
0
<![CDATA[functor applicative 和 monad]]> http://www.udpwork.com/item/16443.html http://www.udpwork.com/item/16443.html#reviews Thu, 05 Oct 2017 22:30:54 +0800 鸟窝 http://www.udpwork.com/item/16443.html Monad 函数式编程中的一个概念, 在 Haskell 和 Scala 语言中用的比较多。

这个概念来源于数学中的范畴学,过于学术化,我看国内的文章介绍的很多,但是准确、清晰而简要的介绍的文章却没有看到。

我也不准备介绍,因为我对它的理解也不够深,这里引用Functors, Applicatives, And Monads In Pictures一文中的图片和总结,来加深一下自己的理解。

  • functors : you apply a function to a wrapped value using fmap or <$>
  • applicatives : you apply a wrapped function to a wrapped value using <*> or liftA
  • monads : you apply a function that returns a wrapped value, to a wrapped value using >>= or liftM

Functor 的作用就是应用一个函数到一个上下文中的值

Applicative 的作用则是应用一个上下文中的函数到一个上下文中的值

Monad 应用的是一个返回上下文的值的函数,将这个函数应用在一个上下文中的值上

这篇文章已经被翻译成中文:Functor, Applicative, 以及 Monad 的图片阐释
kotlin中文版:Kotlin 版图解 Functor、Applicative 与 Monad

]]>
Monad 函数式编程中的一个概念, 在 Haskell 和 Scala 语言中用的比较多。

这个概念来源于数学中的范畴学,过于学术化,我看国内的文章介绍的很多,但是准确、清晰而简要的介绍的文章却没有看到。

我也不准备介绍,因为我对它的理解也不够深,这里引用Functors, Applicatives, And Monads In Pictures一文中的图片和总结,来加深一下自己的理解。

  • functors : you apply a function to a wrapped value using fmap or <$>
  • applicatives : you apply a wrapped function to a wrapped value using <*> or liftA
  • monads : you apply a function that returns a wrapped value, to a wrapped value using >>= or liftM

Functor 的作用就是应用一个函数到一个上下文中的值

Applicative 的作用则是应用一个上下文中的函数到一个上下文中的值

Monad 应用的是一个返回上下文的值的函数,将这个函数应用在一个上下文中的值上

这篇文章已经被翻译成中文:Functor, Applicative, 以及 Monad 的图片阐释
kotlin中文版:Kotlin 版图解 Functor、Applicative 与 Monad

]]>
0
<![CDATA[再谈谈获取 goroutine id 的方法]]> http://www.udpwork.com/item/16374.html http://www.udpwork.com/item/16374.html#reviews Sat, 30 Sep 2017 22:15:44 +0800 鸟窝 http://www.udpwork.com/item/16374.html 去年年初的时候曾经写过一篇关于如何获取goroutine id的方法:如何得到goroutine 的 id?, 当时调研了一些一些获取goid的方法。基本的方法有三种:

  1. 通过Stack信息解析出ID
  2. 通过汇编获取runtime·getg方法的调用结果
  3. 直接修改运行时的代码,export一个可以外部调用的GoID()方法

每个方式都有些问题, #1比较慢, #2因为是hack的方式(Go team并不想暴露go id的信息), 针对不同的Go版本中需要特殊的hack手段, #3需要定制Go运行时,不通用。当时的petermattis/goid提供了 #2 的方法, 但是只能在 go 1.3中才起作用,所以只能选择#1的方式获取go id。

最近一年来, petermattis更新了他的代码,逐步增加了对 Go 1.4、1.5、1.6、1.7、1.8、1.9的支持,同时也提供了#1的方法,在#2方法不起作用的时候作为备选,所以我们可以在当前的所有的版本中可以使用stable的获取go id的方法了。

你或许会遇到一些需要使用Go ID的场景, 比如在多goroutine长时间运行任务的时候,我们通过日志来跟踪任务的执行情况,可以通过go id来大致地跟踪程序并发执行的时候的状况。

12345678910111213141516171819202122
package mainimport (	"log"	"time"	"github.com/petermattis/goid")func main() {	for i := 0; i < 10; i++ {		go func() {			for j := 0; j < 1000000; j++ {				log.Printf("[#%d] %d", goid.Get(), j)				time.Sleep(10e9)			}		}()	}	select {}}

依照Go代码中的文档HACKING, go运行时中实现了一个getg()方法,可以获取当前的goroutine:

getg()alone returns the currentg

当然这个方法是内部方法,不是exported,不能被外部的调用,而且返回的数据结构也是未exported的。如果有办法暴露出这个方法,问题就解决了。

petermattis/goid模仿runtime.getg暴露出一个getg的方法

https://github.com/petermattis/goid/blob/master/goid_go1.5plus.s
12345678910
// +build amd64 amd64p32// +build go1.5#include "textflag.h"// func getg() uintptrTEXT ·getg(SB),NOSPLIT,$0-8	MOVQ (TLS), BX	MOVQ BX, ret+0(FP)	RET

上面的代码实际是将当前的goroutine的结构体的指针(TLS)返回。

参考:Golang Internals以及中文翻译Go语言内幕
TLS 其实是线程本地存储 (Thread Local Storage )的缩写。这个技术在很多编程语言中都有用到(请参考这里)。简单地说,它为每个线程提供了一个这样的变量,不同变量用于指向不同的内存区域。

在 Go 语言中,TLS 存储了一个 G 结构体的指针。这个指针所指向的结构体包括 Go 例程的内部细节(后面会详细谈到这些内容)。因此,当在不同的例程中访问该变量时,实际访问的是该例程相应的变量所指向的结构体。链接器知道这个变量所在的位置,前面的指令中移动到 CX 寄存器的就是这个变量。对于 AMD64,TLS 是用 FS 寄存器来实现的, 所在我们前面看到的命令实际上可以翻译为 MOVQ FS, CX。

不同的Go版本获取的数据结构可能是不同的,所以petermattis/goid针对1.5、1.6、1.9有变动的版本定制了不同的数据结构,因为我们只需要得到goroutine的ID,所以只需实现:

1234
func Get() int64 {	gg := (*g)(unsafe.Pointer(getg()))	return gg.goid}

我比较了一下#1和#2这两种实现方式的性能,差距还是非常大的:

123456789101112131415161718192021222324
package pkgimport (	"runtime"	"testing"	"github.com/petermattis/goid")func BenchmarkASM(b *testing.B) {	b.ReportAllocs()	for i := 0; i < b.N; i++ {		goid.Get()	}}func BenchmarkSlow(b *testing.B) {	b.ReportAllocs()	var buf [64]byte	b.ResetTimer()	for i := 0; i < b.N; i++ {		goid.ExtractGID(buf[:runtime.Stack(buf[:], false)])	}}

性能比较结果:

12
BenchmarkASM-4    	300000000	         3.70 ns/op	       0 B/op	       0 allocs/opBenchmarkSlow-4   	  300000	      4071 ns/op	       1 B/op	       1 allocs/op

一千多倍的差距。

petermattis/goid这种hack的方式可以暴露更多的运行时的细节,比如我们可以扩展一下,得到当前哪个m正在运行,甚至可以得到当前的线程的信息:

1234567891011121314151617181920212223
type m struct {	g0        *g	morebuf   gobuf	divmod    uint32	procid    uint64	gsignal   *g	sigmask   sigset	tls       [6]uintptr	mstartfn  func()	curg      *g	caughtsig uintptr	p         uintptr	nextp     uintptr	id        int32}func GetM() int32 {	gg := (*g)(unsafe.Pointer(getg()))	m := (*m)(unsafe.Pointer(gg.m))	return m.id}

sigset在不同的平台的大小是不一样的,可以参考os_*.go中各平台的定义。上面是得到m的ID, 更全的m的结构定义海包括thread等信息。

2017-09-05 更新

petermattis 最近做了更新, 利用go本身的g_goid宏,直接利用汇编就可以获取goroutine id,比上面的方式更简单,性能还好:

123456789
#include "go_asm.h"#include "textflag.h"// func Get() int64TEXT ·Get(SB),NOSPLIT,$0-8	MOVQ (TLS), R14	MOVQ g_goid(R14), R13	MOVQ R13, ret+0(FP)	RET
]]>
去年年初的时候曾经写过一篇关于如何获取goroutine id的方法:如何得到goroutine 的 id?, 当时调研了一些一些获取goid的方法。基本的方法有三种:

  1. 通过Stack信息解析出ID
  2. 通过汇编获取runtime·getg方法的调用结果
  3. 直接修改运行时的代码,export一个可以外部调用的GoID()方法

每个方式都有些问题, #1比较慢, #2因为是hack的方式(Go team并不想暴露go id的信息), 针对不同的Go版本中需要特殊的hack手段, #3需要定制Go运行时,不通用。当时的petermattis/goid提供了 #2 的方法, 但是只能在 go 1.3中才起作用,所以只能选择#1的方式获取go id。

最近一年来, petermattis更新了他的代码,逐步增加了对 Go 1.4、1.5、1.6、1.7、1.8、1.9的支持,同时也提供了#1的方法,在#2方法不起作用的时候作为备选,所以我们可以在当前的所有的版本中可以使用stable的获取go id的方法了。

你或许会遇到一些需要使用Go ID的场景, 比如在多goroutine长时间运行任务的时候,我们通过日志来跟踪任务的执行情况,可以通过go id来大致地跟踪程序并发执行的时候的状况。

12345678910111213141516171819202122
package mainimport (	"log"	"time"	"github.com/petermattis/goid")func main() {	for i := 0; i < 10; i++ {		go func() {			for j := 0; j < 1000000; j++ {				log.Printf("[#%d] %d", goid.Get(), j)				time.Sleep(10e9)			}		}()	}	select {}}

依照Go代码中的文档HACKING, go运行时中实现了一个getg()方法,可以获取当前的goroutine:

getg()alone returns the currentg

当然这个方法是内部方法,不是exported,不能被外部的调用,而且返回的数据结构也是未exported的。如果有办法暴露出这个方法,问题就解决了。

petermattis/goid模仿runtime.getg暴露出一个getg的方法

https://github.com/petermattis/goid/blob/master/goid_go1.5plus.s
12345678910
// +build amd64 amd64p32// +build go1.5#include "textflag.h"// func getg() uintptrTEXT ·getg(SB),NOSPLIT,$0-8	MOVQ (TLS), BX	MOVQ BX, ret+0(FP)	RET

上面的代码实际是将当前的goroutine的结构体的指针(TLS)返回。

参考:Golang Internals以及中文翻译Go语言内幕
TLS 其实是线程本地存储 (Thread Local Storage )的缩写。这个技术在很多编程语言中都有用到(请参考这里)。简单地说,它为每个线程提供了一个这样的变量,不同变量用于指向不同的内存区域。

在 Go 语言中,TLS 存储了一个 G 结构体的指针。这个指针所指向的结构体包括 Go 例程的内部细节(后面会详细谈到这些内容)。因此,当在不同的例程中访问该变量时,实际访问的是该例程相应的变量所指向的结构体。链接器知道这个变量所在的位置,前面的指令中移动到 CX 寄存器的就是这个变量。对于 AMD64,TLS 是用 FS 寄存器来实现的, 所在我们前面看到的命令实际上可以翻译为 MOVQ FS, CX。

不同的Go版本获取的数据结构可能是不同的,所以petermattis/goid针对1.5、1.6、1.9有变动的版本定制了不同的数据结构,因为我们只需要得到goroutine的ID,所以只需实现:

1234
func Get() int64 {	gg := (*g)(unsafe.Pointer(getg()))	return gg.goid}

我比较了一下#1和#2这两种实现方式的性能,差距还是非常大的:

123456789101112131415161718192021222324
package pkgimport (	"runtime"	"testing"	"github.com/petermattis/goid")func BenchmarkASM(b *testing.B) {	b.ReportAllocs()	for i := 0; i < b.N; i++ {		goid.Get()	}}func BenchmarkSlow(b *testing.B) {	b.ReportAllocs()	var buf [64]byte	b.ResetTimer()	for i := 0; i < b.N; i++ {		goid.ExtractGID(buf[:runtime.Stack(buf[:], false)])	}}

性能比较结果:

12
BenchmarkASM-4    	300000000	         3.70 ns/op	       0 B/op	       0 allocs/opBenchmarkSlow-4   	  300000	      4071 ns/op	       1 B/op	       1 allocs/op

一千多倍的差距。

petermattis/goid这种hack的方式可以暴露更多的运行时的细节,比如我们可以扩展一下,得到当前哪个m正在运行,甚至可以得到当前的线程的信息:

1234567891011121314151617181920212223
type m struct {	g0        *g	morebuf   gobuf	divmod    uint32	procid    uint64	gsignal   *g	sigmask   sigset	tls       [6]uintptr	mstartfn  func()	curg      *g	caughtsig uintptr	p         uintptr	nextp     uintptr	id        int32}func GetM() int32 {	gg := (*g)(unsafe.Pointer(getg()))	m := (*m)(unsafe.Pointer(gg.m))	return m.id}

sigset在不同的平台的大小是不一样的,可以参考os_*.go中各平台的定义。上面是得到m的ID, 更全的m的结构定义海包括thread等信息。

2017-09-05 更新

petermattis 最近做了更新, 利用go本身的g_goid宏,直接利用汇编就可以获取goroutine id,比上面的方式更简单,性能还好:

123456789
#include "go_asm.h"#include "textflag.h"// func Get() int64TEXT ·Get(SB),NOSPLIT,$0-8	MOVQ (TLS), R14	MOVQ g_goid(R14), R13	MOVQ R13, ret+0(FP)	RET
]]>
0
<![CDATA[使用 Go 和 Let's Encrypt 快速配置HTTPS加密]]> http://www.udpwork.com/item/16442.html http://www.udpwork.com/item/16442.html#reviews Sat, 30 Sep 2017 17:04:50 +0800 鸟窝 http://www.udpwork.com/item/16442.html Let's Encrypt在2015年秋季推出了免费的数字证书认证计划,旨在消除当前手动创建和安装证书的复杂性,并推广加密的万维网服务,为安全网站提供免费 的SSL/TLS证书。
Let's Encrypt 是由互联网安全研究小组(ISRG,一个公益组织)提供的服务。主要赞助商包括电子前哨基金会,Mozilla基金会,Akamai以及思科。2015年4月9日,ISRG与Linux基金会宣布合作。

用以实现这一新的数字证书认证机构的协议被称为自动证书管理环境(ACME)。提案的一个版本已作为一个Internet草案发布。

目前, 申请证书的域名只能是特定的域名, 不支持通配符证书(*.example.com),这对于一个拥有众多子域名的公司来说很不方便。但是今年已经说了,将于2018年1月支持通配符证书和ACME v2 API。

原先有一些支持Let's Encrypt的 Go 工具和库, 比如legoacmeletsencryptrsc/letsencrypt, 但是我推荐使用官方的库:x/crypto/acme/autocert, 简单好用,并且官方维护。

稍微复杂一点的使用姿势如下(其实已经很简单了):

12345678910
m := autocert.Manager{    Cache:      autocert.DirCache("secret-dir"),    Prompt:     autocert.AcceptTOS,    HostPolicy: autocert.HostWhitelist("example.org"),}s := &http.Server{    Addr:      ":https",    TLSConfig: &tls.Config{GetCertificate: m.GetCertificate},}s.ListenAndServeTLS("", "")

autocert.Manager提供了GetCertificate方法, 可以用来配置TLSConfig。

更简单的方式是一行搞定:

12345
mux := http.NewServeMux()mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {    fmt.Fprintf(w, "Hello, TLS user! Your config: %+v", r.TLS)})log.Fatal(http.Serve(autocert.NewListener("example.com"), mux))

autocert.NewListener提供了一个自动配置的listener,使用起来非常的方便。

所以,如果你想做一些额外的配置,如果证书要缓存的位置,就用上面的方式, 如果想傻瓜式的使用,就用一行代码的方式。

关于这个包的设计的一些讨论可以看:#17053

]]>
Let's Encrypt在2015年秋季推出了免费的数字证书认证计划,旨在消除当前手动创建和安装证书的复杂性,并推广加密的万维网服务,为安全网站提供免费 的SSL/TLS证书。
Let's Encrypt 是由互联网安全研究小组(ISRG,一个公益组织)提供的服务。主要赞助商包括电子前哨基金会,Mozilla基金会,Akamai以及思科。2015年4月9日,ISRG与Linux基金会宣布合作。

用以实现这一新的数字证书认证机构的协议被称为自动证书管理环境(ACME)。提案的一个版本已作为一个Internet草案发布。

目前, 申请证书的域名只能是特定的域名, 不支持通配符证书(*.example.com),这对于一个拥有众多子域名的公司来说很不方便。但是今年已经说了,将于2018年1月支持通配符证书和ACME v2 API。

原先有一些支持Let's Encrypt的 Go 工具和库, 比如legoacmeletsencryptrsc/letsencrypt, 但是我推荐使用官方的库:x/crypto/acme/autocert, 简单好用,并且官方维护。

稍微复杂一点的使用姿势如下(其实已经很简单了):

12345678910
m := autocert.Manager{    Cache:      autocert.DirCache("secret-dir"),    Prompt:     autocert.AcceptTOS,    HostPolicy: autocert.HostWhitelist("example.org"),}s := &http.Server{    Addr:      ":https",    TLSConfig: &tls.Config{GetCertificate: m.GetCertificate},}s.ListenAndServeTLS("", "")

autocert.Manager提供了GetCertificate方法, 可以用来配置TLSConfig。

更简单的方式是一行搞定:

12345
mux := http.NewServeMux()mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {    fmt.Fprintf(w, "Hello, TLS user! Your config: %+v", r.TLS)})log.Fatal(http.Serve(autocert.NewListener("example.com"), mux))

autocert.NewListener提供了一个自动配置的listener,使用起来非常的方便。

所以,如果你想做一些额外的配置,如果证书要缓存的位置,就用上面的方式, 如果想傻瓜式的使用,就用一行代码的方式。

关于这个包的设计的一些讨论可以看:#17053

]]>
0
<![CDATA[规则与用户体验]]> http://www.udpwork.com/item/16441.html http://www.udpwork.com/item/16441.html#reviews Fri, 29 Sep 2017 22:54:13 +0800 bang http://www.udpwork.com/item/16441.html 一直给自己定下每个月至少要有一篇博客文章的约定,无论是什么类型。这个月临到末尾,因故只发了半篇文章,必须再写一篇扯扯淡把这个空补上。

在杭州一直用电动车上下班,公司所在大楼楼下有电动车停车场,挺好的,不过有一点让人不爽,按这栋大楼规定的路线,电动车从公司骑出来需要绕一个大圈才能到出口,大概需要走300米,但是,如果你不按他规定的路线,逆行50米就可以到达出口。而这个逆行并没有任何危险:

  1. 道路很宽,是单行道但宽得走两辆车没问题 。
  2. 除了道路还有人行道,跟杭州大路非机动车道差不多宽。
  3. 道路很空,在50米距离里人车都很少,很难碰到有人或有车的情况。

这种情况下,你会选择遵守大楼的规则走300米绕个圈,还是不遵守规则走个50米快速到达出口?我选择不遵守规则。但时不时电动车出口处有保安守着,勒令按规则走,挺蛋疼。

我觉得遵守一些不合理、损人不利己、有明显更好解决方案的规则并不是一个好的选择,对于这个案例,在那么宽的道路,只需要划出一小道双向通行的非机动车道就能解决问题了,对行人/机动车没有影响。而规划者不考虑改进这里的用户体验,执行者守着规则站在那里勒令大家遵守,在我看来是比较傻的。

这种情况跟一些蹩脚园林设计一样,草丛捷径被无数人踩出一条路,原本设计的路鲜有人行走,是该质疑行人素质,还是质疑设计者能力?我觉得是后者。

生活中碰到很多这种情况,设计者并不为用户考虑,理所当然地定了一些规则,用户遵守起来感觉像个傻子。像之前住的碧桂园楼盘,访客到门口,必须下车,排队登记,门口工作人员会根据报上来的房号,打电话联系业主,问是否可以让进,再开放行条,才肯让访客进。以给业主和访客添堵的方式去保证小区的安全和人流,这里能让双方都满意的解决方案太多了,但没有人想改进。

而互联网世界则不是这样,不合理的设计大多会被改进,无脑规则并不常见,一是数字世界改进成本低,可以不断迭代,维护成本也相对低;二是覆盖面广,一个小改进能惠泽几百上千万用户,性价比高;三是竞争大,不改进可能导致流失用户。所以再小的细节也会被认真对待。而更重要的是在这些条件影响下,从业者的心智被锻炼成用户体验问题都应该被改进,而现实世界传统行业一些从业者很难有这样的意识,即时是一些成本低的改进也不会想去做,有些人用互联网注重用户体验的意识在传统行业中做事,被称为拥有互联网思维。

今天的扯淡就到这里,下期再见。

]]>
一直给自己定下每个月至少要有一篇博客文章的约定,无论是什么类型。这个月临到末尾,因故只发了半篇文章,必须再写一篇扯扯淡把这个空补上。

在杭州一直用电动车上下班,公司所在大楼楼下有电动车停车场,挺好的,不过有一点让人不爽,按这栋大楼规定的路线,电动车从公司骑出来需要绕一个大圈才能到出口,大概需要走300米,但是,如果你不按他规定的路线,逆行50米就可以到达出口。而这个逆行并没有任何危险:

  1. 道路很宽,是单行道但宽得走两辆车没问题 。
  2. 除了道路还有人行道,跟杭州大路非机动车道差不多宽。
  3. 道路很空,在50米距离里人车都很少,很难碰到有人或有车的情况。

这种情况下,你会选择遵守大楼的规则走300米绕个圈,还是不遵守规则走个50米快速到达出口?我选择不遵守规则。但时不时电动车出口处有保安守着,勒令按规则走,挺蛋疼。

我觉得遵守一些不合理、损人不利己、有明显更好解决方案的规则并不是一个好的选择,对于这个案例,在那么宽的道路,只需要划出一小道双向通行的非机动车道就能解决问题了,对行人/机动车没有影响。而规划者不考虑改进这里的用户体验,执行者守着规则站在那里勒令大家遵守,在我看来是比较傻的。

这种情况跟一些蹩脚园林设计一样,草丛捷径被无数人踩出一条路,原本设计的路鲜有人行走,是该质疑行人素质,还是质疑设计者能力?我觉得是后者。

生活中碰到很多这种情况,设计者并不为用户考虑,理所当然地定了一些规则,用户遵守起来感觉像个傻子。像之前住的碧桂园楼盘,访客到门口,必须下车,排队登记,门口工作人员会根据报上来的房号,打电话联系业主,问是否可以让进,再开放行条,才肯让访客进。以给业主和访客添堵的方式去保证小区的安全和人流,这里能让双方都满意的解决方案太多了,但没有人想改进。

而互联网世界则不是这样,不合理的设计大多会被改进,无脑规则并不常见,一是数字世界改进成本低,可以不断迭代,维护成本也相对低;二是覆盖面广,一个小改进能惠泽几百上千万用户,性价比高;三是竞争大,不改进可能导致流失用户。所以再小的细节也会被认真对待。而更重要的是在这些条件影响下,从业者的心智被锻炼成用户体验问题都应该被改进,而现实世界传统行业一些从业者很难有这样的意识,即时是一些成本低的改进也不会想去做,有些人用互联网注重用户体验的意识在传统行业中做事,被称为拥有互联网思维。

今天的扯淡就到这里,下期再见。

]]>
0