MongoDB 读写分离之主从配置

优点

  1. 只负责各自的写和读,极大程度的缓解X锁和S锁争用,减轻DB的负载,提升并发和吞吐
  2. 故障切换

缺点

  1. 主从延迟

场景

  1. 读多写少
  2. 至少2台服务器
  3. 数据实时性要求不高

例子
keyFile 生成参考 https://docs.mongodb.com/manual/tutorial/enforce-keyfile-access-control-in-existing-replica-set/

1主(27017)+1从(27017)+1裁判机(27018,挂载在任一服务器上, 资源消耗小)

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
32
# mongod.conf 3.4.7
# for documentation of all options, see:
# http://docs.mongodb.org/manual/reference/configuration-options/
# Where and how to store data.
storage:
dbPath: /var/lib/mongodb # 裁判机 需要另设路径 /root/mongo/data/db
journal:
enabled: true
# engine:
# mmapv1:
# wiredTiger:
# where to write logging data.
systemLog:
destination: file
logAppend: true
path: /var/log/mongodb/mongod.log
# network interfaces
net:
port: 27017
bindIp: 0.0.0.0 # 允许远程连接
processManagement:
fork: true
security:
keyFile: /root/mongo/keyfile # 秘钥文件 同一个 chmod 600 keyfile
#operationProfiling:
replication:
oplogSizeMB: 2048 # 数据同步前存储空间
replSetName: rs0 # 副本集名称
#sharding:
## Enterprise-Only Options:
#auditLog:
~

步骤

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
32
33
34
35
36
37
38
39
40
41
// 启动mongo
mongod --config /etc/mongod.conf

// 打开新终端,创建用户后退出
mongod --port 27017 --dbpath /var/lib/mongodb

//
service mongodb stop

//
mkdir rs0-0 rs0-1 rs0-2

// 创建mongod实例
mongod --port 27017 --dbpath /root/mongo/rs0-0 --replSet rs0 --keyFile /root/mongo/keyfile --smallfiles --oplogSize 2048 & #--oplogsize 单位M.

// 主从配置
mongo --port 27017

rsconf = {
"_id" : "rs0",
"members" : [
{
"_id" : 0,
"host" : "127.0.0.1:27017"
},
{
"_id" : 1,
"host" : "127.0.0.1:27018"
},
{
"_id" : 2,
"host" : "127.0.0.1:27019"
}
]
}

