第一期学习总结

版权声明:本文为博主原创文章,转载请标明原文地址,谢谢 ^_^ https://blog.csdn.net/xiaoquantouer/article/details/83450670

在工作的过程中,不断听到新名词,新概念,新方法。觉得是时候总结一波知识点记录下来,促进深入了解的同时,便于以后复习。非常喜欢阮老师的每周分享,但是做不到像他一样每周去浏览那么多东西,而且总能发现很有趣的知识点。现在期望可以做到双月一总结,有些知识点可能还是没有那么深入理解,希望能坚持下来,在工作的同时慢慢进步,也是相当不错了。原本于国庆前就有此计划,并写好了初稿,中间各种原因,又推迟了一个月。= =

1、什么是kafka rebalance?

在Consumer Group中的若干Consumer,会按照一定规则(均匀)分配同一个topic下的所有partition,各自在相应partition上执行sub工作。当一个Group中有Consumer退出时,Group中的其他Consumer会对topic下的partition进行重新分配,并基于重新分配的结果继续执行sub工作。这个重新分配的过程被称为Rebalance。

分配算法如下:

1. 将目标 topic 下的所有 partirtion 排序,存于PT
2. 对某 consumer group 下所有 consumer 排序,存于 CG,第 i 个consumer 记为 Ci
3. N=size(PT)/size(CG),向上取整
4. 解除 Ci 对原来分配的 partition 的消费权(i从0开始)
5. 将第i*N到(i+1)*N-1个 partition 分配给 Ci

参考文档:

(1)如何利用磁盘顺序读写快于内存随机读写这一现象?

https://www.zhihu.com/question/48794778

(2)kafka之Group状态变化分析及Rebalance过程

http://matt33.com/2017/01/16/kafka-group/

(3)Kafka Consumer Rebalance的演进

http://lday.me/2017/07/24/0011_kafka_consumer_rebalance_evolution/

2、Mock功能

经常听到在测试的时候说mock一下,什么是mock?目前一个产品开发都是分前后端,并行开发的时候有可能后端先做完了,然而前端还没有做完,联调前需要自己做测试。有些接口无法得到响应回来的数据,功能没法测怎么办?所谓的mock就是指根据事先定义好的接口信息如协议、URL、接口名、请求头、请求参数、返回数据等对接口自动生成模拟数据。创建者可以自由构造需要的数据进行测试。

     一句话总结:mock(模拟)是在项目测试中,对项目外部或不容易获取的对象/接口,用一个虚拟的对象/接口来模拟,以便测试。

参考文档:

(1)为什么你需要一个mock server

https://juejin.im/entry/57bd37c2c4c9710061606b38

 

3、JSON Schema

JSON Schema(JSON模式),是一种基于JSON格式定义JSON数据结构的规范。目的:

(1)描述现有数据格式

(2)干净的人类和机器可读的文档

(3)完整的结构验证,有利于自动化测试,可用于验证客户端提交的数据

举个栗子:

我们现在有JSON数据(1),现在需要用JSON数据(2)来描述(1)的结构。这时的JSON数据(2)就是JSON数据(1)的JSON Schema,用来描述并且验证JSON数据(1)

JSON数据(1):

{
    "name": "sunwukong",
    "age": 24,
    "gender": "male"
}

JSON数据(2):

{
    "type": "object",
    "properties": {
        "name": {
            "type": "string",
            "minLength": 4
        },
        "age": {
            "type": "integer",
            "minimum": 0,
            "maximum": 130
        },
        "gender": {
            "type": "string",
            "enum": [
                "male",
                "female"
            ]
        }
    }
}

由JSON数据(2)我们可以知道JSON数据(1):

  • 是一个json对象,有三个属性:name、age和gender
  • name属性的值类型是一个字符串,最小长度4
  • age属性的值类型是一个整形,最小值是0,最大值是130
  • gender属性的值类型是一个字符串,只能取"male"或者"female"

参考文档:

(1)JSON Schema参考书

http://imweb.io/topic/57b5f69373ac222929653f23

(2)JSON Schema快速入门

https://www.jianshu.com/p/8278eb2458c4

 

4、传值还是传指针?

