新玩具-树莓派3

被销售同事忽悠,买了个树莓派3 Model B玩一下。记录一下安装步骤,以免忘记。

1、设备到手,安装好散热片,外壳,风扇。因为同时买了官方摄像头模块,还有个电子纸屏幕模块,一起安装。摄像头插入到标准接口里,电子纸屏幕按照官方Demo项目里的README插上对应的8个针脚。

2、官网下载Lite版Raspbian系统,无桌面的,Mac下用软件Etcher把镜像写入到TF卡。把卡插进设备里,通电启动。通电前要连接HDMI显示器,启动后再连接就不会有输出了。

3、连接USB键盘,用默认用户名pi密码raspberry登录进去。启用WIFI:修改/etc/wpa_supplicant/wpa_supplicant.conf,在最后添加:

network={
ssid=“WIFI名称“
psk=”WIFI密码“
}

保存重启,就会连上WIFI,启动过程最后界面上会显示本机IP地址。(PS:可以写多段network,自动识别连接多个WIFI)

4、再用键盘登录进去,sudo raspi-config,在几个选项下面分别启用ssh,启用camera,启用spi。然后重启。应该就可以在别的电脑上用ssh连接进去了。

继续阅读“新玩具-树莓派3”

坑爹的微信支付官方文档

微信支付的官方文档里的代码,原样复制过来,所有参数用官方的库生成好,传递到页面上,进入支付页面以后,没有任何反应。就是这个文档:

https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6

搜索了一下有不少提这个问题,还有人说iOS里没反应,安卓可以。也有人说这个支付接口是第一代接口,实际已经不起作用了,现在都要使用第二代接口,也就是这个文档:

http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html

于是费了老半天劲把代码改成了这个方式,支付过程中多调了两个接口,多生成了一套加密参数,终于成功了。

今天同事用微信开发工具调试了一下原来那个代码,发现坑爹的事情是,官方示例代码里面所有的冒号都是全角的,于是直接脚本错误了……

一个一个改成半角冒号,支付对话框就弹出来了……

微信,你还能再坑一点吗?

.Net网站程序疑难杂症Dump内存分析

有些时候.Net的站点在测试时没问题,上到正式环境出现CPU 突然占用100%的问题,或者突然进程崩溃的问题,当代码量比较大的时候,基本上完全无法下手找到问题,在系统的日志里你也找不到任何有用的信息。这时候几乎只能依赖于Dump内存进行分析,查看当时情况下的进程中正在执行的过程,来查找可能出现问题的原因。
对CPU占用100%的问题处理:
ProcDump 8.0下载,只有一个400K主程序,不需要其它附加内容。
当指定进程的CPU占用超过30%连续3秒的时候抓取完整dump,抓取2次后退出。
另外在进程执行过程中抓取之前,程序中触发的异常信息也会直接显示在命令行窗口中。

procdump -ma -c 30 -s 3 -n 2 -e 1 -f “” 5960(Process Name or PID) -o E:\Procdump\Log

    -ma 生成full dump, 即包括进程的所有内存. 默认的dump格式包括线程和句柄信息.
    -c 在CPU使用率到达这个阀值的时候, 生成dump文件.
    -s CPU阀值必须持续多少秒才抓取dump文件.
    -n 在该工具退出之前要抓取多少个dump文件.
-o dump文件保存目录.
其它可用参数:
-t
Write a dump when the process terminates.
-m
Memory commit threshold in MB at which to create a dump.
-e Write a dump when the process encounters an unhandled exception. Include the 1 to create dump on first chance exceptions.
分析Dump还是需要用windbg(安装WindowsDevelopKit):
打开windbg,将对应的Dump文件拖进来,先加载.Net符号文件:
.load C:\Windows\Microsoft.NET\Framework64\v4.0.30319\sos.dll
然后:
!threadpool,查看线程池的CPU使用量
!runaway,查看线程占用CPU时间
~28s,切换到28这个线程(占用CPU前几名的程序,可能都需要切换过去看一下)
!clrstack,到具体某个线程后,查看当前线程中正在执行的托管代码,到这一步基本就可以看出问题
其它命令:
!analyze -v 自动分析异常信息
输出信息中的关键信息:
然后进入43这个进程,查看托管代码:
基本可以锁定是由于Senparc这个库里面的LocalCacheLock中的RetryLock方法导致的死锁。
另外!dumpstack -ee也可以用来查找异常信息
对进程自动崩溃的问题处理(系统事件查看器中显示StackoverflowException,此异常用try catch无法捕获):
除上面的procdump -t方法之外,还可以用windbg中包含的adplus工具来生成dump:
adplus -Crash -pn w3wp.exe -FullOnFirst -o d:\output
对生成的Dump文件的分析方式同上。

