2017年第三届PHP开发者大会总结

2017PHP开发者大会

写在最前

大会干货很多,讲师阵容也很强大,一场接着一场,连续两天,还是学到了不少东西的,用主持人的话说就是我们干货很干,硬广很硬。不过窃以为讲师里面还是夹杂了“合作方”的,推销一些平台和书籍,不过无伤大雅,权作休息。

不过由于时间和地点的原因不少朋友没能及时参加,今年也不知道是否会有大会视频,所以我打算根据现场朋友用打赏堆出来的排名来对大会进行一个简单的总结,自己复习巩固一下,也希望能给其他人一些帮助。

瓜子后端技术架构的变迁(纪鹏程)

这个分享是热度最高的,原因可能在于它是最接地气的。不仅完整的介绍了瓜子二手车架构一步步由小到大的发展过程,以及这期间的经验教训,还逐条分享了各种最佳实践,可以说他的分享基本可以指导一个新兴业务,技术架构的快速发展方向。

石器时代,业务发展早期

这段时期的第一目的、最高目的是活下去

技术人员要把精力集中在业务上,快速迭代,实时响应业务需求,可能要做不少Quick but dirty work。此时也不要忙着造轮子和优化,能花钱买就花钱买,能用开源的就用开源的

过早优化和造轮子是万恶之源 —— 尼古拉斯·赵四

也不要着急上缓存。一是多一层依赖就多一层的风险,早期快速迭代时,尽量保持架构的简单。二是缓存可能隐藏掉一些代码性能、逻辑缺陷,导致一些性能问题可能到架构复杂到一定程度时才暴露出来,此时的修改的成本就十分高昂了。
尽早确定数据库规范、数据字典。因为在后期业务代码还有修改空间,底层数据库和一些约定一旦养成是很难改动的。
保密工作也要做好。对外开放的数据一定要做好加密和脱敏工作,前期激烈竞争阶段,一个泄密可能拖垮一个公司。

这个阶段的问题也很突出。代码质量差,文档缺失,新人上手难度高。基础服务脆弱,缺少各种管理平台(日志、部署)。不过没关系,坚持过这个阶段,业务只要能活下来,这些问题都不是问题。

瓜子架构-石器.png

铁器时代,业务发展期

熬过业务早期,进入快速发展阶段后,业务种类与规模会迅速增加,各种通用服务的重要性也逐渐显现,同时用户量也会大幅增长,服务性能压力开始出现。这个时期的架构也要做出相应的调整。

  • 业务拆分。包括代码和数据库,业务之间通过http接口调用
  • redis集群。使用Twemproxy来做中间代理
  • php性能监控。使用xhprof和各种日志,指定统一的日志规范

这个阶段常见的问题是,业务快速调整,接口混乱,调用方式混乱、性能缺乏监控;还缺少可靠的持续交付流程

瓜子架构-铁器.png

蒸汽时代,业务稳定期

各项业务逐渐稳定,各种痛点持续的暴露。

  • 业务进行细分,通用服务、业务独立管理。
  • 统一的接口调用方式,性能监控。
  • 规范持续交付流程,代码的上线、回滚,数据库的建立、评审,统一的监控、测试框架,代码之间的隔离
  • 健壮的第三方,一些通用平台没必要由专人维护,可以使用第三方平台,不过最好同时使用多家
瓜子架构-蒸汽时代.png

工作效率实践

本部分讲师分享了实践过程中能够提高效率一些经验方法,比较实用

代码的艺术

  • 代码分层,遵循psr1 psr2 psr4规范
  • 代码提交添加统一hook,控制格式
  • compsor,包括封装公司的内部库

单测

单测的重要性被反复提及。完整的单测是代码发布、升级的重要支撑。尤其是核心代码,覆盖率要保证。这里他推荐使用PATest代替PHPUnit作为主要单测工具

RPC

业务庞大后RPC的场景必不可少。最重要最基本的要求是调用方式统一。业界通用的解决方案一般是使用http接口,他们使用了guzzle基础上进行封装,支持了签名、并发请求和超时设置。另一个常用的方案是鸟哥开源的Yar

最后再啰嗦一句。RPC方法一定要跟普通的方法有明确区别!!!,否则可能被误用、滥用

服务解耦

服务级别,Rabbitmq & Kafka
数据表级别,Canal+Kafka

前后端分离

使用nodejs负责UI层展示与数据准备。php则负责业务逻辑与架构。明确职责范围,提高各自效率。

THE NEXT GENERATION OF PHP(惠新宸)

