分类 程序与生活 下的文章

博客上阿里云了

好久没收拾博客了,折腾了好几天终于迁移到了阿里云上了,同时将SAE环境改成自己搭建的LNMP,完成了从wordpress数据的迁移转换。
接下来继续完善,自己写插件支持七牛云存储。
这篇文章就当是测试的吧。。测试评论、测试分享、测试新的编辑器等等等

使用composer安装yii2出现"could not parse version.."的问题

如果安装时出现这个错误

[UnexpectedValueException]
Could not parse version constraint <=2.*: Invalid version string "2.*"

很有可能是composer Asset插件版本的问题

官网说明使用“composer global require "fxp/composer-asset-plugin:~1.0.0"”

可是会出错,所以如果出错了,使用这个命令"composer global require "fxp / composer-asset-plugin: 1.0.1"重新安装一遍后,再安装yii2应该会正常了

编程中英语单词state和status的区别

state:比较常用,各种状态都可以用它,但是它更着重于一种心理状态或者物理状态。
Status:用在人的身上一般是其身份和地位,作“状态,情形”讲时,多指政治和商业。

state倾向于condition,是一种延续性的状态。status常用于描述一个过程中的某阶段(phase),类似于C语言中枚举型变量某一个固定的值,这个值属于一个已知的集合。
比如淘宝买家问卖家“我的网购现在是什么状况?”
这个问题的背景是讲话双方都清楚,交易状态有“买家选购”“买家已付款”“卖家已发货”“买家已签收”或者有“买家已
投诉”等等状态。这些状态描述一件事情发展过程中的不同阶段。而且,这些阶段的先后顺序也是双方默许的。
所以在这里可以问“What's the status of my purchase?”,此处用state不太贴切,如果硬用上去从语感上可能听着别扭。

说物态变化用state再恰当不过。如果说一个物质的四种状态,可以说“solid state”,但如果你说“solid status”,第
一,这两个词的组合不像是描述物态,更像是在说“确定的状况(solid产生歧义‘确定的/确凿的’)”;第二,这个说法即
使不被误解,也需要事先约定一组物态变化顺序,比如把这个物质从固态开始加热然后电离,可能先后经历固态、液态、气态、等离子态这四个阶段。类似先定义枚举,然后引用的方式。

 

扩展:

ajax中readyState,statusText,onreadystatechange,window.status怎么一会state一会是status都晕乎了

state所指的状态,一般都是有限的、可列举的,status则是不可确定的。
比如
readyState -- 就那么四五种值
statusText -- 描述性的文字,可以任意
onreadystatechange -- 那么四五种值之间发生变化
window.status -- 描述性的文字,可以任意

来个形象的比方,你体重多少公斤,属于status,但说你体重属于偏瘦、正常还是偏胖,那就是state.

Web前后端分离开发思路

1. 问题的提出

开发一个Web应用的时候我们一般都会简单地分为前端工程师和后端工程师(注:在一些比较复杂的系统中,前端可以细分为外观和逻辑,后端可以分为CGI和Server)。前端工程师负责浏览器端用户交互界面和逻辑等,后端负责数据的处理和存储等。前后端的关系可以浅显地概括为:后端提供数据,前端负责显示数据。

在这种前后端的分工下,会经常有一些疑惑:既然前端数据是由后端提供,那么后端数据接口还没有完成,前端是否就无法进行编码?怎么样才能做到前后端独立开发?

考虑这么一个场景:Alex和Bob是一对好基友,他们有个可以颠覆世界的idea,准备把它实现出来,但是他们不需要程序员,因为他们就是程序员。说干就干,两个就干上了。Alex写前端,Bob写后端。

Alex和Bob都经过良好的训练,按部就班地把产品的主要功能设计,交互原型,视觉设计做好了,然后他们根据产品功能和交互制定了一堆叼炸天的前后端交互的API,这套API就类似于一套前后端开发的“协议”,Alex和Bob开发的时候都需要遵守。例如其中一个发表评论的功能:

