2015年9月23日星期三

在windows上用IIS搭建memadmin站点

memcache是使用比较广泛的缓存应用,之前在使用时觉得想要查询之类的非常困难,必须要使用命令行或者写代码,所以想写一个管理的页面应用,但是正好发现了memadmin这个货,正好是做这个的,于是直接搬过来使用就好了,下面是我部署的步骤,记录一下备忘,本来想部署在linux主机上,但是领导非要部署在windows上,还指定必须IIS。。。
  1. 下载需要用到的东西 
    • php-5.6.13-nts-Win32-VC11-x86.zip 下载
    • php_memcache-3.0.8-5.6-nts-vc11-x86.zip 下载 
      解压放入php\ext目录
    • memadmin-1.0.12.tar.gz 下载
  2. 配置php 
    • \php.ini-development复制出一份命名为php.ini
    • 配置相关必要选项,具体见php文档
  3. 配置IIS 
    • 处理程序映射 - 添加模块映射 - 
      *.php FastCgiModule D:\php\php-cgi.exe PHP_via_FastCGI 
      请求限制 - 仅当请求映射至以下内容时才调用处理程序 - 文件或文件夹
  4. 配置memadmin站点 
    • \config.php文件中配置登录memadmin的管理员用户名密码
  5. IIS中添加站点,默认文档中添加index.php

这样就好了,做完回头一看其实很简单,只要php和php_memcache的版本不搞错就好 
其实用linux更简单,有机会再弄个linux版的

2015年9月5日星期六

学习部署flask站点

前段时间工作有点闲,抽出一天时间把之前的chageci的api站点用python代码重构了一遍,使用了简单的flask框架,代码量锐减,少的我都不好意思分模块了,直接在一个py文件中搞定。
但是由于自己没学明白,只会编码而不会部署,加上linux不熟练,所以写好了代码就一直放在那里没去管它,直到叶同学跟我唠叨说要一起弄个vps做东西,我只好硬着头皮去研究flask的部署问题,没想到居然也一天搞定了,下面说说步骤。
  1. 开虚拟机 
    我是在azure上开了一个ubuntu的A1主机,几乎是最低配置了,跑两个个超低流量的api站点够用了
  2. ssh 
    单位用的是windows电脑,客户端用的是securecrt(原谅我用的不是正版,只是找一个熟悉linux的同事要的);家里用的是mac电脑,直接命令行就可以ssh连接了:ssh -p port user@ip
  3. 部署python环境 
    虽然连上了linux服务器,但是对linux命令还是抓瞎,一边google一边做。ubuntu跟mac一样已经安装好了python环境,而且python3也有,挺不错。
    • 安装pip sudo apt-get install python3-pip
    • 安装virtualenv sudu pip3 install virtualenv
    • 到项目目录 cd myapp
    • 创建python3虚拟环境 
      virtualenv -p /usr/bin/python3 venv 
      source venv/bin/activate 
      pip3 install package-name 
      此时环境都安装完毕,用python3 app.py运行可以测试效果
  4. web容器gunicorn 
    gunicorn是运行python站点的容器,可以多线程运行,是专门处理web请求的,而我们的代码则只需要专注于处理逻辑。 
    安装 sudu apt-get install gunicorn 
    运行 gunicorn app.py 
    这样就默认运行在本地的8000端口了,访问http://127.0.0.1:8000就可以直接访问到我们的站点,而如果需要指定一些东西可以添加参数gunicorn -w 4 -b 0.0.0.0:8000 -p rocket.pid -D api:app,-w是指定线程数,-b是监听ip和端口,如果制定为0.0.0.0则可以外网访问,但是不建议这样做,最好用默认的127.0.0.1,然后前面再添加一层反代,-D是添加为守护进程,这样在ssh超时后仍然可以运行(就像winservice)
  5. 反向代理 
    以前用过nignx,配置比较容易,但是同学上的是apache,似乎比nignx难一些,于是让他帮我配好一个反代站点,我也没管了。
  6. 后续问题 
    程序在本地运行速度很快,可是到了服务器上却很慢,差不多要几十秒,后来发现我们的程序都需要发起http请求,看日志发现这个请求消耗了几乎全部的时间,但是直接在服务器的浏览器中请求该域名却很快,原来服务器默认开通了ipv6,所以解析域名时先进行ipv6的解析,时间都消耗在这里了,把ipv6禁用掉就好了