鸟哥本次分享的主要内容是,在php7发布的这两年期间他们的主要工作,包括release的7.1和正在开发中的jit分支。说实话,由于本人水平有限,鸟哥分享的内容只能大概听懂意思,知道他们在做什么,但具体原理细节,鸟哥分享的我还真听不懂。这里就对鸟哥的分享内容做个总结。

php7之后还有什么?JIT

php7于15年正式发布,他的最大卖点是,无感知的100%性能提升,包含了运行速度与内存消耗。那么在此之后php该往哪里发展呢?目前已经在开发的一个大方向就是JIT

JIT是什么?为什么是JIT?

鸟哥并没有做过多的解释。我就谈一些我的肤浅认识,给phper们提供些参考。

首先JIT(just in time)并非是新技术,一大批语言如java早已实现。JIT的思想很简单,即在程序运行时动态对程序进行编译,生成平台相关的机器码,从而加快程序运行速度。

php文件的执行流程大致是首先引擎加载php文件,解释器逐条解释执行代码。引入JIT后,前面一样,重点是JIT编译器会根据Runtime信息对热点代码进行动态编译生成机器码,然后这部分代码以后就可以直接执行了,而不需要解释器逐条解释执行了,运行效率便得到了提升

看到这里不知道大家是否和我有一样的疑问,既然编译为机器码执行的效率那么高,为何不在项目正式部署前全部进行编译,何必在运行时编译?要知道运行时编译也会增加程序的执行时间的。我在查阅了一些资料和一番思考后,有以下一些浅见

代码发布前先编译,是比JIT更早的通用办法,称为AOT(ahead of time),c语言便是这种执行模式。关于这两种模式孰优孰劣,学术界一直争论不休,目前也没有定论。但JIT相比AOT有这样几个优点

  • 发布速度快。不用每次都编译,发布速度自然快
  • 优化效率更好。因为JIT是基于Runtime信息,比AOT更“了解”代码,优化的效率更好。比如分析Runtime得知某个变量虽然声明是10个字节,但运行过程中一直是1个字节,那么就可以减小程序内存消耗;再比如某段代码始终未被执行,JIT则可以直接将其忽略
  • 粒度更精细。JIT可以只针对hotspot(热点)进行编译,热点可能是一个函数或者只是一个代码段
  • 对码农透明。JIT无须码农自己对程序根据不同平台进行编译发布,只需要写高级代码即可

基于以上几个优点,再结合php一贯的简单易用原则,我想JIT确实是不错的选择。不过php也是支持AOT的,有兴趣的同学可以查一下。

但JIT技术也绝不是灵丹妙药,即便是编译也是需要时间的,当代码编译的时间消耗大于运行收益时,程序反而会变慢!会有这种情况吗?有的,比如某个项目中,热点并不明显,JIT编译的代码执行次数都很少,那么编译带来的收益是有可能小于编译本身的消耗的

以下是在标准测试中引入JIT技术后,php运行效率比7.2有100%的性能提升,不过在实际生产环境中效果不会有这么好

jit.png

php7.1做了什么?类型预测

php要想实现JIT,有一个难题必须解决,那就是变量的类型预测。试想如果在动态编译时还要进行大量的类型检查,性能将会大打折扣。php7中已经可以对变量类型进行控制,7.1则是更加完善了这个机制,可以说目前php已经是半强类型语言了。但由于php的弱类型历史,仍有大量代码运行前是无法得知变量类型的,所以在7.1中鸟哥进行了大量变量类型预测的工作,为后续JIT打基础

变量预测

比较简单的一种办法是数据流分析,即分析代码的上下文,推断出变量的可能类型,比如

function calc ($a1, $b2) {        // $a1: [ANY], $b2: [ANY]
    $T3 = $a1 * 2;                // $T3: [LONG, DOUBLE]
    $a4 = $T3 % 1000;             // $a4: [LONG]
    $T5 = $b2 * 3;                // $T5: [LONG, DOUBLE]
    $b6 = $T5 % 1000;             // $b6: [LONG]
    $T7 = $a4 + $b6;              // $T7: [LONG, DOUBLE]
    return $T7;
}

其实这还是很困难的,鸟哥列举了一些开发过程中遇到的困难。比如变量的变量,$$var_name,或者顶层代码(即写在函数和类之外的代码)等等。php的历史包袱还是很重的。解决这些问题的简单办法就是强类型,但这又会降低开发效率,因为优化而影响phper的开发效率这是鸟哥所不愿意的,他认为业务永远是优先的,优化只是支线

目前鸟哥的解决办法就是对JIT进行分级,通过配置实现不同程度的动态编译,从而降低类型预测的难度。另外就是针对具体的场景,进行垂直优化