在实际开发中,前人写的函数传入的都是指针,特别是在struct中喜欢用指针。一直不理解为什么,直到要自己去定义一些变量和结构时,觉得是时候深入理解一下。

两篇参考文档已经写的非常详细了。总结一下:Go里面所有传参都是传值,都是一个副本,一个拷贝。如果传入的是一个值,那么就是传入了值的拷贝,如果传入的是一个指针,那么就是传入了指针的拷贝。因为拷贝的内容有时候是非引用类型(int、string、struct等这些),这样就在函数中无法修改原内容数据。有的是引用类型(指针、map、slice、chan等这些),这样就可以修改原内容数据。

是否可以修改原内容数据,和传值、传引用没有必然的关系。在c++中,传引用肯定是可以修改原内容数据的,在Go语言里,虽然只有传值,但是我们也可以修改原内容数据,因为参数是引用类型。注意:引用类型和传引用是两个概念。Go里只有传值(值传递)

参考文档:

(1)全面分析Go语言中的类型和类型指针的抉择

https://colobu.com/2017/01/05/-T-or-T-it-s-a-question/

(2)Go语言参数传递是传值还是传引用

http://www.flysnow.org/2018/02/24/golang-function-parameters-passed-by-value.html

5、golang的内存分配

关于栈区和堆区的区别,以c++为例一般认为

栈区:由编译器自动分配释放,存放函数的参数值,局部变量的值等。

堆区:一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。比如new,malloc等申请的内存就在堆区。

在栈区分配的代价要远小于在堆上进行分配。因为有垃圾回收机制。所以要避免内存逃逸。

内存逃逸:原本在栈上分配内存的对象,逃逸到了堆上进行分配。如果无法在编译期确定变量的作用域和占用内存大小,则会逃逸到堆上。

(1)指针

在函数传参时,一般都认为传递指针可以减少底层值的拷贝,提高效率。一般情况下亦是如此,但是如果拷贝的是少量的数据,那么传递指针效率不一定会高于值拷贝。指针是间接访址,所指向的地址大多保存在堆上,因此考虑到GC,指针不一定是高效的。

(2)切片

如果编译期无法确定切片的大小或者切片大小过大,超出栈大小限制,或者在append时候会导致重新分配内存,这时候很可能会分配到堆上。

(3)interface

参考文档

(1)深入理解GO语言之内存详解

https://juejin.im/post/59f2e19f5188253d6816d504

6、GC机制

GC是指垃圾回收。有三种经典的GC算法:引用计数(reference counting)、标记-清扫(mark & sweep)、节点复制(Copying Garbage Collection),分代收集(Generational Garbage Collection)。文档(2)中有每一步的图文解说。

参考文档:

(1)Golang垃圾回收剖析

http://legendtkl.com/2017/04/28/golang-gc/

(2)Go语言的实时GC-理论与实践

https://segmentfault.com/a/1190000010753702

7、innodb类的数据库的删除是软删除

就是说会有一个标志位表示当前数据已被删除,但是不会真的删除。这样数据只会越来越多,所以最好的做法是更新,而不是删除。

8、MYSQL prepare预编译

prepare语法:

MySQL prepare语法:
PREPARE statement_name FROM preparable_SQL_statement;  /*定义*/
EXECUTE statement_name [USING @var_name...];   /*执行预处理语句*/
{DEALLOCATE | DROP} PREPARE statement_name;  /*删除定义*/

PREPARE语句用于预备一个语句,并指定名称statement_name,以后引用该语句。语句名称对大小写不敏感。

例子:

select * from t_test where id=1 or 1=1;
-- 正常查询
prepare sel from 'select * from t_test where value = ?';
set @v1='abc';
execute sel using @v1;
-- 注入
set @v1='abc' or 1=1;
execute sel using @v1;
-- 后台查询查询语句变成 select * from t_test where value = 1

通常情况下,一条sql语句在db接收到最终执行完毕返回可以分为三步:

(1)语法和语义解析

(2)优化sql语句,制定执行计划

(3)执行并返回结果

有时候,一条sql需要反复执行,或者只是个别的值不同(比如update的set子句值不同)。如果每次都需要经过上面的语法语义解析、语句优化、制定执行计划等。则效率就明显不同了。