rs.initiate(rsconf);//初始化副本集就完成了
// 添加节点
rs.add(“ip:port”)
// 添加仲裁节点
rs.addArb(“ip:27018")

node 数据库连接
mongodb://username:password@ip:27017,ip:27017,ip:27018/db?replicaSet=rs0&authSource=admin

[个人网站] https://yuluhuang.com/

react vs vue

谷歌趋势

vue

介绍

vue 由尤雨溪 全职开发

react 由Facebook 开发

共同点

  • 支持虚拟dom
  • 支持组件化开发
  • 支付单页面应用(spa)
  • 支持服务端渲染

差异

学习曲线 模板 star
react jsx 92.8K
vue html 94.8k

vue
vue

  • vue属于mvvm框架,支持双向绑定
  • vue可以单独引入作为库使用
  • vue官方维护cli脚手架,自动构建项目,对于中小型快速开发极其有利,大型项目也可以以此为基础
  • react 常见的 creact-react-app 脚手架可以创建一个基础项目,个人开源脚手架繁杂,没用官方统一的解决方案
  • react 一开始只支持es6, 之后也可以单独使用不过要引入react-dom来解析
  • 由于react 专注于 view 层,属于单向绑定,todo 为例 react的代码量往往大于vue

状态管理和数据绑定

  • Vue 官方推荐 vuex
  • React 常见 redux,mobx, 因为react社区活跃,衍生出各个库,所有没有统一公认的解决方案

ui

vue

  • Element UI

React

  • Ant design

    移动端

  • Vue 有 weex 诟病多
  • React 有 react-native 被多数公司采用的移动方案

MySQL运算符介绍

分类

算数运算符, 比较运算符, 逻辑运算符, 位操作运算符

算数运算符

运算符 作用
+ 加法运算符
- 减法运算符
* 乘法运算符
/ 除法运算符
% 求余运算符

比较运算符

比较运算符的结果总是1, 0, null

运算符 作用
= 等于
<=> 安全的等于
<>/!= 不等于
<= 小于等于
>= 大于等于
> 大于
< 小于
IS NULL
IS NOT NULL
LEAST 返回最小值
GREATEST 返回最大值
BETWEEN AND 判断一个值是否在两个值之间
ISNULL 判断一个值是否为null
IN 判断一个值在in 后面的列表中
NOT IN 判断一个值不在in 后面的列表中
LIKE 通配符匹配
REGEXP 正则表达式匹配
  1. 和 null 运算的结果都为null

    = 和 <=>

1
2
3
4
5
6
mysql> select 1=0, '2'=2, null=null, '2'<=>2,null<=>null, 2=null;
+-----+-------+-----------+---------+-------------+--------+
| 1=0 | '2'=2 | null=null | '2'<=>2 | null<=>null | 2=null |
+-----+-------+-----------+---------+-------------+--------+
| 0 | 1 | NULL | 1 | 1 | NULL |
+-----+-------+-----------+---------+-------------+--------+

<> or !=

1
2
3
4
5
6
mysql> select 1<>0, '2'<>2, '2'!=null, null<>null,null!=null;
+------+--------+-----------+------------+------------+
| 1<>0 | '2'<>2 | '2'!=null | null<>null | null!=null |
+------+--------+-----------+------------+------------+
| 1 | 0 | NULL | NULL | NULL |
+------+--------+-----------+------------+------------+

IS NULL AND IS NOT NULL

1
2
3
4
5
6
mysql> select NULL IS NULL, ISNULL(NULL), 10 IS NOT NULL;
+--------------+--------------+----------------+
| NULL IS NULL | ISNULL(NULL) | 10 IS NOT NULL |
+--------------+--------------+----------------+
| 1 | 1 | 1 |
+--------------+--------------+----------------+

BETWEEN AND

1
2
3
4
5
6
mysql> select 4 BETWEEN 4 AND 6, 6 BETWEEN 6 AND 9;
+-------------------+-------------------+
| 4 BETWEEN 4 AND 6 | 6 BETWEEN 6 AND 9 |
+-------------------+-------------------+
| 1 | 1 |
+-------------------+-------------------+

LEAST

1
2
3
4
5
6
mysql> SELECT LEAST(2, 0), LEAST(1, 1.1, 101), LEAST(10, NULL);
+-------------+--------------------+-----------------+
| LEAST(2, 0) | LEAST(1, 1.1, 101) | LEAST(10, NULL) |
+-------------+--------------------+-----------------+
| 0 | 1.0 | NULL |
+-------------+--------------------+-----------------+

GREATEST

1
2
3
4
5
6
mysql> SELECT GREATEST(2, 0), GREATEST(1, 1.1, 101), GREATEST(10, NULL);
+----------------+-----------------------+--------------------+
| GREATEST(2, 0) | GREATEST(1, 1.1, 101) | GREATEST(10, NULL) |
+----------------+-----------------------+--------------------+
| 2 | 101.0 | NULL |
+----------------+-----------------------+--------------------+

IN , NOT IN

1
2
3
4
5
6
mysql> SELECT 2 IN(1,2,3,'A','B'), 'A' IN('A',1), NULL IN(NULL,1);
+---------------------+---------------+-----------------+
| 2 IN(1,2,3,'A','B') | 'A' IN('A',1) | NULL IN(NULL,1) |
+---------------------+---------------+-----------------+
| 1 | 1 | NULL |
+---------------------+---------------+-----------------+

LIKE

1
2
3
4
5
6
mysql> SELECT 'ABC' LIKE 'ABC', 'ABC' LIKE 'AB_', 'ABC' LIKE 'A__', 'A%' LIKE 'ABC';
+------------------+------------------+------------------+-----------------+
| 'ABC' LIKE 'ABC' | 'ABC' LIKE 'AB_' | 'ABC' LIKE 'A__' | 'A%' LIKE 'ABC' |
+------------------+------------------+------------------+-----------------+
| 1 | 1 | 1 | 0 |
+------------------+------------------+------------------+-----------------+

REGEXP

1
2
3
4
5
6
mysql> SELECT 'ABC' REGEXP '^A', 'ABC' REGEXP 'C$', 'ABC' REGEXP '.BC', 'AC' REGEXP 'A-Z';
+-------------------+-------------------+--------------------+-------------------+
| 'ABC' REGEXP '^A' | 'ABC' REGEXP 'C$' | 'ABC' REGEXP '.BC' | 'AC' REGEXP 'A-Z' |
+-------------------+-------------------+--------------------+-------------------+
| 1 | 1 | 1 | 0 |
+-------------------+-------------------+--------------------+-------------------+

echarts-map

需求

西秀地图

行政区域:乡镇(16)
区域颜色:蓝色(深浅代表产量)
合作社标识:黄色气泡 鼠标停留在合作社气泡,可以显示信息列表

分析

  1. echarts 支持省市区规划, 乡镇及以下不支持
  2. ECharts 地图数据在线生成工具 http://ecomfe.github.io/echarts-map-tool/
    已不支持矢量地图数据下载, 需自己采点绘图

解决

  1. 通过百度地图采点
  2. 通过 echarts 绘图

效果

采点工具
图例

最终效果
图例

Demo

github

总结

  1. 内部公共点只使用同一份, 切勿重复取点
  2. 耐心, 取点疏密决定分区线细腻度, 本例比较粗糙
  3. 取完点绘图时可能有瑕疵, 对于有瑕疵区域最好重新采点

deep_and_shallow_copy_js

  1. 深拷贝可以理解为递归进行浅拷贝。
  2. 简单来说,浅复制只复制一层对象的属性,而深复制则递归复制了所有层级

浅拷贝实现

1.

1
2
3
4
5
6
7
8
9
10

function shallowClone (copyObj) {
var obj = {}
for (var prop in copyObj) {
if (copyObj.hasOwnProperty(prop)) {
obj[prop] = copyObj[prop]
}
}
return obj;
}

  1. Object.assign()

  2. Array的slice和concat方法

    • 它看起来像是深拷贝。而实际上它是浅拷贝: 如果该元素是个对象引用, 两个对象引用都引用了同一个对象, 所以会同变
    • 如果向两个数组任一中添加了新元素,则另一个不会受到影响

深拷贝的实现

  1. 对于字符串、数字及布尔值来说 没有深浅之分
  2. JSON对象的parse和stringify
    JSON.parse(JSON.stringify(source))
    这种方法使用较为简单,可以满足基本的深拷贝需求,而且能够处理JSON格式能表示的所有数据类型,
    但是对于正则表达式类型、函数类型等无法进行深拷贝(而且会直接丢失)。还有一点不好的地方是它会抛弃对象的constructor。也就是深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object。同时如果对象中存在循环引用的情况也无法正确处理。

  3. Object.create(source)

    1
    2
    3
    4
    5
    6
    function copy(obj) {
    var F = function () {
    };
    F.prototype = obj;
    return new F();
    }

    是在方法内部创建了一个 function 原型指向传入的 old对象,再返回一个 function 实例,也就是拷贝出来的值,所以添加改变实例属性不会影响原型,但是修改 old 对象的属性值,也就是改变了实例的原型属性,新的对象的所有属性都是继承自 old 对象,自然会跟着变

参考

  1. https://github.com/wengjq/Blog/issues/3

内网穿透实践

#需求
大多数情况下我们使用的ip都属于动态分配ip,
如果有一个静态ip, 就可以做很多事

  1. 场景一
    将自己的电脑变成服务器, 可以有无限大的资源
  2. 场景二
    通过ssh远程控制自己的电脑, 将自己本机作为跳板, 非常方便
  3. 场景三
    给客户直接做展示, 打开连接直接连到本机, 安全方便快捷
  4. 自己脑补
  1. 我找到了ngrok
  2. localtunnel 可行
  3. pagekite

ngrok (已闭源, 收费)

npm install ngrok -g
ngrok http 8080
ngrok http -subdomain=yuluhuang 9999

localtunnel (第三方都不太稳定)

###官网 http://localtunnel.me/

###使用

  1. npm install -g localtunnel
  2. lt –port 8000 //lt –help
    or lt –subdomain yuluhuang –port 8888

pagekite(有限制)

###官网 https://pagekite.net/

  1. 下载py文件
  2. 双击运行

frp (以上都不具实用性)

因为工作需要使用到内网穿透,让远程用户直接访问本地,查看最终效果
之前使用过ngrok, localtunnel, pagekite, 要么不能用,要么不好用,自己部署也很复杂
看到又一款穿透工具 介绍 http://www.sunnyrx.com/2016/10/21/simple-to-use-frp/ ,
官方文档: https://github.com/fatedier/frp/blob/master/README_zh.md
就自己部署了一遍, 发现还是可行的 中文文档也比较详细,我在记录一遍

前提: 你需要一台自己的服务器

  1. 在客户端和服务器端下载安装包
    |-frpc // 客户端
    |-frpc.ini // 客户端配置文件
    |-frps // 服务端
    |-frps.ini // 服务端配置文件
  1. 配置

frpc.ini

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[common]
server_addr = x.x.x.x # 服务端ip
server_port = 7000 # 端口, 记得打开

[ssh]
type = tcp
local_ip = 127.0.0.1
local_port = 22
remote_port = 6000

[http_proxy]
type = tcp
remote_port = 5000
plugin = http_proxy


[web]
type = http
local_port = 8109 # 本地应用使用端口
custom_domains = www.blibao.shop # 解析到服务端的域名, 客户端访问 www.blibao.shop:8080即可访问到本地应用,8080需在服务端配置文件中配置

frps.ini

1
2
3
[common]
bind_port = 7000
vhost_http_port = 8080

  1. 启动
    使用screen让frp在后台运行
    下面的示范是运行服务端的frp,客户端就不示范了,前面提过群晖的系统没有screen指令。

首先使用screen指令创建一个会话。

screen -dmS frp
然后进入这个会话。

screen -r frp
最后使用运行frp的指令,在后面加上” &”。(如果之前断开了SSH连接,记得用cd指令进入frp的目录先。)

./frps -c ./frps.ini &
这样就让frp在后台运行了。

  1. 配置ssh连接本机
    frpc.ini
    1
    2
    3
    4
    5
    6
    [ssh]
    type = tcp
    local_ip = 127.0.0.1
    auth_token = 123
    local_port = 22
    remote_port = 6000

frps.ini

1
2
3
4
5
[ssh]
type = tcp
auth_token = 123
bind_addr = 0.0.0.0
listen_port = 6000

mac 在共享中打开允许远程连接,设置用户
ssh root@x.x.x.x -p 6000

场景1

利用frp主从备份mongo数据库

场景2

利用frp 副本集 mongo数据库(同理)

问题

  1. 对架设frps的服务器要求高,不适合同时架设过多连接,大流量容易堵塞,可用于小网站或搭建远程测试环境

ng2

Question

  1. Unhandled Promise rejection: No base href set. Please provide a value for the APP_BASE_HREF

    页面添加

    1. httpService 自定义 需要在 app.component.ts 声明
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      import { Component } from '@angular/core';

      import { HttpService } from './httpService';

      @Component({
      moduleId: module.id,
      selector: 'app-root',
      providers: [HttpService]
      })

      export class AppComponent {
      constructor() {}
      }

mean-tasklist

准备

  1. nodejs
  2. mongo

目录

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
|-client
|-app
|-components
|-task
|-tasks.component.html
|-tasks.components.ts
|-services
|-task.services.ts
|-app.component.html
|-app.component.ts
|-app.module.ts
|-main.ts
|-bower_components
|-node_modules
|-typings
|-package.json
|-systemjs.config.js
|-Task.ts
|-tsconfig.json
|-typings.json
|-node_modules
|-routes
|-index.js
|-tasks.js
|-views
|-index.html
|-.bowerrc
|-package.json
|-server.js

开始

后端

  1. cd tasklist && npm init #创建项目
  2. npm install mongojs express ejs body-parser –save
  3. touch server.js
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
var express = require('express')
var path = require('path')
var bodyParser = require('body-parser')

var index = require('./routes/index');
var tasks = require('./routes/tasks');

var port = 3000;
var app = express();

app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.engine('html', require('ejs').renderFile);

app.use(express.static(path.join(__dirname, 'client')));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));