Yosemite DP3+iOS8 Beta3 Handoff打电话功能测试

8号一大早就把手机和Mac都升级到了最新的测试版,DP3+Beta3,出现了Handoff开关,但是电话和短信进来的时候Mac上死活就是没反应,在网页上选中电话号码或者在Mac的通讯录里查看电话,也不会出现打电话的图标。查看了很多次,iCloud都已经使用同一个账号登录了,而且在DP2的时候Mac上就提示过你的iPhone已经可以使用Continues功能了。

今天才偶然在威锋论坛上看到一个帖子,原来是需要用同一个账号登录Facetime,恍然大悟。打开Facetime一看,以前用的是另外一个AppleId登录的。重新登录之后打开Facetime的偏好设置,允许联系的方式里面确保手机号已经被选中,下面的iPhone(蜂窝通话)功能选中,就OK了。

再回到通讯录里,每个号码的旁边就会直接出现了拨号的电话图标,在网页上双击选中一个手机或固话,就会出现下拉箭头,里面就有呼叫的菜单了。尝试着通过Mac直接发起呼叫一个号码,不知道是不是因为公司WIFI的问题,反应有点迟钝,接近半分钟以后,手机上出现了通话的信息(就是通话中按Home键回到桌面的那个状态),然后对方接起来之后就可以在Mac上直接通话了,通过过程也有延迟,但是没有那么明显,基本可以接受。用带耳麦的耳机直接插在Mac里打电话也可以,不过对方听到的噪音会比较厉害,缺少了iPhone上的降噪功能。

iOS8地理位置编程的奇葩改进

一个很简单的iBeacon的测试程序,以前都好用的,手机升级到iOS8 b2,xcode6 b2编译运行,就不能正常工作了,总是提示位置授权状态是kCLAuthorizationStatusNotDetermined,进到设置里隐私-地理位置下面找到这个应用,设为总是允许,再重新编译运行,它又会变成这个未知状态。看了文档加上搜索了很多次,都是说当弹出授权对话框的时候如果用户没有选同意,就会出现这个状态。可是,我这应用根本就不会弹出让用户授权的对话框啊。

以为是xcode的Bug,今天等到了beta3,结果还是同样的情况。又重新开始搜索,这次终于找到了眉目。

首先,原来的代码只要走到locationManager startMonitoringForRegion的时候就会自动弹出询问授权的对话框,而现在你需要调用locationManager requestAlwaysAuthorization手动申请授权,并在didChangeAuthorizationStatus这个回调里面继续后面的开始监测的代码。

其次,最坑爹的是,当你调用这个request的时候,它弹出的对话框里面的那句询问语,需要你自己指定。所以需要在你的info.plist里面添加一个key: NSLocationAlwaysUsageDescription,value就是对话框上那句询问语。这时候程序才能正确的弹出授权对话框,用户选了同意以后,程序后面的代码才能够正确的运行。

更有效的全网页深度抓取(样式+图片)

通常情况下的网页采集一般是指我们想要抓取整个站的页面内容,从首页开始,然后分析整个页面里的a链接,然后把链接指向的页面再取出来,然后再往下一层去采集。这种情况下,只需要分析a元素或者链接的正则就可以了。但是还有一种情况,我们想保存一个完整的网页内容下来到本地,希望它能够还原整个访问的体验,以便在离线时也可以使用,这样就需要保存下完整的js和css以及图片等。但是有些网页使用的css做了多层import嵌套,而有些图片还是通过js等方式后加载的,如果要写出一个完整有效的分析代码然后能够层层获取,并不容易。