// API: Create New Comment v2
// Ajax, JSON, RESTful
url: /comments
type: POST
request: {content: "comment content.", userId: 123456}
response: 
    - status: 200
        data: {result: "SUCCESS", msg: "The comment has been created."}
    - status: 404
        data: {result: "failed", msg: "User is not found."}

Alex的前端需要向/comments这个url以POST的方式发送类似于{content: "comment content.", userId: 123456}这样的JSON请求数据;Bob的服务端识别后以后,操作成功则返回200状态和上面的JSON的数据,不同的操作状态有不同的响应数据(为了简单起见只列出了两种,200和404)。

API制定完以后,Alex和Bob就开始编码了。Alex把评论都外观和交互写完了,但是写到发表评论功能就纳闷了:Alex现在需要发Ajax过去,但是只能把Ajax代码写好,因为是本地服务器,却无法获取到数据:

// jQuery Ajax
$.ajax({ // 这个ajax直接报错,因为这个是Alex的前端服务器,请求无法获取数据;
    url: "/comments",
    type: "POST",
    data: {content: content, userId: userId},
    success: funtion(data) {
        // 这里不会被执行
    }
})

相比起来Bob就没有这个烦恼,因为后端是基于测试驱动开发,且后端可以轻易地模拟前端发送请求,可以对前端没有依赖地进行开发和测试。

Alex把这种情况和Bob说了,Bob就说,要不我们把代码弄到你本地前后端连接一下,这不就可以测试了吗。Alex觉得Bob简直是天才。

他们把前后端代码代码都部署到Alex的本地服务器以后,经过一系列的测试,调试,终于把这个API连接成功了。但是他们发现这个方法简直不科学:难道每写一个API都要把前后端链接测试一遍吗?而且,Alex的如果需要测试某个API,而Bob的这个API还没写好,Alex这个功能模块的进度就“阻塞”了。

后面还有168个API需要写,不能这么做。Alex和Bob就开始思考这个问题的解决方案。

2. 解决思路

在这个场景下,前后端是有比较强的数据依赖的关系,后端依赖前端的请求,前端依赖后端的响应。而后端可以轻松模拟前端请求(基本上能写后端的语言都可以直接发送HTTP请求),前端没有一个比较明显的方案来可以做到模拟响应,所以这里的需要解决的点就是:如何给前端模拟的响应数据

先来一句非常形而上的话:如果两个对象具有强耦合的关系,我们一般只要引入第三个对象就可以打破这种强耦合的关系。

QQ截图20150201223003

在我们上述开发的过程中,前后端的耦合性太强了,我们需要借助额外的东西来打破它们的耦合性。所以,在前后端接口定下来以后,我们根据接口构建另外一个Server,这个Server会一一响应前端的请求,并且根据接口返回数据。当然这些数据都是假数据。我们把这个Server叫做

Mock Server

,而Bob真正在开发的Server叫做

Real Server

QQ截图20150201223038

Mock Server是根据API实现的,但是是没有数据逻辑的,只是非常简单地返回数据。例如上面Alex和Bob的发表评论的接口在Mock Server上是这样的:

// Mock Server
// Create New Comment API
route.post("/comments", function(req, res) {
    res.send(200, {result: "Success"});
})

Alex在开发的时候向Mock Server发出请求,而不是向Bob的服务器发出请求:

// Sending Request to Mock Server
// jQuery Ajax
$.ajax({ 
    url: config.HOST + "/comments",
    type: "POST",
    data: {content: content, userId: userId},
    success: funtion(data) {
        // OK
    }
})

注意上面的config.HOST,我们把服务器配置放在一个全局共用的模块当中:

// Front-end Configuration Module
var config = modules.exports;
config.HOST = "http://192.169.10.20" // Mock Server IP

那么上面我们其实是向IP为http://192.169.10.20的Mock Server发出请求http://192.169.10.20/comments发出POST的请求。

