基于令牌桶算法的Java限流实现
令牌桶算法是一种流量限制算法,通常用于限制对服务的请求速率。它通过维护一个桶来存储令牌来实现限流。每当有请求需要被处理时,都需要先从桶中获取一个令牌。如果桶中有可用令牌,请求就会被处理,并且令牌会被从桶中移除。如果桶中没有可用令牌,请求就会被拒绝或等待。
桶中的令牌是按固定的速率进行填充的,这个速率就是限流的速率。这种算法的优点是它可以平滑地处理请求,可以避免突发请求造成的服务崩溃。
令牌桶算法在多种场景下都有应用,如在网络流量控制、服务限流、网络防火墙等场景都可以应用这种算法。
首先,需要创建一个桶来存储令牌,这里可以使用 Java 的 Semaphore 类来实现。Semaphore 类是一个信号量类,可以用来控制线程的并发访问。
接下来,需要启动一个线程来不断地向桶中填充令牌。这里可以使用 Java 的 ScheduledExecutorService 来实现。ScheduledExecutorService 可以让你在给定的延迟之后或者在给定的间隔之后执行任务。
当请求需要被处理时,程序会从桶中尝试获取一个令牌。如果桶中有可用令牌,请求就会被处理,并且令牌会被从桶中移除。如果桶 ...
【Go编程基础】15-项目与坑
slice 与 append 的坑初始容量被超过了,它会新建一个slice,然后把旧的内容拷贝过去,然后append数据,这就会导致底层数组改变了,期望的结果可能就不是预期的了
12345678910111213func Pingpong(s []int) { s = append(s, 3) // 原来的容量为0,append之后,重新分配了内存地址,s跟原来的slice s已经不是同一个了}func main() { s := make([]int, 0) // 初始化slice s,初始容量为0,长度为0 fmt.Println(s) Pingpong(s) fmt.Println(s)}输出(两次打印的slice结果都是空):[][]
怎么解决这个问题?答案是增加返回值
1234567891011121314func Pingpong(s []int) []int { // 希望修改slice的,设置返回值,通过返回值去返回数据 s = append(s, 4) // 原来的容量为0,append之后,重新分配了内存地址,s跟 ...
【Go编程基础】14-并发concurrency
并发concurrency
很多人都是冲着 Go 大肆宣扬的高并发而忍不住跃跃欲试,但其实从源码的解析来看,goroutine 只是由官方实现的超级“线程池”而已。不过话说回来,每个实例 4-5KB 的栈内存占用和由于实现机制而大幅减少的创建和销毁开销,是制造 Go 号称的高并发的根本原因。另外,goroutine 的简单易用,也在语言层面上给予了开发者巨大的便利。
并发不是并行:Concurrency Is Not Parallelism,并发主要由切换时间片来实现“同时”运行,在并行则是直接利用多核实现多线程的运行,但 Go 可以设置使用核数,以发挥多核计算机的能力。
Goroutine 奉行通过通信来共享内存,而不是共享内存来通信。
12345678910func main() { go Go() // time.Sleep(2 * time.Second)}func Go() { fmt.Println("Go Go Go!")}没有输出,因为主线程已经退出了
123456789101112func main() ...
【Go编程基础】13-反射reflection
反射reflection
反射可大大提高程序的灵活性,使得 interface{} 有更大的发挥余地
反射使用 TypeOf 和 ValueOf 函数从接口中获取目标对象信息
反射会将匿名字段作为独立字段(匿名字段本质)
想要利用反射修改对象状态,前提是 interface.data 是 settable,即 pointer-interface
通过反射可以“动态”调用方法
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647type User struct { Id int Name string Age int}func (u User) Hello() { fmt.Println("Hello world.")}func main() { u := User{1, "OK", 18} Info(u)}func Info(o interface& ...
【Go编程基础】12-接口interface
接口interface
接口是一个或多个方法签名的集合
只要某个类型拥有该接口的所有方法签名,即算实现该接口,无需显示
声明实现了哪个接口,这称为 Structural Typing
接口只有方法声明,没有实现,没有数据字段
接口可以匿名嵌入其它接口,或嵌入到结构中
将对象赋值给接口时,会发生拷贝,而接口内部存储的是指向这个
复制品的指针,既无法修改复制品的状态,也无法获取指针
只有当接口存储的类型和对象都为nil时,接口才等于nil
接口调用不会做receiver的自动转换
接口同样支持匿名字段方法
接口也可实现类似OOP中的多态
空接口可以作为任何类型数据的容器
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647type USB interface { Name() string // 嵌入接口 Connecter}type Connecter interface { Connect()}type PhoneConnect ...
【Go编程基础】11-方法method
方法method
Go 中虽没有class,但依旧有method
通过显示说明receiver来实现与某个类型的组合
只能为同一个包中的类型定义方法
Receiver 可以是类型的值或者指针
不存在方法重载
可以使用值或指针来调用方法,编译器会自动完成转换
从某种意义上来说,方法是函数的语法糖,因为receiver其实就是
方法所接收的第1个参数(Method Value vs. Method Expression)
如果外部结构和嵌入结构存在同名方法,则优先调用外部结构的方法
类型别名不会拥有底层类型所附带的方法
方法可以调用结构中的非公开字段
12345678910111213141516171819202122232425262728293031323334type A struct { Name string}type B struct { Name string}func main() { a := A{} a.Print() b := B{} b.Print()}func ...
【Go编程基础】10-结构struct
结构struct
Go 中的struct与C中的struct非常相似,并且Go没有class
使用 type struct{} 定义结构,名称遵循可见性规则
支持指向自身的指针类型成员
支持匿名结构,可用作成员或定义成员变量
匿名结构也可以用于map的值
可以使用字面值对结构进行初始化
允许直接通过指针来读写结构成员
相同类型的成员可进行直接拷贝赋值
支持 == 与 !=比较运算符,但不支持 > 或 <
支持匿名字段,本质上是定义了以某个类型名为名称的字段
嵌入结构作为匿名字段看起来像继承,但不是继承
可以使用匿名字段指针
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253// 结构是一个值类型type person struct { Name string Age int}func main() { a := person{} a.Name = & ...
【Go编程基础】09-函数function
函数function
Go 函数 不支持 嵌套、重载和默认参数
但支持以下特性:
无需声明原型、不定长度变参、多返回值、命名返回值参数
匿名函数、闭包
定义函数使用关键字 func,且左大括号不能另起一行
函数也可以作为一种类型使用
1234567891011121314151617181920package mainimport "fmt"func main() { fmt.Println(A()) fmt.Println(B())}// 非匿名返回值,a,b,c不需要定义可直接赋值func A() (a, b, c int) { a, b, c = 1, 2, 3 return}// 匿名返回值,return后面需要指定变量名称func B() (int, int, int) { a, b, c := 4, 5, 6 return a, b, c}
1234567891011121314151617181920212223242526272829303132333435363738394 ...
【Go编程基础】08-map
map
类似其它语言中的哈希表或者字典,以key-value形式存储数据
Key必须是支持==或!=比较运算的类型,不可以是函数、map或slice
Map查找比线性搜索快很多,但比使用索引访问数据的类型慢100倍_ Map使用make()创建,支持 := 这种简写方式
make([keyType]valueType, cap),cap表示容量,可省略
超出容量时会自动扩容,但尽量提供一个合理的初始值
使用len()获取元素个数
键值对不存在时自动添加,使用delete()删除某键值对
使用 for range 对map和slice进行迭代操作
1234567891011func main() { m := make(map[int]string) m[1]="OK" a := m[1] fmt.Println(a) fmt.Println(m)}输出:OKmap[1:OK]
1234567891011121314151617181920212223func main() { ...
【Go编程基础】07-切片slice
切片Slice
其本身并不是数组,它指向底层的数组
作为变长数组的替代方案,可以关联底层数组的局部或全部
为引用类型
可以直接创建或从底层数组获取生成
使用len()获取元素个数,cap()获取容量
一般使用make()创建
如果多个slice指向相同底层数组,其中一个的值改变会影响全部
make([]T, len, cap)
其中cap可以省略,则和len的值相同
len表示存数的元素个数,cap表示容量
1234567891011121314151617181920212223242526272829func main() { var s1 []int fmt.Println(s1) a := [10]int{1,2,3,4,5,6,7,8,9,10} s2 := a[9] fmt.Println(s2) s3 := a[5:10] fmt.Println(s3) // 获取数组某个位置开始到结束 s4 := a[3:len(a)] s5 := a[3:] fmt.Println(s4) fmt.Println(s5) // 获取 ...