diff --git a/db/ mysql/tips.md b/db/ mysql/tips.md index 50275a4..da158d2 100644 --- a/db/ mysql/tips.md +++ b/db/ mysql/tips.md @@ -2,3 +2,19 @@ - 如何`mysql`处理大批量数据的删除,使用`limit`控制删除的数量 `show processlist` 查看当前mysql正在执行的命令 + +`service mysqld restart`重启mysql服务 + +### 相关问题 +``` +ERROR 2002 (HY000): Can't connect to local MySQL server through socket +``` +方案1. +ps -A|grep mysql +显示类似: +1829 ? 00:00:00 mysqld_safe +1876 ? 00:00:31 mysqld +2.#kill -9 1829 +3.#kill -9 1876 +4.#/etc/init.d/mysql restart +5.#mysql -u root -p diff --git a/elasticsearch/other.md b/elasticsearch/other.md index 3a94eef..956c0af 100644 --- a/elasticsearch/other.md +++ b/elasticsearch/other.md @@ -6,3 +6,5 @@ 使用索引规范 最终索引结构为 + + diff --git "a/elasticsearch/\347\233\270\345\205\263\351\227\256\351\242\230.md" "b/elasticsearch/\347\233\270\345\205\263\351\227\256\351\242\230.md" new file mode 100644 index 0000000..216244f --- /dev/null +++ "b/elasticsearch/\347\233\270\345\205\263\351\227\256\351\242\230.md" @@ -0,0 +1,30 @@ +1. elasticsearch text字段排序报错解决, 一般使用数字排序 + +问题表象 +``` +Fielddata is disabled on text fields by default. Set fielddata=true on [region] in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memory. +``` + +解决方式有两种: + +(1) 使用`region.keyword`进行聚合,排序 +```js +{ + sort: { + 't.keyword': { + order: 'desc' + } + } +} +``` +(2) 将排序字段的`fileddata`设置为true +```js +{ + "properties": { + "t":{ + "type": "text", + "fielddata": true + } + } +} +``` diff --git a/gobasic/defer.md b/gobasic/defer.md new file mode 100644 index 0000000..66ff8c3 --- /dev/null +++ b/gobasic/defer.md @@ -0,0 +1,201 @@ +### 分析defer和panic执行顺序 +```go +func main() { + deferCall() +} + +func deferCall() { + defer func() { fmt.Println("打印前") }() + defer func() { fmt.Println("打印中") }() + defer func() { fmt.Println("打印后") }() + + panic("触发异常") +} + +``` +1. `defer`本身遵循先进后出的顺序, 代码抛出的`panic`如果在所有的defer中都不使用`recover`恢复, 则直接退出程序, 但任然打印数据 +2. 如果手动使用`os.Exit()`退出, 则`defer`不执行 + +### 分析defer和return执行顺序 +```go +func main() { + fmt.Println(double1(5)) + fmt.Println(double1(6)) + fmt.Println() + fmt.Println(double2(5)) + fmt.Println(double2(6)) +} + +// 匿名返回 +// 加倍参数,若结果超过 10 则还原 +func double1(v1 int) int { + var v2 int + defer func() { + if v2 > 10 { + v2 = v1 // v2 不会被修改 + } + }() + + v2 = v1 * 2 + return v2 +} + +// 有名返回 +func double2(v1 int)(v2 int) { + // v2 与函数一起被声明,在 defer 中能被修改 + defer func() { + if v2 > 10 { + v2 = v1 // v2 被修改 + } + }() + + v2 = v1 * 2 + return +} +``` + +注意 return var 会分为三步执行: + +return 语句为 var 赋值 + +匿名返回值函数:先声明,再赋值 +有名返回值函数:直接赋值 +检查是否存在 defer 语句:逆序执行多条 defer,有名返回函数可能会再次修改 var + +真正返回 var 到调用处 + +10 +12 + +10 +6 + +### 分析defer参数的计算时机 +```go +func main() { + a := 1 + b := 2 + defer add("A", a, add("B", a, b)) + a = 0 + defer add("C", a, add("D", a, b)) + b = 1 +} + + +func add(desc string, a, b int) int { + sum := a + b + fmt.Println(desc, a, b, sum) + return sum +} +``` + +defer 语句会计算好 func 的参数,再放入执行栈中。 +B 1 2 3 +D 0 2 2 +C 0 2 2 +A 1 3 4 + +### 考察Go的组合 +```go +type People struct{} + +func (p *People) ShowA() { + fmt.Println("people showA") + p.ShowB() +} +func (p *People) ShowB() { + fmt.Println("people showB") +} + +// Teacher 通过嵌入 People 来获取了 ShowA() 和 showB() +type Teacher struct { + People +} + +// Teacher 实现并覆盖了 showB() +func (t *Teacher) ShowB() { + fmt.Println("teacher showB") +} + +func main() { + t := Teacher{} + t.ShowB() + // 调用未覆盖的 showA(),因为它的 receiver 依旧是 People,相当于 People 调用 + t.ShowA() +} +``` + +```go + +package main +import ( + "fmt" +) +type student struct { + Name string + Age int +} +func pase_student() map[string]*student { + m := make(map[string]*student) + stus := []student{ + {Name: "zhou", Age: 24}, + {Name: "li", Age: 23}, + {Name: "wang", Age: 22}, + } + for _, stu := range stus { + m[stu.Name] = &stu + } + return m +} +func main() { + students := pase_student() + for k, v := range students { + fmt.Printf("key=%s,value=%v \n", k, v) + } +} +``` + + 因为for遍历时,变量stu指针不变,每次遍历仅进行struct值拷贝,故m[stu.Name]=&stu实际上一致指向同一个指针,最终该指针的值为遍历的最后一个struct的值拷贝。形同如下代码: +```go +var stu student +for _, stu = range stus { + m[stu.Name] = &stu +} +``` +修正方案,取数组中原始值的指针: +```go +for i, _ := range stus { + stu:=stus[i] + m[stu.Name] = &stu +} +``` + +```go +package main +import ( + "fmt" +) +type People interface { + Speak(string) string +} +type Stduent struct{} +func (stu *Stduent) Speak(think string) (talk string) { + if think == "bitch" { + talk = "You are a good boy" + } else { + talk = "hi" + } + return +} +func main() { + var peo People = Stduent{} + think := "bitch" + fmt.Println(peo.Speak(think)) +} +``` + +编译失败,值类型 Student{} 未实现接口People的方法,不能定义为 People类型。 + +定义为指针 `var peo People = &Stduent{}` +方法定义在值类型上,指针类型本身是包含值类型的方法。 +`func (stu Stduent) Speak(think string) (talk string) { //... }` diff --git "a/gobasic/fmt\346\240\274\345\274\217\345\214\226\350\276\223\345\207\272.md" "b/gobasic/fmt\346\240\274\345\274\217\345\214\226\350\276\223\345\207\272.md" new file mode 100644 index 0000000..c312aa6 --- /dev/null +++ "b/gobasic/fmt\346\240\274\345\274\217\345\214\226\350\276\223\345\207\272.md" @@ -0,0 +1,82 @@ +```go +type point struct { + x, y int +} + +func main() { + //Go 为常规 Go 值的格式化设计提供了多种打印方式。例如,这里打印了 point 结构体的一个实例。 + p := point{1, 2} + // %v会自行判断传入参数的格式 + fmt.Printf("%v\n", p) // {1 2} + //如果值是一个结构体,%+v 的格式化输出内容将包括结构体的字段名。 + fmt.Printf("%+v\n", p) // {x:1 y:2} + //%#v 形式则输出这个值的 Go 语法表示。例如,值的运行源代码片段。 + fmt.Printf("%#v\n", p) // main.point{x:1, y:2} + //需要打印值的类型,使用 %T。 + fmt.Printf("%T\n", p) // main.point + //格式化布尔值是简单的。 + fmt.Printf("%t\n", true) + //格式化整形数有多种方式,使用 %d进行标准的十进制格式化。 + fmt.Printf("%d\n", 123) + //这个输出二进制表示形式。 + fmt.Printf("%b\n", 14) + //这个输出给定整数的对应字符。 + fmt.Printf("%c\n", 33) + //%x 提供十六进制编码。 + fmt.Printf("%x\n", 456) + //对于浮点型同样有很多的格式化选项。使用 %f 进行最基本的十进制格式化。 + fmt.Printf("%f\n", 78.9) + //%e 和 %E 将浮点型格式化为(稍微有一点不同的)科学技科学记数法表示形式。 + fmt.Printf("%e\n", 123400000.0) + fmt.Printf("%E\n", 123400000.0) + //使用 %s 进行基本的字符串输出。 + fmt.Printf("%s\n", "\"stringdddddddd\"") + //像 Go 源代码中那样带有双引号的输出,使用 %q。 + fmt.Printf("%q\n", "\"stringccccc\"") + //和上面的整形数一样,%x 输出使用 base-16 编码的字符串,每个字节使用 2 个字符表示。 + fmt.Printf("%x\n", "hex this") + //当输出数字的时候,你将经常想要控制输出结果的宽度和精度,可以使用在 % 后面使用数字来控制输出宽度。默认结果使用右对齐并且通过空格来填充空白部分。 + fmt.Printf("|%6d|%6d|\n", 12, 345) + //你也可以指定浮点型的输出宽度,同时也可以通过 宽度.精度 的语法来指定输出的精度。 + fmt.Printf("|%6.2f|%6.2f|\n", 1.2, 3.45) + //要最对齐,使用 - 标志。 + fmt.Printf("|%-6.2f|%-6.2f|\n", 1.2, 3.45) + //你也许也想控制字符串输出时的宽度,特别是要确保他们在类表格输出时的对齐。这是基本的右对齐宽度表示。 + fmt.Printf("|%6s|%6s|\n", "foo", "b") + //要左对齐,和数字一样,使用 - 标志。 + fmt.Printf("|%-6s|%-6s|\n", "foo", "b") + //到目前为止,我们已经看过 Printf了,它通过 os.Stdout输出格式化的字符串。Sprintf 则格式化并返回一个字符串而不带任何输出。 + s := fmt.Sprintf("a %s", "string") + fmt.Println(s) + //你可以使用 Fprintf 来格式化并输出到 io.Writers而不是 os.Stdout。 + fmt.Fprintf(os.Stderr, "an %s\n", "error") + + fmt.Println(fmt.Sprintf("\x1b[%dm%s\x1b[0m", 3, "hello")) +} +``` + +```go +type Data struct { +} + +func (self Data) String() string { + return "data" +} + +func main() { + fmt.Printf("hello world\n") + + fmt.Println("hello world") + + fmt.Printf("num %d\n", 5) + + str := fmt.Sprintf("float %f", 3.14159) + fmt.Print(str) + + fmt.Fprintln(os.Stdout, "A\n") + + // 自动转变格式, 调用接口体中的String()方法 + fmt.Printf("%v\n", Data{}) + +} +``` diff --git a/gobasic/io.md b/gobasic/io.md index 0ebab1b..4f806a1 100644 --- a/gobasic/io.md +++ b/gobasic/io.md @@ -12,7 +12,7 @@ Reader 接口的定义如下: ```go type Reader interface { - Read(p []byte) (n int, err error) + Read(p []byte) (n int, err error)· } ``` diff --git a/gobasic/io2.md b/gobasic/io2.md new file mode 100644 index 0000000..87b2bd2 --- /dev/null +++ b/gobasic/io2.md @@ -0,0 +1,71 @@ +以下三个文件都可以追根到`Reader` +`strings.NewReader("string")` +`os.Stdin` +`file, _ := os.Open("main.go")`中的`file`文件 + +```go +// 使用io.Reader参数 +func ReadFrom(reader io.Reader, num int) ([]byte, error) { + p := make([]byte, num) + n, err := reader.Read(p) + if n > 0 { + return p[:n], nil + } + return p, err +} + +func sampleReadFromString() { + + // 从字符串读取 + data, _ := ReadFrom(strings.NewReader("from string"), 12) + + fmt.Println(data) +} + +func sampleReadStdin() { + + fmt.Println("please input from stdin:") + + // 从标准输入读取 + data, _ := ReadFrom(os.Stdin, 11) + + fmt.Println(data) +} + +func sampleReadFile() { + + file, _ := os.Open("main.go") + defer file.Close() + + // 从普通文件读取,其中file是os.File的实例 + data, _ := ReadFrom(file, 9) + + fmt.Println(data) +} +``` + +```go + // 使用bufio.NewReader将数据置于缓冲区中, 然后从缓冲区读数据 + strReader := strings.NewReader("hello world") + bufReader := bufio.NewReader(strReader) + // 只读拾取, Peek只是看一看 + data, _ := bufReader.Peek(5) + + // bufReader.Buffered() 当前缓冲的数据总量 + fmt.Println(data, bufReader.Buffered()) + + // 读出hello , 查看剩余, + // bufReader.ReadString 读到什么为止, 包括字段, 从缓存区重化工取出 + str, _ := bufReader.ReadString(' ') + + // 取出 + fmt.Println(str, bufReader.Buffered()) + + // 写入 + // os.Stdout表示屏幕, 将数据输出到屏幕中, 注意使用Flush()方法 + w := bufio.NewWriter(os.Stdout) + + fmt.Fprint(w, "Hello, ") + fmt.Fprint(w, "world!") + w.Flush() // Don't forget to flush! +``` diff --git a/gobasic/net-http.png b/gobasic/net-http.png new file mode 100644 index 0000000..534fa17 Binary files /dev/null and b/gobasic/net-http.png differ diff --git a/gobasic/time/index.md b/gobasic/time/index.md new file mode 100644 index 0000000..33b9a21 --- /dev/null +++ b/gobasic/time/index.md @@ -0,0 +1 @@ +`time.sleep(6 * time.Hour)`暂停多久 diff --git "a/gobasic/\345\205\263\344\272\216reflectx\347\232\204\344\275\277\347\224\250.md" "b/gobasic/\345\205\263\344\272\216reflectx\347\232\204\344\275\277\347\224\250.md" new file mode 100644 index 0000000..6656de0 --- /dev/null +++ "b/gobasic/\345\205\263\344\272\216reflectx\347\232\204\344\275\277\347\224\250.md" @@ -0,0 +1,82 @@ +```go +// Copyright 2013 The StudyGolang Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// http://studygolang.com +// Author: polaris polaris@studygolang.com + +package util + +import ( + "fmt" + "reflect" + "strings" +) + +func Struct2Map(dest map[string]interface{}, src interface{}) error { + if dest == nil { + return fmt.Errorf("Struct2Map(dest is %v)", dest) + } + srcType := reflect.TypeOf(src) + srcValue := reflect.Indirect(reflect.ValueOf(src)) + if srcValue.Kind() != reflect.Struct { + return fmt.Errorf("Struct2Map(non-struct %s)", srcType) + } + srcType = srcValue.Type() + fieldNum := srcType.NumField() + for i := 0; i < fieldNum; i++ { + // struct 字段的反射类型(StructField) + fieldType := srcType.Field(i) + // 非导出字段不处理 + if fieldType.PkgPath != "" { + continue + } + tag := fieldType.Tag.Get("json") + fieldValue := srcValue.Field(i) + + // json 有 key,omitempty 的情况 + tag = strings.Split(tag, ",")[0] + + if tag == "" { + tag = UnderscoreName(fieldType.Name) + } + dest[tag] = fieldValue.Interface() + } + return nil +} + +// model中类型提取其中的 idField(int 类型) 属性组成 slice 返回 +func Models2Intslice(models interface{}, idField string) []int { + if models == nil { + return []int{} + } + + // 类型检查 + modelsValue := reflect.ValueOf(models) + if modelsValue.Kind() != reflect.Slice { + return []int{} + } + + var modelValue reflect.Value + + length := modelsValue.Len() + ids := make([]int, 0, length) + + for i := 0; i < length; i++ { + modelValue = reflect.Indirect(modelsValue.Index(i)) + if modelValue.Kind() != reflect.Struct { + continue + } + + val := modelValue.FieldByName(idField) + if val.Kind() != reflect.Int { + continue + } + + ids = append(ids, int(val.Int())) + } + + return ids +} + +``` diff --git "a/gobasic/\345\210\233\345\273\272http\346\234\215\345\212\241\345\231\250.md" "b/gobasic/\345\210\233\345\273\272http\346\234\215\345\212\241\345\231\250.md" new file mode 100644 index 0000000..acd09d2 --- /dev/null +++ "b/gobasic/\345\210\233\345\273\272http\346\234\215\345\212\241\345\231\250.md" @@ -0,0 +1,104 @@ +> https://www.jianshu.com/p/9a666b98d7a2 +> https://www.jianshu.com/p/ae9b5628d18b +> https://blog.csdn.net/lengyuezuixue/article/details/79094323 + +```go +// 方式一: 直接使用http.HandleFunc 和 http.ListenAddServe(":port", nil)方式 +func main() { + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("httpserver v1")) + }) + http.HandleFunc("/bye", sayBye) + log.Println("Starting v1 server ...") + log.Fatal(http.ListenAndServe(":1210", nil)) +} + +func sayBye(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("bye bye ,this is v1 httpServer")) +} +// http.ListenAndServe(":1210", nil) =>使用传入参数构造server对象, 然后执行server.ListenAndServe() +``` + +```go +// 方式二, 关联使用ServeMux对象, 该对象中实现了ServeHttp方法 +func main() { + mux := http.NewServeMux() + mux.Handle("/", &myHandler{}) + // sayBye函数被HandleFunc(sayBye)之后,转化成为了handler + mux.HandleFunc("/bye", sayBye) + + log.Println("Starting v2 httpserver") + log.Fatal(http.ListenAndServe(":1210", mux)) +} +type myHandler struct{} + +func (*myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("this is version 2")) +} +func sayBye(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("bye bye ,this is v2 httpServer")) +} +``` + +```go +// 方式三 +func main() { + mux := http.NewServeMux() + mux.Handle("/", &myHandler{}) + mux.HandleFunc("/bye", sayBye) + + // 不再使用http直接调用ListenAndServer + // 直接使用http.Server定制端口, 超时及路由控制等其他配置 + server := &http.Server{ + Addr: ":1210", + WriteTimeout: time.Second * 3, //设置3秒的写超时 + Handler: mux, + } + log.Println("Starting v3 httpserver") + log.Fatal(server.ListenAndServe()) +} + +type myHandler struct{} + +func (*myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("this is version 3")) +} + +func sayBye(w http.ResponseWriter, r *http.Request) { + // 睡眠4秒 上面配置了3秒写超时,所以访问 “/bye“路由会出现没有响应的现象 + time.Sleep(4 * time.Second) + w.Write([]byte("bye bye ,this is v3 httpServer")) +} +``` + +### 分析 +无论使用哪种方式, 最终都会调用`func (srv *Server) ListenAndServe() error {}`实现服务监听, 逻辑封装使用了`Server`结构体, 最重要的就是使用其中的`Addr`和`Handler`参数, 完成数据的替换, 内部调用了`func (srv *Server) Serve(l net.Listener) error {}`方法, 具体到方法`func (c *conn) serve(ctx context.Context) {}` + +``` go +type Server struct { + Addr string // TCP address to listen on, ":http" if empty + Handler Handler // handler to invoke, http.DefaultServeMux if nil + + // TLSConfig optionally provides a TLS configuration for use + // by ServeTLS and ListenAndServeTLS. Note that this value is + // cloned by ServeTLS and ListenAndServeTLS, so it's not + // possible to modify the configuration with methods like + // tls.Config.SetSessionTicketKeys. To use + // SetSessionTicketKeys, use Server.Serve with a TLS Listener + // instead. + TLSConfig *tls.Config + + // ReadTimeout is the maximum duration for reading the entire + // request, including the body. + // + // Because ReadTimeout does not let Handlers make per-request + // decisions on each request body's acceptable deadline or + // upload rate, most users will prefer to use + // ReadHeaderTimeout. It is valid to use them both. + ReadTimeout time.Duration + // ... + // ... + // ... +} + +``` diff --git "a/gobasic/\345\244\207\345\277\230.md" "b/gobasic/\345\244\207\345\277\230.md" new file mode 100644 index 0000000..2fdc79d --- /dev/null +++ "b/gobasic/\345\244\207\345\277\230.md" @@ -0,0 +1,106 @@ +### golang内置类型 +``` +bool + +string + +int int8 int16 int32 int64 +uint uint8 uint16 uint32 uint64 uintptr + +byte // uint8 类型的别名 // 存储 raw data + +rune // int32 类型的别名 // 一个 Unicode code point 字符 + +float32 float64 + +complex64 complex128 + +``` + +```go +// 防止长时间阻塞, 使用time.After(time.Second *3) 及 阻塞的channel来实现 +func main() { + c := make(chan int) + o := make(chan bool) + go func() { + for { + select { + case i := <-c: + fmt.Println(i) + case <-time.After(time.Duration(3) * time.Second): //设置超时时间为3s,如果channel 3s钟没有响应,一直阻塞,则报告超时,进行超时处理. + fmt.Println("timeout") + o <- true + break + } + } + }() + <-o + +``` +### 切片备注 +[Golang 切片与函数参数“陷阱”](https://www.jianshu.com/p/7dbce907767a) 该文章深入的解析了切片传递和数据分配涉及到的地址变化 +`slice`中截取都是左开右闭 +```go + a := []int{1, 2, 3, 4, 5} + s := a[1:3] // 2 3 + c := append(s, 6) // 2 3 6 + fmt.Println("a ==", a[:cap(a)]) // [1,2,3,4,5] + fmt.Println("s ==", s[:cap(s)]) // [2 3 5 6] + fmt.Println("c ==", c[:cap(c)]) // [2 3 5 6] + + d := append(a, 6) + fmt.Println("a ==", a[:cap(a)]) // [1 2 3 6 5] + fmt.Println("d ==", d[:cap(d)]) // [1 2 3 6 5 6 0 0 0 0] +``` + +### 整形 => 字符串的转化 +```go +// 通过Itoa方法转换 +str1 := strconv.Itoa(i) +// 通过Sprintf方法转换 +str2 := fmt.Sprintf("%d", i) +``` + +### 时间格式 +```go +// 0时区时间戳 +time.Now().Unix() + +// 当前时间格式化 +time.Now().Format("2006-01-02 15:04:05") + +// 时间戳 =》字符串 +time.Unix(1389058332, 0).Format("2006-01-02 15:04:05") + +// 字符串 =》时间戳 +time.Date(2014, 1, 7, 5, 50, 4, 0, time.Local).Unix() + +// 使用parse转化 +the_time, err := time.Parse("2006-01-02 15:04:05", "2014-01-08 09:04:41") +if err == nil { + unix_time := the_time.Unix() + fmt.Println(unix_time) +} + +const ( + date = "2006-01-02" + shortdate = "06-01-02" + times = "15:04:02" + shorttime = "15:04" + datetime = "2006-01-02 15:04:02" + newdatetime = "2006/01/02 15~04~02" + newtime = "15~04~02" +) + +func main() { + thisdate := "2014-03-17 14:55:06" + timeformatdate, _ := time.Parse(datetime, thisdate) + fmt.Println(timeformatdate) + convdate := timeformatdate.Format(date) + convshortdate := timeformatdate.Format(shortdate) + convtime := timeformatdate.Format(times) + convshorttime := timeformatdate.Format(shorttime) + convnewdatetime := timeformatdate.Format(newdatetime) + convnewtime := timeformatdate.Format(newtime) +} +``` diff --git "a/gobasic/\345\246\202\344\275\225\346\216\247\345\210\266goroutine\347\232\204\346\225\260\351\207\217.md" "b/gobasic/\345\246\202\344\275\225\346\216\247\345\210\266goroutine\347\232\204\346\225\260\351\207\217.md" new file mode 100644 index 0000000..cdf5f8b --- /dev/null +++ "b/gobasic/\345\246\202\344\275\225\346\216\247\345\210\266goroutine\347\232\204\346\225\260\351\207\217.md" @@ -0,0 +1,47 @@ +1. 为什么要控制`goroutine`数量? + +goroutine固然好,但是数量太多了,往往会带来很多麻烦,比如耗尽系统资源导致程序崩溃,或者CPU使用率过高导致系统忙不过来。比如: +```go +for i:=0; i < 10000; i++ { + go work() +} +``` + +2 使用通道限制`goroutine`的数量 +```go +var ch chan int + +func work() { + //do something + <-ch +} + +func main() { + // 开启10个goroutine执行任务 + // 当chan中任务为10时, 直接阻塞,不能继续执行任务 + ch = make(chan int, 10) + for i:=0; i < 10000; i++ { + ch <- 1 + go work() + } +} +``` +使用上面这种方式, 主程序直接退出, 然而`work`任务没有执行完, 转而使用`sync.WaitGroup`, `wg.Done`, `wg.Add`,`wg.Wait`方式 +即: `wg := &sync.WaitGroup{}`, `wg.Add(1)`, `defer wg.Done()`, `wg.Wait()` +```go +var wg *sync.WaitGroup + +func work() { + defer wg.Done() + //do something +} + +func main() { + wg = &sync.WaitGroup{} + for i:=0; i < 10000; i++ { + wg.Add(1) + go work() + } + wg.Wait()//等待所有goroutine退出 +} +``` diff --git "a/gobasic/\345\271\266\345\217\221\345\222\214\345\271\266\350\241\214.md" "b/gobasic/\345\271\266\345\217\221\345\222\214\345\271\266\350\241\214.md" new file mode 100644 index 0000000..8eaf0e8 --- /dev/null +++ "b/gobasic/\345\271\266\345\217\221\345\222\214\345\271\266\350\241\214.md" @@ -0,0 +1,144 @@ + +在现代操作系统中,线程是处理器调度和分配的基本单位,进程则作为资源拥有的基本单位。 +每个进程是由私有的虚拟地址空间、代码、数据和其它各种系统资源组成。 +线程是进程内部的一个执行单元。 +每一个进程至少有一个主执行线程,它无需由用户去主动创建,是由系统自动创建的。 +用户根据需要在应用程序中创建其它线程,多个线程并发地运行于同一个进程中。 + +并发: 一段时间执行完多个任务, 不管这些任务是否在同一时刻执行完毕 +并行: 同一时刻执行多个任务(多个线程或者进程)), 属于并发的一种 +`Do not communicate by sharing memory; instead, share memory by communicating.` + +CSP: Communicating Sequential Processes + +支撑整个调度器的主要有4个重要结构,分别是`M、G、P、Sched`,前三个定义在runtime.h中,Sched定义在proc.c中。 + +- Sched结构就是调度器,它维护有存储M和G的队列以及调度器的一些状态信息等。 +- M代表内核级线程,一个M就是一个线程,goroutine就是跑在M之上的;M是一个很大的结构,里面维护`小对象内存cache(mcache)`、`当前执行的goroutine`、`随机数发生器`等等非常多的信息。 +- P全称是Processor,处理器,它的主要用途就是用来执行goroutine的,所以它也维护了一个`goroutine队列`,里面存储了所有需要它来执行的goroutine,这个P的角色可能有一点让人迷惑,一开始容易和M冲突,后面重点聊一下它们的关系。 +- G就是goroutine实现的核心结构了,G维护了goroutine需要的`栈`、`程序计数器`以及它所在的`M`等信息。 + +协程底层就是线程, 但是比线程更加轻量, 协程体现在底层就是5-6个线程, 协程可以是更高效,更轻量,更易用的线程 + +### 关于并发的代码解释 +```go +// 在单核的CPU上运行这段代码, 就是通过切换时间片的方式执行代码的 +// runtime.Gosched()用于让出CPU时间片 +// main函数主进程是先于协程执行, 如果直接注释runtime.Gosched(),则不会执行say("world")的打印 +// runtime.GOMAXPROCS(n)用来指定是否多核数执行代码 +// runtime.NumGoroutine()统计当前的进程数量, 数量总数为go关键字的个数+1(main函数) +func say(s string) { + for i := 0; i < 5; i++ { + runtime.Gosched() + fmt.Println(s) + } +} +func main() { + go say("world") + say("hello") +} +``` + +```go +// runtime.NumGoroutine()统计当前的进程数量, 数量总数为go关键字的个数+1(main函数) +func main() { + runtime.GOMAXPROCS(runtime.NumCPU()) + c := make(chan bool, 100) + t := time.Tick(time.Millisecond * 100) + + go func() { + for { + select { + case <-t: + watching() + } + } + }() + + for i := 0; i < 100; i++ { + c <- true + go worker(i, c) + } + + time.Sleep(time.Minute * 1) +} + +func watching() { + fmt.Printf("NumGoroutine: %d\n", runtime.NumGoroutine()) +} + +func worker(i int, c chan bool) { + fmt.Println("worker", i, "|", <-c) + time.Sleep(time.Second) +} + +``` + +**两个问题** +1. 如果在`select`语句中发现某个通道已关闭,那么应该怎样屏蔽掉它所在的分支? +```go +for { + select { + case _, ok := <-ch1: + if !ok { + // 直接将通道改为非缓冲通道 + // 或者直接使用 ch1 = nil + ch1 = make(chan int) + } + case ..... : + //// + default: + //// + } +} + +``` +2. 在`select`语句与`fo`r语句联用时,怎样直接退出外层的 +可以用 break和标签配合使用,直接break出指定的循环体,或者goto语句直接跳转到指定标签执行 +```go + +func example1() { + ch1 := make(chan int, 1) + time.AfterFunc(time.Second, func() { close(ch1) }) +loop: + for { + select { + case _, ok := <-ch1: + if !ok { + break loop + } + fmt.Println("ch1") + } + } + fmt.Println("END") + +} + +func example2() { + ch1 := make(chan int, 1) + time.AfterFunc(time.Second, func() { close(ch1) }) + for { + select { + case _, ok := <-ch1: + if !ok { + goto loop + } + fmt.Println("ch1") + } + } +loop: + fmt.Println("END") +} +``` + + + +相关的博客 + +[Go 并发原理](https://hacpai.com/article/1524288601994) + +[goroutine与调度器](http://skoo.me/go/2013/11/29/golang-schedule?utm_campaign=studygolang.com&utm_medium=studygolang.com&utm_source=studygolang.com) + +[Goroutine并发调度模型深度解析之手撸一个协程池](https://segmentfault.com/a/1190000015464889#articleHeader0) + +[深入golang之---goroutine并发控制与通信](https://segmentfault.com/a/1190000015564216) diff --git "a/gobasic/\346\227\245\345\277\227\345\244\204\347\220\206.md" "b/gobasic/\346\227\245\345\277\227\345\244\204\347\220\206.md" new file mode 100644 index 0000000..6168b0d --- /dev/null +++ "b/gobasic/\346\227\245\345\277\227\345\244\204\347\220\206.md" @@ -0,0 +1,37 @@ +``` go +package utils + +import ( + "fmt" + "log" + "os" + "time" +) + +const logDir = "/export/log/lending-push-mobile" +const logPrefix = "lending-push-mobile" + +// Logger 对外日志调用 +var Logger *log.Logger + +func init() { + fStr := time.Now().Format("2006-01-02") + // 如果不存在文件夹,则创建 + _, err := os.Stat(logDir) + if err != nil && os.IsNotExist(err) { + err = os.Mkdir(logDir, os.ModePerm) + if err != nil { + panic(err) + } + } + + logfileName := fmt.Sprintf("%s/%s-%s.log", logDir, logPrefix, fStr) + file, err := os.OpenFile(logfileName, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) + if err != nil { + log.Fatalln("failed to create file!") + } + logger := log.New(file, "", log.LstdFlags|log.Llongfile) + logger.SetFlags(log.LstdFlags) + Logger = logger +} +``` diff --git "a/hadoop/phoenix/phoenix\344\270\216hbase\350\241\250\345\257\271\345\272\224.md" "b/hadoop/phoenix/phoenix\344\270\216hbase\350\241\250\345\257\271\345\272\224.md" new file mode 100644 index 0000000..5288906 --- /dev/null +++ "b/hadoop/phoenix/phoenix\344\270\216hbase\350\241\250\345\257\271\345\272\224.md" @@ -0,0 +1,53 @@ +### `hbase`的行键就是`phoenix`的主键 + +在hbase shell 中建表: +```sh +create 'SCORE','A', 'B' + +put 'SCORE','Tom','A:M','5' +put 'SCORE','Lilei','B:M','5' + +scan 'SCORE' + +# ROW COLUMN+CELL +# Lilei column=B:M, timestamp=1427679080104, value=5 +# Tom column=A:M, timestamp=1427679078854, value=5 +``` +phoenix client端建表 + +```sh +create table "SCORE" (pk VARCHAR primary key, "A"."M" VARCHAR , "B"."M" VARCHAR ); +select * from score; +# +------------+------------+------------+ +# | PK | M | M | +# +------------+------------+------------+ +# | Lilei | null | 5 | +# | Tom | 5 | null | +# +------------+------------+------------+ + +# 删除数据表 +drop table score; +``` + + +首先创建一张HBase表,再创建的Phoenix表,表名必须和HBase表名一致即可。 +create 'stu' ,'cf1','cf2' +put 'stu', 'key1','cf1:name','luozhao' +put 'stu', 'key1','cf1:sex','man' +put 'stu', 'key1','cf2:age','24' +put 'stu', 'key1','cf2:adress','cqupt' + + +create table "stu" ( +id VARCHAR NOT NULL PRIMARY KEY , +"cf1"."name" VARCHAR , +"cf1"."sex" VARCHAR , +"cf2"."age" VARCHAR , +"cf2"."adress" VARCHAR ); +upsert into "stu"(id,"cf1"."name","cf1"."sex","cf2"."age","cf2"."adress") values('key6','zkk','man','111','Beijing'); + + +select * from "stu";会发现两张表是数据同步的。 +这里查询表名需要用双引号括起来,强制不转换为大写。 +这里一定要注意的是表名和列族以及列名需要用**双引号**括起来,因为HBase是区分大小写的, +如果不用双引号括起来的话Phoenix在创建表的时候会自动将小写转换为大写字母 diff --git "a/hadoop/phoenix/phoenix\345\237\272\346\234\254\350\257\255\346\263\225.md" "b/hadoop/phoenix/phoenix\345\237\272\346\234\254\350\257\255\346\263\225.md" new file mode 100644 index 0000000..1a8a24b --- /dev/null +++ "b/hadoop/phoenix/phoenix\345\237\272\346\234\254\350\257\255\346\263\225.md" @@ -0,0 +1,109 @@ +### 基本操作 + +!table查看表信息 +!describe tablename可以查看表字段信息 +!history可以查看执行的历史SQL +!dbinfo +!index tb;查看tb的索引 +help查看其他操作 + +0.创建表 +```sql +create table tb (stat varchar not null primary key, city varchar, num varchar) +``` + +1.插入数据 +```sql +upsert into tb values('ak','hhh',222) +upsert into tb(stat,city,num) values('ak','hhh',222) + +upsert into tb1 (state,city,population) select state,city,population from tb2 where population < 40000; +upsert into tb1 select state,city,population from tb2 where population > 40000; +upsert into tb1 select * from tb2 where population > 40000; +``` + +2.删除数据 +```sql +delete from tb; -- 清空表中所有记录,Phoenix中不能使用truncate table tb; +delete from tb where city = 'kenai'; +drop table tb; -- 删除表 +delete from system.catalog where table_name = 'int_s6a'; +drop table if exists tb; +drop table my_schema.tb; +drop table my_schema.tb cascade; -- 用于删除表的同时删除基于该表的所有视图。 +``` + +3.更新数据 +```sql +-- 由于HBase的主键设计,相同rowkey的内容可以直接覆盖,这就变相的更新了数据。 +-- 所以Phoenix的更新操作仍旧是upsert into 和 upsert select +-- 示例中ak为主键 +upsert into us_population (state,city,population) values('ak','juneau',40711); +``` + +4.查询数据 +```sql +-- union all, group by, order by, limit 都支持 +select * from test limit 1000; +select * from test limit 1000 offset 100; +select full_name from sales_person where ranking >= 5.0 union all select reviewer_name from customer_review where score >= 8.0 +``` +5.创建表 +**A.SALT_BUCKETS(加盐)** +加盐Salting能够通过预分区(pre-splitting)数据到多个region中来显著提升读写性能。 +本质是在hbase中,rowkey的byte数组的第一个字节位置设定一个系统生成的byte值, +这个byte值是由主键生成rowkey的byte数组做一个哈希算法,计算得来的。 +Salting之后可以把数据分布到不同的region上,这样有利于phoenix并发的读写操作。 + + +SALT_BUCKETS的值范围在(1 ~ 256): +```sql +create table test(host varchar not null primary key, description varchar)salt_buckets=16; + +upsert into test (host,description) values ('192.168.0.1','s1'); +upsert into test (host,description) values ('192.168.0.2','s2'); +upsert into test (host,description) values ('192.168.0.3','s3'); +``` + +salted table可以自动在每一个rowkey前面加上一个字节,这样对于一段连续的rowkeys,它们在表中实际存储时,就被自动地分布到不同的region中去了。 +当指定要读写该段区间内的数据时,也就避免了读写操作都集中在同一个region上。 +简而言之,如果我们用Phoenix创建了一个saltedtable,那么向该表中写入数据时, +原始的rowkey的前面会被自动地加上一个byte(不同的rowkey会被分配不同的byte),使得连续的rowkeys也能被均匀地分布到多个regions。 + + +**B.Pre-split(预分区)** +Salting能够自动的设置表预分区,但是你得去控制表是如何分区的, +所以在建phoenix表时,可以精确的指定要根据什么值来做预分区,比如: +```sql +create table test (host varchar not null primary key, description varchar) split on ('cs','eu','na'); +``` + +**C.使用多列族** +列族包含相关的数据都在独立的文件中,在Phoenix设置多个列族可以提高查询性能。 +创建两个列族: +```sql +create table test ( + mykey varchar not null primary key, + a.col1 varchar, + a.col2 varchar, + b.col3 varchar +); +upsert into test values ('key1','a1','b1','c1'); +upsert into test values ('key2','a2','b2','c2'); +``` + +D.使用压缩 +```sql +create table test (host varchar not null primary key, description varchar) compression='snappy'; +``` + +6.对列操作 +``` +ALTER TABLE my_schema.my_table ADD d.dept_id char(10) VERSIONS=10 +ALTER TABLE my_table ADD dept_name char(50) +ALTER TABLE my_table ADD parent_id char(15) null primary key +ALTER TABLE my_table DROP COLUMN d.dept_id +ALTER TABLE my_table DROP COLUMN dept_name +ALTER TABLE my_table DROP COLUMN parent_id +ALTER TABLE my_table SET IMMUTABLE_ROWS=true +``` diff --git "a/hadoop/phoenix\345\256\211\350\243\205.md" "b/hadoop/phoenix/phoenix\345\256\211\350\243\205.md" similarity index 100% rename from "hadoop/phoenix\345\256\211\350\243\205.md" rename to "hadoop/phoenix/phoenix\345\256\211\350\243\205.md" diff --git "a/mq/rabbitmq/mac\344\270\212\345\256\211\350\243\205.md" "b/mq/rabbitmq/mac\344\270\212\345\256\211\350\243\205.md" new file mode 100644 index 0000000..780ed7a --- /dev/null +++ "b/mq/rabbitmq/mac\344\270\212\345\256\211\350\243\205.md" @@ -0,0 +1,30 @@ +### 下载 + +(1) 下载页面 +`http://www.rabbitmq.com/install-standalone-mac.html` + +(2) 下载地址`https://github.com/rabbitmq/rabbitmq-server/releases/download/v3.7.7/rabbitmq-server-mac-standalone-3.7.7.tar.xz` + +### 安装 +``` +tar -xvzf rabbitmq-server-mac-standalone-3.7.7.tar.xz +mv rabbitmq_server-3.7.7 rabbitmq +mv rabbitmq /usr/local +``` + +### 启动 +```sh +# 启动之后,使用CMD+C依然不能kill掉MQ,需要执行ps -ef | grep rabbitmq +# 使用http://localhost:15672可访问页面, 默认的用户guest,密码guest +# guest这个默认的用户只能通过http://localhost:15672 来登录,处于安全的考虑,其他的IP无法直接使用这个账号 +./sbin/rabbitmq-server start + +``` + +### 问题 +启动RabbitMQ后,没法访问Web管理页面,需要执行下面的安装命令才行 +```sh +rabbitmqctl start_app +rabbitmq-plugins enable rabbitmq_management +rabbitmqctl stop +``` diff --git "a/mq/rabbitmq/\345\244\207\345\277\230.md" "b/mq/rabbitmq/\345\244\207\345\277\230.md" new file mode 100644 index 0000000..9c2680f --- /dev/null +++ "b/mq/rabbitmq/\345\244\207\345\277\230.md" @@ -0,0 +1,71 @@ + +``` go +func (ch *Channel) QueueDeclare(name string, durable, autoDelete, exclusive, noWait bool, args Table) (Queue, error) {} +``` +**方法作用:** +声明一个队列 +**参数:name** +含义:队列名称 +**参数:durable** +含义:是否持久化,如果设置为true,服务器重启了队列仍然存在 +**参数:autoDelete** +含义:当没有任何消费者使用时,自动删除该队列 +**参数:exclusive** +含义:是否为独享队列(排他性队列),只有自己可见的队列,即不允许其它用户访问 +如果exclusive声明为true,则该队列的特点是: +1、只对首次声明它的连接(Connection)可见 +2、会在其连接断开的时候自动删除。 +备注: + +如果`channel`类型设置为`direct`, 需要满足发送一条消息, 只能被一个终端读取, 则需要设置`exclusive`为`false`是队列不为排他性质的 +``` go +conn, _ := amqp.Dial("amqp://guest:guest@localhost:5672/") +ch, _:= conn.Channel() + +ch.ExchangeDeclare( + "logs_direct", // name + "direct", // type + true, // durable + false, // auto-deleted + false, // internal + false, // no-wait + nil, // arguments +) +qName := "qName" +// 同时需要固定qName和设置exclusive=false才能实现, 多个终端读取一条消息的场景, 也就是入门示例2的场景 +// 即https://github.com/rabbitmq/rabbitmq-tutorials/tree/master/go +q, _ := ch.QueueDeclare( + qName, // name + false, // durable + false, // delete when unused + false, // exclusive + false, // no-wait + nil, // arguments +) +``` + +``` go +func (ch *Channel) Qos(prefetchCount, prefetchSize int, global bool) error {} +``` +**prefetchCount**: 一次获取多少条信息 + +``` go +// queue 队列的名称 +// consumer +// autoAck 是否开启自动应答;默认为true, 表示开启, false表示不开启 +// +func (ch *Channel) Consume(queue, consumer string, autoAck, exclusive, noLocal, noWait bool, args Table) (<-chan Delivery, error) {} +``` + +为了确保消息一定被消费者处理,rabbitMQ提供了消息确认功能,就是在消费者处理完任务之后,就给服务器一个回馈,服务器就会将该消息删除, +如果消费者超时不回馈,那么服务器将就将该消息重新发送给其他消费者, +当autoAck设置为true时,只要消息被消费者处理,不管成功与否,服务器都会删除该消息, +而当autoAck设置为false时,只有消息被处理,且反馈结果后才会删除。 + +```go +// 每次处理完成一个消息后,手动向服务端发送一次应答。 +func (d Delivery) Ack(multiple bool) error {} +``` +`d.deliveryTag`为当前消息的类似编号的号码,服务端为每一个消息生成的类似编号的号码 +`multiple` 是否把小于当前`d.deliveryTag`的小于都应答, true表示应答小于当前所有消息, false表示只应答当前编号消息 + diff --git a/rn/readme.md b/rn/readme.md new file mode 100644 index 0000000..06f2e35 --- /dev/null +++ b/rn/readme.md @@ -0,0 +1,4 @@ +### 关于react native入门基本命令 +主网 [https://reactnative.cn/](https://reactnative.cn/) + +初始化项目 `react-native init SampleAppMovies` diff --git a/shell/11.netstat.md b/shell/11.netstat.md new file mode 100644 index 0000000..e69de29 diff --git a/shell/12.ps.md b/shell/12.ps.md new file mode 100644 index 0000000..e69de29 diff --git a/shell/13.free.md b/shell/13.free.md new file mode 100644 index 0000000..e69de29