所谓预编译语句是指将这类语句中的值用占位符替代,可以视为将sql语句模板化或者说参数化,一般称这类语句叫Prepared Statements或者Parameterized Statements。

预编译语句优点:一次编译、多次运行、省去了解析优化等过程,此外预编译语句能防止sql注入。

参考文档:

(1)预编译语句(Prepared Statements)介绍,以MySQL为例

https://www.cnblogs.com/micrari/p/7112781.html

(2)mysql预编译-Prepared-Statements

http://yihengliu.com/2018/06/04/mysql%E9%A2%84%E7%BC%96%E8%AF%91-Prepared-Statements/

9、slice和数组(Array)的区别

例子:

a := []int{1}  //[]中没有固定长度的就是slice
b := [2]int{1, 2} //固定了长度的是数组array

Array是值类型,固定长度,大小是类型的一部分,当一个数组变量被赋值或者被传递的时候,实际上会复制整个数组。而Slice在新元素加入的时候可以增加长度(增长到容量的上限)。array的长度也是Type的一部分,这样就说明[10]int和[20]int是不一样的。

Slice一个切片是一个数组片段的描述。它包含了指向数组的指针,片段的长度和容量(底层数组的长度)。slice是一个引用类型,是一个动态的指向数组切片的指针,是一个不定长的,总是指向底层的数组array的数据结构。

参考文档:

(1)golang slice和array区别

https://segmentfault.com/a/1190000013148775

10、pb数据格式

pb(Protocol Buffers)是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。很适合做数据存储或RPC数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。

pb提供了一种灵活,高效,自动序列化结构数据的机制,可以联想XML,但是比XML更小,更快,更简单。仅需要自定义一次你所需的数据格式,然后用户就可以使用Protocol Buffers自动生成的特定的源码,方便的读写用户自定义的格式化的数据。不限语言,不限平台。还可以在不破坏元数据格式的基础上,依据老的数据格式,更新现有的数据格式。

11、GO语言中for循环里的变量地址复用

for _, op := range ops中op的地址是复用的

例子:

package main

import(
   "fmt"
)

type data struct{
   age int
   name string
}

func main() {
   testStrs := []string{"hello", "world", "yes", "no"}

   for index, s := range testStrs{
      fmt.Println("index:", index)
      fmt.Println("打印s的地址", &s)
      fmt.Println("s的值", s)
   }

   for i:=0;i<len(testStrs);i++{
      fmt.Println("i:", i)
      fmt.Println("打印testStrs[i]的地址:", &testStrs[i])
      fmt.Println("testStrs[i]的值:", testStrs[i])
   }

}

打印结果:

index: 0
打印s的地址 0xc42000e1e0
s的值 hello
index: 1
打印s的地址 0xc42000e1e0
s的值 world
index: 2
打印s的地址 0xc42000e1e0
s的值 yes
index: 3
打印s的地址 0xc42000e1e0
s的值 no
i: 0
打印testStrs[i]的地址: 0xc4200560c0
testStrs[i]的值: hello
i: 1
打印testStrs[i]的地址: 0xc4200560d0
testStrs[i]的值: world
i: 2
打印testStrs[i]的地址: 0xc4200560e0
testStrs[i]的值: yes
i: 3
打印testStrs[i]的地址: 0xc4200560f0
testStrs[i]的值: no

12、Go语言中多个Recover存在时会怎么样?

例子:

package main

import(
   "fmt"
)

func main(){
   defer func(){
      //第一层defer
      fmt.Println("进入第一层defer")

      if err := recover(); err != nil {
         fmt.Println("进入第一层的recover逻辑中")
         fmt.Printf("%s\n", err)
      }
   }()

   a := 1
   b := 2

   defer func(){
      //第二层defer
      fmt.Println("进入第二层defer")

      if err := recover(); err != nil {
         fmt.Println("进入第二层的recover逻辑中")
         fmt.Printf("%s\n", err)
      }
   }()

   fmt.Println(a, b)
   panic("出错啦")
}

打印结果:

1 2
进入第二层defer
进入第二层的recover逻辑中
出错啦
进入第一层defer

猜你喜欢

转载自blog.csdn.net/xiaoquantouer/article/details/83450670