noblog 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/generate.esm.js +240 -0
- package/dist/generate.esm.js.map +1 -0
- package/dist/generate.js +262 -0
- package/dist/generate.js.map +1 -0
- package/dist/lib/{cvtmd.d.ts → nomd/format.d.ts} +1 -1
- package/dist/lib/nomd/format.d.ts.map +1 -0
- package/dist/lib/nomd/nomd.d.ts +7 -1
- package/dist/lib/nomd/nomd.d.ts.map +1 -1
- package/dist/lib/notion/client.d.ts +1 -0
- package/dist/lib/notion/client.d.ts.map +1 -1
- package/package.json +44 -28
- package/dist/cmds/generate.js +0 -48
- package/dist/cmds/generate.js.map +0 -1
- package/dist/index.js +0 -18
- package/dist/index.js.map +0 -1
- package/dist/lib/cvtmd.d.ts.map +0 -1
- package/dist/lib/cvtmd.js +0 -111
- package/dist/lib/cvtmd.js.map +0 -1
- package/dist/lib/nomd/cvter.d.ts +0 -38
- package/dist/lib/nomd/cvter.d.ts.map +0 -1
- package/dist/lib/nomd/cvter.js +0 -111
- package/dist/lib/nomd/cvter.js.map +0 -1
- package/dist/lib/nomd/nomd.js +0 -73
- package/dist/lib/nomd/nomd.js.map +0 -1
- package/dist/lib/nomd.d.ts +0 -8
- package/dist/lib/nomd.d.ts.map +0 -1
- package/dist/lib/nomd.js +0 -50
- package/dist/lib/nomd.js.map +0 -1
- package/dist/lib/nosm/default.d.ts +0 -2
- package/dist/lib/nosm/default.d.ts.map +0 -1
- package/dist/lib/nosm/default.js +0 -2
- package/dist/lib/nosm/default.js.map +0 -1
- package/dist/lib/nosm/nosm.d.ts +0 -2
- package/dist/lib/nosm/nosm.d.ts.map +0 -1
- package/dist/lib/nosm/nosm.js +0 -3
- package/dist/lib/nosm/nosm.js.map +0 -1
- package/dist/lib/notion/client.js +0 -48
- package/dist/lib/notion/client.js.map +0 -1
- package/dist/lib/notion/object.js +0 -2
- package/dist/lib/notion/object.js.map +0 -1
- package/dist/lib/notion/request.js +0 -2
- package/dist/lib/notion/request.js.map +0 -1
- package/output/00097769-2aac-4b6f-8f37-d9b658d46b2e +0 -435
- package/output/1707063b-7c0a-4572-9526-f7db2f7bbc49 +0 -162
- package/output/1ed588e1-2604-4e33-9685-722b03f66374 +0 -89
- package/output/2b1c0d09-40f7-4151-a053-11391f775ece +0 -33
- package/output/336b6a5a-f383-4d6c-8fe5-54c9a918c4c0 +0 -264
- package/output/37b77a0a-e233-405a-93f5-a60a808eee35 +0 -72
- package/output/3ac660cb-9503-4e88-8939-5aee30c98351 +0 -24
- package/output/5bc1b19a-8350-48b3-aa19-8073561ad45b +0 -109
- package/output/765976b5-7790-497b-8634-4be1d4770e16 +0 -111
- package/output/90f2cb44-90ab-40b7-a736-02bfb83a314c +0 -1167
- package/output/99a7b0c5-f9de-403c-95ec-eba3d700a195 +0 -49
- package/output/c844b66d-0bf3-4f89-bbe1-5974f2de454b +0 -193
- package/output/cd6299f7-54bb-4160-8735-16e629422a57 +0 -193
- package/output/d462cfca-0a39-4b65-8430-4eae35edbb76 +0 -27
- package/output/daafaad2-2570-43da-8e9e-f95a1b29ca17 +0 -12
- package/output/e45ef6bf-6c30-4fb2-9efc-a1984e5dc530 +0 -84
- package/output/sub/05009ca9-cb39-4356-8f24-c0db39cb4661 +0 -64
- package/output/sub/0b903359-2337-4ad9-adef-e77b69142581 +0 -328
- package/output/sub/18f0fae1-b7ca-40ea-b725-4e69404429dc +0 -109
- package/output/sub/232ae8ce-5ec3-4737-a18e-077774b84b1e +0 -940
- package/output/sub/34f4d4af-812e-4e56-a6dd-deb96e7f605c +0 -3
- package/output/sub/44331753-a264-40a4-b86f-eafd676b3da0 +0 -509
- package/output/sub/4bac355b-24f0-49e7-a317-e4f1cad63c79 +0 -65
- package/output/sub/4d6663a2-e93a-402e-89f8-a47cccc0c8cf +0 -76
- package/output/sub/5771c956-25bf-4714-bc2e-65959c9156d9 +0 -52
- package/output/sub/6ff91a18-494e-4ffc-a33b-b9fd2c36f43f +0 -42
- package/output/sub/81e4a2b0-05e2-4013-bd41-14b144979d91 +0 -0
- package/output/sub/99ef1497-0b94-490f-b9a0-f54b0e64194d +0 -6
- package/output/sub/9a33b8cc-101a-4f6a-9852-115595cd166b +0 -172
- package/output/sub/9c060d29-39a2-432b-b06b-b0bd91989a52 +0 -1159
- package/output/sub/a45b383c-d894-4480-8322-9c5d028722e3 +0 -9
- package/output/sub/b875bfe4-3c1e-4a8f-a56c-27a48ab874ca +0 -438
- package/output/sub/b8cbb8f9-f702-4a43-943c-02a6e39683d5 +0 -222
- package/output/sub/e339a686-bbb1-41d9-b6b5-18111e72a68c +0 -1392
- package/output/sub/e7931a8e-45ca-444a-8d6f-e6fed9415c26 +0 -110
- package/output/sub/e859552b-c597-49ed-9ede-fab320fafd87 +0 -150
- package/output/sub/f4237e72-fdce-44a4-b62c-659dce24b1ee +0 -107
- package/output/sub/ff7ea97c-55ac-471c-902f-b7a7bd101ee6 +0 -0
- package/src/cmds/generate.ts +0 -53
- package/src/index.ts +0 -22
- package/src/lib/nomd/cvter.ts +0 -143
- package/src/lib/nomd/nomd.ts +0 -88
- package/src/lib/nosm/default.ts +0 -0
- package/src/lib/nosm/nosm.ts +0 -3
- package/src/lib/notion/client.ts +0 -59
- package/src/lib/notion/object.ts +0 -176
- package/src/lib/notion/request.ts +0 -42
- package/tsconfig.json +0 -35
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import 'dotenv/config';
|
|
2
|
-
import retry from 'async-retry';
|
|
3
|
-
import { Client, APIResponseError } from '@notionhq/client';
|
|
4
|
-
const NOTION_API_SECRET = process.env.NOTION_API_SECRET ?? '';
|
|
5
|
-
const NOBLOG_DATABASE_ID = process.env.NOBLOG_DATABASE_ID ?? '';
|
|
6
|
-
export const client = new Client({
|
|
7
|
-
auth: NOTION_API_SECRET,
|
|
8
|
-
});
|
|
9
|
-
const numberOfRetry = 2;
|
|
10
|
-
export async function GetAllPosts() {
|
|
11
|
-
const params = {
|
|
12
|
-
database_id: NOBLOG_DATABASE_ID,
|
|
13
|
-
filter: {
|
|
14
|
-
and: [{
|
|
15
|
-
property: 'publish',
|
|
16
|
-
checkbox: { equals: true, },
|
|
17
|
-
}]
|
|
18
|
-
},
|
|
19
|
-
sorts: [{
|
|
20
|
-
property: 'date',
|
|
21
|
-
direction: 'descending',
|
|
22
|
-
}],
|
|
23
|
-
page_size: 100,
|
|
24
|
-
};
|
|
25
|
-
let results = [];
|
|
26
|
-
while (true) {
|
|
27
|
-
const res = await retry(async (bail) => {
|
|
28
|
-
try {
|
|
29
|
-
return (await client.databases.query(params));
|
|
30
|
-
}
|
|
31
|
-
catch (error) {
|
|
32
|
-
if (error instanceof APIResponseError) {
|
|
33
|
-
if (error.status && error.status >= 400 && error.status < 500) {
|
|
34
|
-
bail(error);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
throw error;
|
|
38
|
-
}
|
|
39
|
-
}, { retries: numberOfRetry, });
|
|
40
|
-
results = results.concat(res.results);
|
|
41
|
-
if (!res.has_more) {
|
|
42
|
-
break;
|
|
43
|
-
}
|
|
44
|
-
params['start_cursor'] = res.next_cursor;
|
|
45
|
-
}
|
|
46
|
-
return results;
|
|
47
|
-
}
|
|
48
|
-
//# sourceMappingURL=client.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/lib/notion/client.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAA;AAEtB,OAAO,KAAK,MAAM,aAAa,CAAA;AAC/B,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAI3D,MAAM,iBAAiB,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,EAAE,CAAA;AAC7D,MAAM,kBAAkB,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,EAAE,CAAA;AAE/D,MAAM,CAAC,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC;IAC/B,IAAI,EAAE,iBAAiB;CACxB,CAAC,CAAA;AAEF,MAAM,aAAa,GAAG,CAAC,CAAA;AAEvB,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,MAAM,MAAM,GAA0B;QACpC,WAAW,EAAE,kBAAkB;QAC/B,MAAM,EAAE;YACN,GAAG,EAAE,CAAC;oBACJ,QAAQ,EAAE,SAAS;oBACnB,QAAQ,EAAE,EAAE,MAAM,EAAE,IAAI,GAAG;iBAC5B,CAAC;SACH;QACD,KAAK,EAAE,CAAC;gBACN,QAAQ,EAAE,MAAM;gBAChB,SAAS,EAAE,YAAY;aACxB,CAAC;QACF,SAAS,EAAE,GAAG;KACf,CAAA;IAED,IAAI,OAAO,GAAW,EAAE,CAAA;IACxB,OAAO,IAAI,EAAE,CAAC;QACZ,MAAM,GAAG,GAAG,MAAM,KAAK,CACrB,KAAK,EAAE,IAAI,EAAE,EAAE;YACb,IAAI,CAAC;gBACH,OAAO,CAAC,MAAM,MAAM,CAAC,SAAS,CAAC,KAAK,CAClC,MAAa,CACd,CAA0B,CAAA;YAC7B,CAAC;YAAC,OAAO,KAAc,EAAE,CAAC;gBACxB,IAAI,KAAK,YAAY,gBAAgB,EAAE,CAAC;oBACtC,IAAI,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM,IAAI,GAAG,IAAI,KAAK,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;wBAC9D,IAAI,CAAC,KAAK,CAAC,CAAA;oBACb,CAAC;gBACH,CAAC;gBACD,MAAM,KAAK,CAAA;YACb,CAAC;QACH,CAAC,EAAE,EAAE,OAAO,EAAE,aAAa,GAAG,CAC/B,CAAA;QAED,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;QACrC,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;YAAC,MAAK;QAAC,CAAC;QAC5B,MAAM,CAAC,cAAc,CAAC,GAAG,GAAG,CAAC,WAAqB,CAAA;IACpD,CAAC;IAED,OAAO,OAAO,CAAA;AAChB,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"object.js","sourceRoot":"","sources":["../../../src/lib/notion/object.ts"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"request.js","sourceRoot":"","sources":["../../../src/lib/notion/request.ts"],"names":[],"mappings":""}
|
|
@@ -1,435 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
> 💡 **Notion Tip:** Please read [golang修养之路](https://github.com/aceld/golang/tree/main) to develop a basic understanding about go. A lot of the content is from that book. I assume you have already known the syntax and memory model in c.
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
> GO哲学圣经镇楼:
|
|
6
|
-
> Do not communicate by sharing memory; instead, share memory by communicating.
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
# nil
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
In Go, `nil` can represent zero values of the following kinds of types:
|
|
16
|
-
|
|
17
|
-
- pointer types (including type-unsafe ones).
|
|
18
|
-
- map types.
|
|
19
|
-
- slice types.
|
|
20
|
-
- function types.
|
|
21
|
-
- channel types.
|
|
22
|
-
- interface types.
|
|
23
|
-
|
|
24
|
-
---
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
# Interface & Memory model
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
在 Go 语言中,接口类型的内存模型是指,一个接口变量实际上只是一个包含指向底层数据的指针和类型信息的数据结构。该指针指向实现了该接口的具体类型的实例。因此,使用接口类型时,实际上使用的是底层数据的指针。
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
```go
|
|
34
|
-
type Animal interface {
|
|
35
|
-
Speak() string
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
type Dog struct {
|
|
39
|
-
Name string
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
func (d *Dog) Speak() string {
|
|
43
|
-
return "Woof!"
|
|
44
|
-
}
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
我们现在将 `Dog` 类型的值赋给 `Animal` 类型的变量:
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
```go
|
|
52
|
-
var animal Animal = &Dog{"Fido"}
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
在这个例子中,我们为 `Dog` 结构体实现了 `Speak()` 方法,并将其赋给了 `Animal` 接口类型的变量 `animal`。由于 `Dog` 实现了 `Animal` 接口,所以可以将 `Dog` 实例作为 `Animal` 类型的值传递。
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
在这里,变量 `animal` 中实际上存储了 `Dog` 实例的地址信息,以及对应的类型信息。当我们调用 `animal.Speak()` 方法时,由于 `Dog` 类型实现了 `Speak()` 方法,**Go 会自动将底层值指针转换为** **`*Dog`** **类型**,并调用 `Speak()` 方法。
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
需要注意的是,由于指针包含了地址信息,因此通过接口来操作变量的时候,**会使用额外的内存进行指针的转换**。此外,当使用接口类型时,由于需要动**态判断底层数据的类型**,因此会导致一些性能上的损失。**编译器来检查一个类型是否实现了某个接口的所有方法**。如果一个类型没有实现某个接口的全部方法,则会在编译时产生错误。
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
# const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
C++ 和 Go 两种语言中的常量有所不同,可能导致它们在寻址方面的特性表现不同。
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
**在 C++ 中,常量(如** **`const int a = 10`****)实际上是变量,只是它们被声明为不可修改**(即 const 类型),因此编译器会将这些变量存储在内存中的只读区域。因为它们实际上是变量,在内存中占用了一定的空间,所以可以对其取地址。
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
而在 Go 中,常量(如 `const a = 10`)是无类型的,它们在**编译时就被解析并直接替换为使用该常量的值**。也就是说,在编译时,它们不会分配任何内存,并且不属于程序的运行时状态。因此,Go 编译器不支持获取常量的地址,因为它们没有在内存中分配任何空间。
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
总的来说,C++ 和 Go 中的常量有着不同的实现方式和语义约束。C++ 中的常量是实际的变量,但被限制为不可修改;而 **Go 中的常量则是编译时常量,它们在程序运行时不存在**。
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
需要注意的是,某些 C++ 编译器可能会将常量优化为立即数,这样,它们就不会在内存中分配任何空间。这种优化行为类似于 Go 中的常量实现方式。因此,在 C++ 中也应该避免对常量进行不必要的取地址操作,这些操作可能会被编译器优化掉,或者在某些平台上导致未定义的行为。
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
# make & new
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
`make`只能用于 slice, map 和 chan
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
### Declaration of var
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
```text
|
|
93
|
-
var i int
|
|
94
|
-
var s string
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
变量的声明我们可以通过var关键字,然后就可以在程序中使用。当我们不指定变量的默认值时,这些变量的默认值是他们的零值,比如int类型的零值是0,string类型的零值是 `""`,引用类型的零值是 `nil`。
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
```go
|
|
102
|
-
package main
|
|
103
|
-
|
|
104
|
-
import (
|
|
105
|
-
"fmt"
|
|
106
|
-
)
|
|
107
|
-
|
|
108
|
-
func main() {
|
|
109
|
-
var i *int
|
|
110
|
-
*i = 10
|
|
111
|
-
fmt.Println(*i)
|
|
112
|
-
}
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
以上代码会报错,对于引用类型的变量,我们不光要声明它,还要为它分配内容空间,正确代码如下
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
```go
|
|
120
|
-
func main() {
|
|
121
|
-
|
|
122
|
-
var i *int
|
|
123
|
-
i=new(int)
|
|
124
|
-
*i=10
|
|
125
|
-
fmt.Println(*i)
|
|
126
|
-
|
|
127
|
-
}
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
- `new` 函数用于分配一个指定类型的零值并返回该类型的指针。换句话说,`new(T)` 返回类型为 `T` 的指针,并将其初始化为 T 的零值。例如,`new(int)` **创建一个整数类型的指针**,并将其**初始化为零值 0**。
|
|
134
|
-
- `make` 函数用于创建引用类型(如 slice、map 和 channel)的对象,并**返回它们的引用**。它接受一个类型和一些适当的参数,然后**返回初始化后的对象**。例如,`make([]int, 10)` 将创建一个初始长度为 10 的整数切片,并返回该切片的引用。
|
|
135
|
-
|
|
136
|
-
**new always allocate memory on heap?**
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
In Go, the `new` function always returns a pointer to a newly allocated zeroed value of the specified type. However, whether that memory is allocated on the heap or not depends on the context in which the `new` function is called. If it's used with a composite literal, such as `new(T)` where `T` is a struct type, then the memory is allocated on the heap. This is because the returned pointer must survive beyond the lifetime of the function that called `new`.
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
However, if `new` is used with a built-in type or an array type, then the memory is allocated on the stack if possible. This is because these types have a fixed size and can be efficiently allocated on the stack.
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
# Escape
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
go中,这段代码是不会出问题的 rnm,退钱!!!
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
```go
|
|
152
|
-
package main
|
|
153
|
-
|
|
154
|
-
func foo(arg_val int)(*int) {
|
|
155
|
-
|
|
156
|
-
var foo_val int = 11;
|
|
157
|
-
return &foo_val;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
func main() {
|
|
161
|
-
|
|
162
|
-
main_val := foo(666)
|
|
163
|
-
|
|
164
|
-
println(*main_val)
|
|
165
|
-
}
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
go语言编译器会自动决定把一个变量放在栈还是放在堆,编译器会做**逃逸分析(escape analysis)**,**当发现变量的作用域没有跑出函数范围,就可以在栈上,反之则必须分配在堆**。go语言声称这样可以释放程序员关于内存的使用限制,更多的让程序员关注于程序功能逻辑本身。
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
简而言之,不管你想把变量放到栈上还是堆上,编译器觉得应该放在哪,那它就得放在哪,一切以程序的运行逻辑为准。
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
# Pointer type & struct type
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
```go
|
|
179
|
-
package main
|
|
180
|
-
|
|
181
|
-
import "fmt"
|
|
182
|
-
|
|
183
|
-
type User struct {
|
|
184
|
-
Name string
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
func (u *User) Greeting() string {
|
|
188
|
-
u.Name = u.Name+" modify"
|
|
189
|
-
return fmt.Sprintf("Greetings %s!", u.Name)
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
func main() {
|
|
193
|
-
p := &User{"cppgohan by pointer"}
|
|
194
|
-
u := User{"cppgohan by value"}
|
|
195
|
-
|
|
196
|
-
fmt.Println(p.Greeting(), p)
|
|
197
|
-
fmt.Println(u.Greeting(), u)
|
|
198
|
-
}
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
`p.Greeting()`和 `u.Greeting()`的调用方式本质上没有区别
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
对于 `p`,调用 `Greeting()` 就是 `p.Greeting()`。对于 `u` 隐式转换为调用 `(&u).Greeting()`
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
# Garbage collection (IMPORTANT!)
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
[Golang三色标记 混合写屏障GC模式全分析](https://github.com/aceld/golang/blob/main/5%E3%80%81Golang%E4%B8%89%E8%89%B2%E6%A0%87%E8%AE%B0%2B%E6%B7%B7%E5%90%88%E5%86%99%E5%B1%8F%E9%9A%9CGC%E6%A8%A1%E5%BC%8F%E5%85%A8%E5%88%86%E6%9E%90.md)
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
**mark and sweep algorithm**
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
此算法主要有两个主要的步骤:
|
|
218
|
-
|
|
219
|
-
- 标记(Mark phase)
|
|
220
|
-
- 清除(Sweep phase)
|
|
221
|
-
|
|
222
|
-
Mark and Sweep算法在执行的时候,需要程序暂停,即 STW(Stop The World)。STW的过程中,CPU不执行用户代码,全部用于垃圾回收,这个过程的影响很大,所以STW也是一些回收机制最大的难题和希望优化的点。
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
## 一些理解
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
想要理解gc,首先需要理解的是堆和栈的区别,栈空间的特点是容量小,但是要求相应速度快,因为函数调用弹出频繁使用,所以我们希望的是尽可能地减少gc和栈的关联,或者说对栈的元素的gc应该速战速决。
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
$写屏障=插入屏障+删除屏障$
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
插入屏障为了解决这个问题,基于强三色不变式在开始和结束的时间对栈进行了两次扫描,第一次为并发扫描,第二次为STW
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
基于弱三色不变式的删除屏障是比较难理解的(删除屏障中就已经开启了插入屏障,来应对堆区黑色对象引用新建的孤独对象的情况),这里写一下我的理解。设父对象为`x`,其引用对象为`y`,最基础的删除屏障,是将删除的引用全部变为灰色(除非已经是黑色),对栈和堆的对象都是有效的。分两种情况讨论:
|
|
238
|
-
|
|
239
|
-
1. 父对象本来就不可达:此时,反而是救活了这个本应该被删除的对象,但是对不可达对象的操作本来就是违规的(涉及野指针),可以不考虑。
|
|
240
|
-
2. 父对象可达:可以等价认为可达`y`的链路上存在灰色对象,此时,为了让其他的黑色对象(特指栈上的黑色对象,因为栈上的对象不开启写屏障)可以自由引用`y`和`y`链路上的子对象(在删除屏障之前,不然又是野指针了),需要将`y`无脑变灰来满足弱三色不等式。
|
|
241
|
-
|
|
242
|
-
可以理解到,如果`y`和`y`链路上的子对象没有被黑色对象引用,那么删除屏障实际上是活佛再世,抬了这些入土半截的对象一手,回收精度是偏低的。
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-

|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
那么混合写屏障呢?
|
|
249
|
-
|
|
250
|
-
1. 前面说到的插入屏障明显针对的是堆区的元素为黑色的情况,加之中途还可能有新创建的栈对象,所以最后还要使用STW进行栈对象的清理。删除屏障也是没有考虑中途加入的栈元素。于是,可以优化的是:
|
|
251
|
-
|
|
252
|
-
栈上新加入的对象直接变黑
|
|
253
|
-
|
|
254
|
-
2. 对于删除屏障的栈对象:
|
|
255
|
-
|
|
256
|
-
你一个一个从白变灰变黑,回收精度还没提高多少,不如一步到位,在开始的时候就并发扫描直接变黑
|
|
257
|
-
|
|
258
|
-
3. 既然栈对象直接就黑了:
|
|
259
|
-
|
|
260
|
-
堆区的对象可能被栈区的黑色对象引用,删除屏障的祖宗之法不可变
|
|
261
|
-
|
|
262
|
-
4. 连带的:
|
|
263
|
-
|
|
264
|
-
堆区的插入屏障作为删除屏障的影子,也就跟着了。
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
写屏障(删除屏障+插入屏障),其根本目的就是为了保护对象不被删除。而混合写屏障则是结合了go并发的优势,尽可能减少STW的gc方法。
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
**ATTENTION:** 混合写屏障并不能完全消除STW,尽管从原理来看,混合写屏障已经是 STW free 的了,但是由于go的高并发特性,为了使gc的结果保持一致性,在某些时候还是需要 STW 的。
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
# Complement
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
## map
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
[map](9c060d29-39a2-432b-b06b-b0bd91989a52.md)
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
**为什么Go语言中的map并不支持直接对map元素进行取址操作**
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
由于 `map` 中的元素存储在哈希表中,并且哈希表可能会因为容量变化而重新分配内存,因此对 `map` 元素进行取址操作并不可行。如果你试图对 `map` 元素进行取址操作,编译器会报错,提示可能导致指针无效。简而言之,对map元素的修改可能会导致哈希表rehash,所以不能直接修改。在并发程序中,可以使用互斥锁来保证在并发程序中对 `map` 进行安全的访问和修改。另外,还可以考虑使用 Go 语言提供的并发安全的映射类型 `sync.Map`,它可以安全地在多个 goroutine 中读取和写入数据。
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
**以下代码有什么问题,说明原因**
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
```go
|
|
292
|
-
package main
|
|
293
|
-
|
|
294
|
-
import (
|
|
295
|
-
"fmt"
|
|
296
|
-
)
|
|
297
|
-
|
|
298
|
-
type student struct {
|
|
299
|
-
Name string
|
|
300
|
-
Age int
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
func main() {
|
|
304
|
-
//定义map
|
|
305
|
-
m := make(map[string]*student)
|
|
306
|
-
|
|
307
|
-
//定义student数组
|
|
308
|
-
stus := []student{
|
|
309
|
-
{Name: "zhou", Age: 24},
|
|
310
|
-
{Name: "li", Age: 23},
|
|
311
|
-
{Name: "wang", Age: 22},
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
//将数组依次添加到map中
|
|
315
|
-
for _, stu := range stus {
|
|
316
|
-
m[stu.Name] = &stu
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
//打印map
|
|
320
|
-
for k,v := range m {
|
|
321
|
-
fmt.Println(k ,"=>", v.Name)
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
遍历结果出现错误,输出结果为
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
```text
|
|
331
|
-
zhou => wang
|
|
332
|
-
li => wang
|
|
333
|
-
wang => wang
|
|
334
|
-
```
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
map中的3个key均指向数组中最后一个结构体。
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
```text
|
|
341
|
-
0xc000010030
|
|
342
|
-
0xc000010030
|
|
343
|
-
0xc000010030
|
|
344
|
-
zhou => wang
|
|
345
|
-
li => wang
|
|
346
|
-
wang => wang
|
|
347
|
-
```
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
从内存的角度来看,3个key都对应 `&stu`这个指针,这个指针的内存地址是临时的,同时也可以推断出在golang中,for循环的临时变量的虚拟地址是复用的。所以结果出错。
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
**正确写法**
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
```go
|
|
357
|
-
// 遍历结构体数组,依次赋值给map
|
|
358
|
-
for i := 0; i < len(stus); i++ {
|
|
359
|
-
m[stus[i].Name] = &stus[i]
|
|
360
|
-
}
|
|
361
|
-
```
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
## slice
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
[slice](18f0fae1-b7ca-40ea-b725-4e69404429dc.md)
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
## compile
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
[compile](b8cbb8f9-f702-4a43-943c-02a6e39683d5.md)
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
## context
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
[context](34f4d4af-812e-4e56-a6dd-deb96e7f605c.md)
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
## reflect
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
[reflect](6ff91a18-494e-4ffc-a33b-b9fd2c36f43f.md)
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
## unsafe
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
[unsafe](4bac355b-24f0-49e7-a317-e4f1cad63c79.md)
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
## interface
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
[interface](e339a686-bbb1-41d9-b6b5-18111e72a68c.md)
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
## channel
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
[channel](232ae8ce-5ec3-4737-a18e-077774b84b1e.md)
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
## GMP
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
[GMP](0b903359-2337-4ad9-adef-e77b69142581.md)
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
[Golang的协程调度器原理及GMP设计思想](https://github.com/aceld/golang/blob/main/2%E3%80%81Golang%E7%9A%84%E5%8D%8F%E7%A8%8B%E8%B0%83%E5%BA%A6%E5%99%A8%E5%8E%9F%E7%90%86%E5%8F%8AGMP%E8%AE%BE%E8%AE%A1%E6%80%9D%E6%83%B3%EF%BC%9F.md)
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
[对于操作系统而言进程、线程以及Goroutine协程的区别](https://github.com/aceld/golang/blob/main/3%E3%80%81%E5%AF%B9%E4%BA%8E%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E8%80%8C%E8%A8%80%E8%BF%9B%E7%A8%8B%E3%80%81%E7%BA%BF%E7%A8%8B%E4%BB%A5%E5%8F%8AGoroutine%E5%8D%8F%E7%A8%8B%E7%9A%84%E5%8C%BA%E5%88%AB.md)
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
**控制goroutines数量**
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
[Go是否可以无限go?如何限定数量?](https://github.com/aceld/golang/blob/main/4%E3%80%81Go%E6%98%AF%E5%90%A6%E5%8F%AF%E4%BB%A5%E6%97%A0%E9%99%90go%EF%BC%9F%E5%A6%82%E4%BD%95%E9%99%90%E5%AE%9A%E6%95%B0%E9%87%8F%EF%BC%9F.md)
|
|
422
|
-
|
|
423
|
-
- 方法一:只是用有buffer的channel来限制 (无法等待回收goroutine结束,资源回收)
|
|
424
|
-
- 方法二:只使用sync同步机制 (无法控制数量)
|
|
425
|
-
- 方法三:channel与sync同步组合方式 (perfect)
|
|
426
|
-
- 方法四:利用无缓冲channel与任务发送/执行分离方式
|
|
427
|
-
|
|
428
|
-
**WaitGroup与goroutine的竞速问题**
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
[WaitGroup](https://github.com/aceld/golang/blob/main/6%E3%80%81WaitGroup.md)
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
总结:在goroutine前add WaitGroup会好一些。
|
|
435
|
-
|
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
> ZeroMQ is an **asynchronous** messaging library, aimed at use in distributed or concurrent applications. It provides a message queue, but unlike message-oriented middleware, a ZeroMQ system can run without a dedicated message broker; the zero in the name is for zero broker.
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
### 为什么需要ZeroMQ
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
简单,可拓展性。帮助开发者专注于消息的传递和网络拓扑的设计,而不是连接的建立或可用性的保证。(支持自动重连)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
[https://zguide.zeromq.org/docs/chapter1/#Why-We-Needed-ZeroMQ](https://zguide.zeromq.org/docs/chapter1/#Why-We-Needed-ZeroMQ)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
### ZeroMQ的通信哲学
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
> To fix the world, we needed to do two things. One, to solve the general problem of “how to connect any code to any code, anywhere”. Two, to wrap that up in the simplest possible building blocks that people could understand and use _easily_.
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
namely, 用最简单的方式建立连接,另外,在使用zmq进行通信开发时,你需要忘记掉所有有关tcp的知识,因为zmq包装了所有的连接,在套接字之上又抽象包装了一层,因此部分概念会与常规的计算机网络概念相悖。
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
### ZeroMQ的通信特点
|
|
27
|
-
|
|
28
|
-
1. ZeroMQ doesn’t know anything about the data you send except its size in bytes.
|
|
29
|
-
|
|
30
|
-
也就是说,如同 c 这样的以 null 结尾字符串的方式需要进行额外的处理。
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
同时,这也意味着从二进制到解析结果这部分需要用户自己进行处理。(尽管现在的zmq已经提供了常用的解码方式)
|
|
34
|
-
|
|
35
|
-
2. In theory with ZeroMQ sockets, it does not matter which end connects and which end binds. However, in practice there are undocumented differences that I’ll come to later.
|
|
36
|
-
|
|
37
|
-
理论求自由,实践不逾矩
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
### Basic
|
|
41
|
-
|
|
42
|
-
1. 连接就是连接,连接建立一个结构,不要考虑其他的
|
|
43
|
-
|
|
44
|
-
> Now, imagine we start the client _before_ we start the server. In traditional networking, we get a big red Fail flag. But ZeroMQ lets us start and stop pieces arbitrarily. As soon as the client node does [zmq_connect()](http://api.zeromq.org/master:zmq_connect), the connection exists and that node can start to write messages to the socket. At some stage (hopefully before messages queue up so much that they start to get discarded, or the client blocks), the server comes alive, does a [zmq_bind()](http://api.zeromq.org/master:zmq_bind), and ZeroMQ starts to deliver messages.
|
|
45
|
-
|
|
46
|
-
2. 服务节点可以绑定多个端点
|
|
47
|
-
|
|
48
|
-
如果不理解,请重读**ZeroMQ的通信哲学**小节
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
```python
|
|
52
|
-
zmq_bind (socket, "tcp://*:5555");
|
|
53
|
-
zmq_bind (socket, "tcp://*:9999");
|
|
54
|
-
zmq_bind (socket, "inproc://somename");
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
3. 合法的组合
|
|
58
|
-
- PUB and SUB
|
|
59
|
-
- REQ and REP
|
|
60
|
-
- REQ and ROUTER (take care, REQ inserts an extra null frame)
|
|
61
|
-
- DEALER and REP (take care, REP assumes a null frame)
|
|
62
|
-
- DEALER and ROUTER
|
|
63
|
-
- DEALER and DEALER
|
|
64
|
-
- ROUTER and ROUTER
|
|
65
|
-
- PUSH and PULL
|
|
66
|
-
- PAIR and PAIR
|
|
67
|
-
|
|
68
|
-
### 头疼msg,理解msg,维护msg
|
|
69
|
-
|
|
70
|
-
1. 帧是zmq通信的基本传输格式(basic wire format)
|
|
71
|
-
|
|
72
|
-
Frames (also called “message parts” in the ZeroMQ reference manual pages) are the basic wire format for ZeroMQ messages. A frame is a **length-specified block of data**. The length can be zero upwards. If you’ve done any TCP programming you’ll appreciate why frames are a useful answer to the question “how much data am I supposed to read of this network socket now?”
|
|
73
|
-
|
|
74
|
-
2. **从概念来说,ZeroMQ 消息是一个帧**,就像 UDP 一样。但是其可以[用标志位对消息进行扩展](https://rfc.zeromq.org/spec/15/),也就是说,实际使用上,一个zmq消息可以由多个帧组成。
|
|
75
|
-
|
|
76
|
-
Originally, a ZeroMQ message was one frame, like UDP. We later extended this with multipart messages, which are quite simply series of frames with a “more” bit set to one, followed by one with that bit set to zero. The ZeroMQ API then lets you write messages with a “more” flag and when you read messages, it lets you check if there’s “more”.
|
|
77
|
-
|
|
78
|
-
- A message can be one or more parts.
|
|
79
|
-
- These parts are also called “frames”.
|
|
80
|
-
- Each part is a object `zmq_msg_t`
|
|
81
|
-
- You send and receive each part separately, in the low-level API.
|
|
82
|
-
- Higher-level APIs provide wrappers to send entire multipart messages.
|
|
83
|
-
3. msg 的一些注意事项
|
|
84
|
-
- 可以发送0长度的msg作为信号
|
|
85
|
-
|
|
86
|
-
You may send zero-length messages, e.g., for sending a signal from one thread to another.
|
|
87
|
-
|
|
88
|
-
- 保证消息传递的原子性
|
|
89
|
-
|
|
90
|
-
ZeroMQ guarantees to deliver all the parts (one or more) for a message, or none of them.
|
|
91
|
-
|
|
92
|
-
- 异步发送,要保证内存中的队列不会爆
|
|
93
|
-
|
|
94
|
-
ZeroMQ does not send the message (single or multipart) right away,
|
|
95
|
-
but at some indeterminate later time. A multipart message must therefore fit in memory.
|
|
96
|
-
|
|
97
|
-
- 大文件的发送需要自己手动拆分,多次发送
|
|
98
|
-
|
|
99
|
-
A message (single or multipart) must fit in memory. If you want to
|
|
100
|
-
send files of arbitrary sizes, you should break them into pieces and
|
|
101
|
-
send each piece as separate single-part messages. _Using multipart data will not reduce memory consumption._
|
|
102
|
-
|
|
103
|
-
- You must call [zmq_msg_close()](http://api.zeromq.org/master:zmq_msg_close) when finished with a received message, in languages that don’t automatically destroy objects when a scope closes. You don’t call this method after sending a message.
|
|
104
|
-
- [zmq_msg_init_data()](http://api.zeromq.org/master:zmq_msg_init_data) 是一种 zero-copy 的方法,需要谨慎使用
|
|
105
|
-
|
|
106
|
-
### 同步 PUB-SUB,捕获第一条消息
|
|
107
|
-
|
|
108
|
-
<details>
|
|
109
|
-
<summary>The **subscriber** will always miss the first messages that the **publisher** sends.</summary>
|
|
110
|
-
|
|
111
|
-
This is because as the subscriber connects to the publisher (something that takes a small but non-zero time), the publisher may already be sending messages out.
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
> Making a TCP connection involves to and from handshaking that takes several milliseconds depending on your network and the number of hops between peers. In that time, ZeroMQ can send many messages. For sake of argument assume it takes 5 msecs to establish a connection, and that same link can handle 1M messages per second. During the 5 msecs that the subscriber is connecting to the publisher, it takes the publisher only 1 msec to send out those 1K messages.
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
</details>
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
[https://zguide.zeromq.org/docs/chapter2/#Node-Coordination](https://zguide.zeromq.org/docs/chapter2/#Node-Coordination)
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
### 解读 ROUTER-ROUTER
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
zmq 的基础使用中,不应该存在 ROUTER-ROUTER 的双端通信。(虽然 ROUTER-ROUTER 是合法的组合)ROUTER-ROUTER 的强行通信(不交换IDENTITY)会导致tcp握手一直被rst。
|
|
127
|
-
|
|
128
|
-
- ~~在 tdserver 中,DEALER 被封装在了 wrapper 文件定义的对象中,后来又被隐晦地调用了,所以给人一种 ROUTER-ROUTER 通信的错觉~~
|
|
129
|
-
- 本质上是,DEALER 通信时,会隐式地在序列开头添加自己随机的一个 IDENTITY,这个 IDENTITY 会让 ROUTER 在内部维护一个映射关系,即 IDENTITY->DEALER 的映射,如此来把返回的结果正确的发送给对应的 DEALER ,而在封装的实现中,ROUTER 是不会主动添加 IDENTITY 的(其本身也没有初始化的 IDENTITY)
|
|
130
|
-
- **下面说的 DEALER 代指具有 IDENTITY 的 zmq.Socket,使用 DEALER 是为了便于理解**
|
|
131
|
-
- 想要实现 ROUTER-ROUTER 的通信,就需要将 ROUTER 伪装成 DEALER
|
|
132
|
-
- 先从发送讲起,ROUTER A 现在要向另一个 ROUTER B 发送信息,在 A 的眼中,B 是一个 DEALER,所以 A 需要手动添加一个 IDENTITY 在序列的开头,而这个 IDENTITY 必须和 B 的 IDENTITY 完全一样!
|
|
133
|
-
- 接收是正常的流程
|
|
134
|
-
- 发现了吗,在 ROUTER-ROUTER 模型中,他们之间想要实现双端通信,就必须要知道对方的 IDENTITY,但是一般情况下,想要知道对方的 IDENTITY,就要实现双端通信。。。
|
|
135
|
-
- 非常经典的悖论,[官方文档](https://zguide.zeromq.org/docs/chapter4/#reliable-request-reply)中也对这个问题进行了简要的讨论,给出了一些解决方案,但是根据 KISS 原则,如此模型是不应该存在于任何一种设计中的
|
|
136
|
-
|
|
137
|
-
### 为什么是zmq_poll()而不是zmq_epoll()?
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
[https://github.com/zeromq/libzmq/issues/1667](https://github.com/zeromq/libzmq/issues/1667)
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
### zmq_proxy()里的配对瞎配会怎么样?
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
> If you’re like most ZeroMQ users, at this stage your mind is starting to think, “What kind of evil stuff can I do if I plug random socket types into the proxy?” The short answer is: try it and work out what is happening. In practice, you would usually stick to ROUTER/DEALER, XSUB/XPUB, or PULL/PUSH.
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
经过wireshark抓包发现,对于接收信息来说瞎配啥事也没有,能收到。但是一旦proxy的组合不符合配对规律,frontend 到 backend 的通信会连包都发不出去,也就是在函数内部进行了阻断。
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
### zmq 错误处理之道
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
[https://zguide.zeromq.org/docs/chapter2/#Handling-Errors-and-ETERM](https://zguide.zeromq.org/docs/chapter2/#Handling-Errors-and-ETERM)
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
### zmq之信息丢失
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
[https://zguide.zeromq.org/docs/chapter2/#Missing-Message-Problem-Solver](https://zguide.zeromq.org/docs/chapter2/#Missing-Message-Problem-Solver)
|
|
162
|
-
|