package test
//导入包
import (
"github.com/avast/retry-go"
"sync"
"testing"
)
//索引
//一、基本语法
//二、指针
//三、make & new
// ---------基本语法----------------//
// 1.变量 & 声明
func TestVar(t *testing.T) {
var name = "Tom"
println(name)
//:=表示声明并赋值变量,已用var 声明的变量不能再次使用:=声明
gender := "male"
println(gender)
//多个变量声明
var class, number, address = "class1", "001", "st1"
println(class, number, address)
//基本类型强转
}
// 2.常量
// iota是Go语言的一个预定义标识符,它表示的是const声明块(包括单行声明)中每个常量所处位置在块中的偏移值(从零开始)。
// 同时,每一行中的iota自身也是一个无类型常量,可以像无类型常量那样自动参与不同类型的求值过程,而无须对其进行显式类型转换操作。
const (
Apple, Banana = iota, iota + 10 // 0, 10 (iota = 0)
Strawberry, Grape // 1, 11 (iota = 1)
Pear, Watermelon // 2, 12 (iota = 2)
)
// ---------数组、切片、map、字符串----------------//
func TestArray(t *testing.T) {
//对象数组初始化 & 基本类型数组初始化
students := []Student{{Name: "Mike", Number: "001", Class: "class1"}, {Name: "Tom", Number: "002", Class: "class1"}}
println(" student size=", len(students))
sl := []int{23, 24, 25, 26, 27}
println(" sl size=", len(sl))
//在可以预估出元素容量的前提下,使用cap参数创建切片可以提升append的平均操作性能,减少或消除因动态扩容带来的性能损耗。
}
// 线程安全的Map
func TestCurrentMap(t *testing.T) {
//map线程不安全,如果仅仅是并发读,则map是没有问题的。
//sync.Map 支持并发写安全,可以用来在并发读写的场景下替换掉map。
//另外考虑到map可以自动扩容,map中数据元素的value位置可能在这一过程中发生变化,
//因此Go不允许获取map中value的地址,这个约束是在编译期间就生效的
syncMap := sync.Map{}
for i := 0; i < 5; i++ {
go func(j int) {
syncMap.Store(j, j)
}(i)
}
}
//如果可能的话,我们最好对map使用规模做出粗略的估算,并使用cap参数对map实例进行初始化
// ---------指针-------------------//
func (s *Student) updateNamePassByReference(name string) {
println("student local=", s)
s.Name = name
}
func (s Student) updateNamePassByValue(name string) {
//s是一个新的对象
println("student local=", &s)
s.Name = name
}
func updateStudentByPointer(student *Student) {
student.Number = "AAA"
println("this Student is name=", student.Name, " number=", student.Number, " pointer is", student)
}
func updateStudentByRef(student Student) {
student.Number = "AAB"
println("this Student is name=", student.Name, " number=", student.Number, " pointer is ", &student)
}
func TestReferenceAndValue(t *testing.T) {
student := Student{Name: "Tom", Class: "001"}
println("student local=", &student)
student.updateNamePassByReference("Alice")
//输出的name是Alice
println("name = ", student.Name, " class=", student.Class)
//输出的name还是Alice,因为修改的Student对象是值传递的新的对象
student.updateNamePassByValue("Mike")
println("name=", student.Name, " class=", student.Class)
studentJason := Student{Name: "Jason", Class: "002", Number: "00A"}
updateStudentByPointer(&studentJason)
println("Jason's number has update,now is ", studentJason.Number)
updateStudentByRef(studentJason)
println("Jason's number didn't update,still ", studentJason.Number)
}
// ---------make & new------------//
// make 的作用是初始化内置的数据结构,也就是切片、哈希表和 Channel
// new 的作用是根据传入的类型分配一片内存空间并返回指向这片内存空间的指针
// new 函数只接受一个参数,这个参数是一个类型,并且返回一个指向该类型内存地址的指针。同时 new 函数会把分配的内存置为零,也就是类型的零值。
type Student struct {
Name string
Number string
Class string
}
func TestMakeAndNew(t *testing.T) {
//用new 创建Student对象并返回其指针,注意成员变量的初始值都是零值
var stu *Student
stu = new(Student)
println("stu 初始值:class=", stu.Class, " number=", stu.Number, " name=", stu.Name)
stu.Number = "001"
stu.Name = "Mike"
stu.Class = "class1"
println("stu 赋值后:class=", stu.Class, " number=", stu.Number, " name=", stu.Name)
//make 也是用于内存分配的,但是和 new 不同,它只用于 chan、map 以及 slice 的内存创建,而且它返回的类型就是这三个类型本身,而不是他们的指针类型,因为这三种类型就是引用类型,所以就没有必要返回他们的指针了。
var v []int = make([]int, 10, 50)
for i := range v {
println("数组中第", i, "个元素 ", v[i])
}
aMap := make(map[string]int)
aMap["abc"] = 1
println(aMap["abc"])
}
//---------全局变量----------------//
//go语言中没有类似static的静态变量,想要实现全局变量
//作用域
//---------接口继承----------------//
// / ---------类型嵌入------------//
// Go语言提供的最为直观的组合的语法元素是类型嵌入(type embedding)。
// 通过类型嵌入,我们可以将已经实现的功能嵌入新类型中,以快速满足新类型的功能需求。
// 这种方式有些类似经典OO语言中的继承机制,但在原理上与其完全不同,这是一种Go设计者们精心设计的语法糖。
// 被嵌入的类型和新类型之间没有任何关系,甚至相互完全不知道对方的存在,
// 更没有经典OO语言中的那种父类、子类的关系以及向上、向下转型(type casting)。
// 在通过新类型实例调用方法时,方法的匹配取决于方法名字,而不是类型
// $GOROOT/src/sync/pool.go
type poolLocal struct {
private interface{}
shared []interface{}
sync.Mutex
pad [128]byte
}
func TestPoolLocal(t *testing.T) {
var p = poolLocal{}
//
p.Lock()
p.Unlock()
}
//---------map & slice-----------//
//--------- 范型------------------//
// --------- 函数编程 -------------//
// 1.函数作为方法参数
func foo(bar func()) {
println("this is foo")
bar()
}
func TestFoo(t *testing.T) {
//输出 "this is foo" "this is bar"
bar := func() {
println("this is bar")
}
foo(bar)
}
// 2.函数作为成员变量
// 2.1 定义函数
type add func(v1 int, v2 int) bool
type calculate struct {
//2.2 在struct中声明该函数为成员变量
adder add
//3.使用第三方的 retry.Do 函数作为成员变量
//3.1首先声明跟 retry.Do 一样定义的方法
retryDo func(retryableFunc retry.RetryableFunc, opts ...retry.Option) error
}
// 3.2 在struct的构造方法中,将第三方的retry.Do方法赋值给声明的函数
func newCalculate() *calculate {
return &calculate{retryDo: retry.Do}
}
// 4.函数闭包
func TestFuncClosure(t *testing.T) {
}
// ---------管道------------------//
func TestChannel(t *testing.T) {
studentsChan := make(chan Student)
var getStudent = func() {
studentsChan <- Student{
Name: "Tom",
}
}
go getStudent()
student := <-studentsChan
println(student.Name)
//管道的构造参数
//如果管道容量小于实际输入的容量,比如这个case里面将make(chan int, 5)改成make(chan int, 4) 会报错 fatal error: all goroutines are asleep - deadlock!,
//但是反过来不会报错,比如make(chan int, 5)改成make(chan int, 6),可以执行
intChan := make(chan int, 5)
for i := 0; i < 5; i++ {
intChan <- i
}
for i := 0; i < 5; i++ {
println(<-intChan)
}
}
package test
import (
"awesomeProject/util"
"fmt"
"sync"
"testing"
)
//---------协程测试-------------------//
//Go运行时默认为每个goroutine分配的栈空间仅2KB。
//goroutine调度的切换也不用陷入(trap)操作系统内核层完成,代价很低。
//因此,在一个Go程序中可以创建成千上万个并发的goroutine。
//所有的Go代码都在goroutine中执行,哪怕是Go的运行时代码也不例外。
// ---------读写锁--------------------//
func TestReadWriteThread(t *testing.T) {
//类似于java countDownLatch
var wg sync.WaitGroup
wg.Add(100)
var wrapper = util.NewReadWriteWrapper[int, int](&CountExecutor[int, int]{
Count: 0,
})
for i := 0; i < 100; i++ {
var j = i
//声明go协程
go func() {
//WaitGroup计数器减一,类似于CountDownLatch.countdown()
defer wg.Done()
var before = wrapper.Read(1)
wrapper.Write(j)
println("线程 ", j, "写入之前", before, " 写入之后", wrapper.Read(1))
}()
}
//等待所有协程执行完毕,类似于CountDownLatch.await()
wg.Wait()
fmt.Print("=====", wrapper.Read(1))
}
// 读写锁测试类
type CountExecutor[T int, K int] struct {
Count int
}
func (c *CountExecutor[T, K]) Write(data int) bool {
c.Count = c.Count + data
return true
}
func (c *CountExecutor[T, K]) Read(key int) int {
return c.Count
}
package util
import (
"github.com/avast/retry-go"
"github.com/patrickmn/go-cache"
"sync"
"time"
)
var localCache *cache.Cache = nil
var lock = sync.Mutex{}
type HighAvailabilityWrapper[T any] struct {
//正常执行
callableInstance Callable[T]
//降级执行
degradeInstance Callable[T]
//key
keyInit KeyInit
//retry.Do
retryDo func(retryableFunc retry.RetryableFunc, opts ...retry.Option) error
retryIf retry.Option
retryAttempts retry.Option
retryDelay retry.Option
result T
//缓存过期时间
ex time.Duration
}
func NewHighAvailabilityWrapper[T any](callableInstance Callable[T],
degradeInstance Callable[T],
retryConfig *RetryConfig,
cacheConfig *LocalCacheConfig,
keyInit KeyInit) HighAvailabilityWrapper[T] {
if callableInstance == nil {
}
wrapper := HighAvailabilityWrapper[T]{
callableInstance: callableInstance,
degradeInstance: degradeInstance,
keyInit: keyInit,
}
if cacheConfig != nil {
initCache(*cacheConfig, wrapper)
}
if retryConfig != nil {
wrapper.retryDo = retry.Do
if retryConfig.RetryIfFunc != nil {
wrapper.retryIf = retryConfig.RetryIfFunc
} else {
wrapper.retryIf = retry.RetryIf(func(err error) bool {
return err != nil
})
}
wrapper.retryAttempts = retry.Attempts(retryConfig.Attempts)
wrapper.retryDelay = retry.Delay(time.Duration(retryConfig.Delay))
}
return wrapper
}
func initCache[T any](cacheConfig LocalCacheConfig, wrapper HighAvailabilityWrapper[T]) {
lock.Lock()
if localCache == nil {
var defaultExpiration = time.Duration(cacheConfig.DefaultExpiration)
var clearTime = time.Duration(cacheConfig.ClearTime)
localCache = cache.New(defaultExpiration, clearTime)
}
wrapper.ex = time.Duration(cacheConfig.DefaultExpiration)
defer lock.Unlock()
}
func (hp *HighAvailabilityWrapper[T]) Execute() (*T, error) {
err := hp.retryDo(func() error {
var e error
hp.result, e = hp.callableInstance.Call()
return e
}, hp.retryIf, hp.retryAttempts, hp.retryDelay)
if err == nil {
if localCache != nil && hp.keyInit != nil {
localCache.Set(hp.keyInit.KeyGet(), hp.result, hp.ex)
}
return &(hp.result), nil
}
if localCache != nil && hp.keyInit != nil {
get, _ := localCache.Get(hp.keyInit.KeyGet())
if get != nil {
hp.result = get.(T)
return &hp.result, nil
}
}
if hp.degradeInstance != nil {
call, err := hp.degradeInstance.Call()
return &call, err
}
return nil, err
}
type Callable[T any] interface {
Call() (T, error)
}
// 重试配置
type RetryConfig struct {
RetryIfFunc retry.Option
//重试次数
Attempts uint
//延迟时间
Delay int
}
// 本地缓存配置
type LocalCacheConfig struct {
DefaultExpiration int
ClearTime int
}
type KeyInit interface {
KeyGet() string
}
package util
import (
"testing"
)
func TestCountExecutor_Read(t *testing.T) {
wrapper := NewHighAvailabilityWrapper[string](&myCall[string]{}, °radeCall[string]{}, &RetryConfig{Delay: 100, Attempts: 2, RetryIfFunc: nil}, nil, nil)
execute, err := wrapper.Execute()
println("execute=", *execute)
println("error=", err)
}
type myCall[T string] struct {
count int
}
func (m *myCall[T]) Call() (string, error) {
//第一次返回失败
if m.count == 0 {
println("error!")
m.count++
return "", &commonErr{}
}
//第二次成功
return "hello", nil
}
type commonErr struct {
}
func (c *commonErr) Error() string {
//TODO implement me
return ""
}
func (c *commonErr) RuntimeError() {
//TODO implement me
panic("implement me")
}
type degradeCall[s string] struct {
}
func (m *degradeCall[T]) Call() (string, error) {
return "", nil
}
package util
import "sync"
type ReadWriteWrapper[T any, K any] struct {
rwLock sync.RWMutex
executor Executor[T, K]
}
func (r *ReadWriteWrapper[T, K]) Write(data T) bool {
r.rwLock.Lock()
defer r.rwLock.Unlock()
return r.executor.Write(data)
}
func (r *ReadWriteWrapper[T, K]) Read(readKey K) T {
r.rwLock.RLock()
defer r.rwLock.RUnlock()
return r.executor.Read(readKey)
}
func NewReadWriteWrapper[T any, K any](e Executor[T, K]) ReadWriteWrapper[T, K] {
return ReadWriteWrapper[T, K]{
executor: e,
}
}
type Executor[T any, K any] interface {
Write(data T) bool
Read(key K) T
}
package http
import (
"fmt"
"io"
"log"
"net/http"
)
const successCode = 0
const failData = "error"
// 启动入口
func HttpBoostrap() {
httpMap := map[string]RequestProcessor{}
httpMap["/"] = HelloProcessor{}
httpMap["/abc"] = AbcProcess{}
// 定义路由
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
code, data := doDispatch(r.Method, r.RequestURI, func(r *http.Request) string {
all, err := io.ReadAll(r.Body)
if err != nil {
fmt.Println("error =", err)
return ""
}
return string(all)
}(r), httpMap[r.RequestURI])
var writedata string
if code != successCode {
writedata = failData
} else {
writedata = data
}
//var marshal, _ = json.Marshal(res)
_, err := w.Write([]byte(writedata))
if err != nil {
fmt.Println("error =", err)
}
w.WriteHeader(http.StatusOK)
})
// 启动服务器
log.Fatal(http.ListenAndServe(":8085", nil))
}
// 处理
func doDispatch(method string, url string, body string, processor RequestProcessor) (int, string) {
if processor == nil {
return 0, "hello world"
}
return processor.Process(url, body)
}
// http请求内容处理接口
type RequestProcessor interface {
Process(url string, body string) (int, string)
}
// http 请求内容处理 实现
type HelloProcessor struct {
}
func (h HelloProcessor) Process(url string, body string) (int, string) {
return 0, "hello"
}
// http 请求内容处理 实现
type AbcProcess struct {
}
func (h AbcProcess) Process(url string, body string) (int, string) {
return 0, "abcabc"
}