ソースを参照

Gitbook Auto Published

Willin Wang 8 年 前
コミット
592d4b545a
3 ファイル変更261 行追加0 行削除
  1. 1 0
      SUMMARY.md
  2. 141 0
      project/tool/pm2.md
  3. 119 0
      project/user/particulars.md

+ 1 - 0
SUMMARY.md

@@ -29,6 +29,7 @@
     - [数据库设计](project/user/db.md)
     - [缓存设计](project/user/cache.md)
     - [BDD实践](project/user/bdd.md)
+    - [代码细节处理](project/user/particulars.md)
 - [运维](operation/README.md)
   - [SHELL](operation/shell.md)
   - [服务器配置](operation/server.md)

+ 141 - 0
project/tool/pm2.md

@@ -86,3 +86,144 @@ pm2 startup
 ```
 
 参考官方文档获取详细使用说明。
+
+
+## 自动重启
+
+设置内存使用过高上限,可以通过配置文件这一项:
+
+```js
+{
+  "max_memory_restart": "800M"
+}
+```
+
+如果想要根据CPU使用过高自动重启,则稍微麻烦点,需要自己写一个脚本监控。
+
+```bash
+pm2 jlist
+```
+
+结果会是这样的json:
+
+```json
+[
+  {
+      "pid": 28701,
+      "name": "xxxx",
+      "pm2_env": {
+          "name": "xxxx",
+          "max_memory_restart": 838860800,
+          "instances": 0,
+          "cwd": "/xxxx/project",
+          "merge_logs": true,
+          "vizion": true,
+          "pmx": true,
+          "automation": true,
+          "autorestart": true,
+          "treekill": true,
+          "env": {
+              "PM2_JSON_PROCESSING": "true",
+              "LSCOLORS": "Gxfxcxdxbxegedabagacad",
+              "LESS": "-R",
+              "PAGER": "less",
+              "SSH_TTY": "/dev/pts/1",
+              "SSH_CLIENT": "218.94.29.190 53115 22",
+              "SHELL": "/bin/bash",
+              "TERM": "xterm-256color",
+              "XDG_SESSION_ID": "814",
+              "NODE_ENV": "production",
+              "max_memory_restart": 838860800,
+              "instances": 0,
+              "merge_logs": true,
+              "vizion": true,
+              "pmx": true,
+              "automation": true,
+              "autorestart": true,
+              "treekill": true,
+              "log_date_format": "YYYY-MM-DD HH:mm:ss Z",
+              "exec_mode": "cluster_mode",
+              "node_args": [],
+              "exec_interpreter": "node",
+              "pm_out_log_path": "/xxxxx/logs/fish.out.log",
+              "pm_err_log_path": "/xxxxx/logs/fish.err.log"
+          },
+          "log_date_format": "YYYY-MM-DD HH:mm:ss Z",
+          "exec_mode": "cluster_mode",
+          "node_args": [],
+          "pm_exec_path": "/xxxx/babel.js",
+          "pm_cwd": "/xxxxx/project",
+          "exec_interpreter": "node",
+          "pm_out_log_path": "/xxxxxx/xxx.out.log",
+          "pm_err_log_path": "/xxxxxx/xxx.err.log",
+          "NODE_APP_INSTANCE": 0,
+          "vizion_running": false,
+          "PM2_JSON_PROCESSING": "true",
+          "LESSCLOSE": "/usr/bin/lesspipe %s %s",
+          "XDG_RUNTIME_DIR": "/run/user/1000",
+          "LESSOPEN": "| /usr/bin/lesspipe %s",
+          "LC_CTYPE": "zh_CN.UTF-8",
+          "SSH_TTY": "/dev/pts/1",
+          "SHELL": "/bin/bash",
+          "NODE_ENV": "production",
+          "fish": "{}",
+          "status": "online",
+          "pm_uptime": 1472561001434,
+          "axm_actions": [],
+          "axm_monitor": {
+              "Loop delay": {
+                  "value": "0.92ms",
+                  "agg_type": "avg",
+                  "alert": {}
+              }
+          },
+          "axm_options": {
+              "http": false,
+              "http_latency": 200,
+              "http_code": 500,
+              "ignore_routes": [],
+              "profiling": true,
+              "errors": true,
+              "alert_enabled": true,
+              "custom_probes": true,
+              "network": false,
+              "ports": false,
+              "module_conf": {},
+              "module_name": "fish",
+              "module_version": "1.1.3",
+              "pmx_version": "0.6.2",
+              "error": true
+          },
+          "axm_dynamic": {},
+          "created_at": 1472561000966,
+          "pm_id": 3,
+          "restart_time": 1,
+          "unstable_restarts": 0,
+          "_pm2_version": "1.1.3",
+          "versioning": {
+              "type": "git",
+              "update_time": "2016-09-02T07:25:14.113Z",
+              "comment": "v3.6.12 - fix-importer",
+              "unstaged": false,
+              "branch": "HEAD",
+              "remotes": [
+                  "origin"
+              ],
+              "remote": "origin",
+              "branch_exists_on_remote": true,
+              "ahead": false,
+              "next_rev": null
+          },
+          "node_version": "6.3.0",
+          "exit_code": 0
+      },
+      "pm_id": 3,
+      "monit": {
+          "memory": 90591232,
+          "cpu": 0
+      }
+  }
+]
+```
+
+其中每一列数据都有 `rows[i].monit.cpu`,值范围 0~100。可以用`later`写定时脚本监控并重启。

+ 119 - 0
project/user/particulars.md

@@ -0,0 +1,119 @@
+# 代码细节处理
+
+## 请求超时无返回
+
+原因:
+
+### 1.如果CPU没有飙升,可能有异常未捕获
+
+可能情况1,如: sql `SELECT xxxx LIMIT 1` 的查询,直接用了 result[0]。 但也可能并没查到结果。
+
+可能情况2,如: JSON.parse(xxxData),或者在用第三方库的时候注意一下,如果方法不是返回`Promise`对象,很可能异常的时候是`Throw`出一个错误,需要做`try/catch`捕获。
+
+可能情况3,如: Callback方法,如 `client.query((result, err)=> { })`,中,需要加 `if(err)` 的判断。
+
+### 2.CPU飙升:大多数情况是死循环
+
+如:
+
+```js
+for(let i = 0; i < xxx1.length; i++) {
+  for(let j=0; i < xxx2.length; j++) {
+    // xxx
+  }
+}
+```
+
+第二个循环条件中 `j` 用成了 `i` 导致死循环产生。
+
+死循环大多发生于对数据遍历的处理。产生死循环最大的可能原因是***循环的条件***。
+
+如果在循环体内用到以下一些方法,也需要特别注意:
+
+* 对数据数组的改动,如:pop/shift/slice
+* 循环体的退出,如:break/continue
+
+可以配合`PM2`和定时任务脚本对进程CPU占用进行监控,自动重启服务。
+
+## 内存泄露
+
+基本情况排查参考: <https://cnodejs.org/topic/4fa94df3b92b05485007fd87>
+
+比较常见的:
+
+```js
+exports.Func = async() => {
+  // 避免方法内require
+  const redisClient = require('wulian-redis');
+
+  // 没必要放在方法里,可以放到外边,多个方法共用
+  const redis = redisClient({
+    // config
+  });
+  // xxxx
+};
+```
+
+## MySQL 编码细节
+
+```js
+import {pool} from 'wulian-mysql';
+import {isEmpty} from 'wulian-common';
+
+(async() => { // 包裹在async中
+  const client = await pool({ // mysql有await,redis没有
+    // config
+  });
+  const result = await client.query('SELECT 1');
+  if(isEmpty(result) && !Array.isArray(result)) {
+    // 查询出错,不能用 result[]
+  }
+  if(isEmpty(result)) {
+    // 查询结果为空,不能用 result[]
+  }
+  return result[0];
+})();
+```
+
+除了`SELECT`的结果是数组,其他的都是对象,并且包含`result.affectedRows`
+
+```js
+const result = await client.query('UPDATE xxx SET xxx WHERE xxx');
+if(isEmpty(result)) {
+  // 查询出错, 不能用 result.affectedRows
+}
+return result.affectedRows;
+```
+
+## Redis 编码细节
+
+```js
+import redisClient from 'wulian-redis';
+const redis = redisClient({
+  // config
+});
+
+(async() => { // 包裹在async中
+  const result = await redis.get('xxxKey');
+  if(result === null) { // xxxKey不存在,返回值为 null
+
+  }
+})();
+```
+
+如果要存取`JSON`格式数据:
+
+```js
+await redis.set('xxxKey', JSON.stringify(xxxJSONVal));
+
+let result = {};
+try { // 读取要异常捕获,不然篡改值可能导致程序崩
+  result = JSON.parse(await redis.get('xxxKey'));
+} catch(e) { }
+```
+
+如果要设置超时:
+
+```js
+await redis.setex('xxxKey', 3600, JSON.stringify(xxxJSONVal));
+```