app.use('/', index);
app.use('/api', tasks);

app.listen(port, function () {
console.log('Server started on port' + port)
});
  1. mkdir routes && cd routes
  2. touch index.js
1
2
3
4
5
6
7
var express = require('express')
var router = express.Router();

router.get('/', function (req, res, next) {
res.render('index.html');
});
module.exports = router;
  1. mkdir views && cd views && touch index.html
1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
HELLO WORLD
</body>
</html>
  1. touch tasks.js
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
var express = require('express')
var router = express.Router();
var mongojs = require('mongojs')
var db = mongojs('mongodb://localhost:27017/nodeapp', ['tasks'])

router.get('/tasks', function (req, res, next) {
db.tasks.find(function (err, tasks) {
if (err) {
res.send(err);
}
res.json(tasks);
});
});

router.get('/task/:id', function (req, res, next) {
db.tasks.findOne({_id: mongojs.ObjectId(req.params.id)}, function (err, task) {
if (err) {
res.send(err);
}
res.json(task);
})
});

router.post('/task', function (req, res, next) {
var task = req.body;
if (!task.title || !(task.isDone + '')) {
req.status(400);
res.json({
"error": 'Bad Date'
});
} else {
db.tasks.save(task, function (err, task) {
if (err) {
res.send(err);
}
res.json(task);
});
}
});

