在工作的过程中,不断听到新名词,新概念,新方法。觉得是时候总结一波知识点记录下来,促进深入了解的同时,便于以后复习。非常喜欢阮老师的每周分享,但是做不到像他一样每周去浏览那么多东西,而且总能发现很有趣的知识点。现在期望可以做到双月一总结,有些知识点可能还是没有那么深入理解,希望能坚持下来,在工作的同时慢慢进步,也是相当不错了。原本于国庆前就有此计划,并写好了初稿,中间各种原因,又推迟了一个月。= =
在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/
经常听到在测试的时候说mock一下,什么是mock?目前一个产品开发都是分前后端,并行开发的时候有可能后端先做完了,然而前端还没有做完,联调前需要自己做测试。有些接口无法得到响应回来的数据,功能没法测怎么办?所谓的mock就是指根据事先定义好的接口信息如协议、URL、接口名、请求头、请求参数、返回数据等对接口自动生成模拟数据。创建者可以自由构造需要的数据进行测试。
一句话总结:mock(模拟)是在项目测试中,对项目外部或不容易获取的对象/接口,用一个虚拟的对象/接口来模拟,以便测试。
参考文档:
(1)为什么你需要一个mock server
https://juejin.im/entry/57bd37c2c4c9710061606b38
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):
参考文档:
(1)JSON Schema参考书
http://imweb.io/topic/57b5f69373ac222929653f23
(2)JSON Schema快速入门
https://www.jianshu.com/p/8278eb2458c4
在实际开发中,前人写的函数传入的都是指针,特别是在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
关于栈区和堆区的区别,以c++为例一般认为
栈区:由编译器自动分配释放,存放函数的参数值,局部变量的值等。
堆区:一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。比如new,malloc等申请的内存就在堆区。
在栈区分配的代价要远小于在堆上进行分配。因为有垃圾回收机制。所以要避免内存逃逸。
内存逃逸:原本在栈上分配内存的对象,逃逸到了堆上进行分配。如果无法在编译期确定变量的作用域和占用内存大小,则会逃逸到堆上。
(1)指针
在函数传参时,一般都认为传递指针可以减少底层值的拷贝,提高效率。一般情况下亦是如此,但是如果拷贝的是少量的数据,那么传递指针效率不一定会高于值拷贝。指针是间接访址,所指向的地址大多保存在堆上,因此考虑到GC,指针不一定是高效的。
(2)切片
如果编译期无法确定切片的大小或者切片大小过大,超出栈大小限制,或者在append时候会导致重新分配内存,这时候很可能会分配到堆上。
(3)interface
参考文档
(1)深入理解GO语言之内存详解
https://juejin.im/post/59f2e19f5188253d6816d504
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
就是说会有一个标志位表示当前数据已被删除,但是不会真的删除。这样数据只会越来越多,所以最好的做法是更新,而不是删除。
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/
例子:
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
pb(Protocol Buffers)是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。很适合做数据存储或RPC数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。
pb提供了一种灵活,高效,自动序列化结构数据的机制,可以联想XML,但是比XML更小,更快,更简单。仅需要自定义一次你所需的数据格式,然后用户就可以使用Protocol Buffers自动生成的特定的源码,方便的读写用户自定义的格式化的数据。不限语言,不限平台。还可以在不破坏元数据格式的基础上,依据老的数据格式,更新现有的数据格式。
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
打印结果:
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
例子:
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