slan's blog

Loneliness or short time relief?

webpack expose-loader exports-loader区别

webpack expose-loader exports-loader区别

按字面意思理解是很困难的,暴露导出,实际上区别很大,做下实验:

三个文件,require.html entry.js content.js

<!DOCTYPEhtml>

<htmllang="en">

<head>

<metacharset="UTF-8">

<title>testrequire</title>

<scriptsrc="bundle.js"></script>

</head>

<body>

 

</body>

</html>

entry.js:

document.write(require("./content.js"));

content.js:

module.exports="i'mthedatathatyouwanted!";

执行webpack entry.js bundle.js生成bundle.js

渲染效果如下:

很简单的一个基本环境,就引入entry,write content.js的内容。

先实验expose-loader

npm install expose-loader

修改content.js 引入一个内部变量 insideData

varinsideData="I'mhiding";

module.exports="I'mthedatathatyouwanted!";

修改entry.js,用expose导出insideData

document.write(require("expose?insideData!./content.js"));

console.log(insideData);

console.log(window.insideData);

效果如下

发生了什么? console出来的并不是我们的insideData,而是exports出来的变量。所以expose并不是暴露包内部的变量,而是把包的exports变量暴露到全局空间。参数是可以随便自定义的。

再实验exports-loader

安装exports-loader, npm install exports-loader

entry.js,跟之前差不多,不过把expose换成exports:

document.write(require("exports?insideData!./content.js"));

console.log(insideData);

console.log(window.insideData);

content.js不变,效果如下:

require返回的东西换成了insideData,而不是原先定义的内容,所以exports插件理解也很简单,就是把导出的变量换掉,不带修改作用域功能。

总结

expose插件将module的exports变量暴露到全局空间。

exports插件将module的exports变量换掉

2016-03-30 12:23:00 +0000 UTC

DNS优先查询ipv6地址导致curl延时

DNS优先查询ipv6地址导致curl延时

起因

昨天有大活动,服务器进行扩容,跑的新封装的docker镜像,系统ubuntu14,发现用curl调用微信支付接口出现延时。能正确返回结果,但一般有三秒以上的延时。由于线上服务在跑,没有机会详细调试,于是先忽略之。

复现

今天在本地复现,一个简单的tcp连接,没有其他业务操作,时间花了3秒+,肯定哪里出了问题:

故障

1和2之间花了4秒,代码只有golang的dial函数,测试完整代码如下:

package main

import (
    "net"
    "fmt"
    "log"
)

func main() {

    log.Println(1);

    conn,err:=net.Dial("tcp","api.mch.weixin.qq.com:443")

    log.Println(2);

    if err!=nil {

        fmt.Printf("error: %v",err);

    }

    log.Println(3);

   conn.Write([]byte("hello"));

   log.Printf("remote ip: %s",conn.RemoteAddr().String())

   log.Println(4);
}

反馈给微信技术

联系到昨天在阿里云的服务器上的现象,怀疑是不是微信支付服务器出现网络问题,于是发封邮件反馈之,不料马上被呛了回来,我只能说:从来没有见过如此理直气壮让我滚回去查代码的客服:

微信反馈

相比而言阿里的同学还是好多了,发工单基本能回,不是他们问题也能帮忙协助分析,给些建议。

继续分析

对这样的回复我只能表示无语,不过也只能无奈的继续分析,本来准备抓包把结果丢他们糊他们一脸,结果却发现了一些有意思的事情:

抓包

先查询A记录,然后被cname然后查询AAAA记录,AAAA记录就是ipv6版本的A记录,结果dns服务器返回了查询失败,几个来回几秒的时间就过去了。然后ipv6地址解析失败,继续解析ipv4的地址,能正常解析,访问了。

所以延时的原因应该就清楚了,curl先去解析ipv6的记录,然后再解析ipv4的记录,这样出现了延时

疑问

为什么同样是ubuntu14,我们其他的服务器没有出现,单单docker里面每次都延时?

答:其他服务器默认开启了nscd,缓存了dns查询记录,不会每次查询,所以对业务的影响就很小了。