router.delete('/task/:id', function (req, res, next) {
db.tasks.remove({_id: mongojs.ObjectId(req.params.id)}, function (err, task) {
if (err) {
res.send(err);
}
res.json(task);
})
})

router.put('/task/:id', function (req, res, next) {
var task = req.body;
var updTask = {};

if (task.isDone) {
updTask.isDone = task.isDone;
}
if (task.title) {
updTask.title = task.title;
}
if(!updTask) {
res.status(400);
res.json({
"error": 'Bad Data'
})
} else {
db.tasks.update({_id: mongojs.ObjectId(req.params.id)}, updTask, function (err, task) {
if (err) {
res.send(err);
}
res.json(task);
});
}
})

module.exports = router;

前端

  1. mkdir client && mkdir app
  2. touch 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
32
33
34
35
36
37
38
39
40
41
42
{
"name": "angular-quickstart",
"version": "1.0.0",
"scripts": {
"start": "tsc && concurrently \"tsc -w\" \"lite-server\" ",
"lite": "lite-server",
"postinstall": "typings install",
"tsc": "tsc",
"tsc:w": "tsc -w",
"typings": "typings"
},
"licenses": [
{
"type": "MIT",
"url": "https://github.com/angular/angular.io/blob/master/LICENSE"
}
],
"dependencies": {
"@angular/common": "~2.1.0",
"@angular/compiler": "~2.1.0",
"@angular/core": "~2.1.0",
"@angular/forms": "~2.1.0",
"@angular/http": "~2.1.0",
"@angular/platform-browser": "~2.1.0",
"@angular/platform-browser-dynamic": "~2.1.0",
"@angular/router": "~3.1.0",
"@angular/upgrade": "~2.1.0",
"angular-in-memory-web-api": "~0.1.5",
"bootstrap": "^3.3.7",
"core-js": "^2.4.1",
"reflect-metadata": "^0.1.8",
"rxjs": "5.0.0-beta.12",
"systemjs": "0.19.39",
"zone.js": "^0.6.25"
},
"devDependencies": {
"concurrently": "^3.0.0",
"lite-server": "^2.2.2",
"typescript": "^2.0.3",
"typings":"^1.4.0"
}
}
  1. touch typings.json