这时候有一个更简单有效的方式,就是让浏览器告诉你哪些内容是显示这个页面需要的,是你需要保存下来的。而有些css(甚至大部分)中出现的样式和图片,在你想要的这个页面里并没有被用到。怎么做呢?用proxy方式。

接下来就要用到nodejs出场了。用express建立一个简单的网站,express –sessions proxy1,然后这个默认模板下面的public, views, routes等目录可以全部删掉。在package里添加上依赖包request和mkdirp,这就够了。

然后修改app.js,我们要怎么做呢?先把端口改成80,然后下面的route部分,直接加一个app.get(‘*’, function(req, res){});,由它来接管所有的路径请求。然后在这个函数中,我们需要让站点起到代理的作用。此时过来的访问请求的路径在变量req.originalUrl中,我们要把这个请求转发到原网站上去。所以在这里把原网站的域名加上去,var url = ‘http://www.sample.com/’+req.originalUrl,然后把这个请求交给request包去获取。request(url)得到的是一个读取流,基于nodejs的流机制,你可以把它直接pipe给输出流。比如下面写上这样一句:request(url).pipe(res); 这样request就直接去获取这个远程URL并把取到的内容直接转交给了response输出。我们的代理就完成了。

那么怎么来测试这个功能呢?只要node app.js启动你的本地站点,用浏览器访问http://localhost/some/url,访问任何路径,域名后面的部分都会被转发到你要抓取的网站上对应的URL上去,页面的内容都被被nodejs取回来并转发回你的浏览器,理论上,你的浏览器看到的页面内容应该是跟直接访问对方网站没有任何区别的。而且,在这个过程中,浏览器会负责解析现在需要加载哪些js,包括js中的再引入,还有css中的import,以及css样式多层覆盖之后实际需要的背景图片之类的。

这个时候,我们只要稍加改造,就可以让这个proxy做点贡献了。我们只需要把request取到的东西先保存下来,再返回给浏览器就可以了。经过试验,这个pipe貌似并不能转发两次,比如先转发给一个文件写入流,再转发给一个response写入流。那么我们只好把过程分开了,

        var writestream = fs.createWriteStream(file);
        writestream.on('close', function (result) {
            var readstream = fs.createReadStream(file);
            readstream.pipe(res);
        });
        request(url).pipe(writestream);

这里的file参数,就是根据文件的URL,去除了路径后面的参数,加上你要保存的路径,拼起来的一个绝对路径。当然,在写入文件之前,你需要检查一下它的路径是否存在,不存在的话需要先创建路径,这时候就要用到mkdirp了,可以一次创建多层路径。

这个过程写完,再用浏览器访问你的代理服务器,这个页面需要的所有文件都保存在你的本地了,而且保持着相对路径。

剩下的还有一点小优化,比如有的网站页面里的链接、js、css引用的时候全部写绝对地址,那你用localhost访问的时候,只有第一个页面的HTML会经过你的代理,后面的所有的请求都直接回到他的网站上去了,不经过代理了。这时候就要用到另外一台机器(比如虚拟机),把这个域名的IP直接指向你的这台机器的IP,再用浏览器访问他的绝对路径,所有的请求都会到你这儿,再由你的机器去访问真实的路径。为什么要用另一台机器呢,因为如果在你的这台机器上直接改了域名的IP指向,那你的代理服务器本身也访问不了他的网站了。

另外一个需要处理的是,在返回的内容中把可能存在的绝对路径替换掉。换成相对路径。这样保存在本地的所有内容才是可以原样打开的。

这样抓下来的页面,基本上是完整的。(除了ajax请求的部分和流媒体的部分)当然,ajax请求如果是本站内的请求,你也可以把路径替换成相对路径,然后在本地再打开的时候不是直接双击首页文件打开,而是通过nodejs建一个本地服务器来打开,其实应该也是支持的。