如何解决

优先方案是开nscd缓存服务,因为不只是解析ipv6的问题,频繁解析dns有很大的时间开销。

然后换个好点的dns,优先ipv6策略越来越多的被使用,为什么影响还不大,因为即使是dns查询失败也可以很快时间的返回,试验过好几个dns,有的dnsAAAA记录即使没有,但是返回时间很短,阿里的dns就卡了数秒,可能跟dns策略有关系。

修改系统配置可能有用,有搜到/etc/gai.conf,但是试验是没效果的,有兴趣的可以研究下。参考:https://community.rackspace.com/products/f/25/t/5110

最次是写host文件,紧急情况下可以用,但一定只能作为临时救急手段。

2016-03-29 16:09:00 +0000 UTC

docker在高流量情况下出现丢包,网络超时

docker在高流量情况下出现丢包,网络超时

概要

docker在高流量情况下(频繁的进行tcp连接)可能导致网络丢包,超时等网络问题。原因是docker的NAT机制,启用了iptables进行网络中转,可能触发 nf_conntrack table爆掉的问题,导致网络丢包,超时。这本质来说不是docker问题,但是在应用docker时不得不注意这一机制。

现象

某次对一个web应用进行压测,nginx+php-fpm,典型的web环境,不过是跑在docker中。压测一开始很正常,逐步加量,到某个点出现瓶颈,无法访问504,502。系统负载正常,cpu内存占用都很低,网络带宽占用也不高。

分析过程

既然系统没到瓶颈,那么应该是某个配置问题,导致负载上不去,先看fpm的error日志,其中出现:

php_network_getaddresses: getaddrinfo failed: Name or service not known dsn:mysql:host=****;port=3306;dbname=****;charset=utf8

域名解析失败,于是写死host,再次压测,还是一样的情况,不过error中换了日志:

PHP Fatal error:  Uncaught PDOException: SQLSTATE[HY000] [2002] Connection timed

连接数据库出现超时

由此,基本怀疑是系统的网络出现问题,导致dns服务不正常,tcp连接也出现超时,查看netstat,有大量的time_wait,是不是频繁连接数据库,导致端口资源分配完了呢?

为了验证,调整了net.ipv4.tcp_tw_recycle,net.ipv4.tcp_tw_reuse等参数,time_wait是下去了,但网络还是一样不正常。

一时找不到原因,用strace跟踪了下,发现fpm中,同样的请求数,一分钟前是正常,无压力,一分钟左右后,就突然全部卡住,strace日志:

04:33:25.883957 socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 6 <0.000045>

04:33:25.884065 fcntl(6, F_GETFL)       = 0x2 (flags O_RDWR) <0.000027>

04:33:25.884202 fcntl(6, F_SETFL, O_RDWR|O_NONBLOCK) = 0 <0.000035>

04:33:25.884312 connect(6, {sa_family=AF_INET, sin_port=htons(3306), sin_addr=inet_addr("121.199.***.***")}, 16) = -1 EINPROGRESS (Operation now in progress) <0.000048>

04:33:25.884428 poll([{fd=6, events=POLLIN|POLLOUT|POLLERR|POLLHUP}], 1, 60000) = 0 (Timeout) <60.047186>

connect数据库出现60秒的超时,不能访问的原因于是就基本确定了,网络异常导致fpm连接数据库超时,堵塞了所有的fpm进程,导致后续访问完全timeout.这样cpu负载确实是不高的。出现这种情况,调整fpm的进程数是没用的,有多少会堵多少。

继续查看为什么网络出现原因,翻系统日志(/var/log/syslog),发现:

Mar 26 13:02:45 iZ23cf3dp1nZ kernel: [ 7548.212230] nf_conntrack: table full, dropping packet

Mar 26 13:02:45 iZ23cf3dp1nZ kernel: [ 7548.652083] nf_conntrack: table full, dropping packet

Mar 26 13:02:46 iZ23cf3dp1nZ kernel: [ 7549.276076] nf_conntrack: table full, dropping packet

