使用go编写webassembly

使用go编写webassembly并在浏览器执行

参考博客

用go写WebAssembly入门

下载安装

go

编写测试文件hello.go

1
2
3
4
5
6
7
package main

import "fmt"

func main() {
fmt.Println("Hello World!")
}

生成wasm文件

1
在windows下需要先设置环境变量:``` $env:GOARCH="wasm";$env:GOOS="js";

添加依赖

1
cp $(go env GOROOT)/misc/wasm/wasm_exec.{html,js} .

添加一个测试http服务器

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
//http.go
package main

import (
"flag"
"log"
"net/http"
"strings"
)

var (
listen = flag.String("listen", ":8080", "listen address")
dir = flag.String("dir", ".", "directory to serve")
)

func main() {
flag.Parse()
log.Printf("listening on %q...", *listen)
log.Fatal(http.ListenAndServe(*listen, http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
if strings.HasSuffix(req.URL.Path, ".wasm") {
resp.Header().Set("content-type", "application/wasm")
}

http.FileServer(http.Dir(*dir)).ServeHTTP(resp, req)
})))
}

执行

go run http.go

查看效果

浏览器打开http://localhost:8080/wasm_exec.html,点击run,控制台可以看到效果

node执行

1
node wasm_exec.js test.wasm

单例模式

概念介绍

情景介绍

一个类只需要被实例化一次,但是不应该由调用方来判断是否被实例化过

基础介绍

所有类都有构造方法,不编码则系统默认生成空的构造方法,若有显示定义的构造方法,默认的构造方法就会失效。
类把构造方法设置为私有,使所有的方法都不能直接实例化这个类。然后类内部写一个公共方法来判断这个类是否被实例化过,调用方无需判断

单例模式

单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
通常可以让一个全局变量使得一个对象被访问,但它不能防止实例化多个对象。一个最好的办法就是,让类自身负责保存它的唯一实例,这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法。

mongodb学习记录

参考文档

http://www.mongoing.com/docs/reference/operator/aggregation/interface.html
https://www.cnblogs.com/zhoujie/p/mongo1.html
https://www.docs4dev.com/docs/zh/mongodb/v3.6/reference/tutorial-text-search-in-aggregation.html

mongodb精准匹配

假设有需求如下:数据为多层级的数组,需要精准匹配到某一个层级,并进行更新或新增等操作,如何实现?

实现方式

当Mongodb版本大于3.6.1,实现将会非常简单,因为mongodb3.6.1以上可以直接通过语句来实现精准匹配。
但由于实际场景中版本不支持,当匹配超过一级则会报错:Too many positional (i.e. ‘$’) elements found in path ‘files.$.testConstruct.params.list.$.testRange’
当版本大于3.6.1,举例:

1
2
3
db.test_db.update(
{'_id': {$in: ['1242rererwwr']}, 'files.name': 'test1.json', 'files.testConstruct.params.list.type': 'NewData'},
{$set: {'files.$.structuredContent.params.list.$.testRange': {'enable': false, 'min': 14, 'max': 90}}, $currentDate: { updatedDatetime: true }}, { multi: true })

分析:
设置值时,’files.$.testConstruct.params.list.$.testRange’中的第一个”$”将会匹配到name=test1.json的数组,第二个”$”将会匹配到files.structuredContent.params.list.type=NewData的数组
优点: 使用简单,匹配精确,无需代码判断
缺点: 适用版本有限制,且由于多级匹配需要更多的条件,而实际业务中不一定能取到这些条件(例如知道要更新的key为testRange但是不知道上级查询条件无法匹配到)
如果版本过低,可只匹配第一级,其余的通过代码匹配。
当版本小于3.6.1举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
public function batchUpdateContents(string $route, array $ids, string $editor, $updateItems, bool $needPublish): bool
{
$routes = explode('-', $route);
array_shift($routes);
$routes[2] = self::TYPE_LIST[$routes[2]];
$updateRoute = 'files.$.testConstruct.' . implode('.', $routes);
$res = $this->conn->update(['storeId' => ['$in' => $ids], 'files.name' => 'makeup.json', 'files.testConstruct' => ['$ne' => '']],
['$set' => [$updateRoute => $updateItems, 'files.$.editor' => $editor, 'files.$.lastUpdate' => time()], '$currentDate' => [ 'updatedDatetime' => true ]], true);
if ($res) {
// 一些其他操作
} else {
return false;
}
}
?>

枚举出可能存在的下一级数组值,然后进行匹配。update()方法当参数不存在时会进行新增参数操作。

mongodb聚合

mongodb的聚合操作可对数据进行一系列操作并返回结果。mongodb提供三种聚合操作:
aggregation pipeline, map-reduce方法和分片集合

aggregation pipeline

管道操作,基础操作为筛选操作,其他操作:可通过特定的字段来进行分组和排序等

$project

类似于sql的select,筛选出需要查询的字段,例如:{$project: {‘name’: 1, ‘age’:1}},也可以排除字段(0),重命名字段,派生字段
(1): <1 or true> 是否包含该字段,field:1/0,表示选择/不选择 field
(2):<0 or false> v3.4新增功能,指定排除字段
(3): 添加新字段或重置现有字段的值。 在版本3.6中更改:MongoDB 3.6添加变量REMOVE。如果表达式的计算结果为$$REMOVE,则该字段将排除在输出中。
3.6可使用变量REMOVE来有条件地禁止一个字段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
db.books.aggregate( [
{
$project: {
title: 1,
"author.first": 1,
"author.last" : 1,
"author.middle": {
$cond: {
if: { $eq: [ "", "$author.middle" ] },
then: "$$REMOVE",
else: "$author.middle"
}
}
}
}
] )

使用$project派生举例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
db.books.aggregate(
[
{
$project: {
title: 1,
isbn: {
prefix: { $substr: [ "$isbn", 0, 3 ] },
group: { $substr: [ "$isbn", 3, 2 ] },
publisher: { $substr: [ "$isbn", 5, 4 ] },
title: { $substr: [ "$isbn", 9, 3 ] },
checkDigit: { $substr: [ "$isbn", 12, 1] }
},
lastName: "$author.last",
copiesSold: "$copies"
}
}
]
)

投影出新数组:

示例数据:

1
{ "_id" : ObjectId("55ad167f320c6be244eb3b95"), "x" : 1, "y" : 1 }

操作:

1
db.collection.aggregate( [ { $project: { myArray: [ "$x", "$y" ] } } ] )

返回:

1
{ "_id" : ObjectId("55ad167f320c6be244eb3b95"), "myArray" : [ 1, 1 ] }

如果返回的数组中包含了不存在的字段,则会返回null

$match

类似于sql中的where,设置查询条件,例如:{$match: {‘name’: {‘$ne’: ‘’}}}
$match仅接受一个指定查询条件的文档,查询语法与读操作查询语法相同。

语法

{$match: {}}

注意事项

在实际应用中尽可能将$match放在管道的前面位置。这样有两个好处:一是可以快速将不需要的文档过滤掉,以减少管道的工作量;二是如果再投射和分组之前执行$match,查询可以使用索引。

限制条件

(1)不能在$match查询中使用$作为聚合管道的一部分
(2)要在$match阶段使用$text,$match阶段必须是管道的第一阶段

查询条件介绍

$gt:大于
$lt:小于
$gte:大于等于
$lte:小于等于
$in:类似于sql中的in
$nin:不在该范围内的键
$or:包含多个可能的条件

$limit

限制传递到管道中下一阶段的文档数

skip

跳过指定数量的文档,并将其余文档传递到管道中的下一阶段

$unwind

从输入文档解构数组字段以输出每个元素的文档,即:将数组拆分成单独的文档

举例

1
2
3
4
5
6
7
8
9
10
11
12
{
$unwind:
{
path: <field path>,
includeArrayIndex: <string>, #可选,一个新字段的名称用于存放元素的数组索引。该名称不能以$开头。
preserveNullAndEmptyArrays: <boolean> #可选,default :false,若为true,如果路径为空,缺少或为空数组,则$unwind输出文档

}
}
db.getCollection('test').aggregate(
[ { $unwind : "$sizes" } ]
)

$group

释义

按指定的表达式对文档进行分组,并将每个不同分组的文档输出到下一个阶段。输出文档包含一个_id字段,该字段按键包含不同的组。

输出文档还可以包含计算字段,该字段保存由$group的_id字段分组的一些accumulator表达式的值。 $group不会输出具体的文档而只是统计信息。

语法

{ $group: { _id: , : { : }, … } }
解析:_id字段必填,可以指定为null,表示为整个输入文档计算累计值,剩余的计算字段是可选的,并使用运算符进行计算。

accumulator操作符

名称 描述 类比sql
$avg 计算均值 avg
$first 返回每组第一个文档,如果有排序,按照排序,如果没有按照默认的存储的顺序的第一个文档。 limit 0,1
$last 返回每组最后一个文档,如果有排序,按照排序,如果没有按照默认的存储的顺序的最后个文档。 -
$max 根据分组,获取集合中所有文档对应值得最大值。 max
$min 根据分组,获取集合中所有文档对应值得最小值。 min
$push 将指定的表达式的值添加到一个数组中。 -
$addToSet 将表达式的值添加到一个集合中(无重复值,无序)。 -
$sum 计算总和 sum
$stdDevPop 返回输入值的总体标准偏差(population standard deviation) -
$stdDevSamp 返回输入值的样本标准偏差(the sample standard deviation) -

注意事项

(1)$group阶段的内存限制为100M,默认情况下,如果stage超过此限制,$group将产生错误,但是,要允许处理大型数据集,需要将allowDiskUse选项设置为true以启用$group操作写入临时文件
(2)”$addToSet”: expr如果当前数组中不包含expr,那就将它添加到数组中
(3)”$push”:expr,不管expr的值,都将它添加到数组中,返回包含所有值的数组。

举例

1
2
3
4
5
6
7
8
9
10
db.getCollection('test').aggregate([
{
$group: {
_id: {month: {$month: "$data"}, day: {$dayOfMonth: "$date", year: { $year: "$date"}}},
totalPrice: {$sum: {$multiply: ["$price", "$quantity"]}},
averageQuantity: {$avg: "$quantitu"},
count: {$sum: 1}
}
}
])

数据转换

(1)可以根据分组把每组转换成item数组

1
db.getCollection('test').aggregate([{$group: {_id: '$price', items: {$pish: "$item"}}}])

返回值:

1
{["_id": 5, "items": ["abc", "abc"]]}

(2) 可以使用系统变量$$ROOT按item对文档进行分组,生成的文档不得超过BSON文档大小限制

1
db.getCollection('test').aggregate([{$group: {_id: "$item", books: {$push: "$$ROOT"}}}])

返回值:

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
/* 1 */
{
"_id" : "xyz",
"books" : [
{
"_id" : 3,
"item" : "xyz",
"price" : 5,
"quantity" : 10,
"date" : ISODate("2014-03-15T09:00:00.000Z")
},
{
"_id" : 4,
"item" : "xyz",
"price" : 5,
"quantity" : 20,
"date" : ISODate("2014-04-04T11:21:39.736Z")
}
]
}

/* 2 */
{
"_id" : "jkl",
"books" : [
{
"_id" : 2,
"item" : "jkl",
"price" : 20,
"quantity" : 1,
"date" : ISODate("2014-03-01T09:00:00.000Z")
}
]
}

/* 3 */
{
"_id" : "abc",
"books" : [
{
"_id" : 1,
"item" : "abc",
"price" : 10,
"quantity" : 2,
"date" : ISODate("2014-03-01T08:00:00.000Z")
},
{
"_id" : 5,
"item" : "abc",
"price" : 10,
"quantity" : 10,
"date" : ISODate("2014-04-04T21:23:13.331Z")
}
]
}

count

返回包含到输出文档的计数

sort

对文档进行排序,按照排序顺序返回管道。
1:升序排列
-1:降序排列
{$meta:“textScore”}按照降序排列计算出的textScore元数据,表达方式唯一,尽管可以在管道中接受表达式,但{ $meta: “textScore” }表达式仅在包含具有$text查询的$match阶段的管道中有意义。
分析:$text文本搜索,会为包含索引字段中的搜索词的每隔文档匹配一个分数,这个分数表示文档与给定文本搜索查询的相关性
举例:

1
2
3
4
5
6
db.users.aggregate(
[
{ $match: { $text: { $search: "operating" } } },
{ $sort: { score: { $meta: "textScore" }, posts: -1 } }
]
)

$sortByCount

v3.4新增,根据表达式的值对传入文档分组,计算每个不同组中文档的数量,每个输出文档都包含两个字段:包含不同分组值得_id字段和包含属于该分组或类别的文档数的计数字段,文件按降序排列
举例:

1
db.db_test.aggregate({$sortByCount: '$status'});

geoNear

lookup

mongodb在大部分情况下是不需要连表的,但是依然支持连表操作,即:$lookUp

out

indexStats

索引

mongodb正则匹配

mongodb连接方式

mongodb://[username:password@]host1[:port1][,host2[:port2],…[,hostN[:portN]]][/[database][?options]]

mongodb查看版本

mongod –version

代码整洁之道1-9章

提要

要有代码:代码呈现了需求的细节,将需求明确到机器可以执行的细节程度
不要产生糟糕的、混乱的代码,勒布朗法则:稍后等于永不
制造混乱无益于赶上期限,做得快的唯一方法就是始终保持代码整洁。

好代码的特点

优雅、搞笑;代码逻辑直截了当,缺陷难以隐藏;
尽量减少依赖关系,使之便于维护;
根据某种分层战略完善处理错误代码,性能调至最优
整洁的代码力求集中,每个函数、每个类和每个模块都全神贯注于一事,完全不受四周细节的干扰和污染
整洁的代码可由作者之外的开发者阅读和增补,它应当有单元测试和验收测试
尽量使用有意义的命名,它只提供一种而非多种做一件事的途径
尽量少的依赖关系,明确地定义和提供清晰、尽量少的API

总结

(1)能通过所有测试
(2)没有重复代码
(3)体现系统中的全部设计理念
(4)包含尽量少的实体,比如类、方法、函数等
不要重复代码,只做一件事,表达力,小规模抽象

有意义的命名

(1)如果名称需要注释来补充,那就不算是名副其实(之前出现过争议)
(2)不要使用意义含糊的废话,如果名称相同但是意义不同,那么info和data与a an the一样毫无意义,不要使用废话,varable不应出现在便能两种,table不应出现在表中
(3)使用读得出来的名称,方便阅读
(4)使用方便搜索的名称
(5)避免使用编码
(6)应当把类和函数做得足够小,消除对成员前缀的需要,读代码的人通常不会读前缀
(7)不要在类名中使用奇怪的命名
(8)不要使用双关语

函数

(1)函数应该尽可能小,20行封顶最佳
(2)每个函数都一目了然,每个函数都只说一件事,每个函数都依次带到下一个函数
(3)函数的缩进层不应该多余一层或两层

需要遵循的原则

(1)确保每隔switch函数都埋藏在较低的抽象层而且永远不重复
(2)不要向函数传入布尔值(我以前经常这么做),因为传入布尔值表示函数会有多余的操作
(3)使用异常代替返回错误码(错误代码能从主路径代码中分离出来得到简化)
(4)抽离try/catch代码块
(5)不要重复自己

注释

注意

注释存在的时间越久,就离它所描述的代码越远,越来越变得全然错误,因为程序员不能坚持维护注释

必要的注释(好的注释)

(1)法律信息
(2)提供信息的注释
(3)对意图的解释
(4)阐释(如果参数或返回值是某个标准库的一部分或者不能修改的代码,帮助阐释其含义的代码就会有用)
(5)警示

单元测试

设计模式六大原则

##第一:单一职责原则(SPR)
一个类应该有且仅有一个原因导致该类的变更,即一个类应该只负责一项职责
##第二:里氏替换原则(LSP)

##第三:依赖倒置原则(DIP)
抽象不应该依赖细节,细节应该依赖于抽象。针对接口编程,不要针对实现编程
##第四:接口隔离原则(ISP)
##第五:迪米特法则(LoD)
##第六:开放封闭原则
开放-封闭原则是说软件实体(类、模块、函数等等)应该可以扩展,但是不可以修改

单例模式

基础介绍

概念介绍

作为对象的创建模式,单例模式确保某一个类只有一个实例,并且对外提供这个全局实例的访问入口。它不会创建实例副本,而是会向单例类内部存储的实例返回一个引用。

单例模式三要素

  1. 需要一个保存类的唯一实例的静态成员变量。
  2. 构造函数和克隆函数必须声明为私有的,防止外部程序创建或复制实例副本。
  3. 必须提供一个访问这个实例的公共静态方法,从而返回唯一实例的一个引用。

生产者消费者模式

基础概念

概念介绍

某块模块负责产生数据,这些数据由另一个模块来负责处理。产生数据的模块,就形象地称为生产者;而处理数据的模块,就称为消费者。
该模式还需要有一个缓冲区处于生产者和消费者之间,作为一个中介。生产者把数据放入缓冲区,而消费者从缓冲区取出数据
缓冲区作用

  1. 解耦,生产者和消费者只依赖缓冲区,而不互相依赖

  2. 支持并发和异步

  3. 支持生产者和消费者忙闲不均(来不及消费缓冲区可以暂存)

    使用场景

    队列(类似,但并不完全相等,有的队列没有缓冲区);
    swoole(业务逻辑(生产者)将数据单元通过swoole的send函数弄到swoole的一个缓冲区之间,通过work进程进行分发,task进程(消费者)进行消费。)

代码解析

CICD创建项目流程类似于生产者消费者模式;
(1)用户填写表单,创建一条CICD数据,生产者向数据表添加一条状态为pending的数据,类似于向缓冲区添加数据
(2)定时任务读取状态为pending的数据,消费处理后修改状态