React with Node.js in Webstorm

软件环境:

软件 版本
OSX 10.11.6
WebStorm 2016.2
Node.js 6.3.1
React 15.3.1

Node.js 是通过 nvm 安装的最新版本。国内的网络环境,想通过 WebStorm 直接创建工程是做不到的,只能通过命令行,使用淘宝的镜像。

安装 express

1
2
3
4
5
cd reactStudy
npm install --registry=https://registry.npm.taobao.org -g express-generator
express -e -css sass reactStudy
cd reactStudy
npm install --registry=https://registry.npm.taobao.org

安装 react

安装 react、react-dom、babel、browserify、watchify 等模块。package.json 文件的内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
{
"name": "taskmgr",
"version": "0.0.0",
"private": true,
"scripts": {
"start": "node ./bin/www"
},
"dependencies": {
"babel-cli": "^6.11.4",
"babel-preset-es2015": "^6.9.0",
"babel-preset-react": "^6.11.1",
"babelify": "^7.3.0",
"body-parser": "~1.15.1",
"cookie-parser": "~1.4.3",
"debug": "~2.2.0",
"ejs": "~2.4.1",
"express": "~4.13.4",
"express-session": "^1.14.0",
"morgan": "~1.7.0",
"mysql": "^2.11.1",
"node-sass-middleware": "0.8.0",
"passport": "^0.3.2",
"react": "^15.3.0",
"react-dom": "^15.3.0",
"serve-favicon": "~2.3.0"
},
"devDependencies": {
"browserify": "^13.1.0",
"watchify": "^3.7.0"
}
}

创建文件

在 views 目录下创建一个文件作为主文件:main.js。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var React = require('react');
var ReactDOM = require('react-dom');

var Main = React.createClass({

render: function () {
return <div>Hello william</div>
}
});

ReactDOM.render(
<Main />,
document.getElementById('content')
);

也可以使用 ES2015的方式来写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import React from "react"
import ReactDOM from "react-dom"

class App extends React.Component{
constructor(props) {
super(props)
}

render() {
return <div>Hello World!</div>
}
}

ReactDOM.render(<App/>, document.getElementById('content'));

配置babel

WebStorm->Preferences…->Tools->File Watchers下添加Babel。以下字段需要注意修改:

Field Value 说明
Name Babel 这里不要修改,不然还会提示添加 Babel Watcher
File type javascript 也可以选其他类型,只要和你自己的文件匹配
Program $ProjectFileDir$/node_modules/browserify/bin/cmd.js
Arguments -t [ babelify –presets [ es2015 react ] ] ./views/main.js -o public/javascripts/index.bundle.js 如果要调试,需要加上 -d 参数

index.ejs文件的下方添加

1
<script type="text/javascript" src="/javascripts/index.bundle.js"></script>

最后的效果:

1
2
3
4
5
6
<body>

<div id="content"></div>

<script type="text/javascript" src="/javascripts/index.bundle.js"></script>
</body>

因为对 Babel 和 browserify 不熟悉,所以这一段是花时间最多的地方。开始的时候因为用了 jsx 扩展名,在文件头的注释中有@jsx,render 函数翻译出来的代码会带上 file 函数而不是React.createElement,导致错误。后来重启 WebStorm 就好了。

结束.

Node.js from scratch

使用 webstorm,从创建一个空白的工程开始构建一个 Node.js 的websocket服务器。

  1. 创建空白工程
  2. 添加一个 js 文件,比如 main.js
  3. 添加一个 package.json 文件
  4. 在控制台中进入工程的根目录,使用命令npm install --save ws添加websocket模块。使用--save参数会把模块的依赖直接添加到 package.json 文件中。
  5. 编辑webstorm 的 Run/Debug配置,新建一个 Node.js 的配置,在 JavaScript file:这一栏里面写上main.js
  6. 在 main.js 中填入如下代码:
1
2
3
4
5
6
7
8
9
10
var WebSocketServer = require('ws').Server;
var wss = new WebSocketServer({port: 8280});

wss.on('connection', function connection(ws) {
ws.on('message', function incoming(message) {
console.log('received: %s', message);
});

ws.send('something');
});

至此,一个服务器就搭建好了。

注意点:
在 webstorm 中,可能会提示Unresolved function or method require(),这是因为没有开启Node.js Core library。在 webstorm 的Preferences->Languages & Frameworks->Node.js and NPM下开启就可以了。

phpStorm remote debug with xdebug

因为使用了 docker 进行了环境隔离,所以需要对 phpStorm 进行远程调试的配置。

网上有一些教程,不是太复杂就是已经过时。经过摸索,一下是完整的配置流程。

版本: ubuntu 14.04 LTS, phpStorm 9, php5-fpm