问答环节

鸟哥的问答环节也非常精彩,原定一小时的分享最终超了一小时,下面我就凭着记忆对一些问题复现一下,可能存在偏差,将来我可不负责

php7.1那个诡异的函数返回类型限定是如何考虑的?

鸟哥:没什么特别考虑,投票投出来的。首先说明一点,我投的是反对票。包括php的命名空间反斜杠我也是非常反对的,但可能由于我并没有对这方面太深的认识,没有理解其他开发者的意图。不过这些问题用习惯了也不是什么大的问题

升级php7后,遇到了一个诡异的引用计数的问题。具体记不清了,大致是他们发现有个应该回收的变量在升级后没有回收

鸟哥:我现在不能给你准确答复,有可能是个bug,这个我随后跟进一下。但我想说的是你刚才介绍了你们在调试过程中对引用数的反复推算,其实不必纠结这,引用数用于垃圾回收时只有0和非0两种区别,我们在增加引用计数时可能有时候不是加1,而是加2,所以不要太在意具体是多少,确定大于0就行

一位学生提问者表示自己对高并发、分布式感兴趣,如何提升这方面的技能呢?

鸟哥:这里你有一个误区。我们研究学习技术并不是为了学习而学习,而是为了解决实际的业务问题。你没有接触过这方面的业务,自然没有这方面的经验,等你真正有这个业务需求时,好多东西原理都很简单,使用方法也很成熟,自然就会了,这是个水到渠成的过程,不必刻意去追求那个“术”。另外,我多说一句是,其实当你真正处在这样的业务中时,你会发现这些事情很少需要你操心的,OP通过各种集群就已经把这些问题给屏蔽了。

鸟哥你是怎样看待php的前景呢?现在黑php的这么多人

鸟哥:php的前景不要问我,要问你和我,整个php生态。天峰贡献一个swoole,php就有了高性能网络请求功能,xx贡献个php-ml,php就有了大数据处理功能,我今天贡献一个jit,php就有了动态编译能力。php发展到今天就是大家你一个小贡献,他一个小贡献积累出来的,所以php的前景好不好,要看我们生态,也希望大家踊跃贡献。至于黑php,我现在都懒得反驳了,有句话说的好,“黑php之前,先数数他给你挣了多少钱”,我一直认为业务是技术存在的理由,能不能快速响应需求、实现业务才是最根本的。

目前php没有连接池,非常不方便,不知道官方是否有支持计划?

鸟哥:目前没有。不过这不正是一个给社区做贡献的机会吗?你们开发一个连接池,贡献到社区既方便了自己,也方便了大家。天峰昨天的分享PHP-X,不就是为了这样的事

鸟哥你是怎样看待全栈工程师这个概念的?

鸟哥:我并不认同这个概念,我认为这是个伪命题。全栈这个概念最早是前端工程师提出来的,认为从前端到后端这是“全栈”,但我理解的全栈应该是对一个领域从底层原理到上层应用,这不才更应该叫做栈?自称全栈工程师的大部分属于只对各个领域多少有些认识而已。优秀的工程师不必刻意去追求全栈,你只需要在你的领域里不断深入就行,深度达到了,自然就有了广度,广度是深度的副产品,推而广之,就是所谓的全栈工程师是当你在一个领域深入到一定阶段后的副产品,而不是刻意在各个领域学出来的

php7对性能压榨已经比较彻底了,未来php是继续提高性能呢,还是增加新的特性?

鸟哥:你想太多了,目前并未任何打算。JIT开发就非常困难了,这个是否能够成功还是未知数,下次大会如果JIT没有完成,我就没啥可分享的了。

现在在北京很难安家,将来回到二三线城市,php很难找工作,不知道鸟哥有什么看法吗?

鸟哥:不必过于担心,不光是程序猿,其实还有好多公司也很难承受一线城市的成本,也在不断的往二三城市分流,所以找工作问题还是不大的。另外至于你担心php难找工作,那你可以换java、换go啊,一个程序猿不应该给自己打上标签,“xx程序猿”,你作为一个工程师,至少要精通3种以上的语言,而且要有良好的学习能力

鸟哥你是如何放松你的部下呢?会请他们去大保健吗?

鸟哥:这个我没太多经验,不过就我自己来说,有时候加班多了还是比较累的,我有段时间脖子特别疼,一周得去至少三次按摩院按摩才能缓解,当然我说的是盲人按摩。后来我真的研究了颈椎康复指南,不是开玩笑,我是真研究了。人的脑袋大概12斤重,你想你整天顶个西瓜,要是颈椎肌肉不行的话,能不难受吗?所以我后来经常去健身房,锻炼颈椎,后来才慢慢好了