1
2
3
4
5
6
7
{
"globalDependencies": {
"core-js": "registry:dt/core-js#0.0.0+20160725163759",
"jasmine": "registry:dt/jasmine#2.2.0+20160621224255",
"node": "registry:dt/node#6.0.0+20160909174046"
}
}
  1. touch tsconfig.json
1
2
3
4
5
6
7
8
9
10
11
12
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"removeComments": false,
"noImplicitAny": false
}
}
  1. systemjs.config.js
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
32
33
34
35
36
37
38
39
40
41
42
43
/**
* System configuration for Angular samples
* Adjust as necessary for your application needs.
*/
(function (global) {
System.config({
paths: {
// paths serve as alias
'npm:': 'node_modules/'
},
// map tells the System loader where to look for things
map: {
// our app is within the app folder
app: 'app',
// angular bundles
'@angular/core': 'npm:@angular/core/bundles/core.umd.js',
'@angular/common': 'npm:@angular/common/bundles/common.umd.js',
'@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
'@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
'@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
'@angular/http': 'npm:@angular/http/bundles/http.umd.js',
'@angular/router': 'npm:@angular/router/bundles/router.umd.js',
'@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
// other libraries
'rxjs': 'npm:rxjs',
'angular-in-memory-web-api': 'npm:angular-in-memory-web-api',
},
// packages tells the System loader how to load when no filename and/or no extension
packages: {
app: {
main: './main.js',
defaultExtension: 'js'
},
rxjs: {
defaultExtension: 'js'
},
'angular-in-memory-web-api': {
main: './index.js',
defaultExtension: 'js'
}
}
});
})(this);
  1. npm install
  2. touch .bowerrc
1
2
3
{
"directory": "./client/bower_components"
}
  1. bower install bootstrap –save

Demo

一位合格工程师应该具备的要素

引:大多数人在进入软件开发这一行业迷漫无措的,当然也有人是充满激情和理想, 但是并不是每个人都能成为黑客
帝国中的主人公,就算配角也是寥寥无几。平凡是基调但是我们拒绝平凡,曾经的我们都曾梦想改变世界,现在依旧
这么认为

知学善用

简而言之就是善于学习,并且学以致用

注重自身成长

  1. 学会正确提问
  2. 正确使用搜索引擎,养成良好的搜索习惯让搜索引擎成为你的专属工具
  3. 勤做笔记, 勤写博客,知识只有传播才有价值
  4. 维护好你的代码

耐得住寂寞

程序员的日常生活就是:找到问题-评估选择最优解决方案-完成-测试-发现问题
大多数程序员在996的工作环境下变得沉默寡言,拿起你的手机看看微信朋友圈或者qq空间几乎没有自己的说说
炫富的程序员不是好的程序员也是一样道理,这已经成为这个行业的标志:人傻钱多不会花