总结

linux还是很好用的,看着命令行界面下运行任何程序都那么快,心里还是有不小的快感,学会了一些常用的命令 lspskillrmtouch 
退出virtualenv环境的命令 deactivate 
使用了vim直接编辑代码测试问题,最常用的ddi-wq-q!
最后利用周末时间详细看了一遍一个flask的文档,以前看到过,觉得看不懂,没看下去,现在看几乎全是干货,受用匪浅,强烈推荐 
https://spacewander.github.io/explore-flask-zh/index.html

由于我的站点是很简单的微信公众账号的api,昨天看了一下,新增了4个用户:)

2015年4月11日星期六

一次数据库恢复

晚上上着线,领导突然轻声跟我说,我执行了这个。。。然后指着屏幕上的一句DELETE语句,我瞬间明白了,这是错删数据了,好吧,苦逼的我只好去折腾数据恢复工作,谁叫咱是专业擦屁股的呢。
先想想吧,数据库是SQL Server,有日志,可以从操作日志找到记录并恢复,不过这个我没做过,业务不熟;另外我们有备份,每天一个完整的,20分钟一次增量的,许久没做了,业务有点生疏,但是还算做过,心里有底,于是决定采用这种方式。
下面还是贴上代码吧:
restore database RY_20150410 from disk = 'E:\sqlback\RY_backup_2015_04_10_040001_5063465.bak' with norecovery,
MOVE 'RY' TO 'E:\sqldatatemp\AdventureWorksCopy_Data.mdf', 
MOVE 'RY_log' TO 'E:\sqldatatemp\AdventureWorksCopy_Log.ldf';


restore log RY_20150410 from disk = 'E:\sqlback\log\RY_backup_2015_04_10_042001_3408063.trn' with norecovery
restore log RY_20150410 from disk = 'E:\sqlback\log\RY_backup_2015_04_10_044001_9740908.trn' with norecovery
restore log RY_20150410 from disk = 'E:\sqlback\log\RY_backup_2015_04_10_050001_5706013.trn' with norecovery
.
.
.
restore log RY_20150410 from disk = 'E:\sqlback\log\RY_backup_2015_04_10_184002_3865400.trn' with norecovery


select state_desc,name,user_access_desc from sys.databases where name='RY_20150410'

--之前的恢复语句后面都加了with norecovery,就是指定恢复点,但是不进行实际的恢复
--最后恢复整个库
restore database RY_20150410 with recovery
还好我来这家公司之后做的第一件事就是做了数据库的备份计划,数据备份这东西不用的时候永远体现不出价值,一旦需要用了,那价值是无穷的。

2015年2月28日星期六

angularjs与bootstrap搭配,radio标签的checked无法更新到ngModel的问题

最近系统有很大的架构调整,组内没有专业前端,于是我这个半桶水被派去做前端任务,而且在我刚刚熟悉knockout的时候放弃,使用从未用过的angular框架,看着那道高高的学习曲线苦不堪言。
任务中涉及到大量的表单提交,而且同页面可能根据选项的不同而得到不同的表单项,对于这个我提出动态表单的想法,通过后端接口得到具体表单中的项类型、名称、属性、长度、正则等信息,然后前端通过类型判断生成不同的html。一查文档angular中的directive好像就是干这个事的,但是基本上都是一个标签,比如一个<icon> </icon>。而我的目标则是整个<form> </form>
想法不错,可是由于学艺不精,这个用angular我实现不出来,但是C#咱们熟啊,先用C#代码实现动态表单的工作,在使用ng-model绑定模型时发现radio类型的标签模型中获取不到值,经过测试发现还是bootstrap的老问题,该$('input')[0].checked=true,但是attr('checked')却是空,想了几个办法,后来发现一个最为简单暴力的,就是在外层的label上添加一个ng-click方法,直接将该值赋给对应的model。 
ng-click="formData.@input.Code=@radioData.Value"
@foreach (var radioData in input.InputSource)
{
    <label class="btn btn-primary" ng-click="formData.@input.Code=@radioData.Value">
        <input type="radio" name="@input.Code" value="@radioData.Value" autocomplete="off" ng-model="formData.@input.Code">@radioData.Name
    </label>
}
现在学angular貌似有点晚了,最近都是推崇其他的前端框架,像react、vue等,今天居然看到google自己也推了另一个框架叫polymer,感觉有点完全学不过来的节奏啊,不过先学通一个再学别的应该不难,如果熟练掌握了angular我也可以说自己是个基本合格的前端了吧。那个动态的form如果我能实现一定放到github上,希望有朝一日那么牛逼。