使用c++11开发php7扩展(韩天峰)

简单说就是天峰在多年的php扩展开发中,感受到基于c的zend api十分不方便,所以基于c++11对zend api进行了封装,既降低了php扩展的开发难度,提高了开发效率,同时也为php带了更多的想象空间,并把这个项目命名为php-x

什么是php扩展?

定义

php本身是c语言开发的,其实可以想象我们是使用php这个高级语言去驱动一个c开发的引擎(zend engine)去做事情的。php扩展就是允许我们使用c为整个引擎开发新的插件来扩展php的功能,比如网络请求的curl扩展,使用mysql的mysqli扩展等。这种机制有利于社区为php的发展添砖加瓦,可以说zend engine本身就是一个个插件堆积出来的

如何开发扩展?

  1. 使用ext_skel生成扩展开发骨架
  2. 编辑config.m4文件
  3. 在extension.h头文件中定义扩展函数
  4. 修改extension.c源文件,实现扩展函数逻辑
  5. 利用phpize configure make & make install安装扩展

这样当php程序中调用扩展函数时,zend引擎会去调用你所开发的扩展。当然,你在扩展逻辑中只能使用c语言通过zend api来对传入的php变量进行解析、处理,最终再通过zend api返回php变量,继续原流程

目前扩展开发有何痛点?

zend api不好用

  1. 大量使用宏
  2. api名称太长,参数太多,容易出错
  3. api分散在众多.h和.c文件中
  4. 需要精通c语言,大量的指针场景,容易出错
  5. 没有任何,任何的教程或手册!

古老的c语言

  1. 50年历史的古老编程语言
  2. 面向过程的风格,封装性差
  3. 指针容易出错
  4. 缺少原生的数据结构支持
  5. 仅适合编写底层软件

php-x做了哪些工作?

鉴于前面说的问题,php-x使用c++11对zend api进行了封装,选择c++11是因为

  1. c++是现代编程语言
  2. 面向对象风格,封装性好
  3. 模板泛型编程
  4. STL容器
  5. 通用编程,适用范围广

可以简单对比一下封装后的代码

// 变量操作
ZVAL_LONG(&a, 1234);    // zend api
Variant a = 1234;       // php-x

// 类型推断
Z_TYPE_P(value) == IS_LONG;    // zend api
value.isLong();                // php-x

// 类型转换
convert_to_long(value);    // zend api
value.toInt()              // php-x

此外,php-x还提供了数组、字典、资源等多种常见数据结构和方法封装。而且还提供了在c++中嵌套php的功能,c++中也可以访问诸如_GET、_POST等上下文,配合通过php扩展赋予了php调用c++的能力,实际上php-x完全打通了php与c++

实战php7扩展

本小节利用php-x实现一个扩展my_ext,包含my_ext_class类和my_ext_test函数,对php-x开发php7扩展做一个简单的示例

php7扩展注册

PHPX_EXTENSION() {
    Extension *extension = new Extension("my_ext", "0.0.1");
    extension->onStart = [extension]() {
        extension->registerConstant("MY_EXT_VERSION", "0.0.1");
        extension->registerClass(my_ext_class);
    };
    extension->registerFunction(PHPX_FN(my_ext_test));
    return extension;
}

实现扩展函数

PHPX_FUNCTION(my_ext_test) {
    for (int i = 0; i < args.count(); i++) {
        php::echo("arg[%d] type is %d\n", i, args[i].type());
    }
    Variant v1 = arg[0];
    Array arr(v1);
    arr.set(1, 'efg');
    retval = arr;
}

实现扩展类

Class *my_ext_class = new Class('myClass');
my_ext_class->addMethod(PHPX_ME(myClass, test), STATIC);
my_ext_class->addMethod(PHPX_ME(myClass, test2));
my_ext_class->addProperty('name', 'Rango');
extension->registerClass(my_ext_class);

实现类方法

PHPX_METHOND(myClass, test2) {
    Variant res = newResource('String', new String('hello'));
    _this.set('resource', res);
    php::error(E_WARNING, 'err message');
}

注册phpinfo

extension->info({'gtk support', 'enabled'}, {
    {'author', 'Rango'},
    {'version', ext->version},
});

然后和一般扩展一样编译安装就行

php的想象空间

php-x打通了php与c++,理论上c++可以做的事,php同样也能做到。同时php扩展的开发难度也大大降低,可以应用于更多的场景

  1. 可以用c++扩展实现多线程
  2. 程序中计算密集型部分可以轻松使用扩展实现
  3. 可以通过扩展加密商业软件
  4. Fackbook、Google、Microsoft、Tencent等巨头的开源c++库可以为php所用了