首先是服务器端的设置:

  1. 安装xdebug

    1
    apt-get install php5-xdebug
  2. 配置 xdebug

    1
    vim /etc/php5/fpm/conf.d/20-xdebug.ini

加入:

1
2
3
4
5
zend_extension=xdebug.so
xdebug.remote_mode="req"
xdebug.idekey="PHPSTORM"
xdebug.remote_enable=1
xdebug.remote_connect_back=1

上面的设置也可以直接放在 php.ini 中。

接下来进行客户端的设置:

  1. 点击phpStorm菜单Run -> Start Listening for PHP Debug Connections
  2. 设置断点
  3. 安装 xdebug-helper Chrome 浏览器插件
  4. 打开要调试的网页,激活地址栏的 xdebug-helper
  5. 如果一切都设置正确,在 phpStorm 会弹出对话框进行文件映射等设置。

这样就可以了。

PS:
如果在 phpStorm 中创建 Remote debug 配置,也是可以收到 debug 断点的。

如果是apache2,xdebug 的默认配置文件会出现在 /etc/php5modes-available/xdebug.ini

UPDATE
如果web 服务器是在Docker for Mac下使用的话,xdebug.remote_connect_back=1会不起作用,因为这里的 docker container 接受到的 ip 都是来自172.17.0.1,所以需要显式的指定 ip 和端口:

1
2
3
4
5
6
7
zend_extension=xdebug.so
xdebug.remote_mode="req"
xdebug.idekey="PHPSTORM"
xdebug.remote_enable=1
xdebug.remote_host="192.168.1.22"
xdebug.remote_port=9000
xdebug.remote_connect_back=0

这里的嗯Docker for Mac的版本是 v1.12,之后是否会修复这个问题,不得而知。
————2016.08.21

搞定 osx 下 virtualbox 中的 docker container 访问共享文件夹的权限问题

案例是这样的:

  • 有一台 osx 系统
  • 安装了 virtualbox 虚拟机
  • 在虚拟机里面装 Linux
  • 在 Linux 里面装了 docker
  • 用 docker 装了 Ubuntu 的 image
  • 在 Ubuntu 的 container 中装了 nginx

现在把OSX 下的一个 web 开发文件夹共享给 virtualbox下的 Linux,然后在通过 docker 映射到 container 下,nginx 的 root 目录指向这个文件夹,提供 web 服务。

但是问题来了,nginx 会报错说“permission denied”。在 container 下可以很明显的看到,共享文件夹中的内容是存放在了999这个 group 中。

分析一下原因,根源在于 virtualbox 从某一个版本开始,无法修改 osx 共享目录的权限了。可以看到,在 virtualbox 下的 Linux 中,共享文件夹的权限在一个叫做vboxsf的 group 下面。

以下是解决方法:

  • 在 container 下创建一个叫做vboxsf的 group: groupadd vboxsf
  • 修改这个 group 的 id 为999:vim /etc/group
  • 把 www-data 这个用户加入到vboxsf中:usermod -a -G vboxsf www-data
  • 重启 nginx:service nginx restart

java压缩,javascript 解压缩总结

这里描述如何在 client 端使用 javascript 处理通过 ajax 调用返回的压缩数据。服务器端使用java 的DeflaterOutputStream来进行数据的压缩。

最早尝试使用jquery$.ajax方法来做 ajax 调用,但是其返回的数据都是字符串类型的,部分字节被强制转换了,导致这部分数据无法正确的解码。

如果仔细观察,浏览器的开发者模式中Response页面得到的数据格式是正常的,但是在 success: function (data)中数据是不正确的。

接收字节流而不是字符串

所以,如果需要 ajax 能够接收字节流而仅仅是字符串,需要使用 XMLHttpRequest自己来实现,v1和 v2都可以,下面给出的代码使用 v2的特性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function ajaxRequest (data) {
var request = new XMLHttpRequest();
request.open(data.type, data.url, true);
request.responseType = "arraybuffer";
request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
request.onload = function (event) {
if (data.success) {
data.success(request.response);
}
};
request.onerror = function (event) {
if (data.error) {
data.error(request.response);
}
};

request.send(data.data);
}

使用的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var szActionUrl = http_host + path;
var content = "userName=" + userName + "&password=" + password;
ajaxRequest({
url: szActionUrl,
type: "POST",
data: content,
success: function (data) {
console.log("success");
decodeToJson(data);
},
error: function (data) {
console.log("failed");
}
});

解压缩

解压缩使用的是 zlib,在使用前先加入引用:

1
<script language="JavaScript" type="text/javascript" src="zlip/inflate.min.js"></script>

javascript 的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function uintToString(uintArray) {
var encodedString = String.fromCharCode.apply(null, uintArray);
var decodedString = decodeURIComponent(escape(encodedString));
return decodedString;
}