2015年1月17日星期六

以json格式发送对象集合作为参数的方法

一个页面,要同时修改多条数据,我们不可能向后端post多次来实现,可以一次post一个列表的形式将所有的数值都传给后端,然后后端统一处理。
先说前端,使用ajax技术post一个json对象是一个不错的方式,而我们的前端使用了knockout,viewModel正好也是json格式,而且knockout中还有一个ko.toJSON(viewModel)方法,非常简单,唯一需要注意的是在请求头中添加Content-Type: application/json,后端即可以自动反序列化为对象列表
var data = ko.toJSON(viewModel);
$.ajax('url',{
    'data': data,
    'type': 'POST',
    'contentType': 'application/json' 
});

再说后端,只要定义的接口参数中放入结构相同的列表就可以了
[HttpPost]
public ActionResult AfterCreditRepayNotifyDetailEdit(IList<AfterCreditRepayNotifyDetail> details)

以上过程在postman中测试是这样的: 
使用postman通过post方式传送json参数

2015年1月10日星期六

2014年总结与2015年目标

2014年已经过去了,这一年对我来说变化特别大,由于最后两个月换了工作,比原来忙了不少,所以也很少写博客了。在别人纷纷回望自己的一年时,我却累的不想打字,这个周末简单罗列一点,算是对自己过去的一年有个交代吧。
2014年经历的: 
1. 职业转型,没能转为希望的产品经理,却成了个专职程序员; 
2. 这一年,自己写过文档、画过原型、架设过服务器、接触云、从数据库到前端js代码,学了不少东西,感谢老叶的放纵,让我野蛮成长,也感谢老蒋的看重,将到带到新的平台; 
3. 自己写了个微信服务号程序,毫无宣传的情况下居然还有好几十号人订阅; 
4. 收入增加,能稍微改善点生活; 
5. 离开待了9年的公司,没有收到一句祝福与挽留,最后跟一个贱人吵了一架; 
6. 过年时带丁丁去了东北,丁丁很喜欢那里,也出奇的适应; 
7. 听了一场逼哥演唱会; 
8. 感谢所有容忍我一年的人;
现在的自己,年轻的面容早已不在,每天坐地铁时对着玻璃中自己大大的眼袋,心里也万般无奈,再该打拼的年纪自己选择了玩,现在只能还债,还好老天还算公平,晚努力总比不努力好,希望自己新的一年能有新的进步。
2015年计划如下: 
1. 购买VPS并搭建vpn与shadowsocks; 
2. 深入学习javascript,熟悉一个以上前端框架,比如Angular或React; 
3. 学会一门新的编程语言,比如php或swift; 
4. 入个MBP高配并掌握Mac系统; 
5. 锻炼身体; 
6. 每个月写一到两篇博客; 
7. 技术上继续学学学; 
8. 重构公司的系统,如果可能加入自己的产品想法; 
9. 忘记该忘记的,记住该记住的;

2015年1月5日星期一

javascript通过UserAgent判断访问设备

最近做的手机分享html页面中,有下载APP的按钮,需要判断访问设备是ios还是android系统,从而到对应的市场下载应用。
方法其实很简单,js判断userAgent中是否包含iPhone即可,再准确一点再判断个android还有分辨率之类的,但是我图省事就没做,代码如下:
function appDownload(){
    if(navigator.userAgent.indexOf("iPhone")>=0){
        location.href="https://itunes.apple.com/us/app/jin-rong-guan-jia100/id920332764?l=zh&ls=1&mt=8";
    }
    else{
        location.href="http://www.rongyu100.com/Uploads/Apps/FinaManager.apk";
    }
}
按钮点击时调用appDownload函数即可:
<a href="#" onClick="appDownload();" class="down pt-page-scaleUp">立即下载</a>