PHP安全开发:从白帽角度做安全(汤青松)

本节讲师分享了网络安全现状,和常见的安全问题及应对措施

严峻的现状

目前网络黑产发展迅速,聊天工具中搜索‘漏洞、低价、线报’能够找到大量的卖家。同时他表示他认识一个17岁的小孩,不读书,只做黑产,月入8k+

另一个是简单搜索会发现,国内开放3306端口的服务器数量有180万之多。有500T以上的mysql数据库此刻正在使用root账号裸奔!

漏洞的分类

讲师对常见的漏洞进行了分类,我感觉大致可以分为以下三类

  • 代码漏洞。即你所写的代码没有实现你的意图。可能是你水平不行,也可能是语言不行
  • 逻辑漏洞。即你设计的业务逻辑存在你没想到的情况
  • 第三方漏洞。即你麻痹大意了,所托非人~

代码漏洞

常见的代码漏洞包括

  • 代码注入
  • sql注入
  • xss
  • csrf
  • 文件包含
  • 命令执行

总的来说,代码漏洞主要原因是盲目相信用户输入,一条安全口诀始终不能忘记就是用户输入、程序输出始终过滤。另外就是不必要的权限不要开,不使用的代码不加载

比如代码漏洞的一般防护措施是,open_basedir限制程序访问范围,过滤点、斜杠、反斜杠,禁止加载远程文件,eval这种高危函数禁用,使用代码检查工具如Taint

以下几个环节是事故高发区,应着重检查

  • 文件上传
  • 全局变量
  • 裸写sql
  • include参数文件
  • 执行参数代码

逻辑漏洞

常见的逻辑漏洞包括

  • 越权访问
  • 连续id暴露重要信息
  • 身份认证、密码找回

业务安全问题的几个重灾区是

  • 授权
  • 身份认证
  • 用户输入限制
  • 密码找回
  • 验证码

据统计,产品发布上线后的bug修复成本大约是开发阶段的30倍!

2016web漏洞统计.png

第三方漏洞

我们在平时开发中会用到大量的第三方框架与类库,虽然大部分都经过了大量实战测试,但仍然会时不时爆发漏洞,国内常见的php框架ECshop,WordPress,Discuz,Dedecms,PHPcms等都曾报过漏洞。此时我们更多需要做的是保证使用最新版,保证第一时间修复漏洞

  1. 溯源检查。代码版本不能存在已知漏洞
  2. 版本更新。不要遗漏安全补丁更新
  3. 后台隐藏。不要使用默认的地址与账号密码

查看一下16年国内漏洞占比

最后,系统的安全性不是取决于最强的地方有多强,而是取决于最弱的地方有多弱!

swoole2.0原生协程高性能开发实践(思超)

该分享的主要学的是swoole1.0已经实现了协程,不过是用yield和generator,来实现函数的中途退出与再入

但这样存在很多问题,如对程序员不透明,需要掌握yield和gen的用法,其次要求整个流程都要使用这种方式。swoole2.0则是原生支持协程,使用新client可以方便的实现协程特性

mysql5.7不求人(叶金荣)

叶老师核心是讲发布四年的5.7可以代替5.6了,据官方测试,5.7的性能要优于5.6三倍

5.7的主要特性包括

  • 增加json数据类型
  • 支持隐藏索引
  • varchar可以动态加长
  • innodb读性能优化
  • innodb支持全文索引
  • innodb支持地理位置索引
  • 默认使用sql严格模式
  • select支持超时设置
  • 支持多主复制
  • 更多详情可以见另一篇mysql文章(mysql5.7特性分析)

是时候告别myisam了

最后,5.7的下一个版本将是8.0,也已经开始进入测试阶段了

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容

  • 最好的语言发布了新的版本,一个划时代的大版本:PHP7。 PHP7修复了大量BUG,新增了功能和语法糖。这些改动涉...
    人在码途阅读 579评论 2 5
  • 这篇文章是我之前翻阅了不少的书籍以及从网络上收集的一些资料的整理,因此不免有一些不准确的地方,同时不同JDK版本的...
    高广超阅读 15,412评论 3 83
  • 艺术的目的是解决混乱
    Abby阳阅读 187评论 0 0
  • 他在发呆阅读 152评论 0 0
  • 看到任何情话和美好的句子都有想到的人,真好。 女人能给予男人最温暖的两个字是“我懂”,男人能给予女人最温暖的两个字...
    说说时尚吧阅读 111评论 0 0