function decodeToJson(data) {
var uintArray = new Uint8Array(data);

var inflate = new Zlib.Inflate(uintArray);
var plainArray = inflate.decompress();

var s = uintToString(plainArray);
console.log(s);

var result = JSON.parse(s);
return result;
}

其中的uintToString是用来把 Uint8Array转换成字符串,decodeURIComponent可以正确的处理 utf-8编码的字符。

sftp用户创建与设置

环境:ubuntu server 14.04
目的:用户只能通过 sftp 访问指定目录,并且不能登录 shell

网上有很多这方面的介绍,最有价值的是这里。但是按照这篇文档还是遇到了两个坑:

  1. 配置没有加载
  2. sshd 无法正常启动

这并不能说是参考文章,只是它没有提到而已。解决方法在下面会以斜体的字体表明。

下面是需要的步骤;

创建用户组
1
sudo addgroup sftponly

之后将通过用户组来控制权限,所以创建一个新的用户组是有必要的。

创建用户
1
sudo useradd -d /home/bob -s /usr/lib/sftp-server -M -N -g sftponly bob

把新用户 bob 加入到 sftponly 这个用户组。

1
sudo passwd bob

为新用户 bob 设置密码。

1
echo "/usr/lib/sftp-server" >> /etc/shells

把 sftp-server 作为一个 shell 加入到 shell 列表。

创建用户目录
1
2
sudo mkdir -p /home/bob/uploads
sudo chown bob:sftponly /home/bob/uploads

为新用户创建 home 目录和 sftp 目录,并设置所有权。
需要注意的一点是,/home/bob 目录的 owner 需要是 root,权限最好是755

修改配置
1
sudo vim /etc/ssh/sshd_config

打开 sshd_config 配置文件,把 Subsystem sftp /usr/lib/openssh/sftp-server 注释掉,在下面加上 Subsystem sftp internal-sftp

最后一行添加上:

1
2
3
4
5
Match group sftponly
ChrootDirectory %h
X11Forwarding no
AllowTcpForwarding no
ForceCommand internal-sftp

注意:上面的代码一定要加在最有一行,否则 sshd 会无法启动

重启 sshd
1
sudo service ssh restart

注意:参考中使用sudo /etc/init.d/ssh restart来重启 sshd,没有任何的效果。

当使用 service 来重新启动的时候,如果正常会显示:

1
2
ssh stop/waiting
ssh start/running, process 12760

如果出错,则只会显示

1
2
ssh stop/waiting
ssh start/running