当Alex和Bob都代码写好了以后,需要连接调试了,Alex只要简单地改一下配置文件即可把所有的请求都转向Bob所开发的Real Server:

// Front-end Configuration Module
var config = module.exports;
// config.HOST = "http://192.169.10.20" // Mock Server IP
config.HOST = "http://changing-world-app.com" // Real Server Domain

然后Alex和Bob就可以愉快地分离独立开发,而最后只需要联合调试就可以了。

总结一下基本上前后端分离开发包括下面几个步骤:

  1. 根据功能制定前后端接口(API)。
  2. 根据接口构建Mock Server工程及其部署。
  3. 前后端独立开发,前端向Mock Server发送请求,获取模拟的数据进行开发和测试。
  4. 前后端都完成后,前后端连接调试(前端修改配置向Real Server而不是Mock Server发送请求)。

当然要注意,如果接口修改了,Mock Server要同步修改。

3. 实现方案

Mock Server具体应该如何构建?应该存放在哪里?应该怎么维护?

前后端是不同的两个工程,它们各自占用一个仓库。Mock Server应该和它们分离出来,独立进行开发和维护,也就是说会有三个仓库,Mock Server是一个单独的工程。

Mock Server可以部署在本地,也可以部署到远程服务器,两者之间各有优劣。


3.1 远程Mock Server

做法:把Mock Server工程部署到一个远程的always on的远程服务器上,前端开发的时候向该服务器发请求。

优点

  1. 没有给原有的前后端工程增加负担。
  2. 每个前端开发人员向同一个Mock Server服务器发送请求,保持所有人获取响应请求的一致性。

缺点

  1. 有跨域问题(思考:locahost如何向192.169.10.20发请求?)。
  2. 需要额外的远程服务器支持。