Mar 26 13:02:46 iZ23cf3dp1nZ kernel: [ 7549.340070] nf_conntrack: table full, dropping packet

Mar 26 13:02:46 iZ23cf3dp1nZ kernel: [ 7549.340081] nf_conntrack: table full, dropping packet

Mar 26 13:02:46 iZ23cf3dp1nZ kernel: [ 7549.340089] nf_conntrack: table full, dropping packet

Mar 26 13:02:46 iZ23cf3dp1nZ kernel: [ 7549.340094] nf_conntrack: table full, dropping packet

Mar 26 13:02:47 iZ23cf3dp1nZ kernel: [ 7550.364029] net_ratelimit: 26 callbacks suppressed 

google下,发现是iptables的nf_conntrack table爆掉了,而这个一般是系统有做NAT中转服务才会用到,NAT???为什么会用到NAT???查看iptables -l恍然大悟,有host机对docker container的网络转发规则,于是定位到了原因,修改net.ipv4.netfilter.ip_conntrack_max为原来四倍,再压测一切正常,继续加量,真正的达到了系统瓶颈,cpu跑满,负载飙升。

解决方案

我是直接调整了nf_conntrack table size完事,因为我们的服务瓶颈不在网络,然而有时候不一定有效,有可能你们的流量更大,tcp连接更频繁,调整size也有极限,可能会带来内存占用很大的问题,需要权衡。

推荐google下nf_conntrack: table full, dropping packet,原因以及应对方案分析很多,不再重复。

2016-03-27 10:22:00 +0000 UTC

PHP消息队列业务解耦实践

PHP消息队列业务解耦实践

php是web开发的利器,在中小型web开发中,无人能撼动其地位。不过在大型应用中,对比java,python,由于相应生态的缺乏,在构建大型web服务的时候,经常会显得力不从心。

其中缺乏完善的消息中间件,是一个较大问题。

php作为一门偏web开发的语言,一般跑在apache,fastcgi上面,生存期从请求开始到结束,这就决定了他不太适合跑长时间运行的服务,假如需要跑一些例如发送邮件,统计分析,后计费类的业务,一般都是用crontab来定时请求。php的哲学是实用主义,够用就行,crontab在大多数情况下都能满足需求,简单的的后端处理需求,直接用crontab等解决就行,不用多想。

但是在大型web服务中,crontab就开始满足不了了,需要更高精度的业务控制,需要更高性能的异步业务处理。一般的解决方式是通过消息队列,将任务发送到后端应用中处理。前端负责从mysql,redis等取数据,展示。后端负责处理耗时较长,不需要立即返回的任务。而怎样消费消息队列,不用局限于php,python、golang、java等也可以去消费消息队列的任务,这样就实现了服务的解耦合。

php的消息队列,之前用过php-resque,这是基于redis的一款消息队列服务,phpresque被用的比较多,我们用起来也比较稳定,没出过大的坑,大多数情况下,都能很好的满足需求。后来随着业务的发展,我们把消息队列换成了nsq,原因是:

  • 单点故障:

虽然出问题的时候,非常少,但是因为没有很好的多节点机制,一旦出问题,整个系统都会挂掉,不能很快的切到备用节点。

  • 性能

后端消费者限定了php,在性能上面有一些慢。

  • 部署

部署上依赖的东西有点多,需要安装与维护redis,php拓展,配置上有点复杂

  • 拓展

消费者限定php,一些情况下,我们想用golang来处理对性能有要求的业务

nsq是一款非常优秀的消息队列服务,部署很简单,启动程序即可,无依赖,消费消息有python golang的sdk,我们想把对性能要求比较高的业务处理换成golang,所以就把消息服务替换成了nsq。目前我们的结构是这样的:

异步架构

前端是web服务器组,直接处理的数据是mysql,memcache,以及通过nsq往后端发送任务数据。

后端包括抽象的微服务,以及各种业务后处理。消息处理完成之后,结果一般会进入mysql数据库,供前端业务进行消费。

2016-02-02 16:30:00 +0000 UTC