`

docker之程序员系列 (一)

因为我主要使用 ubuntu 做服务器,所以以下的操作都是在 ubuntu server 14.04下进行。

OSX 下的选择

虽然官方提供了一个工具(boot2docker),但是说到底,它其实也是在 OSX 下使用 virtualbox 虚拟了一个 linux 出来,然后再在 linux 下进行 docker 操作。那么对于我这样的凡事都希望清晰、简单的人来说,第一选择肯定不会使用这个工具,而是会选择从头开始。如果你和我一样,那么我们需要下面这几样东西:

  1. virtualbox
  2. ubuntu server 14.04

linux 安装完成之后,为了更好的使用,你需要知道几件关于 virtualbox 的事情:

  1. 如何映射端口
  2. 在命令行启动虚拟机

映射端口在 virtualbox 的gui 界面中就可以简单的完成。下面我贴出命令行启动和保存虚拟机的代码:

1
VBoxManage startvm Ubuntu --type headless
1
VBoxManage controlvm Ubuntu savestate

Ubuntu 下 docker 安装和配置

docker 安装和配置和其他程序没有什么两样。使用apt-get进行安装:

1
2
sudo apt-get udpate
sudo apt-get install docker.io

此时 docker 的每次运行都需要加上前缀sudo,比较麻烦。可以进行如下的处理:

1
2
sudo groupadd docker
sudo gpasswd -a ${USER} docker

其中,USER替换成 ubuntu 的当前用户。具体可以参考 askubuntu.com

第一个 image

现在我们需要一个 image。这里也有几种选择:

  1. 从网上下载。如果网络速度比较快的话,可以使用下面的命令从 docker 官方库中搜索并拉一个 image 下来:

    1
    2
    docker search ubuntu
    docker pull ubuntu
  2. 自己做一个

如果你选择自己做一个,也是有多个选择,但这里我选择简单一点,使用 debootstrap 工具。而使用这个工具,不需要一个参数一个参数自己输入,docker 官方提供了一个脚本mkimage.sh

1
2
3
git clone https://github.com/docker/docker.git
cd docker
./mkimage.sh

按照提示进行即可。

运行容器

现在我们有了一个baseimage,在我们启动它之前,我们可以给它打一个 tag。

1
2
3
4
# 查看当前用于的 image
docker images
# 给 image 打 tag,格式为[repository[:tag]]
docker tag <image_id> ubuntu:14.04

当我们拥有多个 image 之后,可以很容易的通过 tag 来识别各自的用处。

1
2
# 运行这个 dock
docker run -it --name first ubuntu:14.04

这样就启动了一个容器,把这个容器命名为 first,并且获得一个控制台。

保存修改

容器的一大优势就是很方便的丢弃一些数据和状态。如果你只是临时性的运行一下程序,验证一些想法,容器是非常方便的。当退出并且删除容器之后,所有的数据都会被抛弃。
但是当你需要保存数据或是状态,那么需要显示的进行保存。docker 的一些概念和 git 相似,这里也是通过 commit 来保存容器:

1
docker commit first tag

tag可以是原来的 image 的 tag,也可以是一个新的 tag。

blog 从 Dreamhost 搬到了 Linode

Dreamhost 从国内访问越来越差,速度慢不说,有时候还连不上。

除此之外,每月$8.5的共享服务器在价格方面也完全没有优势,没有 root 权限,很多东西都无法操作。

而且,虽然他们说硬盘大小是没有限制的,但是上次放了多一点东西就发邮件过来要求删除,说什么不能作为备份用途云云。

早有打算换一个服务商,只是一直没有找到合适的。这次买了 Linode 的 vps就把 blog 和 web 以及 git 都搬了过来。blog 也从 wordpress 换成了 hexo。因为 vps 上有足够的权限,分别做了 php 和 nginx 两个 docker 来支持这些服务。

Dreamhost 上现在只留下了域名服务,以后发现有更优惠的域名服务商之后再迁移出来。

Pomelo开发环境搭建之 Docker

因为不想把当前的 osx 的环境弄得更复杂以及想实战一下docker,所以选择了在docker下搭建pomelo 的开发环境,大致的过程如下:

一. 安装

  1. 安装 VirtualBox
  2. 在 VirtualBox 安装 Ubuntu server
  3. 在 Ubuntu 下安装 docker
  4. 在 docker 下下载 Ubuntu image
  5. 运行 Ubuntu image,下载各种工具
  6. 根据文档安装 pomelo

二. 配置

  1. 在 VirtualBox 中映射 Ubuntu 到 OSX 的 ssh 端口
  2. 在 VirtualBox 中映射 Ubuntu 到 OSX 的 pomelo 需要的端口
  3. 在 docker 下映射 Ubuntu container 到 Ubuntu host 的 pomelo 需要的端口

三. 文件

  1. 在 VirtualBox 中设置 OSX 到 Ubuntu 的共享文件夹
  2. 在 docker 下把 Ubuntu host 中的共享文件夹映射到 Ubuntu container

四. 调试

  1. 安装node-inspector,并指定web-port和 debug-port
  2. 编辑 pomelo 的工程中的 servers.json,为需要调试的服务器指定调试端口
  3. 在 Chrome 中按照node-inspector 的提示打开调试的 url

五. 问题

  1. 安装 pomelo 的时候花了很多时间,有些包比如 heapdump 总是出错,后来发现是没有安装 make 和 g++等开发工具
  2. 在 container 中用命令创建 pomelo 工程后在 OSX 下无法保存,发现是因为权限的问题。一个方法是在 container 中把目录和文件都改成 other 可写,另一个方法是在 OSX 下把 container 中创建的工程复制一份,把原来的工程目录删除即可
  3. 服务器指定调试端口(3005)后一直没有办法连上,Chrome 中也看不到工程的文件,此时换一个调试端口即可
  4. 在 container 中需要运行 node-inspector、server、client 以及其他的命令,但是只有一个控制台,解决的办法一个是把命令运行到后台,另一个安装 tmux

Quick-cocos2d-x 接入 protobuf

  1. 下载云风的 pbc:

    1
    git clone [email protected]:cloudwu/pbc.git

    把 pbc 下 src 目录复制到 quick 的 lua_extensions 目录下,并重命名为 pbc。

  2. 下载 lpeg,并同样复制到 lua_extensions 下
  3. 下载 struct,并同样复制到 lua_extensions 下
  4. 编辑 lua_extensions\lua_extensions[_more].c 文件,添加:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // protobuf
    #include "pbc/pbc-lua.h"
    // lpeg
    #include "lpeg/lptree.h"
    // struct
    #include "struct/struct.h"
    ... ... static luaL_Reg luax_exts[] = {
    ... ...
    {"lpeg", luaopen_lpeg},
    {"struct", luaopen_struct},
    {"protobuf.c", luaopen_protobuf_c},
    ... ...
  5. 把 pbc,lpeg,struct 目录加入到 xcode 的工程中。比如游戏工程或是 quick-player 的工程

  6. 编译项目。