(在写这篇博客的时候,逛Hacker News,刚好看到有人做了一个开发辅助工具(http://reqr.es/),可以用于开发时响应前端请求,其实也就是这里所说的远程Mock Server。真是不能再巧更多。)

3.2 本地Mock Server

做法:前端把Mock Server克隆到本地,开发的时候,开启前端工程服务器和Mock Server,所有的请求都发向本地服务器,获取到Mock数据。

优点

  1. 节约资源,不需要依赖远程服务器。环保节能。
  2. 没有跨域问题。

缺点

  1. 增加前端工程开发流程复杂程度。
  2. 每个前端开发人员自己部署服务器在本地,可能会有仓库没有及时更新导致API不一致的情况。


Mock Server工程一般可以由后端开发人员来维护。因为在开发的过程中,后端因为各种原因可能需要修改API,后端人员是最熟悉请求的响应数据和格式的人,可以同步维护Mock Server和Real Server,更好保证数据的一致。Mock Server维护起来并不复杂,对于比较大多工程来说,这样的前期准备和过程的维护是非常值得的。

最后

所以要点就是:根据API构建可以模拟服务器响应的Mock Server,用于前端请求模拟数据进行测试

再重复总结一下前后端分离开发包括下面几个步骤:

  1. 根据功能制定前后端接口。
  2. 根据接口构建Mock Server工程及其部署。
  3. 前后端独立开发,前端向Mock Server发送请求,获取模拟的数据进行开发和测试。
  4. 前后端都完成后,前后端连接调试。

当开发只有我一个人的时候,我更喜欢后端独立开发,开发前端的时候开个Real Server来做响应。又爽又快。其实如果团队的人是full-stack的话,完全可以按照功能模块来划分任务,而不是分为前端工程师和后端工程师。

但一般来说还是会选择前后端职能划分,对于这种情况下的多人开发的工程来说,前后端分离开发的方式确实需要考虑和构建的,可以更好帮助我们构建一个高效,规范化,流程化的开发流程。

还是那句话,没有银弹,所有的东西都需要根据实际情况来构建独特的流程。

原文链接:http://segmentfault.com/blog/livoras/1190000002413526

关于php中curl慢的问题

今天在自己的服务器上运行程序时需要远程请求一个接口,发现此时程序响应特别慢,平均时间都要十几秒。

最后断定问题出现在了curl地方,使用服务器命令(我的是centos)curl一个域名也很慢,但curl一个IP却能得到及时响应。(因此其实本文标题说是PHP中的问题是不严谨的)

这说明问题可能就出现在了DNS设置上,后来改了一下DNS后发现正常了,在此写文记一下。

配置DNS:

输入命令vi /etc/resolv.conf

设置一下DNS,我这里使用的阿里DNS

nameserver 223.5.5.5
nameserver 223.6.6.6

最后再重启下php和nginx

/etc/init.d/php-fpm restart
/etc/init.d/php-fpm restart

 

pear安装

说到安装pear,我是因为给一个变量起一个名字,联想到了如何起名才规范,于是又联想到了PHPDocument,要安装PHPDocument可以通过pear安装和手动下载安装,于是乎我就折腾起了这个pear的安装。

根据:http://pear.php.net/manual/en/installation.getting.php中的内容翻译得来

点此http://pear.php.net/go-pear.phar下载文件到本地并命名为go-pear.phar进行保存。在cmd中输入php go-pear.phar 开始安装。

Are you installing a system-wide PEAR or a local copy?
(system|local) [system] :

 选择系统级别安装还是安装本地,默认是system,直接按回车继续。

Below is a suggested file layout for your new PEAR installation.  To
change individual locations, type the number in front of the
directory.  Type 'all' to change all of them or simply press Enter to
accept these locations.

 1. Installation base ($prefix)                   : D:\php5
 2. Temporary directory for processing            : D:\php5\tmp
 3. Temporary directory for downloads             : D:\php5\tmp
 4. Binaries directory                            : D:\php5
 5. PHP code directory ($php_dir)                 : D:\php5\pear
 6. Documentation directory                       : D:\php5\docs
 7. Data directory                                : D:\php5\data
 8. User-modifiable configuration files directory : D:\php5\cfg
 9. Public Web Files directory                    : D:\php5\www
10. Tests directory                               : D:\php5\tests
11. Name of configuration file                    : C:\Windows\pear.ini
12. Path to CLI php.exe                           : D:\php5

1-12, 'all' or Enter to continue:

以上是默认的pear的临时、数据、配置、测试、执行目录的设置,第11项默认的话会显示错误,我所以还是改到了D:\php5目录下(输入需要改的数字项,会提示让你输入新的目录地址,不改的话啥也不用输入直接回车)。然后回车。

然后一系列安装,其中有以下这个警告:

WARNING!  The include_path defined in the currently used php.ini does not
contain the PEAR PHP directory you just specified:
<D:\php5\pear>
If the specified directory is also not in the include_path used by
your scripts, you will have problems getting any PEAR packages working.

Would you like to alter php.ini<D:\php5\php.ini>?[Y/n]

需要将pear配置目录D:\php5\pear加入php.ini的include_path指令中。输入Y后自动修改php.ini中的路径。

php.ini <D:\php5\php.ini> include_path updated.

Current include path           : .;C:\php\pear
Configured directory           : D:\php5\pear
Currently used php.ini (guess) : D:\php5\php.ini
Press Enter to continue:

 到这里也没啥了,按回车基本安装成功了。会在D:\php5下面生成一个PEAR_ENV.reg的文件,双击运行进行注册表注册即可。

通过命令pear install package安装程序包,如果出现failed to mkdir的错误,只要以管理员身份重新运行cmd即可。

Python乱码问题解决方法

最近要写个python脚本,遇到各种乱码问题,后来一查原来python乱码问题还真是层出不穷,让人头疼啊。

我主要是用的sublime进行脚本编写,使用cmd或python自带的GUI运行脚本调试查看。

比如下列的脚本代码在运行时会产生乱码

#coding:utf-8
print "的是"

QQ截图20150106092343因为编写代码时保存的编码为UTF-8,而在Windows中运行读取脚本时以系统的编码GBK去执行,因此GBK与UTF-8冲突导致编码错误。

解决方法有三种:

1、我们往往在代码顶部加入#coding:utf-8 这样指明解码时的字符集,而指明utf-8还是会出现所说的乱码,还是因为运行时采用的GBK与我们指定的UTF-8冲突。所以要指明编码的话就只好指明为和系统编码一样的字符集,如这样指明#coding:gbk ,不写coding的话默认为与系统一样的编码

QQ截图20150106094006

2、在打印的字符前加上u,如print u"是" ,这样就指明了使用头部coding指定的字符集解码,当然前提是要在头部添加coding,并且coding所指的的字符集与操作系统用的字符集一样(刚才第一条说明了)

QQ截图20150106094135

3、输出时指定使用的字符集,如print '是'.decode('utf-8')

QQ截图20150106094339

当然保存脚本时的编码要与coding一样。

另外据说还有种方法是加入这三行代码:

import sys
reload(sys)
sys.setdefaultencoding('utf-8')

这种方法我没试过,看起来好像是从系统编码进行下手的,将系统编码临时改掉,这样的话你们懂的。

关于python提示no module named win32api问题的解决

本篇只介绍对于本机环境下问题的解决方案。我的系统是Win7 64位,并且安装的是64位的python2.7.6版本

当提示“no module named win32api”该信息时,需要安装pywin32模块,原本我是想通过pip安装的,但有问题。

于是上网找了对应的安装包,对应于我的python版本的,如果你也是用的2.7.6 64位的,可以下载我提供的这个附件:pywin32-218.win-amd64-py2.7 然后进行安装,如果打开程序安装报错的话,很有可能是找不到python的注册表信息,我的解决方法是重新安装python(当然也有写个脚本进行注册的方法),重装时选择只对本机用户,而不是所有用户。完成后就能成功安装pywin32了。

sublime下配置编译和运行C程序

我使用的是sublime3,首先下载MinGW,别问我是啥,我也不怎么了解,网上说 MinGW 提供了一套简单方便的Windows下的基于GCC 程序开发环境,提供了GNU工具集。在这里我就理解它提供了gcc、g++、make等编译器吧。具体自行了解。

你可以上minGW官网http://www.mingw.org/或其他地方可以下载到,可视化安装界面,自行选择目录安装。比如我安装到了D:\minGW

安装完毕后,将minGW安装目录下的bin目录添加到环境变量,如下图,我将D:\minGW\bin添加到环境变量

QQ图片20141219150247

接着配置sublime,工具栏选择工具(tools)-编译系统,然后新建一个编译系统

QQ图片20141219150736输入以下配置:

{
	"shell_cmd": "gcc \"${file}\" -o \"${file_path}/${file_base_name}\" -std=c11 -O2 -Wall -lm --static",
	"file_regex": "^(..[^:]*):([0-9]+):?([0-9]+)?:? (.*)$",
	"working_dir": "${file_path}",
	"selector": "source.c, source.c++",

	"variants":
	[
		{
			"name": "Run",
			"shell_cmd": "gcc -std=c11 -O2 -Wall -lm --static \"${file}\" -o \"${file_path}/${file_base_name}\" && \"${file_path}/${file_base_name}\""
		}
	]
}

保存在Sublime Text的Packages目录下即可。

然后敲一段C程序然后保存为.c文件,按ctrl+B会在这文件旁生成exe程序,按ctrl+shift+B会在sublime控制台中显示运行结果(如下图)

QQ图片20141219152044

 

至此大功告成。

使用渐进式JPEG来提升用户体验

今天才认识到原来JPEG文件有两种保存方式他们分别是Baseline JPEG(标准型)和Progressive JPEG(渐进式)。两种格式有相同尺寸以及图像数据,他们的扩展名也是相同的,唯一的区别是二者显示的方式不同。

Baseline JPEG

这种类型的JPEG文件存储方式是按从上到下的扫描方式,把每一行顺序的保存在JPEG文件中。打开这个文件显示它的内容时,数据将按照存储时的顺序从上到下一行一行的被显示出来,直到所有的数据都被读完,就完成了整张图片的显示。如果文件较大或者网络下载速度较慢,那么就会看到图片被一行行加载的效果,这种格式的JPEG没有什么优点,因此,一般都推荐使用Progressive JPEG。

baseline

 

Progressive JPEG

和Baseline一遍扫描不同,Progressive JPEG文件包含多次扫描,这些扫描顺寻的存储在JPEG文件中。打开文件过程中,会先显示整个图片的模糊轮廓,随着扫描次数的增加,图片变得越来越清晰。这种格式的主要优点是在网络较慢的情况下,可以看到图片的轮廓知道正在加载的图片大概是什么。在一些网站打开较大图片时,你就会注意到这种技术。

progressive

 

渐进式图片带来的好处是可以让用户在没有下载完图片就可以看到最终图像的大致轮廓,一定程度上可以提升用户体验。(瀑布留的网站建议还是使用标准型的)

baseline_vs_progressive

 

另外渐进式的图片的大小并不会和基本的图片大小相差很多,有时候可能会比基本图片更小。渐进式的图片的缺点就是吃用户的CPU和内存,不过对于现在的电脑来说这点图片的计算并不算什么。

说了这边多下面就改讲讲怎么讲图片保存为或者转化为Progressive JPEG了。

1、PhotoShop

在photoshop中有“存储为web所用格式”,打开后选择“连续”就是渐进式JPEG。

photoshop

 

2、Linux

检测是否为progressive jpeg : identify -verbose filename.jpg | grep Interlace(如果输出 None 说明不是progressive jpeg;如果输出 Plane 说明是 progressive jpeg。)

将basic jpeg转换成progressive jpeg:> convert infile.jpg -interlace Plane outfile.jpg

3、PHP

使用imageinterlaceimagejpeg函数我们可以轻松解决转换问题。

<?php
    $im = imagecreatefromjpeg('pic.jpg');
    imageinterlace($im, 1);
    imagejpeg($im, './php_interlaced.jpg', 100);
    imagedestroy($im);
?>

4、Python

import PIL
from exceptions import IOError
&nbsp;&nbsp;
img = PIL.Image.open("c:\\users\\biaodianfu\\pictures\\in.jpg")
destination = "c:\\users\\biaodianfu\\pictures\\test.jpeg"
try:
&nbsp;&nbsp;&nbsp;&nbsp;img.save(destination, "JPEG", quality=80, optimize=True, progressive=True)
except IOError:
&nbsp;&nbsp;&nbsp;&nbsp;PIL.ImageFile.MAXBLOCK = img.size[0] * img.size[1]
&nbsp;&nbsp;&nbsp;&nbsp;img.save(destination, "JPEG", quality=80, optimize=True, progressive=True)

5、jpegtran

jpegtran -copy none -progressive <inputfile> <outputfile>

6、C#

using (Image source = Image.FromFile(@"D:\temp\test2.jpg")) {
    ImageCodecInfo codec = ImageCodecInfo.GetImageEncoders().First(c => c.MimeType == "image/jpeg");
    EncoderParameters parameters = new EncoderParameters(3);
    parameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L);
    parameters.Param[1] = new EncoderParameter(System.Drawing.Imaging.Encoder.ScanMethod, (int)EncoderValue.ScanMethodInterlaced);
    parameters.Param[2] = new EncoderParameter(System.Drawing.Imaging.Encoder.RenderMethod, (int)EncoderValue.RenderProgressive);
    source.Save(@"D:\temp\saved.jpg", codec, parameters);
}