wget 下载文件重命名
2017-08-30 tech linux 1 mins 37 字
wget -c "www.baidu.com" -O baidu.html
wget -c "www.baidu.com" -O baidu.html
go 的复合类型包括:
// 数组访问索引从0开始 var variable_name [SIZE] variable_type // 声明 var balance = [5] float32 {1000.0, 2.0, 3.4, 7.0, 50.0} // 初始化 var balance = […] float32 {1000.0, 2.0, 3.4, 7.0, 50.0} // 与上个语句效果相同
多维数组
var threedim [5][10][4]int
a = [3][4]int{
{0, 1, 2, 3} , /* 第一行索引为 0 */
{4, 5, 6, 7} , /* 第二行索引为 1 */
{8, 9, 10, 11} /* 第三行索引为 2 */
}
var var_name *var-type
var ip *int /* 指向整型*/
var fp *float32 /* 指向浮点型 */
var a int= 20 /* 声明实际变量 */
var ip *int /* 声明指针变量 */
ip = &a /* 指针变量的存储地址 */
当一个指针被定义后没有分配到任何变量时,它的值为 nil。nil 指针也称为空指针。
type Books struct {
title string
author string
subject string
book_id int
}
var Book1 Books /* 声明 Book1 为 Books 类型 */
/* book 1 描述 */ Book1.title = “Go 语言” Book1.author = “www.runoob.com” Book1.subject = “Go 语言教程” Book1.book_id = 6495407
切片是对数组的抽象。
Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go中提供了一种灵活,功能强悍的内置类型切片(“动态数组”),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。
var xxx []type // 声明,值为 nil,是空切片
或使用make()函数来创建切片,其中capacity为可选参数,说明切片可达到的最大数。
make([]type, length, capacity)
// 初始化
s :=[] int {1,2,3 }
s := arr[:] // 初始化切片s,是数组arr的引用
s := arr[startIndex:endIndex] // 从下标startIndex到endIndex-1 下的元素创建为一个新的切片
s1 := s[startIndex:endIndex] 通过切片s初始化切片s1
s :=make([]int,len,cap) 通过内置函数make()初始化切片s,[]int 标识为其元素类型为int的切片
切片可以由 len() 方法获取长度。由 cap() 测量切片最长可以达到多少。
可以通过设置下限、上限来设置截取切片 [lower-bound:upper-bound]
/* 同时添加多个元素 */
numbers = append(numbers, 2,3,4)
/* 拷贝 numbers 的内容到 numbers1 */
copy(numbers1,numbers)
Map 是一种无序的键值对的集合,使用 hash 表来实现的。
/* 声明变量,默认 map 是 nil */
var xxx map[key_data_type]value_data_type
/* 使用 make 函数 */
xxx := make(map[key_data_type]value_data_type)
/* 创建 map */
countryCapitalMap :=
map[string] string {"France":"Paris","Italy":"Rome","Japan":"Tokyo","India":"New Delhi"}
/* 删除元素 */
delete(countryCapitalMap,"France");
一个标准 Go 语句如下:
fmt.Println("Hello, World!")
Go 代码中会使用到的 25 个关键字或保留字:
36 个预定义标识符:
本部分的内容基本来自 《Go 语言设计与实现》 - draveness.me ,这里只是一些关键内容摘录,为了保持上下文内容连贯,还是推荐查看原文。
布尔型 常量 true 或者 false。一个简单的例子:var b bool = true
数字类型 整型 int 和浮点型 float,并且原生支持复数,其中位的运算采用补码。更详细的信息看文章末尾。
字符串类型 使用UTF-8编码标识Unicode文本
派生类型:
arr1 := [3]int{1, 2, 3}
arr2 := [...]int{1, 2, 3}
字面量初始化方式,元素的个数小于或者等于四个时,在编译时会转换成如下命令:
var arr [3]int
arr[0] = 1
arr[1] = 2
arr[2] = 3
当前数组的元素大于四个,则会:
var arr [5]int
statictmp_0[0] = 1
statictmp_0[1] = 2
statictmp_0[2] = 3
statictmp_0[3] = 4
statictmp_0[4] = 5
arr = statictmp_0
数组在 Go 语言中没那么常用,更常用的数据结构是切片,即动态数组。
[]int
[]interface{}
切片与数组的关系非常密切。切片引入了一个抽象层,提供了对数组中部分连续片段的引用,而作为数组的引用,我们可以在运行区间可以修改它的长度和范围。当切片底层的数组长度不足时就会触发扩容,切片指向的数组可能会发生变化,不过在上层看来切片是没有变化的.
Go 语言中包含三种初始化切片的方式:
make
创建切片(运行时处理相对多):如果使用字面量的方式创建切片,大部分的工作都会在编译期间完成。但是当我们使用 make
关键字创建切片时,很多工作都需要运行时的参与。
arr[0:3] or slice[0:3]
slice := []int{1, 2, 3}
slice := make([]int, 10)
使用 len
和 cap
获取长度、容量是切片最常见的操作,编译器将这它们看成两种特殊操作,即 OLEN
和 OCAP
。
切片的长度是它所包含的元素个数。 切片的容量是从它的第一个元素开始数,到其底层数组元素末尾的个数。
切片的操作(init、查、改、长度、容量)基本都是在编译期间完成的,编译期间也会将包含 range
关键字的遍历转换成形式更简单的循环。
切片追加和扩容时,会先解构切片结构体获取它的数组指针、大小和容量,切片容量足够时向切片中追加元素,切片容量不足时会调用 runtime.growslice
对切片进行扩容,扩容根据切片的当前容量选择不同的策略:
确定了切片的大致容量,此外还需要根据切片中的元素大小对齐内存,runtime.roundupsize
函数会将待申请的内存向上取整,取整时会使用 runtime.class_to_size
数组(以提高内存的分配效率并减少碎片)
var arr []int64
arr = append(arr, 1, 2, 3, 4, 5)
简单总结一下扩容的过程,当我们执行上述代码时,会触发 runtime.growslice
函数扩容 arr
切片并传入期望的新容量 5,这时期望分配的内存大小为 40 字节;不过因为切片中的元素大小等于 sys.PtrSize
,所以运行时会调用 runtime.roundupsize
向上取整内存的大小到 48 字节,所以新切片的容量为 48 / 8 = 6。
如果当前 copy
不是在运行时调用的, runtime.memmove
会负责拷贝内存。
而如果拷贝是在运行时发生的,例如:go copy(a, b)
,编译器会使用 runtime.slicecopy
替换运行期间调用的 copy
,再通过 runtime.memmove
将整块内存的内容拷贝到目标的内存区域中。
相比于依次拷贝元素,runtime.memmove
能够提供更好的性能。需要注意的是,整块拷贝内存仍然会占用非常多的资源,在大切片上执行拷贝操作时一定要注意对性能的影响。
哈希表/map是除了数组之外,最常见的数据结构。几乎所有的语言都会有数组和哈希表两种集合元素,有的语言将数组实现成列表,而有的语言将哈希称作字典或者映射。无论如何命名或者如何实现,数组和哈希是两种设计集合元素的思路,数组用于表示元素的序列,而哈希表示的是键值对之间映射关系。
所谓哈希(hash),就是将不同的输入映射成独一无二的、固定长度的值(又称”哈希值”)。如果不同的输入得到了同一个哈希值,就发生了”哈希碰撞”(collision)。
黑客攻击的一种方法,就是设法制造”哈希碰撞”,然后入侵系统,窃取信息。防止哈希碰撞的最有效方法,就是扩大哈希值的取值空间。
16个二进制位的哈希值,产生碰撞的可能性是 65536 分之一。也就是说,如果有65537个用户,就一定会产生碰撞。哈希值的长度扩大到32个二进制位,碰撞的可能性就会下降到 4,294,967,296 分之一。
更长的哈希值意味着更大的存储空间、更多的计算,将影响性能和成本。开发者必须做出抉择,在安全与成本之间找到平衡。
哈希表是计算机科学中的最重要数据结构之一,这不仅因为它 O(1)O(1) 的读写性能非常优秀,还因为它提供了键值之间的映射。想要实现一个性能优异的哈希表,需要注意两个关键点 —— 哈希函数和冲突解决方法。
实现哈希表的关键点在于哈希函数的选择,哈希函数的选择在很大程度上能够决定哈希表的读写性能。在理想情况下,哈希函数应该能够将不同键映射到不同的索引上,这要求哈希函数的输出范围大于输入范围,但是由于键的数量会远远大于映射的范围,所以在实际使用时,这个理想的效果是不可能实现的。
如果使用结果分布较为均匀的哈希函数,那么哈希的增删改查的时间复杂度为 O(1);但是如果哈希函数的结果分布不均匀,那么所有操作的时间复杂度可能会达到 O(n),由此看来,使用好的哈希函数是至关重要的。
比较实际的方式是让哈希函数的结果能够尽可能的均匀分布,然后通过工程上的手段解决哈希碰撞的问题。
开放寻址法 #
开放寻址法中对性能影响最大的是装载因子,它是数组中元素的数量与数组大小的比值。随着装载因子的增加,线性探测的平均用时就会逐渐增加,这会影响哈希表的读写性能。当装载率超过 70% 之后,哈希表的性能就会急剧下降,而一旦装载率达到 100%,整个哈希表就会完全失效,这时查找和插入任意元素的时间复杂度都是 O(n)O(n) 的,这时需要遍历数组中的全部元素,所以在实现哈希表时一定要关注装载因子的变化。
拉链法 #
拉链法是哈希表最常见的实现方法,大多数的编程语言都用拉链法实现哈希表,它的实现比较开放地址法稍微复杂一些,但是平均查找的长度也比较短,各个用于存储节点的内存都是动态申请的,可以节省比较多的存储空间。
实现拉链法一般会使用数组加上链表,不过一些编程语言会在拉链法的哈希中引入红黑树以优化性能,拉链法会使用链表数组作为哈希底层的数据结构,我们可以将它看成可以扩展的二维数组
哈希表可能会在装载因子过高或者溢出桶过多时进行扩容,哈希表扩容并不是原子过程,在扩容的过程中保证哈希的访问是比较有意思的话题.
Go 语言使用拉链法来解决哈希碰撞的问题实现了哈希表,它的访问、写入和删除等操作都在编译期间转换成了运行时的函数或者方法。哈希在每一个桶中存储键对应哈希的前 8 位,当对哈希进行操作时,这些 tophash
就成为可以帮助哈希快速遍历桶中元素的缓存。
哈希表的每个桶都只能存储 8 个键值对,一旦当前哈希的某个桶超出 8 个,新的键值对就会存储到哈希的溢出桶中。随着键值对数量的增加,溢出桶的数量和哈希的装载因子也会逐渐升高,超过一定范围就会触发扩容,扩容会将桶的数量翻倍,元素再分配的过程也是在调用写操作时增量进行的,不会造成性能的瞬时巨大抖动。
Go 语言运行时同时使用了多个数据结构组合表示哈希表,其中 runtime.hmap
是最核心的结构体, runtime.hmap
的桶是 runtime.bmap
。每一个 runtime.bmap
都能存储 8 个键值对,当哈希表中存储的数据过多,单个桶已经装满时就会使用 extra.nextOverflow
中桶存储溢出的数据。
溢出桶是在 Go 语言还使用 C 语言实现时使用的设计,由于它能够减少扩容的频率所以一直使用至今。
随着哈希表存储的数据逐渐增多,我们会扩容哈希表或者使用额外的桶存储溢出的数据,不会让单个桶中的数据超过 8 个。
溢出桶也只是临时的解决方案,创建过多的溢出桶最终也会导致哈希的扩容。
runtime.mapassign
函数会在以下两种情况发生时触发哈希的扩容:
不过因为 Go 语言哈希的扩容不是一个原子的过程,所以 runtime.mapassign
还需要判断当前哈希是否已经处于扩容状态,避免二次扩容造成混乱。
字面量
hashMap := map[string]int{
"1": 2,
"3": 4,
"5": 6,
}
当哈希表中的元素数量少于或者等于 25 个时,编译器会将字面量初始化的结构体转换成以下的代码,将所有的键值对一次加入到哈希表中,初始化的方式与数组和切片几乎完全相同
hashMap := make(map[string]int, 3)
hashMap["1"] = 2
hashMap["3"] = 4
hashMap["5"] = 6
一旦哈希表中元素的数量超过了 25 个,编译器会创建两个数组分别存储键和值,这些键值对会通过如下所示的 for 循环加入哈希:
hashMap := make(map[string]int, 26)
vstatk := []string{"1", "2", "3", ... , "26"}
vstatv := []int{1, 2, 3, ... , 26}
for i := 0; i < len(vstak); i++ {
hashMap[vstatk[i]] = vstatv[i]
}
运行时
hashMap := make(map[string]int, 3)
当创建的哈希被分配到栈上并且其容量小于 BUCKETSIZE = 8
时,Go 语言在编译阶段会使用如下方式快速初始化哈希,这也是编译器对小容量的哈希做的优化:
var h *hmap
var hv hmap
var bv bmap
h := &hv
b := &bv
h.buckets = b
h.hash0 = fashtrand0()
除了上述特定的优化之外,无论 make
是从哪里来的,只要我们使用 make
创建哈希,Go 语言编译器都会在类型检查期间将它们转换成 runtime.makemap
,使用字面量初始化哈希也只是语言提供的辅助工具,最后调用的都是 runtime.makemap
_ = hashMap[key]
for k, v := range hashMap {
// k, v
}
hashMap[key] = value
hashMap[key] = newValue
delete(hashMap, key)
声明变量的一般形式是使用 var 关键字:
var identifier type
操作符 := 可以高效地创建一个新的变量,是使用变量的首选形式,但是只能被用在函数体内,不可以用于全局变量的声明与赋值。
var a int = 10 // 指定变量类型,声明后若不赋值,使用默认值。
var b = 10 // 根据值自行判定变量类型。
c : = 10 // 注意 :=左侧的变量不应该是已经声明过的,否则会导致编译错误。
多变量声明
//类型相同多个变量, 非全局变量
var vname1, vname2, vname3 type
vname1, vname2, vname3 = v1, v2, v3
var vname1, vname2, vname3 = v1, v2, v3
vname1, vname2, vname3 := v1, v2, v3
// 这种因式分解关键字的写法一般用于声明全局变量
var (
vname1 v_type1
vname2 v_type2
)
常量的定义格式:
const identifier [type] = value
多个相同类型的声明可以简写为:
const c_name1, c_name2 = value1, value2
常量还可以用作枚举:
const (
Unknown = 0
Female = 1
Male = 2
)
iota,特殊常量,可以认为是一个可以被编译器修改的常量。每当 iota 在新的一行被使用时,它的值都会自动加 1。
与 C/C++ 相当接近,不写了。速查手册
条件语句
if 布尔表达式 {
/* 在布尔表达式为 true 时执行 */
} else {
/* 在布尔表达式为 false 时执行 */
}
switch var1 {
case val1: ...
case val2: ...
default: ...
}
var c1, c2, c3 chan int
var i1, i2 int
select {
case i1 = <-c1:
fmt.Printf("received ", i1, " from c1\n")
case c2 <- i2:
fmt.Printf("sent ", i2, " to c2\n")
default:
fmt.Printf("no communication\n")
}
循环语句
package main
import "fmt"
func main() {
var b int = 15
var a int
numbers := [6]int{1, 2, 3, 5}
/* for 循环 */
for a := 0; a < 10; a++ {
fmt.Printf("a 的值为: %d\n", a)
}
for a < b {
a++
fmt.Printf("a 的值为: %d\n", a)
}
for i,x:= range numbers {
fmt.Printf("第 %d 位 x 的值 = %d\n", i,x)
}
}
整型:
序号 | 类型和描述 |
---|---|
1 | uint8 无符号 8 位整型 (0 到 255) |
2 | uint16 无符号 16 位整型 (0 到 65535) |
3 | uint32 无符号 32 位整型 (0 到 4294967295) |
4 | uint64 无符号 64 位整型 (0 到 18446744073709551615) |
5 | int8 有符号 8 位整型 (-128 到 127) |
6 | int16 有符号 16 位整型 (-32768 到 32767) |
7 | int32 有符号 32 位整型 (-2147483648 到 2147483647) |
8 | int64 有符号 64 位整型 (-9223372036854775808 到 9223372036854775807) |
浮点型:
序号 | 类型和描述 |
---|---|
1 | float32 IEEE-754 32位浮点型数 |
2 | float64 IEEE-754 64位浮点型数 |
3 | complex64 32 位实数和虚数 |
4 | complex128 64 位实数和虚数 |
其他数字类型
序号 | 类型和描述 |
---|---|
1 | byte 类似 uint8 |
2 | rune 类似 int32 |
3 | uint 32 或 64 位 |
4 | int 与 uint 一样大小 |
5 | uintptr 无符号整型,用于存放一个指针 |
最近买了台32寸小米电视放卧室,还没有拿来当写代码的显示器,我估计可能颗粒感会比较重吧。
不清楚是 Mac 的问题还是小米电视的问题,感觉不太稳定,有的时候搜不到,有的时候又没问题。姑且把软件放上来了。
SMBup下载链接: https://pan.baidu.com/s/1skAgCwd 密码: jr4w
本文转自 负载均衡、LVS概述
1. 负载均衡概述
负载均衡的基本思路是:在一个服务器集群中尽可能的平衡负载量。通常的做法是在服务器前端设置一个负载均衡器(一般是专门的硬件设备)。然后负载均衡器将请求的连接路由到最空闲的可用服务器。下图显示了一个典型的大型网站负载均衡设置,其中一个负载均衡器用于HTTP流量,另一个用于MySQL访问。
负载均衡有5个常见的目的。
(1)可扩展性:负载均衡对于某些扩展策略有所帮助,例如读写分离时从备库读数据。
(2)高效性:负载均衡有助于更有效的使用资源,因为它能够控制请求被路由到何处。
(3)可用性:一个灵活的负载均衡解决方案能够使用时刻保持可用的服务器。
(4)透明性:客户端无须知道是否存在负载均衡设置,也不需要关心在负载均衡器的背后有多少机器,名字是什么。负载均衡器给客户端看到的只是一个虚拟的服务器。
(5)一致性:如果应用是有状态的(数据库事务、网站会话等),那么负载均衡器就应将相关的查询指向同一个服务器,以防止数据丢失。应用无须去跟踪到底连接哪一个服务器。
2. 四层/七层负载均衡
服务器负载均衡,顾名思义就是对一组服务器提供负载均衡业务。这一组服务器一般来说都处于同一个局域网络内,并同时对外提供一组(或多组)相同(或相似)的服务。服务器负载均衡是数据中心最常见的组网模型。
服务器负载均衡分为四层(L4)服务器负载均衡和七层(L7)服务器负载均衡两种:
NAT方式L4服务器负载均衡的组网灵活,后端服务器可以位于不同的物理位置,不同的局域网内。NAT方式下,LB设备分发服务请求时,进行目的IP地址转换(目的IP地址为实服务的IP),通过路由将报文转发给各个实服务。NAT方式L4服务器负载均衡的典型组网如图所示。
嗯,最近一个项目需要用到 Go 语言开发。捡起来复习一下,于是出了这篇文章。最近几天还会继续总结,欢迎和我讨论。
Go 语言被设计成一门应用于搭载 Web 服务器,存储集群或类似用途的巨型中央服务器的系统编程语言。
对于高性能分布式系统领域而言,Go 语言无疑比大多数其它语言有着更高的开发效率。它提供了海量并行的支持,这对于游戏服务端的开发而言是再好不过了。
Go 语言支持以下系统:
安装包下载地址为:https://golang.org/dl/。
Windows 只要安装 msi 安装包即可。
安装包下载地址为:https://golang.org/dl/。
我用的是 Mac,直接下载 pkg 安装包即可。需要在系统路径里增加 go 的地址