├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── base ├── README.md ├── ast │ ├── ast.go │ ├── ast_test.go │ └── example │ │ ├── code.go │ │ └── code_msg.go ├── cgo │ ├── 1 │ │ └── cgo.go │ ├── 2 │ │ ├── cgo.go │ │ └── hello.c │ ├── 3 │ │ ├── cgo.go │ │ ├── hello.h │ │ └── main.go │ ├── 4 │ │ ├── hello.cpp │ │ ├── hello.h │ │ └── main.go │ └── README.md ├── chan │ ├── chan_test.go │ ├── chan_try_lock_test.go │ └── chanx.go ├── cond │ └── cond.go ├── context │ ├── context.go │ └── context_test.go ├── csv │ ├── csv.go │ ├── csv_test.go │ └── test.csv ├── defer │ └── defer_test.go ├── embed │ ├── embed_test.go │ └── test.txt ├── file │ ├── file_demo.go │ ├── file_test.go │ └── read_file.go ├── flag │ ├── flag_test.go │ └── version │ │ ├── build.bat │ │ └── version.go ├── generics │ └── generics.go ├── goroutine │ ├── cyclicbarrier │ │ └── cyclicbarrier_test.go │ ├── errgroup │ │ ├── err_group.go │ │ └── err_group_test.go │ ├── goroutine.go │ ├── goroutinue_test.go │ ├── pool │ │ ├── pool.go │ │ ├── pool_test.go │ │ └── sema │ │ │ └── sema_pool_test.go │ └── singleflight │ │ ├── singleflight_test.go │ │ └── test.txt ├── http │ ├── getpost │ │ ├── get_test.go │ │ └── post_test.go │ ├── html │ │ ├── read_html.go │ │ └── root │ │ │ ├── hello.html │ │ │ └── index.html │ ├── middleware │ │ ├── auth.go │ │ ├── body.go │ │ ├── ip.go │ │ └── main │ │ │ └── main.go │ ├── restful │ │ └── restful.go │ ├── server │ │ ├── http_server.go │ │ └── index.html │ ├── test │ │ └── http_test.go │ └── url │ │ └── url_test.go ├── instruct │ ├── instruct.go │ └── linkname │ │ ├── impl │ │ └── impl.go │ │ ├── linkname.go │ │ └── outer │ │ ├── i.s │ │ └── outer.go ├── io │ ├── io.txt │ └── reader_writer_test.go ├── json │ ├── json.go │ └── json_test.go ├── mail │ └── send_email.go ├── net │ ├── chat-room │ │ ├── README.md │ │ ├── client │ │ │ └── client.go │ │ └── server │ │ │ └── server.go │ ├── net.go │ ├── net_test.go │ ├── tcp │ │ ├── client │ │ │ └── client.go │ │ └── server │ │ │ └── server.go │ └── udp │ │ ├── client │ │ └── client.go │ │ └── server │ │ └── server.go ├── pool │ └── pool_test.go ├── reflect │ └── reflect_test.go ├── regexp │ └── regexp_test.go ├── rpc │ ├── README.md │ ├── lv1 │ │ ├── client │ │ │ └── main.go │ │ └── server │ │ │ └── main.go │ ├── lv2 │ │ ├── HelloService.pb.go │ │ ├── client │ │ │ └── main.go │ │ └── server │ │ │ └── main.go │ ├── lv3 │ │ ├── HelloService.pb.go │ │ ├── client │ │ │ └── main.go │ │ └── server │ │ │ └── main.go │ ├── lv4 │ │ ├── HelloService.pb.go │ │ └── server │ │ │ └── main.go │ └── lv5 │ │ ├── HelloService.pb.go │ │ ├── client │ │ └── main.go │ │ ├── server │ │ └── main.go │ │ └── ssl │ │ ├── server.crt │ │ └── server.key ├── runtime │ ├── runtime_test.go │ └── trace_test.go ├── semaphore │ └── semaphore_test.go ├── shell │ └── shell_test.go ├── sort │ └── sort_test.go ├── string │ └── string_test.go ├── sync │ ├── atomic │ │ ├── atomic.go │ │ ├── atomic_test.go │ │ └── spinlock.go │ ├── cond_test.go │ ├── sync_map_test.go │ └── sync_mutex_test.go ├── template │ ├── template.go │ └── template_test.go ├── time │ ├── ticker_test.go │ ├── time_test.go │ └── timer_test.go ├── trace │ └── trace.go ├── unsafe │ └── unsafe_test.go ├── xml │ └── xml_test.go └── zip │ ├── file.zip │ ├── test │ ├── 1.txt │ ├── 2.txt │ └── 3.txt │ ├── zip_demo.go │ └── zip_test.go ├── blockchain ├── README.md ├── block_test.go ├── core │ ├── block.go │ └── blockchain.go └── server │ └── server.go ├── design ├── README.md ├── adaptor │ ├── adaptor.go │ └── adaptor_test.go ├── chain │ ├── chain.go │ └── chain_test.go ├── decorator │ ├── decorator.go │ ├── decorator_func.go │ ├── decorator_func_test.go │ └── decorator_test.go ├── facade │ ├── facade.go │ └── facade_test.go ├── factory │ ├── README.md │ ├── abstract │ │ ├── abstract_factory.go │ │ └── abstract_factory_test.go │ └── simple │ │ ├── simple_factory.go │ │ └── simple_factory_test.go ├── observer │ ├── observer.go │ └── observer_test.go ├── options │ ├── options.go │ └── options_test.go ├── proxy │ ├── proxy.go │ └── proxy_test.go ├── singleton │ ├── go_single.go │ ├── other_single.go │ └── single_test.go ├── strategy │ ├── strategy.go │ └── strategy_test.go ├── template │ ├── template.go │ └── template_test.go └── worker │ ├── worker.go │ └── worker_test.go ├── go.mod ├── go.sum ├── interview ├── README.md ├── handpick │ └── README.md ├── interview_test.go ├── others │ ├── 10_test.go │ ├── 1_test.go │ ├── 2_test.go │ ├── 3_test.go │ ├── 4_test.go │ ├── 5_test.go │ ├── 6_test.go │ ├── 7_test.go │ ├── 8_test.go │ └── 9_test.go └── sql │ └── README.md ├── leetcode ├── 1 │ ├── 1.go │ └── 1_test.go ├── 2 │ └── 2.go ├── 3 │ └── 3.go ├── 4 │ └── 4.go ├── 5 │ └── 5.go ├── 6 │ └── 6.go ├── 7 │ └── 7.go ├── 8 │ └── 8.go ├── 9 │ └── 9.go ├── 10 │ └── 10.go ├── 11 │ └── 11.go ├── 12 │ └── 12.go ├── 13 │ └── 13.go ├── 14 │ └── 14.go ├── 15 │ └── 15.go ├── 16 │ └── 16.go ├── 17 │ └── 17.go ├── 18 │ ├── 18.go │ └── 18_test.go ├── 19 │ ├── 19.go │ └── 19_test.go ├── 20 │ ├── 20.go │ └── 20_test.go ├── 21 │ ├── 21.go │ └── 21_test.go ├── 22 │ ├── 22.go │ └── 22_test.go ├── 23 │ ├── 23.go │ └── 23_test.go ├── 24 │ ├── 24.go │ └── 24_test.go ├── 25 │ ├── 25.go │ └── 25_test.go ├── 26 │ ├── 26.go │ └── 26_test.go ├── README.md ├── common │ ├── list │ │ └── list.go │ └── tree │ │ └── tree.go └── interview │ ├── lru │ ├── array │ │ ├── lru_array.go │ │ └── lru_array_test.go │ └── list │ │ ├── lru_list.go │ │ └── lru_list_test.go │ └── sort │ ├── charu │ └── main.go │ ├── kuaipai │ └── main.go │ ├── maopao │ └── main.go │ └── xuanze │ └── main.go ├── pprof ├── README.md ├── gc │ └── main.go └── main.go ├── revive.toml ├── sdk ├── README.md ├── alipay │ └── alipay.go ├── consul │ ├── consul.go │ └── consul_test.go ├── elasticsearch │ ├── doc │ │ ├── Makefile │ │ └── docker-compose.yaml │ ├── elasticsearch.go │ ├── elasticsearch_test.go │ └── trace_transport.go ├── etcd │ └── etcd_test.go ├── gf │ ├── .gitattributes │ ├── .gitignore │ ├── Dockerfile │ ├── README.MD │ ├── app │ │ ├── api │ │ │ └── hello.go │ │ ├── dao │ │ │ └── .gitkeep │ │ ├── model │ │ │ └── .gitkeep │ │ └── service │ │ │ └── .gitkeep │ ├── boot │ │ └── boot.go │ ├── config │ │ └── config.toml │ ├── docker │ │ └── .gitkeep │ ├── document │ │ └── .gitkeep │ ├── i18n │ │ └── .gitkeep │ ├── main.go │ ├── packed │ │ └── packed.go │ ├── public │ │ ├── html │ │ │ └── .gitkeep │ │ ├── plugin │ │ │ └── .gitkeep │ │ └── resource │ │ │ ├── css │ │ │ └── .gitkeep │ │ │ ├── image │ │ │ └── .gitkeep │ │ │ └── js │ │ │ └── .gitkeep │ ├── router │ │ ├── .gitkeep │ │ └── router.go │ └── template │ │ └── .gitkeep ├── gin │ └── gin.go ├── kafka │ ├── consumer │ │ └── consumer.go │ └── producer │ │ └── producer.go ├── kite │ └── kite.go ├── ldap │ ├── ldap.go │ └── ldap_test.go ├── mongodb │ └── mongodb_test.go ├── mqtt │ ├── mqtt.go │ └── mqtt_test.go ├── mysql │ ├── mysql.go │ ├── mysql_test.go │ └── pool │ │ ├── mysql_pool.go │ │ └── mysql_pool_test.go ├── nsq │ ├── nsq.go │ ├── nsqio │ │ └── main.go │ └── test │ │ └── nsqTest.go ├── openai │ └── openai.go ├── oss │ ├── oss.go │ ├── oss_test.go │ └── test.txt ├── qq │ └── qq_pc_login.go ├── rabbitmq │ ├── rabbitmq.go │ └── rabbitmq_test.go ├── redis │ ├── redis.go │ └── redis_test.go ├── robot │ ├── robot.go │ └── robot_test.go ├── rocketmq │ ├── aliyun_rocketmq.go │ └── rocketmq.go ├── shortdomain │ ├── shorten.go │ └── shorten_test.go ├── sms │ └── sms.go ├── trace │ ├── README.md │ ├── app │ │ ├── db │ │ │ └── db.go │ │ ├── main.go │ │ └── middleware │ │ │ ├── gin_trace.go │ │ │ ├── gorm_trace.go │ │ │ ├── grpc_trace.go │ │ │ └── trace.go │ └── demo │ │ ├── client │ │ └── client.go │ │ ├── server │ │ └── server.go │ │ └── trace.go ├── websocket │ ├── main.go │ └── ws.html ├── weixin │ └── weixin_pay.go └── wire │ ├── Makefile │ ├── wire.go │ └── wire_gen.go ├── spider ├── README.md ├── agent │ └── user_agents.go ├── colly │ └── douban │ │ └── douban.go ├── gift │ ├── auto │ │ ├── auto_get_gift.go │ │ └── build.bat │ ├── cookie │ └── hand │ │ └── get_gift.go └── qq │ ├── qq.go │ ├── qq_interface.go │ ├── qq_login.go │ └── qq_login_test.go └── utils ├── README.md ├── ants ├── ants.go └── ants_test.go ├── apriori └── apriori.go ├── bar ├── progress │ └── progress_bar.go └── spinner │ └── spinner.go ├── cmp ├── cmp.go └── cmp_test.go ├── cmux └── cmux.go ├── code ├── code_test.go ├── code_util.go └── html │ └── code.go ├── copy ├── copy.go └── copy_test.go ├── cron └── cron_demo.go ├── crypto ├── aes.go ├── aes_test.go ├── base64.go ├── base64_test.go ├── hash.go ├── hash_test.go ├── md5_test.go ├── md5_util.go ├── rsa.go └── rsa_test.go ├── ctxkit ├── ctxkit.go └── ctxkit_test.go ├── disk ├── disk.go └── disk_test.go ├── encode └── encode_test.go ├── env └── env.go ├── fsnotify └── fsnotify_test.go ├── goleak └── goleak_test.go ├── gops └── main.go ├── govaluate └── govaluate_test.go ├── gse └── gse.go ├── hystrix └── hystrix_test.go ├── i18n ├── i18n.go ├── i18n_test.go └── locales │ ├── path.go │ ├── zh-CN.json │ └── zh-CN.yaml ├── images ├── image.jpg ├── new.jpg ├── resize_image.go └── resize_image_test.go ├── inject └── inject.go ├── ip ├── address │ ├── address.datx │ ├── city.go │ └── get_address_by_ip.go ├── ip.go └── ip_test.go ├── jieba └── jieba.go ├── js ├── otto_test.go └── test.js ├── jsondiff └── jsondiff.go ├── jsonpath └── jsonpath.go ├── kmeans └── kmeans.go ├── markdown ├── index.html ├── index.md ├── markdown.go └── markdown_test.go ├── metadata ├── metadata.go └── metadata_test.go ├── mock ├── README.md ├── mock_spider.go ├── mock_test.go └── spider.go ├── multiconfig ├── config.toml └── multiconfig_test.go ├── name ├── name.go └── name_test.go ├── oauth2 ├── README.md ├── client │ └── main.go └── server │ ├── main.go │ └── static │ ├── auth.html │ ├── auth.png │ ├── login.html │ ├── login.png │ └── token.png ├── ocr └── ocr.go ├── pinyin └── pinyin_test.go ├── pool ├── batch.go └── routine.go ├── qrcode ├── 1.jpg ├── 2.jpg ├── qrcode.go ├── qrcode_test.go └── terminal │ ├── qr_terminal.go │ └── qr_terminal_test.go ├── rand └── rand.go ├── ratelimit ├── ip_rate.go ├── main.go └── ratelimit │ └── ratelimit.go ├── registry └── registry.go ├── retry ├── retry.go └── retry_test.go ├── robot ├── robot.exe └── robot.go ├── sentinel ├── fallback.go ├── init.go ├── middleware.go └── option.go ├── seq ├── id.go ├── id_test.go ├── uuid.go └── uuid_test.go ├── sqlbuilder └── sqlbuilder.go ├── sqlparse └── sqlparse.go ├── stack ├── stack.go └── stack_test.go ├── timex ├── timex.go └── timex_test.go ├── token ├── token.go └── token_test.go ├── walk ├── walk.exe.manifest └── walk.go ├── word ├── new.docx ├── old.docx ├── word.go └── word_test.go └── xlsx ├── test_write.xlsx ├── xlsx.go └── xlsx_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # ignore 3 | 4 | .idea/ 5 | .idea/workspace.xml 6 | boss.log 7 | .DS_Store 8 | cookie.txt -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # http://www.ruanyifeng.com/blog/2017/12/travis_ci_tutorial.html 2 | language: go 3 | 4 | go: 5 | - "1.16.x" 6 | 7 | branches: 8 | only: 9 | - master 10 | - develop 11 | 12 | # https://docs.travis-ci.com/user/database-setup/ 13 | services: 14 | - mysql 15 | - redis-server 16 | - elasticsearch 17 | 18 | env: 19 | - GO111MODULE=on 20 | - GOPROXY=https://goproxy.io 21 | 22 | install: 23 | - cd /usr/bin 24 | - go get -u github.com/mgechev/revive 25 | - cd - 26 | 27 | before_script: 28 | - mysql -e 'CREATE DATABASE IF NOT EXISTS test;' 29 | - find . -name "*.go" | xargs gofmt -w 30 | - git diff --exit-code 31 | - revive -config=revive.toml -formatter friendly ./... 32 | 33 | script: 34 | - go test ./... || exit 0 35 | - go test ./... -coverprofile=coverage.txt -covermode=atomic || exit 0 36 | 37 | after_success: 38 | - bash <(curl -s https://codecov.io/bash) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 派大星 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /base/README.md: -------------------------------------------------------------------------------- 1 | # Go语言内部库使用 2 | 3 | - [ast](ast): ast语法树使用 4 | - [cgo](cgo): cgo使用 5 | - [chan](chan): chan使用 6 | - [cond](cond): 条件变量 7 | - [context](context): 上下文使用详解 8 | - [csv](csv): csv文件操作 9 | - [defer](defer): 巧用defer 10 | - [embed](embed): 内嵌资源使用 11 | - [file](file):Go操作文件 12 | - [flag](flag):读取运行时参数 13 | - [goroutine](goroutine):协程池(pool)、错误组(errorgroup)、合并并发请求(singleflight)、循环栅栏(cyclicbarrier) 14 | - [http](http):http相关操作 15 | - [json](json):json相关操作 16 | - [mail](mail): Go发送邮件 17 | - [net](net):网络相关操作(写一个简单的聊天室) 18 | - [reflect](reflect): Go反射相关操作 19 | - [regexp](regexp): Go正则表达式操作 20 | - [rpc](rpc): Go使用rpc进行远程服务调用 21 | - [runtime](runtime): Go runtime包使用 22 | - [semaphore](semaphore): 信号量 23 | - [shell](shell): Go执行shell命令 24 | - [sort](sort): Go切片排序 25 | - [string](string): Go字符串操作 26 | - [sync](sync): Go并发相关包使用 27 | - [template](template): 模板用法 28 | - [time](time): 时间操作 29 | - [xml](xml): xml操作 30 | - [zip](zip): 压缩解压文件 -------------------------------------------------------------------------------- /base/ast/ast_test.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import "testing" 4 | 5 | func TestGenConstComments(t *testing.T) { 6 | genConstComment("example/code.go", "example/code_msg.go") 7 | } 8 | -------------------------------------------------------------------------------- /base/ast/example/code.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | const ( 4 | Test1 = 1 // 测试1 5 | Test2 = 2 // 测试2 6 | Test3 = 3 // 测试3 7 | Test4 = 4 // 测试4 8 | ) 9 | -------------------------------------------------------------------------------- /base/ast/example/code_msg.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | // messages get msg from const comment 4 | var messages = map[int]string{ 5 | 6 | Test1: "测试1", 7 | Test2: "测试2", 8 | Test3: "测试3", 9 | Test4: "测试4", 10 | } 11 | 12 | // GetErrMsg get error msg 13 | func GetErrMsg(code int) string { 14 | if msg, ok := messages[code]; ok { 15 | return msg 16 | } 17 | return "" 18 | } 19 | -------------------------------------------------------------------------------- /base/cgo/1/cgo.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | /* 4 | #cgo LDFLAGS: -L/usr/local/lib 5 | 6 | #include 7 | #include 8 | // CGO会保留C代码块中的宏定义 9 | #define REPEAT_LIMIT 3 10 | 11 | // 自定义结构体 12 | typedef struct{ 13 | int repeat_time; 14 | char* str; 15 | }blob; 16 | 17 | // 自定义函数 18 | int SayHello(blob* pblob) { 19 | for ( ;pblob->repeat_time < REPEAT_LIMIT; pblob->repeat_time++){ 20 | puts(pblob->str); 21 | } 22 | return 0; 23 | } 24 | */ 25 | import "C" 26 | import ( 27 | "fmt" 28 | "unsafe" 29 | ) 30 | 31 | func main() { 32 | cblob := C.blob{} // 在GO程序中创建的C对象,存储在Go的内存空间 33 | cblob.repeat_time = 0 34 | 35 | cblob.str = C.CString("Hello, World\n") // C.CString 会在C的内存空间申请一个C语言字符串对象,再将Go字符串拷贝到C字符串 36 | 37 | // &cblob 取C语言对象cblob的地址 38 | ret := C.SayHello(&cblob) 39 | 40 | fmt.Println("ret", ret) 41 | fmt.Println("repeat_time", cblob.repeat_time) 42 | 43 | // C.CString 申请的C空间内存不会自动释放,需要显示调用C中的free释放 44 | C.free(unsafe.Pointer(cblob.str)) 45 | } 46 | -------------------------------------------------------------------------------- /base/cgo/2/cgo.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | /* 4 | #include "hello.c" 5 | int SayHello(); 6 | */ 7 | import "C" 8 | import ( 9 | "fmt" 10 | ) 11 | 12 | func main() { 13 | ret := C.SayHello() 14 | fmt.Println(ret) 15 | } 16 | -------------------------------------------------------------------------------- /base/cgo/2/hello.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int SayHello() { 4 | puts("Hello World"); 5 | return 0; 6 | } -------------------------------------------------------------------------------- /base/cgo/3/cgo.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | //#include 4 | import "C" 5 | import "fmt" 6 | 7 | // 通过go实现C函数,并导出 8 | // //export SayHello 指令将 Go 语言实现的 SayHello 函数导出为 C 语言函 9 | 10 | //export SayHello 11 | func SayHello(str *C.char) { 12 | fmt.Println("Go.....") 13 | fmt.Println(C.GoString(str)) 14 | } 15 | -------------------------------------------------------------------------------- /base/cgo/3/hello.h: -------------------------------------------------------------------------------- 1 | // 自定义函数,并通过go来实现C函数 2 | void SayHello(char* s); -------------------------------------------------------------------------------- /base/cgo/3/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | //#include "hello.h" 4 | import "C" 5 | 6 | func main() { 7 | // Go 程序先调用 C 的 SayHello 接口,由于 SayHello 接口链接在 Go 的实现上,又调到 Go 8 | C.SayHello(C.CString("Hello World")) 9 | } 10 | -------------------------------------------------------------------------------- /base/cgo/4/hello.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern "C" { 4 | #include "hello.h" 5 | } 6 | // 使用c++实现SayHello 7 | int SayHello() { 8 | std::cout<<"Hello World"; 9 | return 0; 10 | } -------------------------------------------------------------------------------- /base/cgo/4/hello.h: -------------------------------------------------------------------------------- 1 | int SayHello(); -------------------------------------------------------------------------------- /base/cgo/4/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | /* 4 | #include "hello.h" 5 | */ 6 | import "C" 7 | import ( 8 | "fmt" 9 | ) 10 | 11 | func main() { 12 | ret := C.SayHello() 13 | fmt.Println(ret) 14 | } 15 | -------------------------------------------------------------------------------- /base/cgo/README.md: -------------------------------------------------------------------------------- 1 | # CGO 2 | 3 | 1. Go内嵌C代码并调用 4 | 2. Go调用外部C文件 5 | 3. Go实现C函数并被调用 6 | 4. Go调用C++文件 7 | -------------------------------------------------------------------------------- /base/cond/cond.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | func main() { 9 | cond := sync.NewCond(new(sync.Mutex)) 10 | condition := 0 11 | 12 | // 消费者 13 | go func() { 14 | for i := 0; i < 100; i++ { 15 | // 消费者开始消费时,锁住 16 | cond.L.Lock() 17 | // 如果没有可消费的值,则等待 18 | for condition == 0 { 19 | cond.Wait() 20 | } 21 | // 消费 22 | condition-- 23 | fmt.Printf("Consumer: %d\n", condition) 24 | 25 | // 唤醒一个生产者 26 | cond.Signal() 27 | // 解锁 28 | cond.L.Unlock() 29 | } 30 | }() 31 | 32 | // 生产者 33 | for i := 0; i < 100; i++ { 34 | // 生产者开始生产 35 | cond.L.Lock() 36 | 37 | // 当生产太多时,等待消费者消费 38 | for condition == 10 { 39 | cond.Wait() 40 | } 41 | // 生产 42 | condition++ 43 | fmt.Printf("Producer: %d\n", condition) 44 | 45 | // 通知消费者可以开始消费了 46 | cond.Signal() 47 | // 解锁 48 | cond.L.Unlock() 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /base/context/context_test.go: -------------------------------------------------------------------------------- 1 | package context 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | func TestContextWithCancel(t *testing.T) { 11 | ctx := context.Background() 12 | ctx, cancel := context.WithCancel(ctx) 13 | cancel() 14 | select { 15 | case <-ctx.Done(): 16 | fmt.Println("手动取消") 17 | return 18 | } 19 | } 20 | 21 | func TestContextWithValue1(t *testing.T) { 22 | kv := make(map[string]interface{}) 23 | kv["name"] = "pibigstar" 24 | kv["age"] = 20 25 | contextWithValue(kv) 26 | } 27 | 28 | func TestContextWithDeadLine(t *testing.T) { 29 | ctx := context.Background() 30 | deadTime := time.Now().Add(time.Second * 3) 31 | ctx, _ = context.WithDeadline(ctx, deadTime) 32 | 33 | select { 34 | case <-ctx.Done(): 35 | fmt.Println("到达deadLine,结束执行") 36 | return 37 | } 38 | } 39 | 40 | func TestContextWithTimeout(t *testing.T) { 41 | ctx := context.Background() 42 | ctx, _ = context.WithTimeout(ctx, time.Second*3) 43 | select { 44 | case <-ctx.Done(): 45 | fmt.Println("已超时,结束执行") 46 | } 47 | } 48 | 49 | func TestContextWithValue(t *testing.T) { 50 | ctx := context.WithValue(context.Background(), "name", "派大星") 51 | fmt.Println(ctx.Value("name")) 52 | } 53 | -------------------------------------------------------------------------------- /base/csv/csv.go: -------------------------------------------------------------------------------- 1 | package csv 2 | 3 | import ( 4 | "encoding/csv" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | func writeCsv(file *os.File) { 10 | w := csv.NewWriter(file) 11 | w.Write([]string{"123", "456", "789", "666"}) 12 | w.Flush() 13 | file.Close() 14 | } 15 | 16 | func readCsv(file *os.File) { 17 | r := csv.NewReader(file) 18 | strs, _ := r.Read() 19 | for _, str := range strs { 20 | fmt.Println(str) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /base/csv/csv_test.go: -------------------------------------------------------------------------------- 1 | package csv 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | ) 7 | 8 | func TestWriteCsv(t *testing.T) { 9 | file, _ := os.OpenFile("test.csv", os.O_WRONLY|os.O_CREATE, os.ModePerm) 10 | writeCsv(file) 11 | } 12 | 13 | func TestReadCsv(t *testing.T) { 14 | file, _ := os.Open("test.csv") 15 | readCsv(file) 16 | } 17 | -------------------------------------------------------------------------------- /base/csv/test.csv: -------------------------------------------------------------------------------- 1 | 123,456,789,666 2 | -------------------------------------------------------------------------------- /base/defer/defer_test.go: -------------------------------------------------------------------------------- 1 | package deferTest 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | // 巧用 defer, 在函数入口和出口时做一些操作 10 | func TestDefer(t *testing.T) { 11 | doSomething() 12 | } 13 | 14 | func doSomething() { 15 | defer countTime("doSomething")() 16 | // 模拟耗时操作 17 | time.Sleep(3 * time.Second) 18 | fmt.Println("done") 19 | } 20 | 21 | // 统计某函数的运行时间 22 | func countTime(msg string) func() { 23 | start := time.Now() 24 | fmt.Printf("run func: %s", msg) 25 | return func() { 26 | fmt.Printf("func name: %s run time: %f s \n", msg, time.Since(start).Seconds()) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /base/embed/embed_test.go: -------------------------------------------------------------------------------- 1 | //go:build go1.18 2 | // +build go1.18 3 | 4 | package embed 5 | 6 | import ( 7 | _ "embed" 8 | "fmt" 9 | "testing" 10 | ) 11 | 12 | // 通过 go:embed 文件名,可以将该文件内容读入到变量bs中 13 | //go:embed test.txt 14 | var bs []byte 15 | 16 | func TestEmbed(t *testing.T) { 17 | fmt.Println(string(bs)) 18 | } 19 | -------------------------------------------------------------------------------- /base/embed/test.txt: -------------------------------------------------------------------------------- 1 | Hello World -------------------------------------------------------------------------------- /base/file/file_test.go: -------------------------------------------------------------------------------- 1 | package file 2 | 3 | import ( 4 | "go-demo/utils/env" 5 | "testing" 6 | ) 7 | 8 | func TestMain(m *testing.M) { 9 | if env.IsCI() { 10 | return 11 | } 12 | m.Run() 13 | } 14 | 15 | const fileName = "demo.text" 16 | 17 | func TestCreateFile(t *testing.T) { 18 | CreateFile(fileName) 19 | } 20 | 21 | func TestWriteFile(t *testing.T) { 22 | WriteFile(fileName, "Hello,World") 23 | } 24 | 25 | func TestAppendToFile(t *testing.T) { 26 | AppendToFile(fileName, "Hello,Pibigstar") 27 | } 28 | 29 | func TestReadFile(t *testing.T) { 30 | ReadFile(fileName) 31 | } 32 | 33 | func TestMkdirFile(t *testing.T) { 34 | MkOneDir("demo") 35 | MkAllDir("test/user/one") 36 | } 37 | 38 | func TestReadAllDir(t *testing.T) { 39 | ReadAllDir(".") 40 | } 41 | 42 | func TestDeleteFile(t *testing.T) { 43 | DeleteFile(fileName) 44 | } 45 | 46 | func TestFileAbs(t *testing.T) { 47 | t.Log(GetFileAbs("file_demo.go")) 48 | } 49 | 50 | func TestInode(t *testing.T) { 51 | t.Log(Inode("file_demo.go")) 52 | } 53 | 54 | func TestCopy(t *testing.T) { 55 | fileMd5, err := GetFileMd5("file_demo.go") 56 | if err != nil { 57 | t.Error(err) 58 | } 59 | t.Log(fileMd5) 60 | } 61 | -------------------------------------------------------------------------------- /base/flag/flag_test.go: -------------------------------------------------------------------------------- 1 | package flag 2 | 3 | import ( 4 | "flag" 5 | "testing" 6 | ) 7 | 8 | // 获取命令行参数 9 | // go run flag_demo.go -name pibigstar -age 15 10 | func TestFlag(t *testing.T) { 11 | // 如果不传,则默认值为 test 12 | // 如果输入错误参数,那么会打印出 describe 13 | name := flag.String("name", "pibigstar", "set your name") 14 | flag.Parse() 15 | t.Log(*name) 16 | // 也可以这样使用 17 | var env string 18 | flag.StringVar(&env, "env", "dev", "the project environment") 19 | t.Log(env) 20 | } 21 | -------------------------------------------------------------------------------- /base/flag/version/build.bat: -------------------------------------------------------------------------------- 1 | go build -ldflags "-X 'main.goVersion=$(go version)' -X 'main.gitHash=$(git show -s --format=%H)' -X 'main.buildTime=$(git show -s --format=%cd)'" -o main.exe version.go -------------------------------------------------------------------------------- /base/flag/version/version.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | ) 7 | 8 | var ( 9 | gitHash string 10 | buildTime string 11 | goVersion string 12 | ) 13 | 14 | func main() { 15 | args := os.Args 16 | if len(args) == 2 && (args[1] == "--version" || args[1] == "-v") { 17 | fmt.Printf("Git Commit Hash: %s \n", gitHash) 18 | fmt.Printf("Build TimeStamp: %s \n", buildTime) 19 | fmt.Printf("GoLang Version: %s \n", goVersion) 20 | return 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /base/generics/generics.go: -------------------------------------------------------------------------------- 1 | //go:build go1.18 2 | // +build go1.18 3 | 4 | package main 5 | 6 | import ( 7 | "fmt" 8 | ) 9 | 10 | func printSlice[T any](s []T) { 11 | for _, v := range s { 12 | fmt.Printf("%v \n", v) 13 | } 14 | } 15 | 16 | func main() { 17 | printSlice([]int{1, 2, 3}) 18 | } 19 | -------------------------------------------------------------------------------- /base/goroutine/errgroup/err_group.go: -------------------------------------------------------------------------------- 1 | package errgroup 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | 8 | "golang.org/x/sync/errgroup" 9 | ) 10 | 11 | // 错误组是用来将一个通用的父任务拆成几个小任务并发执行 12 | // 一旦有一个子任务返回错误,或者是Wait调用返回 13 | // 那么整个context将会被取消,所有任务终止执行 14 | func Run(work []int) error { 15 | eg, ctx := errgroup.WithContext(context.Background()) 16 | for _, w := range work { 17 | w := w 18 | eg.Go(func() error { 19 | return DoSomething(ctx, w) 20 | }) 21 | } 22 | if err := eg.Wait(); err != nil { 23 | return err 24 | } 25 | fmt.Println("All success!") 26 | return nil 27 | } 28 | 29 | func DoSomething(ctx context.Context, w int) error { 30 | if w == 2 { 31 | return errors.New("w is two") 32 | } 33 | fmt.Printf("w is %d \n", w) 34 | return nil 35 | } 36 | -------------------------------------------------------------------------------- /base/goroutine/errgroup/err_group_test.go: -------------------------------------------------------------------------------- 1 | package errgroup 2 | 3 | import "testing" 4 | 5 | func TestErrGroup(t *testing.T) { 6 | work := []int{1, 2, 3} 7 | err := Run(work) 8 | if err != nil { 9 | t.Log(err) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /base/goroutine/goroutinue_test.go: -------------------------------------------------------------------------------- 1 | package goroutinue 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | // 当任意有一个完成之后即可返回 9 | func TestGetOne(t *testing.T) { 10 | t.Log(GetOne()) 11 | } 12 | 13 | func TestGetAll(t *testing.T) { 14 | t.Log(GetAll()) 15 | } 16 | 17 | func TestGetAllWithGroup(t *testing.T) { 18 | t.Log(GetAllWithGroup()) 19 | } 20 | 21 | func TestGetWithTimeout(t *testing.T) { 22 | // no time out 23 | result, err := GetWithTimeout(time.Millisecond * 50) 24 | if err != nil { 25 | t.Log(err.Error()) 26 | } 27 | t.Log(result) 28 | // time out 29 | result, err = GetWithTimeout(time.Millisecond * 150) 30 | if err != nil { 31 | t.Log(err.Error()) 32 | } 33 | } 34 | 35 | func TestCancelTask(t *testing.T) { 36 | CancelTask() 37 | } 38 | 39 | func TestCancelAllTask(t *testing.T) { 40 | CancelAllTask() 41 | } 42 | -------------------------------------------------------------------------------- /base/goroutine/pool/pool_test.go: -------------------------------------------------------------------------------- 1 | package pool 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func TestPool(t *testing.T) { 10 | pool, err := NewPool(10) 11 | if err != nil { 12 | panic(err) 13 | } 14 | 15 | for i := 0; i < 20; i++ { 16 | pool.Put(&Task{ 17 | Handler: func(v ...interface{}) { 18 | fmt.Println(v) 19 | }, 20 | Params: []interface{}{i}, 21 | }) 22 | } 23 | 24 | time.Sleep(time.Second * 1) 25 | } 26 | -------------------------------------------------------------------------------- /base/goroutine/pool/sema/sema_pool_test.go: -------------------------------------------------------------------------------- 1 | package sema 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "golang.org/x/sync/semaphore" 7 | "testing" 8 | "time" 9 | ) 10 | 11 | // 通过信号量控制goroutine并发执行的数量 12 | func TestSemaphorePool(t *testing.T) { 13 | s := semaphore.NewWeighted(3) 14 | 15 | ctx := context.Background() 16 | for i := 0; i < 20; i++ { 17 | s.Acquire(ctx, 1) 18 | 19 | go func(i int) { 20 | defer s.Release(1) 21 | fmt.Println(i) 22 | time.Sleep(3 * time.Second) 23 | }(i) 24 | } 25 | 26 | // 请求3个资源保证前面的任务都已经执行完毕 27 | err := s.Acquire(ctx, 3) 28 | if err != nil { 29 | t.Error(err) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /base/goroutine/singleflight/singleflight_test.go: -------------------------------------------------------------------------------- 1 | package singleflight 2 | 3 | import ( 4 | "fmt" 5 | "golang.org/x/sync/singleflight" 6 | "io/ioutil" 7 | "testing" 8 | "time" 9 | ) 10 | 11 | var ( 12 | data string 13 | loadGroup singleflight.Group 14 | ) 15 | 16 | // 使用SingleFlight合并并发请求 17 | // 例如当缓存被击穿时,可以通过singleFlight让一个请求去请求DB即可 18 | // 其他goroutine共享请求DB的结果 19 | func TestSingleFlight(t *testing.T) { 20 | for i := 0; i < 10; i++ { 21 | go func(i int) { 22 | if i == 8 { 23 | // 告诉 group 忘记这个 key。这样一来,之后这个 key 请求会执行 fn 24 | // 而不是等待前一个未完成的 fn 函数的结果 25 | loadGroup.Forget("test") 26 | } 27 | // shared 表示是否将结果分享给了其他goroutine 28 | result, err, shared := loadGroup.Do("test", func() (interface{}, error) { 29 | // 模拟从缓存取 30 | if data != "" { 31 | return data, nil 32 | } 33 | // 模拟从数据库中取 34 | fmt.Println(i) 35 | bs, err := ioutil.ReadFile("test.txt") 36 | return string(bs), err 37 | }) 38 | if err != nil { 39 | t.Error(err) 40 | } 41 | fmt.Println(result, shared) 42 | }(i) 43 | } 44 | 45 | time.Sleep(1 * time.Second) 46 | } 47 | -------------------------------------------------------------------------------- /base/goroutine/singleflight/test.txt: -------------------------------------------------------------------------------- 1 | Hello World -------------------------------------------------------------------------------- /base/http/getpost/get_test.go: -------------------------------------------------------------------------------- 1 | package getpost 2 | 3 | import ( 4 | "io/ioutil" 5 | "net/http" 6 | "testing" 7 | ) 8 | 9 | func TestHttpGet(t *testing.T) { 10 | response, err := http.Get("http://www.baidu.com") 11 | if err != nil { 12 | t.Error(err) 13 | } 14 | defer response.Body.Close() 15 | 16 | data, err := ioutil.ReadAll(response.Body) 17 | if err != nil { 18 | t.Error(err) 19 | } 20 | t.Log(string(data)) 21 | } 22 | -------------------------------------------------------------------------------- /base/http/html/read_html.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | ) 6 | 7 | // 访问静态资源 8 | func main() { 9 | dir := http.Dir("base/http/html/root") 10 | staticHandler := http.FileServer(dir) 11 | http.Handle("/", http.StripPrefix("/", staticHandler)) 12 | 13 | err := http.ListenAndServe(":9090", nil) 14 | if err != nil { 15 | panic(err) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /base/http/html/root/hello.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hello 6 | 7 | 8 | 你好 9 | 10 | -------------------------------------------------------------------------------- /base/http/html/root/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | index 6 | 7 | 8 | 首页 9 | 10 | -------------------------------------------------------------------------------- /base/http/middleware/auth.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import "net/http" 4 | 5 | // 鉴权,判断用户是否登录 6 | func Auth(h http.HandlerFunc) http.HandlerFunc { 7 | return func(w http.ResponseWriter, r *http.Request) { 8 | name := r.FormValue("token") 9 | if name == "pi" { 10 | h.ServeHTTP(w, r) 11 | } else { 12 | w.WriteHeader(403) 13 | } 14 | 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /base/http/middleware/body.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "encoding/json" 5 | "io" 6 | "net/http" 7 | ) 8 | 9 | // 限制body长度 10 | func BodyLimit(handler http.HandlerFunc) http.HandlerFunc { 11 | return func(w http.ResponseWriter, r *http.Request) { 12 | if r.Method != "POST" { 13 | w.WriteHeader(http.StatusMethodNotAllowed) 14 | return 15 | } 16 | 17 | var maxLength int64 = 128 18 | var body = make(map[string]interface{}) 19 | 20 | err := json.NewDecoder(io.LimitReader(r.Body, maxLength)).Decode(&body) 21 | if err != nil { 22 | w.Header().Set("Content-Type", "application/json; charset=UTF-8") 23 | w.WriteHeader(http.StatusBadRequest) 24 | w.Write([]byte("Body length illegal")) 25 | } else { 26 | handler.ServeHTTP(w, r) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /base/http/middleware/ip.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "golang.org/x/time/rate" 5 | "net/http" 6 | "sync" 7 | ) 8 | 9 | /** 10 | 基于 IP 限制 HTTP 访问频率 11 | */ 12 | var ( 13 | ipLimitMaps = make(map[string]*rate.Limiter) 14 | mu sync.Mutex 15 | rateLimit = 1 // 每秒往池子填充的令牌数 16 | rateMax = 5 // 池子最大的令牌数 17 | ) 18 | 19 | func GetIPLimiter(ip string) *rate.Limiter { 20 | mu.Lock() 21 | defer mu.Unlock() 22 | 23 | if limiter, ok := ipLimitMaps[ip]; ok { 24 | return limiter 25 | } 26 | limiter := rate.NewLimiter(rate.Limit(rateLimit), rateMax) 27 | ipLimitMaps[ip] = limiter 28 | 29 | return limiter 30 | } 31 | 32 | // 限制IP访问频率 33 | // 作用在Server, 对所有访问进行限制 34 | func IPRateLimit(handler http.Handler) http.Handler { 35 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 36 | limiter := GetIPLimiter(r.RemoteAddr) 37 | // 如果想不丢掉此次请求,请使用Wait方法 38 | if !limiter.Allow() { 39 | http.Error(w, http.StatusText(http.StatusTooManyRequests), http.StatusTooManyRequests) 40 | return 41 | } 42 | handler.ServeHTTP(w, r) 43 | }) 44 | } 45 | -------------------------------------------------------------------------------- /base/http/middleware/main/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "go-demo/base/http/middleware" 6 | "net/http" 7 | ) 8 | 9 | func main() { 10 | mux := http.NewServeMux() 11 | 12 | mux.HandleFunc("/", middleware.BodyLimit(hello)) 13 | 14 | mux.HandleFunc("/admin", middleware.Auth(hello)) 15 | 16 | go fmt.Println("server staring...") 17 | 18 | if err := http.ListenAndServe(":8081", middleware.IPRateLimit(mux)); err != nil { 19 | panic(err) 20 | } 21 | } 22 | 23 | func hello(w http.ResponseWriter, r *http.Request) { 24 | fmt.Fprint(w, "Hello") 25 | } 26 | -------------------------------------------------------------------------------- /base/http/server/index.html: -------------------------------------------------------------------------------- 1 | 2 | Pibigstar 3 | 4 |

欢迎上传

5 | 6 |
7 |    8 |    9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /base/http/url/url_test.go: -------------------------------------------------------------------------------- 1 | package url 2 | 3 | import ( 4 | "net/url" 5 | "testing" 6 | ) 7 | 8 | func TestUrlEncode(t *testing.T) { 9 | v := url.Values{} 10 | v.Add("orgId", "123456") 11 | v.Add("userId", "pibigstar") 12 | 13 | // url编码 14 | body := v.Encode() 15 | t.Log(body) 16 | // url解码 17 | values, err := url.ParseQuery(body) 18 | if err != nil { 19 | t.Error(err) 20 | } 21 | t.Log(values.Get("orgId")) 22 | } 23 | 24 | func TestPathEscape(t *testing.T) { 25 | // 将 // ? 这些特殊字符编码 26 | encode := url.PathEscape("http://www.baidu.com?username=123&password=456") 27 | t.Log(encode) 28 | 29 | // 将 %2F 解码 为 / 30 | decode, err := url.PathUnescape(encode) 31 | if err != nil { 32 | t.Error(err) 33 | } 34 | t.Log(decode) 35 | } 36 | -------------------------------------------------------------------------------- /base/instruct/instruct.go: -------------------------------------------------------------------------------- 1 | package instruct 2 | 3 | // 禁止内联 4 | //go:noinline 5 | func Test1() { 6 | 7 | } 8 | 9 | // 禁止进行竞态检测 10 | //go:norace 11 | func Test2() { 12 | 13 | } 14 | 15 | // 禁止编译器对其做逃逸分析 16 | //go:noescape 17 | func Test3() { 18 | 19 | } 20 | 21 | // 禁止内联 22 | //go:noinline 23 | func Test4() { 24 | 25 | } 26 | -------------------------------------------------------------------------------- /base/instruct/linkname/impl/impl.go: -------------------------------------------------------------------------------- 1 | package impl 2 | 3 | import ( 4 | "fmt" 5 | _ "unsafe" // for go:linkname 6 | ) 7 | 8 | // 通过访问 outer.Linkname 就是访问该函数 9 | //go:linkname hello go-demo/base/instruct/linkname/outer.Linkname 10 | func hello() { 11 | fmt.Println("hello world") 12 | } 13 | -------------------------------------------------------------------------------- /base/instruct/linkname/linkname.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "go-demo/base/instruct/linkname/outer" 5 | ) 6 | 7 | // 原本 impl 里面的 hello 函数是非导出的 8 | // 通过 //go:linkname 变相的调用了 impl 包里非导出函数 9 | 10 | func main() { 11 | outer.Linkname() 12 | } 13 | -------------------------------------------------------------------------------- /base/instruct/linkname/outer/i.s: -------------------------------------------------------------------------------- 1 | // 绕过编译时的 -complete 检查 -------------------------------------------------------------------------------- /base/instruct/linkname/outer/outer.go: -------------------------------------------------------------------------------- 1 | package outer 2 | 3 | import ( 4 | _ "go-demo/base/instruct/linkname/impl" 5 | ) 6 | 7 | func Linkname() 8 | -------------------------------------------------------------------------------- /base/io/io.txt: -------------------------------------------------------------------------------- 1 | pibigstar111 -------------------------------------------------------------------------------- /base/io/reader_writer_test.go: -------------------------------------------------------------------------------- 1 | package io 2 | 3 | import ( 4 | "bytes" 5 | "os" 6 | "testing" 7 | ) 8 | 9 | // io.Writer接口,将数据写入到某处 10 | // io.Reader接口,从某处读取数据 11 | func TestWriterReader(t *testing.T) { 12 | var b bytes.Buffer 13 | b.WriteString("Hello ") 14 | 15 | // 以只可读的方式打开 16 | file, err := os.Open("io.txt") 17 | if err != nil { 18 | t.Error(err) 19 | } 20 | 21 | // 从实现了io.Reader接口的对象中读取全部数据 22 | // ioutil.ReadAll 底层就是用该函数实现的 23 | _, err = b.ReadFrom(file) 24 | if err != nil { 25 | t.Error(err) 26 | } 27 | // 返回字节数组 28 | bs := b.Bytes() 29 | t.Log(string(bs)) 30 | 31 | // 返回字符串 32 | s := b.String() 33 | t.Log(s) 34 | 35 | // 重置 36 | b.Reset() 37 | 38 | b.WriteString("pibigstar") 39 | // 以可读写的方式打开 40 | file, err = os.OpenFile("io.txt", os.O_RDWR, 0) 41 | if err != nil { 42 | t.Error(err) 43 | } 44 | // 将数据写入到实现了io.Writer接口的对象中 45 | _, err = b.WriteTo(file) 46 | if err != nil { 47 | t.Error(err) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /base/mail/send_email.go: -------------------------------------------------------------------------------- 1 | package mail 2 | 3 | import ( 4 | "log" 5 | "net/smtp" 6 | ) 7 | 8 | const ( 9 | username = "741047261@qq.com" 10 | password = "授权码" 11 | host = "smtp.qq.com" 12 | addr = "smtp.qq.com:25" 13 | ) 14 | 15 | // 发送邮件 16 | func SendEmail() { 17 | 18 | auth := smtp.PlainAuth("", username, password, host) 19 | 20 | user := "741047261@qq.com" 21 | to := []string{"123456789@qq.com"} 22 | msg := []byte(`From: 741047261@qq.com 23 | To: 123456789@qq.com 24 | Subject: 测试邮件 25 | 26 | 这是邮件内容 27 | `) 28 | 29 | err := smtp.SendMail(addr, auth, user, to, msg) 30 | if err != nil { 31 | log.Printf("Error when send email:%s", err.Error()) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /base/net/chat-room/README.md: -------------------------------------------------------------------------------- 1 | # GO的Socket编程 2 | 3 | ## 实现一个简单的聊天室 4 | 1. 运行server.go 5 | 2. 运行client.go 6 | -------------------------------------------------------------------------------- /base/net/net.go: -------------------------------------------------------------------------------- 1 | package netx 2 | 3 | import ( 4 | "net" 5 | "net/url" 6 | ) 7 | 8 | // 取url的ip地址 9 | func GetIp(_url string) (string, error) { 10 | pu, err := url.Parse(_url) 11 | if err != nil { 12 | return "", err 13 | } 14 | host := pu.Hostname() 15 | port := pu.Port() 16 | if port == "" { 17 | port = "80" 18 | if pu.Scheme == "https" { 19 | port = "443" 20 | } 21 | } 22 | 23 | addr, err := net.ResolveTCPAddr("tcp", net.JoinHostPort(host, port)) 24 | if err != nil { 25 | return "", err 26 | } 27 | return addr.IP.String(), nil 28 | } 29 | -------------------------------------------------------------------------------- /base/net/net_test.go: -------------------------------------------------------------------------------- 1 | package netx_test 2 | 3 | import ( 4 | netx "go-demo/base/net" 5 | "testing" 6 | ) 7 | 8 | func TestGetIp(t *testing.T) { 9 | ip, err := netx.GetIp("http://www.baidu.com") 10 | if err != nil { 11 | t.Error(err) 12 | } 13 | t.Log(ip) 14 | } 15 | -------------------------------------------------------------------------------- /base/net/tcp/client/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "net" 7 | "os" 8 | "strings" 9 | ) 10 | 11 | func main() { 12 | conn, err := net.Dial("tcp", ":8002") 13 | if err != nil { 14 | panic(err) 15 | } 16 | defer conn.Close() 17 | 18 | reader := bufio.NewReader(os.Stdin) 19 | fmt.Println("Please write your msg....") 20 | for { 21 | data, err := reader.ReadString('\n') 22 | if err != nil { 23 | fmt.Printf("read string from terminal, err: %v \n", err) 24 | break 25 | } 26 | data = strings.TrimSpace(data) 27 | 28 | _, err = conn.Write([]byte(data)) 29 | if err != nil { 30 | fmt.Printf("failed to write server: %v \n", err) 31 | } 32 | if data == "exit" { 33 | break 34 | } 35 | 36 | var result [1024]byte 37 | n, err := conn.Read(result[:]) 38 | if err != nil { 39 | fmt.Printf("failed to read result from server, err: %v \n", err) 40 | continue 41 | } 42 | fmt.Printf("read result from server: %s \n", string(result[:n])) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /base/net/tcp/server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net" 7 | ) 8 | 9 | func main() { 10 | listener, err := net.Listen("tcp", ":8002") 11 | if err != nil { 12 | panic(err) 13 | } 14 | 15 | fmt.Println("tcp server is running...") 16 | 17 | for { 18 | conn, err := listener.Accept() 19 | if err != nil { 20 | panic(err) 21 | } 22 | go process(conn) 23 | } 24 | } 25 | 26 | func process(conn net.Conn) { 27 | defer conn.Close() 28 | 29 | for { 30 | var data [1024]byte 31 | n, err := conn.Read(data[:]) 32 | if err != nil && err != io.EOF { 33 | fmt.Printf("failed to read msg from client, err: %v \n", err) 34 | break 35 | } 36 | 37 | str := string(data[:n]) 38 | if str == "exit" { 39 | fmt.Println("client exit...") 40 | break 41 | } 42 | fmt.Printf("read msg: %s \n", str) 43 | 44 | // 回复ACK 45 | conn.Write([]byte(fmt.Sprintf("%s OK!", str))) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /base/net/udp/client/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | ) 7 | 8 | func main() { 9 | conn, err := net.DialUDP("udp", nil, &net.UDPAddr{ 10 | IP: net.IPv4(0, 0, 0, 0), 11 | Port: 8001, 12 | }) 13 | if err != nil { 14 | panic(err) 15 | } 16 | 17 | for i := 0; i < 100; i++ { 18 | _, err := conn.Write([]byte("Hello World!")) 19 | if err != nil { 20 | fmt.Printf("failed to write msg, err: %v \n", err) 21 | break 22 | } 23 | var result [1024]byte 24 | n, addr, err := conn.ReadFromUDP(result[:]) 25 | if err != nil { 26 | fmt.Printf("failed to receiver msg, addr: %v, err: %v \n", addr, err) 27 | } 28 | fmt.Printf("receiver msg from: %v, msg: %s \n", addr, string(result[:n])) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /base/net/udp/server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net" 7 | ) 8 | 9 | func main() { 10 | // 监听UDP 11 | listen, err := net.ListenUDP("udp", &net.UDPAddr{ 12 | IP: net.IPv4(0, 0, 0, 0), 13 | Port: 8001, 14 | }) 15 | if err != nil { 16 | panic(err) 17 | } 18 | fmt.Println("upd server listening...") 19 | 20 | for { 21 | var data [1024]byte 22 | n, addr, err := listen.ReadFromUDP(data[:]) 23 | if err != nil && err != io.EOF { 24 | fmt.Println(err) 25 | break 26 | } 27 | 28 | // 回复消息 29 | go func() { 30 | str := string(data[:n]) 31 | fmt.Printf("read data: %s, addr: %v, count: %d \n", str, addr, n) 32 | 33 | n, err = listen.WriteToUDP([]byte(fmt.Sprintf("%s OK!", str)), addr) 34 | if err != nil { 35 | fmt.Printf("failed to write udp, addr: %v \n", addr) 36 | } 37 | }() 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /base/pool/pool_test.go: -------------------------------------------------------------------------------- 1 | package pool 2 | 3 | import ( 4 | "sync" 5 | "testing" 6 | ) 7 | 8 | var pool = sync.Pool{ 9 | New: func() interface{} { 10 | return new(smmall) 11 | }} 12 | 13 | type smmall struct { 14 | a int 15 | } 16 | 17 | //go:noinline 18 | func inc(s *smmall) { s.a++ } 19 | 20 | func BenchmarkWithoutPool(b *testing.B) { 21 | var s *smmall 22 | for i := 0; i < b.N; i++ { 23 | s = &smmall{a: 1} 24 | b.StopTimer() 25 | inc(s) 26 | b.StartTimer() 27 | } 28 | } 29 | 30 | func BenchmarkWithPool(b *testing.B) { 31 | var s *smmall 32 | for i := 0; i < b.N; i++ { 33 | s = pool.Get().(*smmall) 34 | s.a = 1 35 | b.StopTimer() 36 | inc(s) 37 | b.StartTimer() 38 | pool.Put(s) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /base/rpc/lv1/client/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/rpc" 6 | ) 7 | 8 | func main() { 9 | client, err := rpc.Dial("tcp", ":8000") 10 | if err != nil { 11 | panic(err) 12 | } 13 | var result string 14 | err = client.Call("HelloService.Hello", "demo1", &result) 15 | if err != nil { 16 | panic(err) 17 | } 18 | fmt.Println(result) 19 | } 20 | -------------------------------------------------------------------------------- /base/rpc/lv1/server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "net/rpc" 7 | ) 8 | 9 | type HelloService struct { 10 | } 11 | 12 | // 方法只能有两个可序列化的参数,其中第二个参数是指针类型, 13 | // 并且返回一个error类型,同时必须是公开的方法。 14 | func (*HelloService) Hello(req string, resp *string) error { 15 | fmt.Printf("Hello: %s \n", req) 16 | *resp = "Hello: " + req 17 | return nil 18 | } 19 | 20 | func main() { 21 | // 注册服务 22 | rpc.RegisterName("HelloService", &HelloService{}) 23 | 24 | listener, err := net.Listen("tcp", ":8000") 25 | if err != nil { 26 | panic(err) 27 | } 28 | for { 29 | conn, err := listener.Accept() 30 | if err != nil { 31 | panic(err) 32 | } 33 | // 调用 rpc server 34 | go rpc.ServeConn(conn) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /base/rpc/lv2/HelloService.pb.go: -------------------------------------------------------------------------------- 1 | package pb 2 | 3 | import ( 4 | "net/rpc" 5 | ) 6 | 7 | // 定义 rpc server 8 | const HelloServiceName = "rpc/demo/HelloService" 9 | 10 | type HelloServiceInterface = interface { 11 | Hello(req string, resp *string) error 12 | } 13 | 14 | func RegisterHelloService(svc HelloServiceInterface) error { 15 | return rpc.RegisterName(HelloServiceName, svc) 16 | } 17 | 18 | // 定义 rpc client 19 | type HelloServiceClient struct { 20 | *rpc.Client 21 | } 22 | 23 | var _ HelloServiceInterface = (*HelloServiceClient)(nil) 24 | 25 | func (h *HelloServiceClient) Hello(req string, resp *string) error { 26 | return h.Call(HelloServiceName+".Hello", req, resp) 27 | } 28 | 29 | func DialHelloServiceClient(address string) (*HelloServiceClient, error) { 30 | client, err := rpc.Dial("tcp", address) 31 | if err != nil { 32 | return nil, err 33 | } 34 | return &HelloServiceClient{Client: client}, nil 35 | } 36 | -------------------------------------------------------------------------------- /base/rpc/lv2/client/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | pb "go-demo/base/rpc/lv2" 6 | ) 7 | 8 | func main() { 9 | client, err := pb.DialHelloServiceClient(":8000") 10 | if err != nil { 11 | panic(err) 12 | } 13 | var resp string 14 | err = client.Hello("demo2", &resp) 15 | if err != nil { 16 | fmt.Printf("err: %s \n", err.Error()) 17 | } 18 | fmt.Println(resp) 19 | } 20 | -------------------------------------------------------------------------------- /base/rpc/lv2/server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "net/rpc" 7 | 8 | pb "go-demo/base/rpc/lv2" 9 | ) 10 | 11 | type HelloService struct { 12 | } 13 | 14 | // 方法只能有两个可序列化的参数,其中第二个参数是指针类型, 15 | // 并且返回一个error类型,同时必须是公开的方法。 16 | func (*HelloService) Hello(req string, resp *string) error { 17 | fmt.Printf("Hello: %s \n", req) 18 | *resp = "Hello: " + req 19 | return nil 20 | } 21 | 22 | func main() { 23 | err := pb.RegisterHelloService(&HelloService{}) 24 | if err != nil { 25 | panic(err) 26 | } 27 | 28 | listener, err := net.Listen("tcp", ":8000") 29 | if err != nil { 30 | panic(err) 31 | } 32 | 33 | for { 34 | conn, err := listener.Accept() 35 | if err != nil { 36 | continue 37 | } 38 | go rpc.ServeConn(conn) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /base/rpc/lv3/HelloService.pb.go: -------------------------------------------------------------------------------- 1 | package lv3 2 | 3 | import ( 4 | "net" 5 | "net/rpc" 6 | "net/rpc/jsonrpc" 7 | ) 8 | 9 | // 定义 rpc server 10 | const HelloServiceName = "rpc/demo/HelloService" 11 | 12 | type HelloServiceInterface = interface { 13 | Hello(req string, resp *string) error 14 | } 15 | 16 | func RegisterHelloService(svc HelloServiceInterface) error { 17 | return rpc.RegisterName(HelloServiceName, svc) 18 | } 19 | 20 | // 定义 rpc client 21 | type HelloServiceClient struct { 22 | *rpc.Client 23 | } 24 | 25 | var _ HelloServiceInterface = (*HelloServiceClient)(nil) 26 | 27 | func (h *HelloServiceClient) Hello(req string, resp *string) error { 28 | return h.Call(HelloServiceName+".Hello", req, resp) 29 | } 30 | 31 | func DialHelloServiceClient(address string) (*HelloServiceClient, error) { 32 | conn, err := net.Dial("tcp", address) 33 | if err != nil { 34 | return nil, err 35 | } 36 | client := rpc.NewClientWithCodec(jsonrpc.NewClientCodec(conn)) 37 | return &HelloServiceClient{Client: client}, nil 38 | } 39 | -------------------------------------------------------------------------------- /base/rpc/lv3/client/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | pb "go-demo/base/rpc/lv3" 6 | ) 7 | 8 | func main() { 9 | client, err := pb.DialHelloServiceClient(":8000") 10 | if err != nil { 11 | panic(err) 12 | } 13 | var resp string 14 | err = client.Hello("demo3", &resp) 15 | if err != nil { 16 | fmt.Printf("err: %s \n", err.Error()) 17 | } 18 | fmt.Println(resp) 19 | } 20 | -------------------------------------------------------------------------------- /base/rpc/lv3/server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "net/rpc" 7 | "net/rpc/jsonrpc" 8 | 9 | pb "go-demo/base/rpc/lv3" 10 | ) 11 | 12 | type HelloService struct { 13 | } 14 | 15 | // 方法只能有两个可序列化的参数,其中第二个参数是指针类型, 16 | // 并且返回一个error类型,同时必须是公开的方法。 17 | func (*HelloService) Hello(req string, resp *string) error { 18 | fmt.Printf("Hello: %s \n", req) 19 | *resp = "Hello: " + req 20 | return nil 21 | } 22 | 23 | func main() { 24 | err := pb.RegisterHelloService(&HelloService{}) 25 | if err != nil { 26 | panic(err) 27 | } 28 | 29 | listener, err := net.Listen("tcp", ":8000") 30 | if err != nil { 31 | panic(err) 32 | } 33 | 34 | for { 35 | conn, err := listener.Accept() 36 | if err != nil { 37 | continue 38 | } 39 | go rpc.ServeCodec(jsonrpc.NewServerCodec(conn)) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /base/rpc/lv4/HelloService.pb.go: -------------------------------------------------------------------------------- 1 | package pb 2 | 3 | import ( 4 | "io" 5 | "net/http" 6 | "net/rpc" 7 | "net/rpc/jsonrpc" 8 | ) 9 | 10 | // 定义 rpc server 11 | const HelloServiceName = "HelloService" 12 | 13 | type HelloServiceInterface = interface { 14 | Hello(req string, resp *string) error 15 | } 16 | 17 | func RegisterHelloService(svc HelloServiceInterface) error { 18 | return rpc.RegisterName(HelloServiceName, svc) 19 | } 20 | 21 | // 定义 rpc client 22 | type HelloServiceClient struct { 23 | *rpc.Client 24 | } 25 | 26 | var _ HelloServiceInterface = (*HelloServiceClient)(nil) 27 | 28 | func (h *HelloServiceClient) Hello(req string, resp *string) error { 29 | return h.Call(HelloServiceName+".Hello", req, resp) 30 | } 31 | 32 | func HandleRPCHTTP(w http.ResponseWriter, r *http.Request) { 33 | var conn io.ReadWriteCloser = struct { 34 | io.Writer 35 | io.ReadCloser 36 | }{ 37 | ReadCloser: r.Body, 38 | Writer: w, 39 | } 40 | 41 | err := rpc.ServeRequest(jsonrpc.NewServerCodec(conn)) 42 | if err != nil { 43 | panic(err) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /base/rpc/lv4/server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | pb "go-demo/base/rpc/lv4" 6 | "net/http" 7 | ) 8 | 9 | type HelloService struct { 10 | } 11 | 12 | // 方法只能有两个可序列化的参数,其中第二个参数是指针类型, 13 | // 并且返回一个error类型,同时必须是公开的方法。 14 | func (*HelloService) Hello(req string, resp *string) error { 15 | fmt.Printf("Hello: %s \n", req) 16 | *resp = "Hello: " + req 17 | return nil 18 | } 19 | 20 | func main() { 21 | err := pb.RegisterHelloService(&HelloService{}) 22 | if err != nil { 23 | panic(err) 24 | } 25 | 26 | http.HandleFunc("/hello", pb.HandleRPCHTTP) 27 | 28 | if err := http.ListenAndServe(":8000", nil); err != nil { 29 | panic(err) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /base/rpc/lv5/client/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | pb "go-demo/base/rpc/lv5" 6 | ) 7 | 8 | func main() { 9 | client, err := pb.DialHelloServiceClient("localhost:1234") 10 | if err != nil { 11 | panic(err) 12 | } 13 | var result string 14 | err = client.Hello("test", &result) 15 | if err != nil { 16 | panic(err) 17 | } 18 | fmt.Println(result) 19 | 20 | // 异步调用 21 | call := client.AsyncHello("async test", &result, nil) 22 | <-call.Done 23 | fmt.Println(result) 24 | } 25 | -------------------------------------------------------------------------------- /base/rpc/lv5/server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/tls" 5 | "fmt" 6 | pb "go-demo/base/rpc/lv5" 7 | "net/rpc" 8 | ) 9 | 10 | type HelloService struct { 11 | } 12 | 13 | func (*HelloService) Hello(req string, resp *string) error { 14 | *resp = "this is http rpc: " + req 15 | return nil 16 | } 17 | 18 | func main() { 19 | err := pb.RegisterHelloService(&HelloService{}) 20 | if err != nil { 21 | panic(err) 22 | } 23 | 24 | pb.HandleHTTP() 25 | fmt.Println("server staring....") 26 | 27 | cert, err := tls.LoadX509KeyPair("base/rpc/lv5/ssl/server.crt", "base/rpc/lv5/ssl/server.key") 28 | if err != nil { 29 | panic(err) 30 | } 31 | config := &tls.Config{ 32 | Certificates: []tls.Certificate{cert}, 33 | } 34 | listener, _ := tls.Listen("tcp", ":1234", config) 35 | 36 | for { 37 | conn, _ := listener.Accept() 38 | defer conn.Close() 39 | go rpc.ServeConn(conn) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /base/runtime/trace_test.go: -------------------------------------------------------------------------------- 1 | package runtime 2 | 3 | import ( 4 | "runtime/debug" 5 | "testing" 6 | ) 7 | 8 | func TestPrintTrace(t *testing.T) { 9 | test1() 10 | } 11 | 12 | func test1() { 13 | test2() 14 | } 15 | 16 | func test2() { 17 | debug.PrintStack() 18 | } 19 | -------------------------------------------------------------------------------- /base/semaphore/semaphore_test.go: -------------------------------------------------------------------------------- 1 | package semaphore 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "golang.org/x/sync/semaphore" 7 | "runtime" 8 | "testing" 9 | "time" 10 | ) 11 | 12 | var ( 13 | maxWorkers = runtime.GOMAXPROCS(0) // worker数量 14 | sema = semaphore.NewWeighted(int64(maxWorkers)) // 信号量 15 | task = make([]int, maxWorkers*4) // 任务数,是worker的四倍 16 | ) 17 | 18 | // 通过信号量可以有效的控制goroutine的数量 19 | func TestSemaphore(t *testing.T) { 20 | ctx := context.Background() 21 | for i := range task { 22 | // 请求一个worker,如果没有worker可用,会阻塞在这里,直到某个worker被释放 23 | if err := sema.Acquire(ctx, 1); err != nil { 24 | break 25 | } 26 | 27 | // 启动worker goroutine 28 | go func(i int) { 29 | // 释放一个worker 30 | defer sema.Release(1) 31 | time.Sleep(100 * time.Millisecond) // 模拟一个耗时操作 32 | task[i] = i + 1 33 | }(i) 34 | } 35 | 36 | // 请求所有的worker,这样能确保前面的worker都执行完 37 | if err := sema.Acquire(ctx, int64(maxWorkers)); err != nil { 38 | t.Logf("获取所有的worker失败: %v", err) 39 | } 40 | 41 | fmt.Println(task) 42 | } 43 | -------------------------------------------------------------------------------- /base/sync/atomic/atomic.go: -------------------------------------------------------------------------------- 1 | package sync 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "reflect" 7 | "sync/atomic" 8 | ) 9 | 10 | //存储type,可以实现安全存储不会引发panic 11 | type AtomicValue struct { 12 | v atomic.Value 13 | t reflect.Type 14 | } 15 | 16 | func NewAtomicValue() *AtomicValue { 17 | return &AtomicValue{} 18 | } 19 | 20 | func (av *AtomicValue) Store(v interface{}) error { 21 | if v == nil { 22 | return errors.New("atomic value cannot be nil") 23 | } 24 | // first set value 25 | if av.v.Load() == nil { 26 | av.v.Store(v) 27 | av.t = reflect.TypeOf(v) 28 | return nil 29 | } 30 | t := reflect.TypeOf(v) 31 | if t != av.t { 32 | return fmt.Errorf("failed to store value,type:%s", t) 33 | } 34 | av.v.Store(v) 35 | return nil 36 | } 37 | 38 | func (av *AtomicValue) Load() interface{} { 39 | return av.v.Load() 40 | } 41 | 42 | func (av *AtomicValue) TypeOfValue() reflect.Type { 43 | return av.t 44 | } 45 | -------------------------------------------------------------------------------- /base/sync/atomic/atomic_test.go: -------------------------------------------------------------------------------- 1 | package sync 2 | 3 | import ( 4 | "sync" 5 | "sync/atomic" 6 | "testing" 7 | ) 8 | 9 | // 原子存储 10 | func TestAtomicValue(t *testing.T) { 11 | v := "pibigstar" 12 | av := NewAtomicValue() 13 | av.Store(v) 14 | t.Log(av.Load()) 15 | } 16 | 17 | // 不使用原子操作做加减 18 | func TestNotUserAtomic(t *testing.T) { 19 | // not use atomic 20 | var sum = 10 21 | var wg sync.WaitGroup 22 | for i := 0; i < 10; i++ { 23 | wg.Add(1) 24 | go func(t int) { 25 | defer wg.Done() 26 | sum += t 27 | }(i) 28 | } 29 | wg.Wait() 30 | t.Log(sum) 31 | } 32 | 33 | // 使用原子操作进行加减 34 | func TestUseAtomic(t *testing.T) { 35 | var sum int32 = 10 36 | var wg sync.WaitGroup 37 | for i := int32(0); i < 10; i++ { 38 | wg.Add(1) 39 | go func(t int32) { 40 | defer wg.Done() 41 | // 确保了同一时间只有一个Goroutine执行该操作 42 | atomic.AddInt32(&sum, t) 43 | }(i) 44 | } 45 | wg.Wait() 46 | t.Log(sum) 47 | } 48 | 49 | // 采用CSA的原子操作 50 | func TestUseAtomicCAS(t *testing.T) { 51 | var sum int32 = 10 52 | var wg sync.WaitGroup 53 | for i := int32(0); i < 10; i++ { 54 | wg.Add(1) 55 | go func(t int32) { 56 | defer wg.Done() 57 | // 先比较变量的值是否等于给定旧值,等于旧值的情况下才赋予新值,最后返回新值是否设置成功 58 | atomic.CompareAndSwapInt32(&sum, sum, sum+t) 59 | }(i) 60 | } 61 | wg.Wait() 62 | t.Log(sum) 63 | } 64 | 65 | func TestSpinLock(t *testing.T) { 66 | t.Log(SpinLock()) 67 | } 68 | -------------------------------------------------------------------------------- /base/sync/atomic/spinlock.go: -------------------------------------------------------------------------------- 1 | package sync 2 | 3 | import ( 4 | "sync/atomic" 5 | "time" 6 | ) 7 | 8 | // 利用CompareAndSwapInt32实现一个自旋锁 9 | func SpinLock() int32 { 10 | var num int32 = 1 11 | for { 12 | if atomic.CompareAndSwapInt32(&num, 1, 0) { 13 | break 14 | } 15 | time.Sleep(time.Microsecond * 100) 16 | } 17 | return num 18 | } 19 | -------------------------------------------------------------------------------- /base/sync/cond_test.go: -------------------------------------------------------------------------------- 1 | package sync 2 | 3 | import ( 4 | "sync" 5 | "testing" 6 | ) 7 | 8 | var ( 9 | mailbox uint8 10 | lock sync.RWMutex 11 | sendCond = sync.NewCond(&lock) 12 | recvCond = sync.NewCond(lock.RLocker()) 13 | // sign 用于传递演示完成的信号。 14 | sign = make(chan struct{}, 2) 15 | max = 5 16 | ) 17 | 18 | // 条件变量,主要起到一个通知的作用 19 | func TestCond(t *testing.T) { 20 | // 往mailbox放置信息 21 | go func(max int) { 22 | defer func() { 23 | sign <- struct{}{} 24 | }() 25 | for i := 1; i <= max; i++ { 26 | lock.Lock() 27 | // 如果信箱罗里面有信息了,则让发送方等待 28 | for mailbox == 1 { 29 | sendCond.Wait() 30 | } 31 | // 信箱没信息,放置信息 32 | mailbox = 1 33 | t.Logf("第%d次:放置信息", i) 34 | lock.Unlock() 35 | // 通知接收方可以获取信了 36 | recvCond.Signal() 37 | } 38 | }(max) 39 | 40 | // 从mailbox读取信息 41 | go func(max int) { 42 | defer func() { 43 | sign <- struct{}{} 44 | }() 45 | for i := 1; i <= max; i++ { 46 | lock.RLock() 47 | // 如果信箱没信息,则让接受方等待 48 | for mailbox == 0 { 49 | recvCond.Wait() 50 | } 51 | mailbox = 0 52 | t.Logf("第%d次:取走信息", i) 53 | lock.RUnlock() 54 | // 通知发送方可以继续放置信了 55 | sendCond.Signal() 56 | } 57 | }(max) 58 | 59 | <-sign 60 | <-sign 61 | } 62 | -------------------------------------------------------------------------------- /base/sync/sync_map_test.go: -------------------------------------------------------------------------------- 1 | package sync 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "testing" 7 | ) 8 | 9 | func TestSyncMap(t *testing.T) { 10 | // 开箱即用 11 | var users sync.Map 12 | 13 | users.Store("pi", 1) 14 | users.Store("big", 2) 15 | users.Store("star", 3) 16 | 17 | if u1, ok := users.Load("pi"); ok { 18 | fmt.Println(u1) 19 | } 20 | 21 | // 如果存在则value为存在中的,如果不存在,则为66,并将其放入map 22 | value, exist := users.LoadOrStore("hello", 66) 23 | fmt.Println(value, exist) 24 | 25 | users.Range(func(key, value interface{}) bool { 26 | fmt.Println(key, value) 27 | return true 28 | }) 29 | 30 | users.Delete("pi") 31 | fmt.Println("=======delete========") 32 | users.Range(func(key, value interface{}) bool { 33 | fmt.Println(key, value) 34 | return true 35 | }) 36 | 37 | } 38 | -------------------------------------------------------------------------------- /base/sync/sync_mutex_test.go: -------------------------------------------------------------------------------- 1 | package sync 2 | 3 | import ( 4 | "log" 5 | "os" 6 | "sync" 7 | "testing" 8 | "time" 9 | ) 10 | 11 | var ( 12 | locker sync.Mutex 13 | rwLocker sync.RWMutex 14 | ) 15 | 16 | /** 17 | 排它锁sync.Mutex 18 | 只能加一次锁,重复加锁会导致死锁 19 | */ 20 | func TestMutex(t *testing.T) { 21 | go mutex("1") 22 | go mutex("2") 23 | time.Sleep(time.Millisecond * 10) 24 | } 25 | 26 | // 读写锁 27 | func TestRWMutex(t *testing.T) { 28 | go rwMutex("1") 29 | go rwMutex("2") 30 | time.Sleep(time.Millisecond * 10) 31 | } 32 | 33 | func mutex(str string) string { 34 | log.Printf("first:%s", str) 35 | locker.Lock() 36 | log.Printf("this is test %s", str) 37 | if str == "1" { 38 | return "1" 39 | } 40 | time.Sleep(1 * time.Second) //模拟数据库耗时操作 41 | os.Exit(0) //直接退出,验证2 是否执行了 42 | locker.Unlock() 43 | return "2" 44 | } 45 | 46 | func rwMutex(str string) string { 47 | log.Printf("first:%s", str) 48 | rwLocker.RLock() // 加读锁 49 | rwLocker.RLock() // 可重复加读锁 50 | 51 | //rwLocker.Lock() // 加写锁,只能加一次,一个goroutine中加过之后,另一个再加锁会抛出恐慌 52 | 53 | log.Printf("this is test %s", str) 54 | if str == "1" { 55 | return "1" 56 | } 57 | time.Sleep(1 * time.Second) //模拟数据库耗时操作 58 | defer rwLocker.RUnlock() // 释放读锁 59 | defer rwLocker.RUnlock() 60 | 61 | return "2" 62 | } 63 | -------------------------------------------------------------------------------- /base/template/template.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import ( 4 | "bytes" 5 | "github.com/pkg/errors" 6 | "go/format" 7 | "text/template" 8 | ) 9 | 10 | // tpl 生成代码需要用到模板 11 | const tpl = ` 12 | package {{.pkg}} 13 | 14 | // messages get msg from const comment 15 | var messages = map[string]string{ 16 | {{range $key, $value := .comments}} 17 | {{$key}}: "{{$value}}",{{end}} 18 | } 19 | 20 | // GetErrMsg get error msg 21 | func GetErrMsg(code string) string { 22 | if msg, ok := messages[code]; ok { 23 | return msg 24 | } 25 | return "" 26 | } 27 | ` 28 | 29 | // gen 生成代码 30 | func gen(comments map[string]string) ([]byte, error) { 31 | var buf = bytes.NewBufferString("") 32 | 33 | data := map[string]interface{}{ 34 | "pkg": "main", 35 | "comments": comments, 36 | } 37 | 38 | t, err := template.New("").Parse(tpl) 39 | if err != nil { 40 | return nil, errors.Wrapf(err, "template init err") 41 | } 42 | 43 | err = t.Execute(buf, data) 44 | if err != nil { 45 | return nil, errors.Wrapf(err, "template data err") 46 | } 47 | 48 | return format.Source(buf.Bytes()) 49 | } 50 | -------------------------------------------------------------------------------- /base/template/template_test.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestGen(t *testing.T) { 9 | m := make(map[string]string) 10 | m["Hello"] = "你好" 11 | m["Test"] = "测试" 12 | m["Error"] = "错误" 13 | bs, err := gen(m) 14 | if err != nil { 15 | t.Error(err) 16 | } 17 | fmt.Println(string(bs)) 18 | } 19 | -------------------------------------------------------------------------------- /base/time/ticker_test.go: -------------------------------------------------------------------------------- 1 | package time 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | // 打点器 10 | func TestTicker(t *testing.T) { 11 | // 打点器和定时器的机制有点相似:一个通道用来发送数据。 12 | // 这里我们在这个通道上使用内置的 `range` 来迭代值每隔 13 | // 500ms 发送一次的值。 14 | ticker := time.NewTicker(time.Millisecond * 500) 15 | go func() { 16 | for t := range ticker.C { 17 | fmt.Println("Tick at", t) 18 | } 19 | }() 20 | 21 | // 打点器可以和定时器一样被停止。一旦一个打点停止了, 22 | // 将不能再从它的通道中接收到值。我们将在运行后 1600ms 23 | // 停止这个打点器。 24 | time.Sleep(time.Millisecond * 1600) 25 | ticker.Stop() 26 | fmt.Println("Ticker stopped") 27 | } 28 | -------------------------------------------------------------------------------- /base/time/time_test.go: -------------------------------------------------------------------------------- 1 | package time 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | const defaultTime = "2006-01-02 15:04:05" 10 | 11 | // 时间格式化 12 | func TimeFormat(date time.Time) string { 13 | nowTime := date.Format(defaultTime) 14 | return nowTime 15 | } 16 | 17 | func TestTimeParse(t *testing.T) { 18 | now := time.Now() 19 | // 时间格式化 20 | format := TimeFormat(now) 21 | t.Log(format) 22 | // 字符串转时间 23 | parse, err := time.Parse(defaultTime, format) 24 | if err != nil { 25 | t.Error(err) 26 | } 27 | // 三小时后 28 | parse.Add(time.Hour * 3) 29 | // 一小时前 30 | parse.Add(time.Hour * -1) 31 | // 获取时间戳 32 | t1 := parse.Unix() 33 | // 获取纳秒级时间戳 34 | t2 := parse.UnixNano() 35 | t.Log(t1, t2) 36 | } 37 | 38 | func TestTimeout(t *testing.T) { 39 | done := make(chan struct{}) 40 | deadLine := time.Now().Add(time.Second * 1) 41 | 42 | dur := time.Until(deadLine) 43 | if dur <= 0 { 44 | fmt.Println("当前已超过deadLine,停止方法") 45 | } 46 | 47 | time.AfterFunc(dur, func() { 48 | fmt.Println("到达deadLine,停止方法") 49 | done <- struct{}{} 50 | 51 | }) 52 | select { 53 | case <-done: 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /base/time/timer_test.go: -------------------------------------------------------------------------------- 1 | package time 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | // 定时器 10 | func TestTimer(t *testing.T) { 11 | 12 | // 定时器表示在未来某一时刻的独立事件。你告诉定时器 13 | // 需要等待的时间,然后它将提供一个用于通知的通道。 14 | // 这里的定时器将等待 1 秒。 15 | timer1 := time.NewTimer(time.Second * 1) 16 | 17 | // `<-timer1.C` 直到这个定时器的通道 `C` 明确的发送了 18 | // 定时器失效的值之前,将一直阻塞。 19 | <-timer1.C 20 | fmt.Println("Timer 1 expired") 21 | 22 | // 如果你需要的仅仅是单纯的等待,你需要使用 `time.Sleep`。 23 | // 定时器是有用原因之一就是你可以在定时器失效之前,取消这个 24 | // 定时器。这是一个例子 25 | timer2 := time.NewTimer(time.Second * 1) 26 | go func() { 27 | <-timer2.C 28 | fmt.Println("Timer 2 expired") 29 | }() 30 | // 取消定时器 31 | if timer2.Stop() { 32 | fmt.Println("Timer 2 stopped") 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /base/xml/xml_test.go: -------------------------------------------------------------------------------- 1 | package xml 2 | 3 | import ( 4 | "encoding/xml" 5 | "log" 6 | "testing" 7 | ) 8 | 9 | /** 10 | 对象转xml 11 | */ 12 | type Address struct { 13 | City string 14 | State string 15 | } 16 | 17 | type Person struct { 18 | XMLName xml.Name `xml:"person"` 19 | Id int `xml:"id,attr"` 20 | FirstName string `xml:"name>first"` 21 | LastName string `xml:"name>last"` 22 | Age int `xml:"age"` 23 | Height float32 `xml:"height,omitempty"` 24 | Married bool 25 | Address 26 | Comment string `xml:",comment"` 27 | } 28 | 29 | func TestXml(t *testing.T) { 30 | addr := Address{City: "上海", State: "中国"} 31 | p := &Person{Id: 13, 32 | FirstName: "Pi", 33 | LastName: "bigstar", 34 | Age: 20, Height: 17.8, 35 | Married: true, 36 | Address: addr, 37 | Comment: "我是注释", 38 | } 39 | // 对象转xml 40 | personXML, err := xml.Marshal(p) 41 | if err != nil { 42 | log.Println("Error when marshal", err.Error()) 43 | } 44 | t.Log(string(personXML)) 45 | //xml 文本转 对象 46 | obj := &Person{} 47 | xml.Unmarshal(personXML, obj) 48 | t.Logf("%+v", obj) 49 | } 50 | -------------------------------------------------------------------------------- /base/zip/file.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibigstar/go-demo/1b4058972b01ec376d8c2131f8839bdf1fe909ab/base/zip/file.zip -------------------------------------------------------------------------------- /base/zip/test/1.txt: -------------------------------------------------------------------------------- 1 | first -------------------------------------------------------------------------------- /base/zip/test/2.txt: -------------------------------------------------------------------------------- 1 | second -------------------------------------------------------------------------------- /base/zip/test/3.txt: -------------------------------------------------------------------------------- 1 | third -------------------------------------------------------------------------------- /base/zip/zip_test.go: -------------------------------------------------------------------------------- 1 | package zip 2 | 3 | import "testing" 4 | 5 | func TestZipFile(t *testing.T) { 6 | var files = []zipFile{ 7 | {"1.txt", "first"}, 8 | {"2.txt", "second"}, 9 | {"3.txt", "third"}, 10 | } 11 | zipfile("file.zip", files) 12 | 13 | unzip("file.zip", "test") 14 | 15 | } 16 | -------------------------------------------------------------------------------- /blockchain/block_test.go: -------------------------------------------------------------------------------- 1 | package blockchain 2 | 3 | import ( 4 | "go-demo/blockchain/core" 5 | "strconv" 6 | "testing" 7 | ) 8 | 9 | func TestBlock(t *testing.T) { 10 | t.Log(core.GenerateGenesisBlock()) 11 | } 12 | 13 | func TestBlockChain(t *testing.T) { 14 | 15 | chain := core.NewBlockChain() 16 | for i := 0; i < 10; i++ { 17 | chain.SendData("block:" + strconv.Itoa(i)) 18 | } 19 | 20 | for _, value := range chain.Blocks { 21 | t.Logf("%+v", value) 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /blockchain/core/block.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "crypto/sha256" 5 | "encoding/hex" 6 | "time" 7 | ) 8 | 9 | /** 10 | 区块 11 | */ 12 | 13 | type Block struct { 14 | Index int64 `json:"index"` // 区块编号 15 | Data string `json:"data"` // 区块保存的数据 16 | Hash string `json:"hash"` // 当前区块的Hash值 17 | Timestamp int64 `json:"timestamp"` // 时间戳 18 | PrevBlockHash string `json:"pervHash"` // 上一个区块的Hash值 19 | 20 | } 21 | 22 | /** 23 | 生成新的区块 24 | */ 25 | func GenerateNewBlock(prevBlock *Block, data string) *Block { 26 | newBlock := new(Block) 27 | 28 | newBlock.Index = prevBlock.Index + 1 29 | newBlock.Timestamp = time.Now().Unix() 30 | newBlock.Data = data 31 | newBlock.PrevBlockHash = prevBlock.Hash 32 | newBlock.Hash = calculateHash(newBlock) 33 | 34 | return newBlock 35 | } 36 | 37 | /** 38 | 计算 Hash 值 39 | */ 40 | func calculateHash(block *Block) string { 41 | blockHash := string(block.Index) + string(block.Timestamp) + block.Hash + block.Data + block.PrevBlockHash 42 | blockBytes := sha256.Sum256([]byte(blockHash)) 43 | return hex.EncodeToString(blockBytes[:]) 44 | } 45 | 46 | /** 47 | 生成创始区块 48 | */ 49 | func GenerateGenesisBlock() *Block { 50 | block := new(Block) 51 | block.Index = -1 52 | block.Timestamp = time.Now().Unix() 53 | block.Hash = "" 54 | return GenerateNewBlock(block, "Genesis Block") 55 | } 56 | -------------------------------------------------------------------------------- /blockchain/core/blockchain.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import "github.com/smallnest/rpcx/log" 4 | 5 | type BlockChain struct { 6 | Blocks []*Block 7 | } 8 | 9 | /** 10 | 创建一个新的区块链 11 | */ 12 | func NewBlockChain() *BlockChain { 13 | block := GenerateGenesisBlock() 14 | bc := new(BlockChain) 15 | bc.AppendBlock(block) 16 | return bc 17 | } 18 | 19 | /** 20 | 根据data生成一个新的区块并加入到区块链中 21 | */ 22 | func (bc *BlockChain) SendData(data string) { 23 | preBlock := bc.Blocks[len(bc.Blocks)-1] 24 | nextBlock := GenerateNewBlock(preBlock, data) 25 | bc.AppendBlock(nextBlock) 26 | } 27 | 28 | /** 29 | 将区块加入到区块链中 30 | */ 31 | func (bc *BlockChain) AppendBlock(block *Block) { 32 | if len(bc.Blocks) == 0 { 33 | bc.Blocks = append(bc.Blocks, block) 34 | } else { 35 | if bc.isValid(block) { 36 | bc.Blocks = append(bc.Blocks, block) 37 | } else { 38 | log.Errorf("%+v is invalid", block) 39 | } 40 | } 41 | } 42 | 43 | /** 44 | 验证区块是否有效 45 | */ 46 | func (bc *BlockChain) isValid(block *Block) bool { 47 | prevBlock := bc.Blocks[len(bc.Blocks)-1] 48 | if prevBlock.Index != block.Index-1 { 49 | return false 50 | } 51 | 52 | if prevBlock.Hash != block.PrevBlockHash { 53 | return false 54 | } 55 | 56 | return true 57 | } 58 | -------------------------------------------------------------------------------- /blockchain/server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "go-demo/blockchain/core" 6 | "io" 7 | "net/http" 8 | ) 9 | 10 | type BlockChainResponse struct { 11 | BlockChain *core.BlockChain 12 | Total int 13 | } 14 | 15 | var bcr *BlockChainResponse 16 | 17 | func main() { 18 | blockchain := core.NewBlockChain() 19 | bcr = &BlockChainResponse{} 20 | bcr.BlockChain = blockchain 21 | bcr.Total = len(blockchain.Blocks) 22 | run() 23 | } 24 | 25 | func run() { 26 | http.HandleFunc("/block/get", GetBlockChain) 27 | http.HandleFunc("/block/write", WriteBlockChain) 28 | http.ListenAndServe(":9000", nil) 29 | } 30 | 31 | func WriteBlockChain(writer http.ResponseWriter, request *http.Request) { 32 | data := request.URL.Query().Get("data") 33 | bcr.BlockChain.SendData(data) 34 | bcr.Total = len(bcr.BlockChain.Blocks) 35 | GetBlockChain(writer, request) 36 | 37 | } 38 | 39 | func GetBlockChain(w http.ResponseWriter, r *http.Request) { 40 | bytes, err := json.Marshal(bcr) 41 | if err != nil { 42 | http.Error(w, err.Error(), http.StatusInternalServerError) 43 | return 44 | } 45 | io.WriteString(w, string(bytes)) 46 | } 47 | -------------------------------------------------------------------------------- /design/adaptor/adaptor.go: -------------------------------------------------------------------------------- 1 | package adaptor 2 | 3 | import "fmt" 4 | 5 | // 我们的接口(新接口)——音乐播放 6 | type MusicPlayer interface { 7 | play(fileType string, fileName string) 8 | } 9 | 10 | // 在网上找的已实现好的库 音乐播放 11 | // ( 旧接口) 12 | type ExistPlayer struct { 13 | } 14 | 15 | func (*ExistPlayer) playMp3(fileName string) { 16 | fmt.Println("play mp3 :", fileName) 17 | } 18 | func (*ExistPlayer) playWma(fileName string) { 19 | fmt.Println("play wma :", fileName) 20 | } 21 | 22 | // 适配器 23 | type PlayerAdaptor struct { 24 | // 持有一个旧接口 25 | existPlayer ExistPlayer 26 | } 27 | 28 | // 实现新接口 29 | func (player *PlayerAdaptor) play(fileType string, fileName string) { 30 | switch fileType { 31 | case "mp3": 32 | player.existPlayer.playMp3(fileName) 33 | case "wma": 34 | player.existPlayer.playWma(fileName) 35 | default: 36 | fmt.Println("暂时不支持此类型文件播放") 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /design/adaptor/adaptor_test.go: -------------------------------------------------------------------------------- 1 | package adaptor 2 | 3 | import "testing" 4 | 5 | func TestAdaptor(t *testing.T) { 6 | player := PlayerAdaptor{} 7 | player.play("mp3", "死了都要爱") 8 | player.play("wma", "滴滴") 9 | player.play("mp4", "复仇者联盟") 10 | } 11 | -------------------------------------------------------------------------------- /design/chain/chain_test.go: -------------------------------------------------------------------------------- 1 | package chain 2 | 3 | import "testing" 4 | 5 | func TestChain(t *testing.T) { 6 | adHandler := &AdHandler{} 7 | yellowHandler := &YellowHandler{} 8 | sensitiveHandler := &SensitiveHandler{} 9 | // 将责任链串起来 10 | adHandler.handler = yellowHandler 11 | yellowHandler.handler = sensitiveHandler 12 | 13 | adHandler.Handle("我是正常内容,我是广告,我是涉黄,我是敏感词,我是正常内容") 14 | } 15 | -------------------------------------------------------------------------------- /design/decorator/decorator.go: -------------------------------------------------------------------------------- 1 | package decorator 2 | 3 | import "fmt" 4 | 5 | type Person interface { 6 | cost() int 7 | show() 8 | } 9 | 10 | // 被装饰对象 11 | type laowang struct { 12 | } 13 | 14 | func (*laowang) show() { 15 | fmt.Println("赤裸裸的老王。。。") 16 | } 17 | func (*laowang) cost() int { 18 | return 0 19 | } 20 | 21 | // 衣服装饰器 22 | type clothesDecorator struct { 23 | // 持有一个被装饰对象 24 | person Person 25 | } 26 | 27 | func (*clothesDecorator) cost() int { 28 | return 0 29 | } 30 | 31 | func (*clothesDecorator) show() { 32 | } 33 | 34 | // 夹克 35 | type Jacket struct { 36 | clothesDecorator 37 | } 38 | 39 | func (j *Jacket) cost() int { 40 | return j.person.cost() + 10 41 | } 42 | func (j *Jacket) show() { 43 | // 执行已有的方法 44 | j.person.show() 45 | fmt.Println("穿上夹克的老王。。。") 46 | } 47 | 48 | // 帽子 49 | type Hat struct { 50 | clothesDecorator 51 | } 52 | 53 | func (h *Hat) cost() int { 54 | return h.person.cost() + 5 55 | } 56 | func (h *Hat) show() { 57 | fmt.Println("戴上帽子的老王。。。") 58 | } 59 | -------------------------------------------------------------------------------- /design/decorator/decorator_func.go: -------------------------------------------------------------------------------- 1 | package decorator 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "reflect" 7 | ) 8 | 9 | // 方法的装饰器模式, 包装某个方法 10 | // http 鉴权 11 | func Auth(h http.HandlerFunc) http.HandlerFunc { 12 | return func(w http.ResponseWriter, r *http.Request) { 13 | name := r.FormValue("token") 14 | if name == "pi" { 15 | h.ServeHTTP(w, r) 16 | } else { 17 | w.WriteHeader(403) 18 | } 19 | 20 | } 21 | } 22 | 23 | func f(w http.ResponseWriter, r *http.Request) { 24 | w.Write([]byte("Hello World")) 25 | } 26 | 27 | // 用 reflection 机制写的一个比较通用的修饰器 28 | // https://coolshell.cn/articles/17929.html 29 | func Decorator(decoPtr, fn interface{}) (err error) { 30 | var decoratedFunc, targetFunc reflect.Value 31 | 32 | decoratedFunc = reflect.ValueOf(decoPtr).Elem() 33 | targetFunc = reflect.ValueOf(fn) 34 | 35 | v := reflect.MakeFunc(targetFunc.Type(), 36 | func(in []reflect.Value) (out []reflect.Value) { 37 | fmt.Println("before") 38 | out = targetFunc.Call(in) 39 | fmt.Println("after") 40 | return 41 | }) 42 | 43 | decoratedFunc.Set(v) 44 | return 45 | } 46 | 47 | func foo(a, b, c int) int { 48 | fmt.Printf("%d, %d, %d \n", a, b, c) 49 | return a + b + c 50 | } 51 | 52 | func bar(a, b string) string { 53 | fmt.Printf("%s, %s \n", a, b) 54 | return a + b 55 | } 56 | -------------------------------------------------------------------------------- /design/decorator/decorator_func_test.go: -------------------------------------------------------------------------------- 1 | package decorator 2 | 3 | import ( 4 | "go-demo/utils/env" 5 | "net/http" 6 | "testing" 7 | ) 8 | 9 | func TestAuth(t *testing.T) { 10 | if env.IsCI() { 11 | return 12 | } 13 | 14 | http.HandleFunc("/", Auth(f)) 15 | if err := http.ListenAndServe(":8088", nil); err != nil { 16 | t.Error(err) 17 | } 18 | } 19 | 20 | func TestReflectDecorator(t *testing.T) { 21 | type MyFoo func(int, int, int) int 22 | var myfoo MyFoo 23 | Decorator(&myfoo, foo) 24 | myfoo(1, 2, 3) 25 | 26 | mybar := bar 27 | Decorator(&mybar, bar) 28 | mybar("hello", "world!") 29 | } 30 | -------------------------------------------------------------------------------- /design/decorator/decorator_test.go: -------------------------------------------------------------------------------- 1 | package decorator 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestDecorator(t *testing.T) { 9 | 10 | laowang := &laowang{} 11 | 12 | jacket := &Jacket{} 13 | jacket.person = laowang 14 | jacket.show() 15 | 16 | hat := &Hat{} 17 | hat.person = jacket 18 | hat.show() 19 | 20 | fmt.Println("cost:", hat.cost()) 21 | 22 | } 23 | -------------------------------------------------------------------------------- /design/facade/facade.go: -------------------------------------------------------------------------------- 1 | package facade 2 | 3 | import "fmt" 4 | 5 | // CPU 6 | type CPU struct { 7 | } 8 | 9 | func (CPU) start() { 10 | fmt.Println("启动CPU。。。") 11 | } 12 | 13 | // 内存 14 | type Memory struct { 15 | } 16 | 17 | func (Memory) start() { 18 | fmt.Println("启动内存管理。。。") 19 | } 20 | 21 | // 硬盘 22 | type Disk struct { 23 | } 24 | 25 | func (Disk) start() { 26 | fmt.Println("启动硬盘。。。") 27 | } 28 | 29 | // 开机键 30 | type StartBtn struct { 31 | } 32 | 33 | func (StartBtn) start() { 34 | cpu := &CPU{} 35 | cpu.start() 36 | memory := &Memory{} 37 | memory.start() 38 | disk := &Disk{} 39 | disk.start() 40 | } 41 | -------------------------------------------------------------------------------- /design/facade/facade_test.go: -------------------------------------------------------------------------------- 1 | package facade 2 | 3 | import "testing" 4 | 5 | func TestFacade(t *testing.T) { 6 | startBtn := &StartBtn{} 7 | startBtn.start() 8 | } 9 | -------------------------------------------------------------------------------- /design/factory/README.md: -------------------------------------------------------------------------------- 1 | # 工厂模式 2 | 3 | 简单工厂和抽象工厂有些区别,除了结构上的区别,主要区别在于使用场景不同。 4 | 5 | ## 简单工厂 6 | > 用于创建单一产品,将所有子类的创建过程集中在一个工厂中, 7 | 如要修改,只需修改一个工厂即可。简单工厂经常和单例模式一起使用, 8 | 例如用简单工厂创建缓存对象(文件缓存),某天需要改用redis缓存,修改工厂即可。 9 | 10 | 11 | ## 抽象工厂 12 | 13 | > 常用于创建一整个产品族,而不是单一产品。 14 | 通过选择不同的工厂来达到目的,其优势在于可以通过替换工厂而快速替换整个产品族。 15 | 例如上面的例子美国工厂生产美国girl,中国工厂生产中国girl。 16 | 17 | -------------------------------------------------------------------------------- /design/factory/abstract/abstract_factory_test.go: -------------------------------------------------------------------------------- 1 | package abstract 2 | 3 | import "testing" 4 | 5 | func TestAbstractFactory(t *testing.T) { 6 | 7 | store := new(GirlFactoryStore) 8 | // 提供美国工厂 9 | store.factory = new(AmericanGirlFactory) 10 | americanFatGirl := store.createGirl("fat") 11 | americanFatGirl.weight() 12 | 13 | // 提供中国工厂 14 | store.factory = new(ChineseGirlFactory) 15 | chineseFatGirl := store.createGirl("fat") 16 | chineseFatGirl.weight() 17 | } 18 | -------------------------------------------------------------------------------- /design/factory/simple/simple_factory.go: -------------------------------------------------------------------------------- 1 | package simple 2 | 3 | import "fmt" 4 | 5 | // 简单工厂模式 6 | 7 | type Girl interface { 8 | weight() 9 | } 10 | 11 | // 胖女孩 12 | type FatGirl struct { 13 | } 14 | 15 | func (FatGirl) weight() { 16 | fmt.Println("80kg") 17 | } 18 | 19 | // 瘦女孩 20 | type ThinGirl struct { 21 | } 22 | 23 | func (ThinGirl) weight() { 24 | fmt.Println("50kg") 25 | } 26 | 27 | type GirlFactory struct { 28 | } 29 | 30 | func (*GirlFactory) CreateGirl(like string) Girl { 31 | if like == "fat" { 32 | return &FatGirl{} 33 | } else if like == "thin" { 34 | return &ThinGirl{} 35 | } 36 | return nil 37 | } 38 | -------------------------------------------------------------------------------- /design/factory/simple/simple_factory_test.go: -------------------------------------------------------------------------------- 1 | package simple 2 | 3 | import "testing" 4 | 5 | func TestSimple(t *testing.T) { 6 | 7 | // 创建工厂 8 | girlFactory := new(GirlFactory) 9 | 10 | // 传递你喜欢类型的姑娘即可 11 | girl := girlFactory.CreateGirl("fat") 12 | girl.weight() 13 | 14 | girl = girlFactory.CreateGirl("thin") 15 | girl.weight() 16 | } 17 | -------------------------------------------------------------------------------- /design/observer/observer.go: -------------------------------------------------------------------------------- 1 | package observer 2 | 3 | import "fmt" 4 | 5 | // 报社 —— 客户 6 | type Customer interface { 7 | update() 8 | } 9 | 10 | type CustomerA struct { 11 | } 12 | 13 | func (*CustomerA) update() { 14 | fmt.Println("我是客户A, 我收到报纸了") 15 | } 16 | 17 | type CustomerB struct { 18 | } 19 | 20 | func (*CustomerB) update() { 21 | fmt.Println("我是客户B, 我收到报纸了") 22 | } 23 | 24 | // 报社 (被观察者) 25 | type NewsOffice struct { 26 | customers []Customer 27 | } 28 | 29 | func (n *NewsOffice) addCustomer(customer Customer) { 30 | n.customers = append(n.customers, customer) 31 | } 32 | 33 | func (n *NewsOffice) newspaperCome() { 34 | // 通知所有客户 35 | n.notifyAllCustomer() 36 | } 37 | 38 | func (n *NewsOffice) notifyAllCustomer() { 39 | for _, customer := range n.customers { 40 | customer.update() 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /design/observer/observer_test.go: -------------------------------------------------------------------------------- 1 | package observer 2 | 3 | import "testing" 4 | 5 | func TestObserver(t *testing.T) { 6 | 7 | customerA := &CustomerA{} 8 | customerB := &CustomerB{} 9 | 10 | office := &NewsOffice{} 11 | // 模拟客户订阅 12 | office.addCustomer(customerA) 13 | office.addCustomer(customerB) 14 | // 新的报纸 15 | office.newspaperCome() 16 | 17 | } 18 | -------------------------------------------------------------------------------- /design/options/options.go: -------------------------------------------------------------------------------- 1 | package options 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | /** 8 | 1. 需要Option选项得对象,里面是每一个配置 9 | 2. 需要一个Option得接口,拥有一个设置option得函数 10 | 3. 需要一个自定义类型得func,实现了Option接口 11 | 4. func要返回一个Option类型得接口,并且参数为要设置得内容 12 | */ 13 | 14 | type Connection struct { 15 | addr string 16 | cache bool 17 | timeout time.Duration 18 | } 19 | 20 | const ( 21 | defaultTimeout = 10 22 | defaultCaching = false 23 | ) 24 | 25 | type options struct { 26 | timeout time.Duration 27 | caching bool 28 | } 29 | 30 | // Option overrides behavior of Connect. 31 | type Option interface { 32 | apply(*options) 33 | } 34 | 35 | type optionFunc func(*options) 36 | 37 | func (f optionFunc) apply(o *options) { 38 | f(o) 39 | } 40 | 41 | func WithTimeout(t time.Duration) Option { 42 | return optionFunc(func(o *options) { 43 | o.timeout = t 44 | }) 45 | } 46 | 47 | func WithCaching(cache bool) Option { 48 | return optionFunc(func(o *options) { 49 | o.caching = cache 50 | }) 51 | } 52 | 53 | // Connect creates a connection. 54 | func Connect(addr string, opts ...Option) (*Connection, error) { 55 | 56 | options := options{ 57 | timeout: defaultTimeout, 58 | caching: defaultCaching, 59 | } 60 | 61 | for _, o := range opts { 62 | o.apply(&options) 63 | } 64 | 65 | return &Connection{ 66 | addr: addr, 67 | cache: options.caching, 68 | timeout: options.timeout, 69 | }, nil 70 | } 71 | -------------------------------------------------------------------------------- /design/options/options_test.go: -------------------------------------------------------------------------------- 1 | package options 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestConnect(t *testing.T) { 8 | connection, err := Connect("127.0.0.1", WithCaching(false), WithTimeout(20)) 9 | if err != nil { 10 | panic(err) 11 | } 12 | t.Logf("%+v", connection) 13 | } 14 | -------------------------------------------------------------------------------- /design/proxy/proxy.go: -------------------------------------------------------------------------------- 1 | package proxy 2 | 3 | import "fmt" 4 | 5 | type Seller interface { 6 | sell(name string) 7 | } 8 | 9 | // 火车站 10 | type Station struct { 11 | stock int //库存 12 | } 13 | 14 | func (station *Station) sell(name string) { 15 | if station.stock > 0 { 16 | station.stock-- 17 | fmt.Printf("代理点中:%s买了一张票,剩余:%d \n", name, station.stock) 18 | } else { 19 | fmt.Println("票已售空") 20 | } 21 | 22 | } 23 | 24 | // 火车代理点 25 | type StationProxy struct { 26 | station *Station // 持有一个火车站对象 27 | } 28 | 29 | func (proxy *StationProxy) sell(name string) { 30 | if proxy.station.stock > 0 { 31 | proxy.station.stock-- 32 | fmt.Printf("代理点中:%s买了一张票,剩余:%d \n", name, proxy.station.stock) 33 | } else { 34 | fmt.Println("票已售空") 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /design/proxy/proxy_test.go: -------------------------------------------------------------------------------- 1 | package proxy 2 | 3 | import "testing" 4 | 5 | func TestProxy(t *testing.T) { 6 | 7 | station := &Station{3} 8 | proxy := &StationProxy{station} 9 | station.sell("小华") 10 | proxy.sell("派大星") 11 | proxy.sell("小明") 12 | proxy.sell("小兰") 13 | } 14 | -------------------------------------------------------------------------------- /design/singleton/go_single.go: -------------------------------------------------------------------------------- 1 | package singleton 2 | 3 | import "sync" 4 | 5 | type singleton struct{} 6 | 7 | var ( 8 | goInstance *Instance 9 | ins *singleton 10 | once sync.Once 11 | ) 12 | 13 | // 使用go 实现单例模式 14 | func GetIns() *singleton { 15 | once.Do(func() { 16 | ins = &singleton{} 17 | }) 18 | return ins 19 | } 20 | -------------------------------------------------------------------------------- /design/singleton/other_single.go: -------------------------------------------------------------------------------- 1 | package singleton 2 | 3 | import "sync" 4 | 5 | //传统方式实现单例模式 6 | var ( 7 | instance *Instance 8 | lock sync.Mutex 9 | ) 10 | 11 | type Instance struct { 12 | Name string 13 | } 14 | 15 | // 双重检查 16 | func GetInstance(name string) *Instance { 17 | if instance == nil { 18 | lock.Lock() 19 | defer lock.Unlock() 20 | if instance == nil { 21 | instance = &Instance{Name: name} 22 | } 23 | } 24 | return instance 25 | } 26 | -------------------------------------------------------------------------------- /design/singleton/single_test.go: -------------------------------------------------------------------------------- 1 | package singleton 2 | 3 | import "testing" 4 | 5 | // Go语言模式 6 | func TestGoSingleton(t *testing.T) { 7 | instance1 := GoInstance("pibigstar") 8 | //查看其内存地址 9 | t.Logf("%p", instance1) 10 | 11 | instance2 := GoInstance("pibigstar") 12 | t.Logf("%p", instance2) 13 | } 14 | 15 | // 传统模式 16 | func TestSingleton(t *testing.T) { 17 | instance1 := GetInstance("pibigstar") 18 | //查看其内存地址 19 | t.Logf("%p", instance1) 20 | 21 | instance2 := GetInstance("pibigstar") 22 | t.Logf("%p", instance2) 23 | } 24 | -------------------------------------------------------------------------------- /design/strategy/strategy.go: -------------------------------------------------------------------------------- 1 | package strategy 2 | 3 | // 策略模式 4 | 5 | // 实现此接口,则为一个策略 6 | type IStrategy interface { 7 | do(int, int) int 8 | } 9 | 10 | // 加 11 | type add struct{} 12 | 13 | func (*add) do(a, b int) int { 14 | return a + b 15 | } 16 | 17 | // 减 18 | type reduce struct{} 19 | 20 | func (*reduce) do(a, b int) int { 21 | return a - b 22 | } 23 | 24 | // 具体策略的执行者 25 | type Operator struct { 26 | strategy IStrategy 27 | } 28 | 29 | // 设置策略 30 | func (operator *Operator) setStrategy(strategy IStrategy) { 31 | operator.strategy = strategy 32 | } 33 | 34 | // 调用策略中的方法 35 | func (operator *Operator) calculate(a, b int) int { 36 | return operator.strategy.do(a, b) 37 | } 38 | -------------------------------------------------------------------------------- /design/strategy/strategy_test.go: -------------------------------------------------------------------------------- 1 | package strategy 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestStrategy(t *testing.T) { 9 | operator := Operator{} 10 | 11 | operator.setStrategy(&add{}) 12 | result := operator.calculate(1, 2) 13 | fmt.Println("add:", result) 14 | 15 | operator.setStrategy(&reduce{}) 16 | result = operator.calculate(2, 1) 17 | fmt.Println("reduce:", result) 18 | } 19 | -------------------------------------------------------------------------------- /design/template/template.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import "fmt" 4 | 5 | type Cooker interface { 6 | open() 7 | fire() 8 | cooke() 9 | outfire() 10 | close() 11 | } 12 | 13 | // 类似于一个抽象类 14 | type CookMenu struct { 15 | } 16 | 17 | func (CookMenu) open() { 18 | fmt.Println("打开开关") 19 | } 20 | 21 | func (CookMenu) fire() { 22 | fmt.Println("开火") 23 | } 24 | 25 | // 做菜,交给具体的子类实现 26 | func (CookMenu) cooke() { 27 | } 28 | 29 | func (CookMenu) outfire() { 30 | fmt.Println("关火") 31 | } 32 | 33 | func (CookMenu) close() { 34 | fmt.Println("关闭开关") 35 | } 36 | 37 | // 封装具体步骤 38 | func doCook(cook Cooker) { 39 | cook.open() 40 | cook.fire() 41 | cook.cooke() 42 | cook.outfire() 43 | cook.close() 44 | } 45 | 46 | type XiHongShi struct { 47 | CookMenu 48 | } 49 | 50 | func (*XiHongShi) cooke() { 51 | fmt.Println("做西红柿") 52 | } 53 | 54 | type ChaoJiDan struct { 55 | CookMenu 56 | } 57 | 58 | func (ChaoJiDan) cooke() { 59 | fmt.Println("做炒鸡蛋") 60 | } 61 | -------------------------------------------------------------------------------- /design/template/template_test.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import "testing" 4 | 5 | func TestTemplate(t *testing.T) { 6 | 7 | // 做西红柿 8 | xihongshi := &XiHongShi{} 9 | doCook(xihongshi) 10 | 11 | // 做炒鸡蛋 12 | chaojidan := &ChaoJiDan{} 13 | doCook(chaojidan) 14 | 15 | } 16 | -------------------------------------------------------------------------------- /design/worker/worker_test.go: -------------------------------------------------------------------------------- 1 | package worker 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func TestWorker(t *testing.T) { 10 | go makeJob() 11 | 12 | dispatcher := NewDispatcher(10) 13 | dispatcher.Run() 14 | 15 | time.Sleep(time.Second * 1) 16 | } 17 | 18 | func makeJob() { 19 | for i := 0; i < 100; i++ { 20 | job := Job{Content: fmt.Sprintf("第%d个任务", i)} 21 | JobQueue <- job 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /interview/others/10_test.go: -------------------------------------------------------------------------------- 1 | package others 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | /** 9 | 即使空对象,长度为0,可对象依然是合法存在的,拥有合法的内存地址,这与nil的定义不符合 10 | 11 | 输出: 12 | false 13 | false 14 | */ 15 | type Student struct { 16 | } 17 | 18 | func TestNil(t *testing.T) { 19 | var stu1, stu2 Student 20 | // 如果把注释放开,那么结果为false,true 不清楚为啥,有知道希望能给我提个issue 21 | // fmt.Println(&stu1,&stu2) 22 | fmt.Println(&stu1 == nil) 23 | fmt.Println(&stu1 == &stu2) 24 | } 25 | -------------------------------------------------------------------------------- /interview/others/1_test.go: -------------------------------------------------------------------------------- 1 | package others 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | /* 9 | defer函数属延迟执行,延迟到调用者函数执行 return 命令前被执行。多个defer之间按LIFO先进后出顺序执行。 10 | 在Panic触发时结束函数运行,在return前先依次打印:打印后、打印中、打印前 。最后由runtime运行时抛出打印panic异常信息。 11 | 需要注意的是,函数的return value 不是原子操作.而是在编译器中分解为两部分:返回值赋值 和 return 。 12 | 而defer刚好被插入到末尾的return前执行。故可以在derfer函数中修改返回值 13 | 14 | 输出: 15 | 3 16 | 2 17 | 1 18 | 发现恐慌 19 | */ 20 | 21 | func Test1(t *testing.T) { 22 | defer fmt.Println("1") 23 | defer fmt.Println("2") 24 | defer fmt.Println("3") 25 | // 发生恐慌,结束执行,结束执行前会先运行defer中的语句 26 | //panic("发现恐慌") 27 | } 28 | 29 | /* 30 | 下面这段代码输出什么? 31 | 输出: 5 32 | 解析: PrintContent() 函数的参数在执行 defer 语句的时候会保存一份副本, 33 | 在实际调用 PrintContent() 函数时用,所以是 5. 34 | */ 35 | func Test2(t *testing.T) { 36 | i := 5 37 | defer PrintContent(i) 38 | i = i + 5 39 | } 40 | 41 | func PrintContent(i int) { 42 | fmt.Println(i) 43 | } 44 | -------------------------------------------------------------------------------- /interview/others/2_test.go: -------------------------------------------------------------------------------- 1 | package others 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | /* 9 | 因为for遍历时,变量stu指针不变,每次遍历仅进行struct值拷贝, 10 | 故m[stu.UserName]=&stu实际上是将所有值指向同一个指针 11 | 最终该指针的值为遍历的最后一个struct的值拷贝 12 | */ 13 | type student struct { 14 | Name string 15 | Age int 16 | } 17 | 18 | func parseStudent() map[string]*student { 19 | m := make(map[string]*student) 20 | stus := []student{ 21 | {Name: "zhou", Age: 24}, 22 | {Name: "li", Age: 23}, 23 | {Name: "wang", Age: 22}, 24 | } 25 | //for遍历时,变量stu指针不变 26 | //最终将所有值都指向同一个指针(最后一个) 27 | for _, stu := range stus { 28 | m[stu.Name] = &stu 29 | } 30 | return m 31 | } 32 | 33 | func TestForRange(t *testing.T) { 34 | students := parseStudent() 35 | for k, v := range students { 36 | fmt.Printf("key=%s,value=%+v \n", k, v) 37 | } 38 | } 39 | 40 | // 下面是解决方案 41 | // 遍历时重新赋值 42 | func parseStudentTrue() map[string]*student { 43 | m := make(map[string]*student) 44 | stus := []student{ 45 | {Name: "zhou", Age: 24}, 46 | {Name: "li", Age: 23}, 47 | {Name: "wang", Age: 22}, 48 | } 49 | //用中间变量来过渡它 50 | for _, stu := range stus { 51 | s := stu 52 | m[stu.Name] = &s 53 | } 54 | return m 55 | } 56 | 57 | func TestParseStudentTrue(t *testing.T) { 58 | students := parseStudentTrue() 59 | for k, v := range students { 60 | fmt.Printf("key=%s,value=%+v \n", k, v) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /interview/others/3_test.go: -------------------------------------------------------------------------------- 1 | package others 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | "sync" 7 | "testing" 8 | ) 9 | 10 | /** 11 | go2中i: 4 12 | go1中i: 5 13 | go1中i: 5 14 | go1中i: 5 15 | go1中i: 5 16 | go1中i: 5 17 | go2中i: 0 18 | go2中i: 1 19 | go2中i: 2 20 | go2中i: 3 21 | */ 22 | 23 | func Test3(t *testing.T) { 24 | runtime.GOMAXPROCS(1) // 将CPU设置为 1 个 25 | wg := sync.WaitGroup{} 26 | wg.Add(10) 27 | //i是外部for的一个变量,地址不变化。遍历完成后,最终i=5。故go func执行时,i的值始终是5(5次遍历很快完成)。 28 | for i := 0; i < 5; i++ { 29 | go func() { 30 | fmt.Println("go1中i: ", i) // 5次遍历完之后,i 的值为 5, 而此时goroutine才开始打印 31 | wg.Done() 32 | }() 33 | } 34 | 35 | //i是函数参数,与外部for中的i完全是两个变量。尾部(i)将发生值拷贝,go func内部指向值拷贝地址。 36 | for i := 0; i < 5; i++ { 37 | go func(i int) { 38 | fmt.Println("go2中i: ", i) 39 | wg.Done() 40 | }(i) 41 | } 42 | wg.Wait() 43 | } 44 | -------------------------------------------------------------------------------- /interview/others/4_test.go: -------------------------------------------------------------------------------- 1 | package others 2 | -------------------------------------------------------------------------------- /interview/others/5_test.go: -------------------------------------------------------------------------------- 1 | package others 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | "testing" 7 | ) 8 | 9 | /** 10 | 单个chan如果无缓冲时,将会阻塞。但结合 select可以在多个chan间等待执行。有三点原则: 11 | 1. select 中只要有一个case能return,则立刻执行。 12 | 2. 当如果同一时间有多个case均能return则伪随机方式抽取任意一个执行。 13 | 3. 如果没有一个case能return则可以执行 default 块。 14 | 15 | 输出: 有可能触发异常,也有可能不触发,是随机事件 16 | */ 17 | func TestSelectChan(t *testing.T) { 18 | runtime.GOMAXPROCS(1) 19 | intChan := make(chan int, 1) 20 | strChan := make(chan string, 1) 21 | intChan <- 1 22 | strChan <- "hello" 23 | select { 24 | case value := <-intChan: 25 | fmt.Println(value) 26 | case _ = <-strChan: 27 | //panic(value) 28 | fmt.Println("触发异常") 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /interview/others/6_test.go: -------------------------------------------------------------------------------- 1 | package others 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | /** 9 | 当执行make([]int,5)时返回的是一个含义默认值(int的默认值为0)的数组:[0,0,0,0,0]。 10 | 而append函数是便是在一个数组或slice后面追加新的元素,并返回一个新的数组或slice。 11 | 12 | 输出: [0 0 0 0 0 1 2 3] 13 | */ 14 | func TestMakeSlice(t *testing.T) { 15 | s := make([]int, 5) 16 | s = append(s, 1, 2, 3) 17 | fmt.Println(s) 18 | } 19 | -------------------------------------------------------------------------------- /interview/others/7_test.go: -------------------------------------------------------------------------------- 1 | package others 2 | 3 | import ( 4 | "runtime" 5 | "sync" 6 | "testing" 7 | ) 8 | 9 | /** 10 | 虽然有使用sync.Mutex做写锁,但是map是并发读写不安全的。 11 | map属于引用类型,并发读写时多个协程见是通过指针访问同一个地址,即访问共享变量, 12 | 此时同时读写资源存在竞争关系。会报错误信息:“fatal error: concurrent map read and map write”。 13 | */ 14 | type UserAges struct { 15 | ages map[string]int 16 | sync.Mutex 17 | } 18 | 19 | func (ua *UserAges) Add(name string, age int) { 20 | ua.Lock() 21 | defer ua.Unlock() 22 | ua.ages[name] = age 23 | } 24 | 25 | func (ua *UserAges) Get(name string) int { 26 | if age, ok := ua.ages[name]; ok { 27 | return age 28 | } 29 | return -1 30 | } 31 | 32 | // 压力测试要以Benchmark打头 33 | func BenchmarkMapRW(b *testing.B) { 34 | for i := 0; i < b.N; i++ { 35 | runtime.GOMAXPROCS(1) 36 | userAges := &UserAges{} 37 | userAges.ages = make(map[string]int) 38 | userAges.Add("A", 1) 39 | userAges.Get("A") 40 | userAges.Add("B", 2) 41 | userAges.GoodGet("B") 42 | } 43 | } 44 | 45 | /** 46 | 在执行 Get方法时可能被panic 47 | */ 48 | 49 | // 改正后, 加个读锁 50 | // 1. sync.Mutex互斥锁 51 | // 2. sync.RWMutex读写锁,基于互斥锁的实现,可以加多个读锁或者一个写锁。 52 | var rwMutex sync.RWMutex 53 | 54 | func (ua *UserAges) GoodGet(name string) int { 55 | rwMutex.Lock() 56 | defer rwMutex.Unlock() 57 | if age, ok := ua.ages[name]; ok { 58 | return age 59 | } 60 | return -1 61 | } 62 | -------------------------------------------------------------------------------- /interview/others/8_test.go: -------------------------------------------------------------------------------- 1 | package others 2 | 3 | import ( 4 | "sync" 5 | "testing" 6 | ) 7 | 8 | /** 9 | chan在使用make初始化时可附带一个可选参数来设置缓冲区。默认无缓冲, 10 | 题目中便初始化的是无缓冲区的chan,这样只有写入的元素直到被读取后才能继续写入,不然就一直阻塞。 11 | 设置缓冲区大小后,写入数据时可连续写入到缓冲区中,直到缓冲区被占满。 12 | 从chan中接收一次便可从缓冲区中释放一次。可以理解为chan是可以设置吞吐量的处理池。 13 | */ 14 | var rw sync.RWMutex 15 | 16 | func TestChan(t *testing.T) { 17 | str := "你好,HelloWorld" 18 | ch := make(chan int) 19 | go func() { 20 | rw.Lock() 21 | for elem := range str { 22 | ch <- elem 23 | } 24 | rw.Unlock() 25 | //close(ch) 26 | }() 27 | } 28 | 29 | /** 30 | 内部迭代出现阻塞。默认初始化时无缓冲区,需要等待接收者读取后才能继续写入。 31 | */ 32 | 33 | /*** 34 | ch := make(chan interface{}) 和 ch := make(chan interface{},1)是不一样的 35 | 无缓冲的 不仅仅是只能向 ch 通道放 一个值 而是一直要有人接收,那么ch <- elem才会继续下去 36 | 要不然就一直阻塞着,也就是说有接收者才去放,没有接收者就阻塞。 37 | 而缓冲为1则即使没有接收者也不会阻塞,因为缓冲大小是1 38 | 只有当 放第二个值的时候 第一个还没被人拿走,这时候才会阻塞 39 | */ 40 | -------------------------------------------------------------------------------- /interview/others/9_test.go: -------------------------------------------------------------------------------- 1 | package others 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | /** 9 | 考题中的 func (stu *Student) Speak(think string) (talk string) 是表示结构类型*Student的指针有提供该方法 10 | 但该方法并不属于结构类型Student的方法。因为struct是值类型。 11 | 12 | 修改方法: 13 | 定义为指针 go var peo People = &Student{} 14 | 方法定义在值类型上,指针类型本身是包含值类型的方法 15 | go func (stu Student) Speak(think string) (talk string) { //... } 16 | 17 | 解析: 类型T方法集包含所有 receiver T 方法, 类型*T方法集包含所有 receiver T 和 receiver *T 方法 18 | */ 19 | type People interface { 20 | Speak(string) string 21 | } 22 | 23 | func (stu *Student) Speak(think string) (talk string) { 24 | if think == "bitch" { 25 | talk = "You are a good boy" 26 | } else { 27 | talk = "hi" 28 | } 29 | return 30 | } 31 | 32 | func TestInterface(t *testing.T) { 33 | // var peo People = Student{} 不可以这样写, 因为是*Student实现了People,不是Student 34 | var peo People = &Student{} 35 | think := "bitch" 36 | fmt.Println(peo.Speak(think)) 37 | } 38 | -------------------------------------------------------------------------------- /leetcode/10/10.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // 硬币排列 6 | // 第一行排一个硬币,第二个排两个,第三个排三个以此类推 7 | // 求 n 个硬币可以完整排列几行 8 | 9 | func main() { 10 | fmt.Println(find(10)) 11 | fmt.Println(find2(10)) 12 | } 13 | 14 | // 暴力求解 15 | func find(n int) int { 16 | for i := 1; i <= n; i++ { 17 | n = n - i 18 | if n <= i { 19 | return i 20 | } 21 | } 22 | return 0 23 | } 24 | 25 | // 优化,二分求解 26 | func find2(n int) int { 27 | low := 0 28 | high := n 29 | for low <= high { 30 | mid := low + (high-low)/2 31 | // 1,2,3,4...mid 32 | // 1+2+3+4+5....+mid = ((mid+1)*mid) / 2 33 | costs := ((mid + 1) * mid) / 2 34 | if costs == n { 35 | return mid 36 | } 37 | if costs > n { 38 | high = mid - 1 39 | } else if costs < n { 40 | low = mid + 1 41 | } 42 | } 43 | return high 44 | } 45 | -------------------------------------------------------------------------------- /leetcode/11/11.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | // 抢红包算法 10 | // 每次抢到的金额=随机范围[1,M/N *2) 11 | func main() { 12 | rand.Seed(time.Now().UnixNano()) 13 | // 10元 按分计算 14 | total := 1000 15 | // 人数 16 | num := 10 17 | for i := 0; i < 10; i++ { 18 | t := getMoney(total, num) 19 | fmt.Println(t) 20 | total -= t 21 | num-- 22 | } 23 | } 24 | 25 | func getMoney(m int, n int) int { 26 | // 如果就剩一个人,则直接全部返回 27 | if n == 1 { 28 | return m 29 | } 30 | //区间 [1, 剩余金额/人数的两倍-1) 31 | t := rand.Intn(m/n*2-1) + 1 32 | return t 33 | } 34 | -------------------------------------------------------------------------------- /leetcode/12/12.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sort" 6 | ) 7 | 8 | // 合并两个有序数组 9 | // 返回数组1 10 | func main() { 11 | array1 := []int{1, 2, 3, 7, 9, 0, 0, 0} 12 | array2 := []int{4, 5, 10} 13 | fmt.Println(merge(array1, array2)) 14 | fmt.Println(merge2(array1, array2, 5, 3)) 15 | } 16 | 17 | func merge(array1 []int, array2 []int) []int { 18 | array1 = append(array1, array2...) 19 | sort.Ints(array1) 20 | return array1 21 | } 22 | 23 | // 双指针,倒序遍历 24 | // 节省空间复杂度,不需要重新创建数组 25 | func merge2(array1 []int, array2 []int, m, n int) []int { 26 | p1 := m - 1 27 | p2 := n - 1 28 | p := m + n - 1 29 | 30 | // 倒序遍历 31 | for p1 >= 0 && p2 >= 0 { 32 | // 当array2最后一个值较大时,将其挪到array1 p的位置 33 | // 并且挪动p2的指针 34 | if array1[p1] < array2[p2] { 35 | array1[p] = array2[p2] 36 | p2-- 37 | p-- 38 | } else { 39 | // 当array1最后一个值较大时,将其挪到array1 p的位置 40 | // 并且挪动p1的指针 41 | array1[p] = array1[p1] 42 | p1-- 43 | p-- 44 | } 45 | } 46 | return array1 47 | } 48 | -------------------------------------------------------------------------------- /leetcode/13/13.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // 子数组最大平均数 6 | // 给定一个数组,求长度为k且下标连续的子数组的最大平均数 7 | 8 | func main() { 9 | fmt.Println(maxMean([]int{1, 5, 3, 4, 6, 2, 4}, 3)) 10 | fmt.Println(maxMean2([]int{1, 5, 3, 4, 6, 2, 4}, 3)) 11 | } 12 | 13 | // 滑动窗口,双指针演变 14 | func maxMean(array []int, k int) int { 15 | if len(array) < k { 16 | return 0 17 | } 18 | low := 0 19 | high := k - 1 20 | 21 | var max int 22 | for high < len(array) { 23 | 24 | var count int 25 | for i := low; i <= high; i++ { 26 | count += array[i] 27 | } 28 | if count > max { 29 | max = count 30 | } 31 | // 滑动,两个指针一起走 32 | high++ 33 | low++ 34 | } 35 | return max / k 36 | } 37 | 38 | // 滑动窗口,双指针演变 39 | // 优化,每次滑动其实是整个窗口的值的合减去第一个值,再加上下一次滑动的值 40 | func maxMean2(array []int, k int) int { 41 | if len(array) < k { 42 | return 0 43 | } 44 | var sum int 45 | // 第一个滑动窗口的值的合 46 | for i := 0; i < k; i++ { 47 | sum += array[i] 48 | } 49 | 50 | max := sum 51 | for i := k; i < len(array); i++ { 52 | // 每次滑动对sum的值进行修改 53 | sum = sum - array[i-k] + array[i] 54 | if sum > max { 55 | max = sum 56 | } 57 | } 58 | return max / k 59 | } 60 | -------------------------------------------------------------------------------- /leetcode/15/15.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // 最长连续递增序列 6 | // 给定一个未排序的数组,找到最长且连续递增的子序列,并返回序列长度 7 | 8 | func main() { 9 | fmt.Println(maxLength([]int{1, 2, 4, 2, 3, 4, 1, 2, 3, 4, 5})) 10 | } 11 | 12 | // 贪心算法 13 | // 找到所有递增子序列的长度,取长度最长的 14 | func maxLength(array []int) int { 15 | var ( 16 | start int 17 | max int 18 | ) 19 | for i := 1; i < len(array); i++ { 20 | // 当不是递增时,将start赋值到当前i位置 21 | if array[i] <= array[i-1] { 22 | start = i 23 | } 24 | // 求出当前子序列的长度 25 | length := i - start + 1 26 | // 如果当前子序列长度大于最大值 27 | // 则将当前子序列的长度设为最大值 28 | if length > max { 29 | max = length 30 | } 31 | } 32 | return max 33 | } 34 | -------------------------------------------------------------------------------- /leetcode/16/16.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // 柠檬水找零 6 | // 柠檬水为5元,面值只有 5, 10, 20 7 | // 判断最终能否成功找零 8 | func main() { 9 | fmt.Println(change([]int{5, 10, 20})) 10 | } 11 | 12 | func change(bills []int) bool { 13 | var ( 14 | five int // 5元的个数 15 | ten int // 10元的个数 16 | ) 17 | for _, b := range bills { 18 | if b == 5 { 19 | five++ 20 | } 21 | if b == 10 { 22 | // 如果此时手里没有5元的,那么肯定没法找零了 23 | if five == 0 { 24 | return false 25 | } 26 | // 可以成功找零,减去一个5元 27 | five-- 28 | ten++ 29 | } 30 | if b == 20 { 31 | // 局部最优,优先找10+5的 32 | if five > 0 && ten > 0 { 33 | five-- 34 | ten-- 35 | } else if five >= 3 { // 只能找3个5元了 36 | five -= 3 37 | } else { 38 | return false 39 | } 40 | } 41 | } 42 | 43 | return true 44 | } 45 | -------------------------------------------------------------------------------- /leetcode/17/17.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sort" 6 | ) 7 | 8 | // 三角形最大周长 9 | // 给定一个数组,找出这个数组中能组成三角形最大周长 10 | 11 | func main() { 12 | fmt.Println(findMaxPerimeter([]int{3, 6, 2, 3})) 13 | } 14 | 15 | func findMaxPerimeter(array []int) int { 16 | // 先排序 17 | sort.Ints(array) 18 | 19 | // 从后往前找 20 | for i := len(array) - 1; i >= 2; i-- { 21 | a := array[i-1] 22 | b := array[i-2] 23 | c := array[i] 24 | // 是否能组成三角形 25 | if a+b > c { 26 | return a + b + c 27 | } 28 | } 29 | return 0 30 | } 31 | -------------------------------------------------------------------------------- /leetcode/18/18_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "go-demo/leetcode/common/tree" 6 | "testing" 7 | ) 8 | 9 | // 前序遍历-递归 10 | func TestPreorderByRecursion(t *testing.T) { 11 | root := tree.GetTree() 12 | PreorderByRecursion(root) 13 | } 14 | 15 | // 中序遍历-递归 16 | func TestInorderByRecursion(t *testing.T) { 17 | root := tree.GetTree() 18 | InorderByRecursion(root) 19 | } 20 | 21 | // 后序遍历-递归 22 | func TestAfterOderByRecursion(t *testing.T) { 23 | root := tree.GetTree() 24 | AfterOderByRecursion(root) 25 | } 26 | 27 | func TestPrintDFS(t *testing.T) { 28 | list := printDFS(tree.GetTree()) 29 | for i := 0; i < len(list); i++ { 30 | vs := list[i] 31 | fmt.Printf("第%d层 \n", i) 32 | for _, v := range vs { 33 | fmt.Printf("%d,", v) 34 | } 35 | fmt.Println() 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /leetcode/2/2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // 字符串切分 6 | // 保证左边与右边字符种类完全相同 7 | // 比如左右有ab字符,那么右边也要有ab字符 8 | // 给定一个不大于10000长度的字符,输出有多少种切分方式 9 | 10 | func main() { 11 | s1 := "abcsabc" // 无解 0 12 | s2 := "abcssabc" // 全对称,1种解 13 | s3 := "abcssssabc" // 3种 14 | fmt.Println(findStrCutNum(s1)) 15 | fmt.Println(findStrCutNum(s2)) 16 | fmt.Println(findStrCutNum(s3)) 17 | } 18 | 19 | // 每个字符 [l1, r1] l1表示第一次出现位置,r1表示最后一次出现位置 20 | // 为了保证左右字符种类完全相同,那么只能取 [maxL1, minR1] 之间 21 | // 一共有 minR1 - maxL1 种切分方式 22 | func findStrCutNum(s string) int { 23 | var maxL int // 保存最大左边界 24 | var minR = len(s) // 保存最小右边界 25 | 26 | sL := make(map[string]int) // 字符第一次出现的位置 27 | sR := make(map[string]int) // 字符最后一次出现的位置 28 | 29 | ss := []byte(s) 30 | for i := 0; i < len(ss); i++ { 31 | str := string(ss[i]) 32 | if _, ok := sL[str]; ok { 33 | sR[str] = i 34 | } else { 35 | sL[str] = i 36 | } 37 | } 38 | 39 | flag := 1 // 标识是否有解,当字符出现为1次时那么肯定无解 40 | 41 | // 找出sL中的最大值,sR中的最小值 42 | for k, v := range sL { 43 | // 仅出现在左边,无解 44 | v2, ok := sR[k] 45 | if !ok { 46 | flag = 0 47 | break 48 | } 49 | if maxL < v { 50 | maxL = v 51 | } 52 | if minR > v2 { 53 | minR = v2 54 | } 55 | } 56 | 57 | if flag == 0 || minR < maxL { 58 | return 0 59 | } 60 | return minR - maxL 61 | } 62 | -------------------------------------------------------------------------------- /leetcode/20/20.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "go-demo/leetcode/common/tree" 4 | 5 | // BST 的完整定义如下: 6 | //1、BST 中任意一个节点的左子树所有节点的值都小于该节点的值,右子树所有节点的值都大于该节点的值。 7 | //2、BST 中任意一个节点的左右子树都是 BST 8 | 9 | // BST 的中序遍历结果是有序的(升序) 10 | 11 | // 寻找BST中第K小的元素 12 | var ( 13 | index int 14 | result int 15 | ) 16 | 17 | func kthSmallest(root *tree.TreeNode, k int) { 18 | if root == nil { 19 | return 20 | } 21 | kthSmallest(root.Left, k) 22 | index++ 23 | if index == k { 24 | result = root.Val 25 | } 26 | kthSmallest(root.Right, k) 27 | } 28 | 29 | // 在 BST 中搜索元素 30 | func searchByBST(root *tree.TreeNode, target int) *tree.TreeNode { 31 | if root == nil { 32 | return nil 33 | } 34 | // 去左子树搜索 35 | if root.Val > target { 36 | return searchByBST(root.Left, target) 37 | } 38 | // 去右子树搜索 39 | if root.Val < target { 40 | return searchByBST(root.Right, target) 41 | } 42 | return root 43 | } 44 | -------------------------------------------------------------------------------- /leetcode/20/20_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "go-demo/leetcode/common/tree" 5 | "testing" 6 | ) 7 | 8 | func TestKthSmallest(t *testing.T) { 9 | kthSmallest(tree.GetBstTree(), 2) 10 | t.Log(result) 11 | } 12 | 13 | func TestSearchByBST(t *testing.T) { 14 | t.Log(searchByBST(tree.GetBstTree(), 3).Val) 15 | } 16 | -------------------------------------------------------------------------------- /leetcode/21/21.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "strconv" 4 | 5 | // 给你一个字符串表达式s ,实现一个基本计算器来计算并返回它的值 6 | // 思路:用一个栈保存,所有运算最后都是一个个数做加法 7 | func calculate(s string) int { 8 | // 栈,将解析过的数字入栈 9 | var stack []int 10 | // 上一个遇到的运算符 11 | prevOp := "+" 12 | // 当前的num值 13 | var currNum int 14 | 15 | for i := 0; i < len(s); i++ { 16 | c := s[i] 17 | if c == ' ' { 18 | continue 19 | } 20 | 21 | if c >= '0' && c <= '9' { 22 | n, _ := strconv.Atoi(string(c)) 23 | // n := int(s[i] - '0') 24 | currNum = currNum*10 + n 25 | } 26 | 27 | switch prevOp { 28 | case "+": 29 | stack = append(stack, currNum) 30 | case "-": 31 | // 将其改为负数入栈 32 | stack = append(stack, -currNum) 33 | case "*": 34 | // 将栈顶值与其做计算 35 | stack[len(stack)-1] = stack[len(stack)-1] * currNum 36 | case "/": 37 | stack[len(stack)-1] = stack[len(stack)-1] / currNum 38 | } 39 | 40 | prevOp = string(rune(s[i])) 41 | currNum = 0 42 | } 43 | 44 | var sum int 45 | for _, v := range stack { 46 | sum += v 47 | } 48 | return sum 49 | } 50 | -------------------------------------------------------------------------------- /leetcode/21/21_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "testing" 4 | 5 | func TestCalculate(t *testing.T) { 6 | result := calculate("1+5*2-1+2") 7 | t.Log(result) 8 | } 9 | -------------------------------------------------------------------------------- /leetcode/22/22_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "testing" 4 | 5 | func TestTwoSum(t *testing.T) { 6 | arrays := []int{1, 2, 3, 5, 2, 6} 7 | result := twoSum(arrays, 8) 8 | t.Log(result) 9 | } 10 | 11 | func TestThreeSum(t *testing.T) { 12 | arrays := []int{1, 2, 3, 5, 2, 6} 13 | result := threeSum(arrays, 8) 14 | t.Log(result) 15 | 16 | result = threeSumV2(arrays, 8) 17 | t.Log(result) 18 | } 19 | -------------------------------------------------------------------------------- /leetcode/23/23.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | //你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金, 4 | //影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统, 5 | //如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 6 | //给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。 7 | 8 | // 动态规划 9 | // 时间复杂度O(n) 10 | // 空间复杂度O(n) 11 | func rob(nums []int) int { 12 | if len(nums) == 0 { 13 | return 0 14 | } 15 | if len(nums) == 1 { 16 | return nums[0] 17 | } 18 | // 存放前i个能偷到的最大金额 19 | // dp[5]=10 表示从前5个最大可偷到5个金额 20 | dp := make([]int, len(nums)) 21 | dp[0] = nums[0] 22 | dp[1] = max(nums[0], nums[1]) 23 | for i := 2; i < len(nums); i++ { 24 | // 偷第i间能得到的最大金额为:dp[i-2]+nums[i] 25 | // 不偷第i间能得到的最大金额为: dp[i-1] 26 | // 那么dp[i] 就是取偷与不偷的最大值即可 27 | dp[i] = max(dp[i-2]+nums[i], dp[i-1]) 28 | } 29 | return dp[len(nums)-1] 30 | } 31 | 32 | // 动态规划+滚动数组 33 | // 考虑到每间房屋的最高总金额只和该房屋的前两间房屋的最高总金额相关, 34 | // 因此可以使用滚动数组,在每个时刻只需要存储前两间房屋的最高总金额。 35 | // 时间复杂度O(n) 36 | // 空间复杂度O(1) 37 | func rob2(nums []int) int { 38 | if len(nums) == 0 { 39 | return 0 40 | } 41 | if len(nums) == 1 { 42 | return nums[0] 43 | } 44 | 45 | first := nums[0] 46 | second := max(nums[0], nums[1]) 47 | for i := 2; i < len(nums); i++ { 48 | first, second = second, max(second, first+nums[i]) 49 | } 50 | return second 51 | } 52 | 53 | func max(i, j int) int { 54 | if i > j { 55 | return i 56 | } 57 | return j 58 | } 59 | -------------------------------------------------------------------------------- /leetcode/23/23_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "testing" 4 | 5 | func TestRob(t *testing.T) { 6 | nums := []int{2, 7, 9, 3, 1} 7 | t.Log(rob(nums)) 8 | t.Log(rob2(nums)) 9 | } 10 | -------------------------------------------------------------------------------- /leetcode/24/24.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "math" 4 | 5 | // 最小覆盖子串 6 | // 滑动窗口 7 | func minWindow(s string, t string) string { 8 | window := make(map[byte]int) 9 | need := make(map[byte]int) 10 | 11 | // 统计t中每个字符出现的次数 12 | for i := 0; i < len(t); i++ { 13 | need[t[i]]++ 14 | } 15 | 16 | // 判断need中的字符是否都在当前window中出现过 17 | // 并且出现次数一致 18 | // window中出现字符的次数可能会大于need中的,但不能小于,小于肯定不满足 19 | check := func() bool { 20 | for k, v := range need { 21 | // 如果字符k出现的次数少于need中的次数 22 | // 那么该窗口肯定不满足 23 | if window[k] < v { 24 | return false 25 | } 26 | } 27 | return true 28 | } 29 | 30 | minWinLen := math.MaxInt64 31 | // 能满足需要的最小左边界,与右边界 32 | minL, minR := -1, -1 33 | 34 | for l, r := 0, 0; r < len(s); r++ { 35 | // 统计当前窗口中每个字符出现的次数 36 | window[s[r]]++ 37 | // 如果当前的window窗口满足需求,则开始缩写左边界 38 | for check() && l <= r { 39 | // 当前窗口的长度 40 | currWinLen := r - l + 1 41 | 42 | // 如果当前的窗口长度小于之前的窗口长度 43 | // 那么就更新最小左右边界 44 | if minWinLen > currWinLen { 45 | minL = l 46 | // 最右边界等于左边界加上窗口的长度 47 | minR = l + minWinLen 48 | // 将当前窗口更新为最小窗口长度 49 | minWinLen = currWinLen 50 | } 51 | 52 | // window窗口中需要减去一次该字符出现的次数 53 | window[s[l]]-- 54 | 55 | // 右移左边界 56 | l++ 57 | } 58 | } 59 | 60 | if minL == -1 { 61 | return "" 62 | } 63 | // 左闭右开 64 | return s[minL : minR-1] 65 | } 66 | -------------------------------------------------------------------------------- /leetcode/24/24_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "testing" 4 | 5 | func TestMinWindow(t *testing.T) { 6 | s := "abcdefgslfjslkj" 7 | tt := "cfg" 8 | result := minWindow(s, tt) 9 | t.Log(result) 10 | } 11 | -------------------------------------------------------------------------------- /leetcode/25/25.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | var path []int 8 | var result [][]int 9 | 10 | // 找出1...n 之间的所有子集 11 | // 回溯算法 12 | func backtrack(n int, index int) { 13 | if len(path) > 0 { 14 | // 每一个路径都是一个子集,加入到结果集中 15 | result = append(result, append([]int{}, path...)) 16 | } 17 | // 当起始下标大于n时表示已经到叶子节点了 18 | if index > n { 19 | return 20 | } 21 | for i := index; i <= n; i++ { 22 | // 将本次选择的路径加入到选择列表中 23 | path = append(path, i) 24 | // 下标+1,进入下一次选择 25 | backtrack(n, i+1) 26 | // 回溯 27 | path = path[:len(path)-1] 28 | } 29 | } 30 | 31 | // 找数组nums的子集 32 | func subsets(nums []int) [][]int { 33 | ans := make([][]int, 0) 34 | track := make([]int, 0) 35 | var backtrack func(start int) 36 | backtrack = func(start int) { 37 | an := append([]int{}, track...) 38 | fmt.Println(an) 39 | ans = append(ans, an) 40 | for i := start; i < len(nums); i++ { 41 | // 收集子集 42 | track = append(track, nums[i]) 43 | // 下标+1,同一元素不能重复取 44 | backtrack(i + 1) 45 | // 移除最后一个值,回溯 46 | track = track[:len(track)-1] 47 | } 48 | } 49 | backtrack(0) 50 | return ans 51 | } 52 | -------------------------------------------------------------------------------- /leetcode/25/25_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "testing" 4 | 5 | func TestBacktrack(t *testing.T) { 6 | path = nil 7 | result = nil 8 | backtrack(3, 1) 9 | t.Log(result) 10 | } 11 | -------------------------------------------------------------------------------- /leetcode/26/26.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // 一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。 4 | // 求该青蛙跳上一个 n 级的台阶总共有多少种跳法。 5 | func numWays(n int) int { 6 | if n == 0 { 7 | return 1 8 | } 9 | if n == 2 || n == 1 { 10 | return n 11 | } 12 | dp := make([]int, n+1) 13 | dp[0] = 1 14 | dp[1] = 1 15 | for i := 2; i <= n; i++ { 16 | dp[i] = dp[i-1] + dp[i-2] 17 | } 18 | return dp[n] 19 | } 20 | -------------------------------------------------------------------------------- /leetcode/26/26_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "testing" 4 | 5 | func TestNumWays(t *testing.T) { 6 | // 7 -> 21 7 | t.Log(numWays(7)) 8 | } 9 | -------------------------------------------------------------------------------- /leetcode/3/3.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // 寻找100之间有多少个素数(排除0和1) 6 | // 素数:除了1和本身之外没有其他数能整除 7 | func main() { 8 | // 暴力求解法 9 | fmt.Println(bf(100)) 10 | // 埃筛法 11 | fmt.Println(eratosthenes(100)) 12 | } 13 | 14 | // 暴力算法 15 | func bf(n int) int { 16 | var count int 17 | for i := 2; i < n; i++ { 18 | if isPrime(i) { 19 | count++ 20 | } 21 | } 22 | return count 23 | } 24 | 25 | // 是否是素数 26 | func isPrime(x int) bool { 27 | // 只需要遍历 根号x即可,也就是 i*i <= x 28 | for i := 2; i*i <= x; i++ { 29 | if x%i == 0 { 30 | return false 31 | } 32 | } 33 | return true 34 | } 35 | 36 | // 埃筛法 37 | // 素数 * 另一个数 = 非素数,提前把非素数给过滤掉 38 | func eratosthenes(n int) int { 39 | // true 是非素数 40 | notPrimes := make([]bool, n) 41 | var count int 42 | for i := 2; i < n; i++ { 43 | // 如果是素数 44 | if !notPrimes[i] { 45 | count++ 46 | for j := i * i; j < n; j += i { 47 | notPrimes[j] = true 48 | } 49 | } 50 | } 51 | return count 52 | } 53 | -------------------------------------------------------------------------------- /leetcode/4/4.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // 删除数组中重复元素,返回删除后新数组长度 6 | // 数组是个排序好的数组,要求不能创建新的数组 7 | func main() { 8 | r := filter([]int{1, 2, 2, 3, 3, 3, 4}) 9 | fmt.Println(r) 10 | } 11 | 12 | // 使用双指针算法(快慢指针) 13 | func filter(nums []int) int { 14 | if len(nums) == 0 { 15 | return 0 16 | } 17 | var i int // i是慢指针,j是快指针 18 | for j := 1; j < len(nums); j++ { 19 | // 当两者不相同时移动i,j指针 20 | // 当两者相同时只移动 j 指针 21 | if nums[i] != nums[j] { 22 | i++ 23 | // 交换两者的值 24 | // 也就是将不重复的值往前移 25 | nums[i] = nums[j] 26 | } 27 | } 28 | newNums := nums[0 : i+1] 29 | fmt.Println(newNums) 30 | return i + 1 31 | } 32 | -------------------------------------------------------------------------------- /leetcode/5/5.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // 寻找数组中心下标 6 | // 中心下标:该下标左侧的值加一起的合等于右侧值加一起的合 7 | func main() { 8 | index := findIndex([]int{1, 3, 5, 1, 1, 2}) 9 | fmt.Println(index) 10 | } 11 | 12 | // 双指针 13 | // 先计算出总合,慢指针从左向右累加,sum累减 14 | // 当累加值等于累减值,那么该下标即为中心下标 15 | func findIndex(nums []int) int { 16 | var sum int 17 | for _, n := range nums { 18 | sum += n 19 | } 20 | 21 | var count int 22 | for i, n := range nums { 23 | count += n 24 | if count == sum { 25 | return i 26 | } 27 | sum -= n 28 | } 29 | return -1 30 | } 31 | -------------------------------------------------------------------------------- /leetcode/6/6.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // 求x的平方根,输出最接近的整数 6 | 7 | func main() { 8 | fmt.Println(square(25)) 9 | fmt.Println(square(24)) 10 | } 11 | 12 | // 二分法暴力求解 13 | func square(x int) int { 14 | var mid = -1 15 | var l = 0 // 左边界 16 | var r = x // 右边界 17 | 18 | for l <= r { 19 | mid = l + (r-l)/2 // 取 l 与 r 的中间位置 20 | if mid*mid == x { 21 | return mid 22 | } 23 | if mid*mid < x { 24 | l = mid + 1 // 左边界往右挪一格 25 | } else { 26 | r = mid - 1 // 右边界往左挪一格 27 | } 28 | } 29 | return mid 30 | } 31 | -------------------------------------------------------------------------------- /leetcode/8/8.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // 两数之和 6 | // 给定一个数组和一个target,返回该数组中 7 | // 两个元素相加等于target的这两个元素的下标 8 | func main() { 9 | // 暴力循环求解 10 | fmt.Println(findByBf([]int{2, 7, 11, 15}, 18)) 11 | // 优化,空间换时间 12 | fmt.Println(findByBf2([]int{2, 7, 11, 15}, 18)) 13 | } 14 | 15 | // 暴力循环求解 16 | func findByBf(nums []int, target int) (int, int) { 17 | n := len(nums) 18 | for i := 0; i < n; i++ { 19 | for j := i + 1; j < n; j++ { 20 | if nums[i]+nums[j] == target { 21 | return i, j 22 | } 23 | } 24 | } 25 | return 0, 0 26 | } 27 | 28 | // 优化,空间换时间 29 | func findByBf2(nums []int, target int) (int, int) { 30 | n := len(nums) 31 | flag := make(map[int]int) 32 | for i := 0; i < n; i++ { 33 | if j, ok := flag[target-nums[i]]; ok { 34 | return j, i 35 | } 36 | flag[nums[i]] = i 37 | } 38 | return 0, 0 39 | } 40 | -------------------------------------------------------------------------------- /leetcode/9/9.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // 斐波那契数列 6 | // 每一个数都等于前面那两个数之和 7 | // 0,1,1,2,3,5,8,13..... 8 | 9 | func main() { 10 | // 简单递归,暴力求解 11 | fmt.Println(findByBf(5)) 12 | // 优化,空间换时间 13 | fmt.Println(findByBf2(5)) 14 | } 15 | 16 | // 暴力递归求解 17 | func findByBf(num int) int { 18 | if num == 0 || num == 1 { 19 | return num 20 | } 21 | return findByBf(num-1) + findByBf(num-2) 22 | } 23 | 24 | // 优化,空间换时间 25 | // 在num-1的递归中已经寻找过了num-2的,没有必要在递归一次 26 | func findByBf2(num int) int { 27 | arr := make([]int, num+1) 28 | return findWithArr(arr, num) 29 | } 30 | 31 | func findWithArr(arr []int, num int) int { 32 | if num == 0 || num == 1 { 33 | return num 34 | } 35 | if arr[num] != 0 { 36 | return arr[num] 37 | } 38 | arr[num] = findWithArr(arr, num-1) + findWithArr(arr, num-2) 39 | return arr[num] 40 | } 41 | -------------------------------------------------------------------------------- /leetcode/README.md: -------------------------------------------------------------------------------- 1 | # LeetCode算法题 2 | > Go实现常见的算法题 3 | 4 | 刷题: [https://leetcode-cn.com/problemset/algorithms/](https://leetcode-cn.com/problemset/algorithms/) 5 | 6 | ## 目录 7 | - [单链表相关](1) 8 | - [字符串切分](2) 9 | - [寻找素数](3):埃筛法 10 | - [删除数组重复元素](4): 双指针算法(快慢指针算法) 11 | - [寻找数组中心下标](5): 双指针算法 12 | - [x的平方根](6): 二分法 13 | - [三个数乘积最大值](7): 线性扫描 14 | - [两数之和](8): 空间换时间 15 | - [斐波那契数列](9): 递归与空间换时间 16 | - [排列硬币](10): 二分法 17 | - [抢红包算法](11): 巧妙随机值 18 | - [合并有序数组](12): 双指针倒序遍历 19 | - [子数组最大平均数](13): 滑动窗口 20 | - [二叉树最小深度](14): 深度优先与广度优先 21 | - [最长递增子序列](15): 贪心算法 22 | - [柠檬水找零](16): 贪心算法 23 | - [三角形最大周长](17): 贪心算法 24 | - [二叉树遍历](18): 前中后层序遍历 25 | - [二叉树相关算法](19): 二叉树相关算法 26 | - [二叉搜索树相关算法](20): BST(二叉搜索树)相关算法 27 | - [表达式计算](21): 栈的使用 28 | - [两数与三数之和](22): 双指针之夹逼法 29 | - [打家劫舍](23): 动态规划 30 | - [最小覆盖子串](24): 滑动窗口 31 | - [数组所有子集](25): 回溯算法 32 | - [台阶问题](26): 动态规划-斐波那契变种 -------------------------------------------------------------------------------- /leetcode/common/list/list.go: -------------------------------------------------------------------------------- 1 | package list 2 | 3 | type Node struct { 4 | Val int 5 | Next *Node 6 | } 7 | 8 | func NewNode(val int, next *Node) *Node { 9 | return &Node{ 10 | Val: val, 11 | Next: next, 12 | } 13 | } 14 | 15 | // 1 -> 2 -> 3 -> 4 -> 5 16 | func GetHead() *Node { 17 | node5 := NewNode(5, nil) 18 | node4 := NewNode(4, node5) 19 | node3 := NewNode(3, node4) 20 | node2 := NewNode(2, node3) 21 | node1 := NewNode(1, node2) 22 | return node1 23 | } 24 | 25 | // 0 -> 6 -> 7 26 | func GetHead2() *Node { 27 | node7 := NewNode(7, nil) 28 | node6 := NewNode(6, node7) 29 | node0 := NewNode(0, node6) 30 | return node0 31 | } 32 | 33 | // 9 -> 11 -> 18 34 | func GetHead3() *Node { 35 | node18 := NewNode(18, nil) 36 | node11 := NewNode(11, node18) 37 | node9 := NewNode(9, node11) 38 | return node9 39 | } 40 | 41 | // 1 -> 2 -> 2 -> 1 -> nil 42 | func GetPalindromes() *Node { 43 | node4 := NewNode(1, nil) 44 | node3 := NewNode(2, node4) 45 | node2 := NewNode(2, node3) 46 | node1 := NewNode(1, node2) 47 | return node1 48 | } 49 | 50 | // 1 -> 2 -> 3 -> 1 51 | func GetCycle() *Node { 52 | var node1 *Node 53 | node3 := NewNode(3, nil) 54 | node2 := NewNode(2, node3) 55 | node1 = NewNode(1, node2) 56 | 57 | node3.Next = node1 58 | return node1 59 | } 60 | -------------------------------------------------------------------------------- /leetcode/common/tree/tree.go: -------------------------------------------------------------------------------- 1 | package tree 2 | 3 | type TreeNode struct { 4 | Val int 5 | Depth int 6 | Left *TreeNode 7 | Right *TreeNode 8 | } 9 | 10 | func NewTreeNode(val int, left *TreeNode, right *TreeNode) *TreeNode { 11 | return &TreeNode{ 12 | Val: val, 13 | Left: left, 14 | Right: right, 15 | } 16 | } 17 | 18 | func GetTree() *TreeNode { 19 | node8 := NewTreeNode(8, nil, nil) 20 | node7 := NewTreeNode(7, nil, nil) 21 | node6 := NewTreeNode(6, node7, nil) 22 | node5 := NewTreeNode(5, nil, nil) 23 | node4 := NewTreeNode(4, node8, nil) 24 | node3 := NewTreeNode(3, node6, nil) 25 | node2 := NewTreeNode(2, node4, node5) 26 | node1 := NewTreeNode(1, node2, node3) 27 | return node1 28 | } 29 | 30 | func GetHaveDuplicateTree() *TreeNode { 31 | node444 := NewTreeNode(4, nil, nil) 32 | node44 := NewTreeNode(4, nil, nil) 33 | node22 := NewTreeNode(2, node44, nil) 34 | node4 := NewTreeNode(4, nil, nil) 35 | node3 := NewTreeNode(3, node22, node444) 36 | node2 := NewTreeNode(2, node4, nil) 37 | node1 := NewTreeNode(1, node2, node3) 38 | return node1 39 | } 40 | 41 | // 返回一个二叉搜索树 42 | func GetBstTree() *TreeNode { 43 | node7 := NewTreeNode(7, nil, nil) 44 | node3 := NewTreeNode(3, nil, nil) 45 | node1 := NewTreeNode(1, nil, nil) 46 | node2 := NewTreeNode(2, node1, node3) 47 | root := NewTreeNode(4, node2, node7) 48 | return root 49 | } 50 | -------------------------------------------------------------------------------- /leetcode/interview/lru/array/lru_array.go: -------------------------------------------------------------------------------- 1 | package lru 2 | 3 | import "fmt" 4 | 5 | type LRUArray struct { 6 | len int 7 | cap int 8 | arrays []int 9 | } 10 | 11 | func NewLRUArray(capacity int) *LRUArray { 12 | return &LRUArray{ 13 | cap: capacity, 14 | arrays: make([]int, capacity), 15 | } 16 | } 17 | 18 | func (l *LRUArray) Put(value int) { 19 | index := l.findValue(value) 20 | if index != -1 { 21 | l.delete(index) 22 | } else { 23 | if l.len >= l.cap { 24 | l.removeLast() 25 | } 26 | } 27 | l.insertToFirst(value) 28 | } 29 | 30 | func (l *LRUArray) findValue(value int) int { 31 | for i, v := range l.arrays { 32 | if v == value { 33 | return i 34 | } 35 | } 36 | return -1 37 | } 38 | 39 | func (l *LRUArray) insertToFirst(value int) { 40 | if l.len > 1 { 41 | // 将所有元素的位置往后挪一下 42 | for i := l.len - 1; i >= 0; i-- { 43 | l.arrays[i+1] = l.arrays[i] 44 | } 45 | } 46 | l.arrays[0] = value 47 | l.len++ 48 | } 49 | 50 | func (l *LRUArray) removeLast() { 51 | l.len-- 52 | } 53 | 54 | func (l *LRUArray) delete(index int) { 55 | for i := index + 1; i < l.len; i++ { 56 | l.arrays[i-1] = l.arrays[i] 57 | } 58 | fmt.Println("删除元素...") 59 | l.len-- 60 | } 61 | -------------------------------------------------------------------------------- /leetcode/interview/lru/array/lru_array_test.go: -------------------------------------------------------------------------------- 1 | package lru 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestLRU(t *testing.T) { 9 | l := NewLRUArray(10) 10 | for i := 1; i < 15; i++ { 11 | l.Put(i) 12 | } 13 | fmt.Println(l.arrays) 14 | } 15 | -------------------------------------------------------------------------------- /leetcode/interview/lru/list/lru_list_test.go: -------------------------------------------------------------------------------- 1 | package lru 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestLRU(t *testing.T) { 9 | l := NewLRUCache(10) 10 | for i := 1; i < 15; i++ { 11 | l.Put(i, fmt.Sprintf("第%d个", i)) 12 | } 13 | for _, key := range l.Keys() { 14 | fmt.Println(key) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /leetcode/interview/sort/charu/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | arrays := []int{1, 10, 5, 2, 6, 7, 10, 8, 9, 4, 3} 7 | charu(arrays) 8 | fmt.Println(arrays) 9 | } 10 | 11 | /** 12 | 插入排序的原理是,从第二个数开始向右侧遍历,' 13 | 每次均把该位置的元素移动至左侧,放在放在一个正确的位置(比左侧大,比右侧小)。 14 | */ 15 | func charu(arrays []int) { 16 | for i := 1; i < len(arrays); i++ { 17 | if arrays[i] < arrays[i-1] { 18 | j := i - 1 19 | temp := arrays[i] 20 | for j >= 0 && arrays[j] > temp { 21 | arrays[j+1] = arrays[j] 22 | j-- 23 | } 24 | arrays[j+1] = temp 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /leetcode/interview/sort/kuaipai/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | arrays := []int{1, 10, 5, 2, 6, 7, 10, 8, 9, 4, 3} 7 | kuaipai(arrays) 8 | fmt.Println(arrays) 9 | } 10 | 11 | /** 12 | 快速排序的原理是,首先找到一个数pivot把数组‘平均’分成两组, 13 | 使其中一组的所有数字均大于另一组中的数字, 14 | 此时pivot在数组中的位置就是它正确的位置。然后,对这两组数组再次进行这种操作 15 | */ 16 | func kuaipai(arrays []int) { 17 | recursionSort(arrays, 0, len(arrays)-1) 18 | 19 | } 20 | 21 | func recursionSort(nums []int, left int, right int) { 22 | if left < right { 23 | pivot := partition(nums, left, right) 24 | recursionSort(nums, left, pivot-1) 25 | recursionSort(nums, pivot+1, right) 26 | } 27 | } 28 | 29 | func partition(nums []int, left int, right int) int { 30 | for left < right { 31 | for left < right && nums[left] <= nums[right] { 32 | right-- 33 | } 34 | if left < right { 35 | nums[left], nums[right] = nums[right], nums[left] 36 | left++ 37 | } 38 | 39 | for left < right && nums[left] <= nums[right] { 40 | left++ 41 | } 42 | if left < right { 43 | nums[left], nums[right] = nums[right], nums[left] 44 | right-- 45 | } 46 | } 47 | 48 | return left 49 | } 50 | -------------------------------------------------------------------------------- /leetcode/interview/sort/maopao/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | arrays := []int{1, 10, 5, 2, 6, 7, 10, 8, 9, 4, 3} 7 | maopao(arrays) 8 | fmt.Println(arrays) 9 | } 10 | 11 | /** 12 | 对给定的数组进行多次遍历,每次均比较相邻的两个数, 13 | 如果前一个比后一个大,则交换这两个数。经过第一次遍历之后, 14 | 最大的数就在最右侧了;第二次遍历之后,第二大的数就在右数第二个位置了 15 | */ 16 | func maopao(arrays []int) { 17 | for i := 0; i < len(arrays); i++ { 18 | for j := 1; j < len(arrays)-i; j++ { 19 | if arrays[j-1] > arrays[j] { 20 | arrays[j], arrays[j-1] = arrays[j-1], arrays[j] 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /leetcode/interview/sort/xuanze/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | arrays := []int{1, 10, 5, 2, 6, 7, 10, 8, 9, 4, 3} 7 | xuanze(arrays) 8 | fmt.Println(arrays) 9 | } 10 | 11 | /** 12 | 对给定的数组进行多次遍历,每次均找出最大的一个值的索引 13 | */ 14 | func xuanze(arrays []int) { 15 | length := len(arrays) 16 | for i := 0; i < length; i++ { 17 | // 最大值的下标 18 | maxIndex := 0 19 | for j := 1; j < length-i; j++ { 20 | if arrays[j] > arrays[maxIndex] { 21 | maxIndex = j 22 | } 23 | } 24 | arrays[length-i-1], arrays[maxIndex] = arrays[maxIndex], arrays[length-i-1] 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /pprof/gc/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "runtime" 7 | "time" 8 | ) 9 | 10 | // 查看当前程序内存分配情况 11 | func readMemStats() { 12 | var ms runtime.MemStats 13 | runtime.ReadMemStats(&ms) 14 | fmt.Printf("Alloc:%d(bytes) HeapIdle:%d(bytes) HeapReleased:%d(bytes) \n", ms.Alloc, ms.HeapIdle, ms.HeapReleased) 15 | } 16 | 17 | func main() { 18 | readMemStats() 19 | 20 | arrays := make([]int, 8) 21 | for i := 0; i < 32*1000*1000; i++ { 22 | arrays = append(arrays, i) 23 | if i == 16*1000*1000 { 24 | readMemStats() 25 | } 26 | } 27 | readMemStats() 28 | 29 | log.Println(" ===> [start gc]") 30 | runtime.GC() //强制调用gc回收 31 | 32 | log.Println(" ===> [gc Done].") 33 | readMemStats() 34 | 35 | time.Sleep(3 * time.Second) 36 | } 37 | -------------------------------------------------------------------------------- /pprof/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/charmbracelet/lipgloss" 6 | _ "net/http/pprof" 7 | ) 8 | 9 | var style = lipgloss.NewStyle().Bold(false).Foreground(lipgloss.Color("#FAFAFA")).Background(lipgloss.Color("#7D56F4")).PaddingTop(2).PaddingLeft(4).Width(22) 10 | 11 | func main() { 12 | fmt.Println(style.Render("Hello, kitty.")) 13 | } 14 | -------------------------------------------------------------------------------- /revive.toml: -------------------------------------------------------------------------------- 1 | ignoreGeneratedHeader = false 2 | severity = "error" 3 | confidence = 0.8 4 | errorCode = -1 5 | warningCode = -1 6 | 7 | # [rule.blank-imports] 8 | [rule.context-as-argument] 9 | # [rule.context-keys-type] 10 | [rule.error-return] 11 | [rule.error-strings] 12 | [rule.error-naming] 13 | # [rule.exported] 14 | [rule.if-return] 15 | [rule.increment-decrement] 16 | # [rule.var-naming] 17 | arguments = [["ID"]] 18 | # [rule.var-declaration] 19 | [rule.package-comments] 20 | [rule.range] 21 | [rule.receiver-naming] 22 | [rule.time-naming] 23 | [rule.unexported-return] 24 | [rule.indent-error-flow] 25 | [rule.errorf] 26 | # [rule.empty-block] 27 | [rule.superfluous-else] 28 | # [rule.unused-parameter] 29 | # [rule.unreachable-code] 30 | [rule.redefines-builtin-id] -------------------------------------------------------------------------------- /sdk/README.md: -------------------------------------------------------------------------------- 1 | # 使用Go语言对接第三方工具 2 | 3 | 4 | ## 框架 5 | - [x] [gin](gin) 6 | - [x] [gf](gf) 7 | - [ ] [kit](kit) 8 | - [ ] [beego](beego) 9 | - [x] [kite](kite) 10 | - [x] [wire](wire) 11 | 12 | ## 数据库 13 | - [x] [Mysql](mysql) 14 | - [x] [Redis](redis) 15 | - [x] [ElasticSearch](elasticsearch) 16 | - [x] [Etcd](etcd) 17 | - [x] [MongoDB](mongodb) 18 | ## 支付 19 | - [x] [支付宝支付](alipay) 20 | - [x] [微信支付](weixin) 21 | 22 | ## 消息队列 23 | - [x] [Kafka](kafka) 24 | - [x] [Nsq](nsq) 25 | - [x] [Mqtt](mqtt) 26 | - [x] [RabbitMQ](rabbitmq) 27 | - [x] [rocketmq](rocketmq) 28 | 29 | ## 第三方登陆 30 | - [x] [QQ登录](qq) 31 | - [ ] 微信登录 32 | 33 | ## 其他 34 | - [x] [ldap登录](ldap) 35 | - [x] [阿里云OSS](oss) 36 | - [x] [钉钉机器人](robot) 37 | - [x] [SMS短信发送](sms) 38 | - [x] [短域名生成](shortdomain) 39 | - [x] [链路跟踪](trace) 40 | - [x] [consul使用](consul) 41 | - [x] [websocket](websocket) -------------------------------------------------------------------------------- /sdk/consul/consul_test.go: -------------------------------------------------------------------------------- 1 | package consul 2 | 3 | import "testing" 4 | 5 | func TestRegister(t *testing.T) { 6 | Register("10.54.212.69") 7 | } 8 | 9 | func TestReadConfig(t *testing.T) { 10 | result := ReadConfig("test") 11 | t.Log(result) 12 | } 13 | 14 | func TestDeleteService(t *testing.T) { 15 | err := DeleteService("test") 16 | if err != nil { 17 | t.Error(err) 18 | } 19 | } 20 | 21 | func TestListService(t *testing.T) { 22 | err := ListService() 23 | if err != nil { 24 | t.Error(err) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /sdk/elasticsearch/doc/Makefile: -------------------------------------------------------------------------------- 1 | run: 2 | docker-compose up -d 3 | 4 | stop: 5 | docker-compose down 6 | 7 | cleanup: stop 8 | -------------------------------------------------------------------------------- /sdk/elasticsearch/doc/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | # Ref: https://www.elastic.co/guide/en/elastic-stack-get-started/current/get-started-docker.html 2 | version: '2.2' 3 | services: 4 | es: 5 | image: docker.elastic.co/elasticsearch/elasticsearch:7.15.1 6 | container_name: es 7 | environment: 8 | - node.name=es 9 | - cluster.name=es-docker-cluster 10 | - cluster.initial_master_nodes=es 11 | - bootstrap.memory_lock=true 12 | - "ES_JAVA_OPTS=-Xms512m -Xmx512m" 13 | ulimits: 14 | memlock: 15 | soft: -1 16 | hard: -1 17 | volumes: 18 | - data01:/usr/share/elasticsearch/data 19 | ports: 20 | - 9200:9200 21 | networks: 22 | - elastic 23 | 24 | volumes: 25 | data01: 26 | driver: local 27 | 28 | networks: 29 | elastic: 30 | driver: bridge -------------------------------------------------------------------------------- /sdk/elasticsearch/elasticsearch_test.go: -------------------------------------------------------------------------------- 1 | package elastic 2 | 3 | import ( 4 | "encoding/json" 5 | "os" 6 | "testing" 7 | ) 8 | 9 | type User struct { 10 | Name string `json:"name"` 11 | Age int `json:"age"` 12 | } 13 | 14 | func TestElasticSearch(t *testing.T) { 15 | hostname, _ := os.Hostname() 16 | if hostname == "pibigstar" { 17 | user := &User{Name: "pibigstar", Age: 18} 18 | 19 | response, err := client.Insert("test", user) 20 | if err != nil { 21 | t.Error(err) 22 | } 23 | t.Logf("%+v", response) 24 | 25 | bytes, err := client.GetById("test", response.Id) 26 | if err != nil { 27 | t.Error(err) 28 | } 29 | result := new(User) 30 | json.Unmarshal(bytes, result) 31 | t.Logf("%+v", result) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /sdk/gf/.gitattributes: -------------------------------------------------------------------------------- 1 | * linguist-language=GO 2 | -------------------------------------------------------------------------------- /sdk/gf/.gitignore: -------------------------------------------------------------------------------- 1 | .buildpath 2 | .hgignore.swp 3 | .project 4 | .orig 5 | .swp 6 | .idea/ 7 | .settings/ 8 | .vscode/ 9 | vender/ 10 | log/ 11 | composer.lock 12 | gitpush.sh 13 | pkg/ 14 | bin/ 15 | cbuild 16 | */.DS_Store 17 | main 18 | gf-empty 19 | .vscode 20 | go.sum -------------------------------------------------------------------------------- /sdk/gf/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM loads/alpine:3.8 2 | 3 | LABEL maintainer="john@goframe.org" 4 | 5 | ############################################################################### 6 | # INSTALLATION 7 | ############################################################################### 8 | 9 | # 设置固定的项目路径 10 | ENV WORKDIR /var/www/demo 11 | 12 | # 添加应用可执行文件,并设置执行权限 13 | ADD ./bin/linux_amd64/main $WORKDIR/main 14 | RUN chmod +x $WORKDIR/main 15 | 16 | # 添加I18N多语言文件、静态文件、配置文件、模板文件 17 | ADD i18n $WORKDIR/i18n 18 | ADD public $WORKDIR/public 19 | ADD config $WORKDIR/config 20 | ADD template $WORKDIR/template 21 | 22 | ############################################################################### 23 | # START 24 | ############################################################################### 25 | WORKDIR $WORKDIR 26 | CMD ./main 27 | -------------------------------------------------------------------------------- /sdk/gf/README.MD: -------------------------------------------------------------------------------- 1 | # GoFrame Project 2 | 3 | # 工具安装 4 | 5 | ```bash 6 | go get github.com/gogf/gf-cli/v2/gf@master 7 | ``` 8 | 9 | # 项目初始化 10 | 11 | ```bash 12 | gf init go-demo 13 | ``` 14 | 15 | # 开发文档 16 | > https://goframe.org/pages/viewpage.action?pageId=1114399 -------------------------------------------------------------------------------- /sdk/gf/app/api/hello.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/gogf/gf/net/ghttp" 5 | ) 6 | 7 | var Hello = helloApi{} 8 | 9 | type helloApi struct {} 10 | 11 | // Index is a demonstration route handler for output "Hello World!". 12 | func (*helloApi) Index(r *ghttp.Request) { 13 | r.Response.Writeln("Hello World!") 14 | } 15 | -------------------------------------------------------------------------------- /sdk/gf/app/dao/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibigstar/go-demo/1b4058972b01ec376d8c2131f8839bdf1fe909ab/sdk/gf/app/dao/.gitkeep -------------------------------------------------------------------------------- /sdk/gf/app/model/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibigstar/go-demo/1b4058972b01ec376d8c2131f8839bdf1fe909ab/sdk/gf/app/model/.gitkeep -------------------------------------------------------------------------------- /sdk/gf/app/service/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibigstar/go-demo/1b4058972b01ec376d8c2131f8839bdf1fe909ab/sdk/gf/app/service/.gitkeep -------------------------------------------------------------------------------- /sdk/gf/boot/boot.go: -------------------------------------------------------------------------------- 1 | package boot 2 | 3 | import ( 4 | _ "go-demo/sdk/gf/packed" 5 | ) 6 | 7 | func init() { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /sdk/gf/config/config.toml: -------------------------------------------------------------------------------- 1 | # HTTP Server 2 | [server] 3 | Address = ":8199" 4 | ServerRoot = "public" 5 | ServerAgent = "gf-app" 6 | LogPath = "/tmp/log/gf-app/server" 7 | 8 | # Logger. 9 | [logger] 10 | Path = "/tmp/log/gf-app" 11 | Level = "all" 12 | Stdout = true 13 | 14 | # Template. 15 | [viewer] 16 | Path = "template" 17 | DefaultFile = "index.html" 18 | Delimiters = ["{{", "}}"] 19 | 20 | # Database. 21 | [database] 22 | link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test" 23 | debug = true 24 | # Database logger. 25 | [database.logger] 26 | Path = "/tmp/log/gf-app/sql" 27 | Level = "all" 28 | Stdout = true -------------------------------------------------------------------------------- /sdk/gf/docker/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibigstar/go-demo/1b4058972b01ec376d8c2131f8839bdf1fe909ab/sdk/gf/docker/.gitkeep -------------------------------------------------------------------------------- /sdk/gf/document/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibigstar/go-demo/1b4058972b01ec376d8c2131f8839bdf1fe909ab/sdk/gf/document/.gitkeep -------------------------------------------------------------------------------- /sdk/gf/i18n/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibigstar/go-demo/1b4058972b01ec376d8c2131f8839bdf1fe909ab/sdk/gf/i18n/.gitkeep -------------------------------------------------------------------------------- /sdk/gf/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | _ "go-demo/sdk/gf/boot" 5 | _ "go-demo/sdk/gf/router" 6 | 7 | "github.com/gogf/gf/frame/g" 8 | ) 9 | 10 | func main() { 11 | g.Server().Run() 12 | } 13 | -------------------------------------------------------------------------------- /sdk/gf/packed/packed.go: -------------------------------------------------------------------------------- 1 | package packed 2 | -------------------------------------------------------------------------------- /sdk/gf/public/html/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibigstar/go-demo/1b4058972b01ec376d8c2131f8839bdf1fe909ab/sdk/gf/public/html/.gitkeep -------------------------------------------------------------------------------- /sdk/gf/public/plugin/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibigstar/go-demo/1b4058972b01ec376d8c2131f8839bdf1fe909ab/sdk/gf/public/plugin/.gitkeep -------------------------------------------------------------------------------- /sdk/gf/public/resource/css/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibigstar/go-demo/1b4058972b01ec376d8c2131f8839bdf1fe909ab/sdk/gf/public/resource/css/.gitkeep -------------------------------------------------------------------------------- /sdk/gf/public/resource/image/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibigstar/go-demo/1b4058972b01ec376d8c2131f8839bdf1fe909ab/sdk/gf/public/resource/image/.gitkeep -------------------------------------------------------------------------------- /sdk/gf/public/resource/js/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibigstar/go-demo/1b4058972b01ec376d8c2131f8839bdf1fe909ab/sdk/gf/public/resource/js/.gitkeep -------------------------------------------------------------------------------- /sdk/gf/router/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibigstar/go-demo/1b4058972b01ec376d8c2131f8839bdf1fe909ab/sdk/gf/router/.gitkeep -------------------------------------------------------------------------------- /sdk/gf/router/router.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "github.com/gogf/gf/frame/g" 5 | "github.com/gogf/gf/net/ghttp" 6 | "go-demo/sdk/gf/app/api" 7 | ) 8 | 9 | func init() { 10 | s := g.Server() 11 | s.Group("/", func(group *ghttp.RouterGroup) { 12 | group.ALL("/hello", api.Hello) 13 | }) 14 | } 15 | -------------------------------------------------------------------------------- /sdk/gf/template/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibigstar/go-demo/1b4058972b01ec376d8c2131f8839bdf1fe909ab/sdk/gf/template/.gitkeep -------------------------------------------------------------------------------- /sdk/kafka/consumer/consumer.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | 7 | "github.com/Shopify/sarama" 8 | ) 9 | 10 | var ( 11 | wg sync.WaitGroup 12 | ) 13 | 14 | func main() { 15 | consumer, err := sarama.NewConsumer([]string{"localhost:9092"}, nil) 16 | if err != nil { 17 | panic(err) 18 | } 19 | partitionList, err := consumer.Partitions("go-test") 20 | if err != nil { 21 | panic(err) 22 | } 23 | for partition := range partitionList { 24 | pc, err := consumer.ConsumePartition("go-test", int32(partition), sarama.OffsetNewest) 25 | if err != nil { 26 | panic(err) 27 | } 28 | defer pc.AsyncClose() 29 | wg.Add(1) 30 | 31 | go func(sarama.PartitionConsumer) { 32 | defer wg.Done() 33 | for msg := range pc.Messages() { 34 | fmt.Printf("Partition:%d, Offset:%d, Key:%s, Value:%s\n", msg.Partition, msg.Offset, string(msg.Key), string(msg.Value)) 35 | } 36 | }(pc) 37 | wg.Wait() 38 | consumer.Close() 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /sdk/kafka/producer/producer.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | "strings" 8 | 9 | "github.com/Shopify/sarama" 10 | ) 11 | 12 | // 生产者 13 | 14 | func main() { 15 | config := sarama.NewConfig() 16 | config.Producer.Return.Successes = true 17 | config.Producer.RequiredAcks = sarama.WaitForAll 18 | config.Producer.Partitioner = sarama.NewRandomPartitioner 19 | 20 | producer, err := sarama.NewSyncProducer([]string{"localhost:9092"}, config) 21 | if err != nil { 22 | panic(err) 23 | } 24 | defer producer.Close() 25 | msg := &sarama.ProducerMessage{ 26 | Topic: "go-test", 27 | Partition: int32(-1), 28 | Key: sarama.StringEncoder("key"), 29 | } 30 | var value string 31 | for { 32 | inputReader := bufio.NewReader(os.Stdin) 33 | value, err = inputReader.ReadString('\n') 34 | if err != nil { 35 | panic(err) 36 | } 37 | value = strings.Replace(value, "\n", "", -1) 38 | msg.Value = sarama.ByteEncoder(value) 39 | paritition, offset, err := producer.SendMessage(msg) 40 | if err != nil { 41 | fmt.Println("Send Message Fail") 42 | } 43 | fmt.Printf("Partion = %d, offset = %d\n", paritition, offset) 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /sdk/kite/kite.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/koding/kite" 4 | 5 | func main() { 6 | // Create a kite 7 | k := kite.New("math", "1.0.0") 8 | 9 | // Add our handler method with the name "square" 10 | k.HandleFunc("square", func(r *kite.Request) (interface{}, error) { 11 | a := r.Args.One().MustFloat64() 12 | result := a * a // calculate the square 13 | return result, nil // send back the result 14 | }).DisableAuthentication() 15 | 16 | // Attach to a server with port 3636 and run it 17 | k.Config.Port = 3636 18 | k.Run() 19 | } 20 | -------------------------------------------------------------------------------- /sdk/ldap/ldap_test.go: -------------------------------------------------------------------------------- 1 | package ldap 2 | 3 | import "testing" 4 | 5 | func TestLogin(t *testing.T) { 6 | _, err := LoginBind("hello", "123456") 7 | if err != nil { 8 | t.Error(err) 9 | } 10 | } 11 | 12 | func TestAddUser(t *testing.T) { 13 | // 登录管理员账号 14 | conn, err := LoginBind("admin", "123456") 15 | if err != nil { 16 | t.Error(err) 17 | } 18 | 19 | username := "xiao ming" 20 | // 创建新用户 21 | err = AddUser(conn, username, "123456") 22 | if err != nil { 23 | t.Error(err) 24 | } 25 | 26 | // 验证新增用户登录 27 | _, err = LoginBind(username, "123456") 28 | if err != nil { 29 | t.Error(err) 30 | } 31 | 32 | // 删除新用户 33 | err = DeleteUser(conn, username) 34 | if err != nil { 35 | t.Error(err) 36 | } 37 | } 38 | 39 | func TestListUser(t *testing.T) { 40 | // 登录管理员账号 41 | conn, err := LoginBind("admin", "pibigstar") 42 | if err != nil { 43 | t.Error(err) 44 | } 45 | 46 | users, err := ListUser(conn) 47 | if err != nil { 48 | t.Error(err) 49 | } 50 | t.Log(users) 51 | } 52 | -------------------------------------------------------------------------------- /sdk/mqtt/mqtt_test.go: -------------------------------------------------------------------------------- 1 | package mqtt 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "os" 7 | "sync" 8 | "testing" 9 | "time" 10 | ) 11 | 12 | func TestMqtt(t *testing.T) { 13 | hostname, _ := os.Hostname() 14 | if hostname == "pibigstar" { 15 | var ( 16 | clientId = hostname 17 | wg sync.WaitGroup 18 | ) 19 | client := NewClient(clientId) 20 | err := client.Connect() 21 | if err != nil { 22 | t.Errorf(err.Error()) 23 | } 24 | 25 | wg.Add(1) 26 | go func() { 27 | err := client.Subscribe(func(c *Client, msg *Message) { 28 | fmt.Printf("接收到消息: %+v \n", msg) 29 | wg.Done() 30 | }, 1, "mqtt") 31 | 32 | if err != nil { 33 | panic(err) 34 | } 35 | }() 36 | 37 | msg := &Message{ 38 | ClientID: clientId, 39 | Type: "text", 40 | Data: "Hello Pibistar", 41 | Time: time.Now().Unix(), 42 | } 43 | data, _ := json.Marshal(msg) 44 | 45 | err = client.Publish("mqtt", 1, false, data) 46 | if err != nil { 47 | panic(err) 48 | } 49 | 50 | wg.Wait() 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /sdk/mysql/mysql.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "github.com/jinzhu/gorm" 7 | _ "github.com/jinzhu/gorm/dialects/mysql" 8 | "os" 9 | ) 10 | 11 | var ( 12 | user = "root" 13 | password = "" 14 | host = "127.0.0.1" 15 | port = 3306 16 | db = "test" 17 | ) 18 | 19 | func init() { 20 | // for ci success 21 | name, _ := os.Hostname() 22 | if name == "pibigstar" { 23 | password = "123456" 24 | } 25 | } 26 | 27 | //使用gorm链接mysql 28 | func GetGormDB() (*gorm.DB, error) { 29 | dns := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?parseTime=true&loc=Local&charset=utf8mb4,utf8", 30 | user, password, host, port, db) 31 | return gorm.Open("mysql", dns) 32 | } 33 | 34 | // 原生mysql链接 35 | func GetMySQLDB() (*sql.DB, error) { 36 | dns := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?parseTime=true&loc=Local&charset=utf8mb4,utf8", 37 | user, password, host, port, db) 38 | return sql.Open("mysql", dns) 39 | } 40 | -------------------------------------------------------------------------------- /sdk/mysql/mysql_test.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestConnectMysql(t *testing.T) { 8 | db, err := GetGormDB() 9 | defer db.Close() 10 | if err != nil { 11 | t.Error(err) 12 | } 13 | err = db.Exec("SELECT CURRENT_TIMESTAMP();").Error 14 | if err != nil { 15 | t.Error(err) 16 | } 17 | } 18 | 19 | func TestMySQLDB(t *testing.T) { 20 | db, err := GetMySQLDB() 21 | if err != nil { 22 | t.Error(err) 23 | } 24 | defer db.Close() 25 | _, err = db.Exec("SELECT CURRENT_TIMESTAMP();") 26 | if err != nil { 27 | t.Error(err) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /sdk/mysql/pool/mysql_pool_test.go: -------------------------------------------------------------------------------- 1 | package pool 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "testing" 7 | ) 8 | 9 | func TestPool1(t *testing.T) { 10 | ctx := context.Background() 11 | config := &Config{ 12 | MaxConn: 2, 13 | MaxIdle: 1, 14 | MaxWait: 10, 15 | MaxWaitTimeout: 3000, 16 | } 17 | conn := Prepare(ctx, config) 18 | if _, err := conn.New(ctx); err != nil { 19 | fmt.Println(err.Error()) 20 | return 21 | } 22 | // 释放连接 23 | go conn.Release(ctx) 24 | 25 | if _, err := conn.New(ctx); err != nil { 26 | fmt.Println(err.Error()) 27 | return 28 | } 29 | // 释放连接 30 | go conn.Release(ctx) 31 | 32 | if _, err := conn.New(ctx); err != nil { 33 | fmt.Println(err.Error()) 34 | return 35 | } 36 | 37 | if _, err := conn.New(ctx); err != nil { 38 | fmt.Println(err.Error()) 39 | return 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /sdk/nsq/nsqio/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/nsqio/go-nsq" 6 | "time" 7 | ) 8 | 9 | var ( 10 | addr = "127.0.0.1:4150" 11 | defaultConfig = nsq.NewConfig() 12 | ) 13 | 14 | // 主函数 15 | func main() { 16 | Producer("test-dev", []byte("Hello Pibigstar")) 17 | 18 | Consumer("test-dev", "default", HandleMessage) 19 | time.Sleep(time.Second * 3) 20 | } 21 | 22 | func HandleMessage(msg *nsq.Message) error { 23 | fmt.Println(string(msg.Body)) 24 | return nil 25 | } 26 | 27 | // nsq发布消息 28 | func Producer(topic string, data []byte) error { 29 | // 新建生产者 30 | p, err := nsq.NewProducer(addr, defaultConfig) 31 | if err != nil { 32 | panic(err) 33 | } 34 | // 发布消息 35 | return p.Publish(topic, data) 36 | } 37 | 38 | // 消费消息 39 | func Consumer(topic, channel string, handlerFunc nsq.HandlerFunc) error { 40 | //新建一个消费者 41 | c, err := nsq.NewConsumer(topic, channel, defaultConfig) 42 | if err != nil { 43 | panic(err) 44 | } 45 | //添加消息处理 46 | c.AddHandler(handlerFunc) 47 | //建立连接 48 | return c.ConnectToNSQD(addr) 49 | } 50 | 51 | // 运行将会打印: hello NSQ!!! 52 | -------------------------------------------------------------------------------- /sdk/nsq/test/nsqTest.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/pibigstar/go-sdk/nsq" 6 | ) 7 | 8 | func main() { 9 | err := nsq.Publish("test", []byte("Hello Pibigstar")) 10 | if err != nil { 11 | fmt.Println(err.Error()) 12 | return 13 | } 14 | c := make(chan struct{}) 15 | 16 | nsq.Consume("test", "default", func(msg *nsq.Message) error { 17 | fmt.Println(string(msg.Body)) 18 | select { 19 | case <-c: 20 | default: 21 | close(c) 22 | } 23 | return nil 24 | }, 5) 25 | <-c 26 | } 27 | -------------------------------------------------------------------------------- /sdk/oss/oss_test.go: -------------------------------------------------------------------------------- 1 | package oss 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "testing" 7 | ) 8 | 9 | func TestOSS(t *testing.T) { 10 | file, err := os.Open("test.txt") 11 | if err != nil { 12 | fmt.Println(err.Error()) 13 | return 14 | } 15 | err = client.Put("oss/test.txt", file) 16 | if err != nil { 17 | fmt.Println(err.Error()) 18 | } 19 | 20 | url := client.GetDownloadURL("oss/test.txt") 21 | fmt.Println(url) 22 | } 23 | -------------------------------------------------------------------------------- /sdk/oss/test.txt: -------------------------------------------------------------------------------- 1 | 测试文件 -------------------------------------------------------------------------------- /sdk/rabbitmq/rabbitmq_test.go: -------------------------------------------------------------------------------- 1 | package mq 2 | 3 | import "testing" 4 | 5 | func TestRabbitMq(t *testing.T) { 6 | t.Log("RabbitMQ Success!") 7 | } 8 | -------------------------------------------------------------------------------- /sdk/redis/redis_test.go: -------------------------------------------------------------------------------- 1 | package redis 2 | 3 | import ( 4 | "github.com/go-redis/redis" 5 | "testing" 6 | ) 7 | 8 | func TestRedis(t *testing.T) { 9 | Redis.SSet("test", "pibigstar") 10 | t.Log(Redis.SGet("test")) 11 | } 12 | 13 | var lua = ` 14 | local key1 = tostring(KEYS[1]) 15 | local key2 = tostring(KEYS[2]) 16 | local args1 = tonumber(ARGV[1]) 17 | local args2 = tonumber(ARGV[2]) 18 | 19 | if key1 == "user" 20 | then 21 | redis.call('SET',key1,args1) 22 | return 1 23 | else 24 | redis.call('SET',key2,args2) 25 | return 2 26 | end 27 | return 0 28 | ` 29 | 30 | func TestLua(t *testing.T) { 31 | client, err := GetRedisClient() 32 | if err != nil { 33 | t.Error(err) 34 | } 35 | script := redis.NewScript(lua) 36 | 37 | cmd := script.Run(client, []string{"user", "test"}, 1, 2) 38 | t.Log(cmd.Result()) 39 | } 40 | -------------------------------------------------------------------------------- /sdk/shortdomain/shorten.go: -------------------------------------------------------------------------------- 1 | package shortdomain 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | "net/url" 9 | ) 10 | 11 | const ( 12 | apiURL = "https://api.uomg.com/api/long2dwz?%s" 13 | ) 14 | 15 | type ShortResponse struct { 16 | Code int `json:"code"` 17 | ShortURL string `json:"ae_url"` 18 | } 19 | 20 | // https://www.free-api.com/doc/300 21 | func GetShortURL(longURL string) (string, error) { 22 | 23 | params := url.Values{} 24 | params.Add("url", longURL) 25 | params.Add("dwzapi", "urlcn") 26 | 27 | resp, err := http.Get(fmt.Sprintf(apiURL, params.Encode())) 28 | defer resp.Body.Close() 29 | 30 | body, err := ioutil.ReadAll(resp.Body) 31 | if err != nil { 32 | return "", err 33 | } 34 | shortResponse := new(ShortResponse) 35 | err = json.Unmarshal(body, shortResponse) 36 | 37 | return shortResponse.ShortURL, err 38 | } 39 | -------------------------------------------------------------------------------- /sdk/shortdomain/shorten_test.go: -------------------------------------------------------------------------------- 1 | package shortdomain 2 | 3 | import "testing" 4 | 5 | func TestGetShortURL(t *testing.T) { 6 | url, err := GetShortURL("https://pibigstar.github.io") 7 | if err != nil { 8 | t.Error(err) 9 | } 10 | t.Log(url) 11 | } 12 | -------------------------------------------------------------------------------- /sdk/trace/README.md: -------------------------------------------------------------------------------- 1 | # 链路跟踪 2 | 3 | ## demo 4 | > 简单示例 5 | 6 | ### 1. 安装 jaeger 7 | doc: [点击查看](https://www.jaegertracing.io/docs/1.17/getting-started) 8 | ```bash 9 | docker run -d --name jaeger \ 10 | -e COLLECTOR_ZIPKIN_HTTP_PORT=9411 \ 11 | -p 5775:5775/udp \ 12 | -p 6831:6831/udp \ 13 | -p 6832:6832/udp \ 14 | -p 5778:5778 \ 15 | -p 16686:16686 \ 16 | -p 14268:14268 \ 17 | -p 14250:14250 \ 18 | -p 9411:9411 \ 19 | jaegertracing/all-in-one:1.17 20 | ``` 21 | 22 | ### 2. 启动demo 23 | 1. 启动server 24 | 2. 启动client 25 | 3. 查看 http://localhost:16686 26 | 27 | ## app 28 | > 实际使用,gin、gorm、grpc 集成trace 29 | 30 | 1. 启动 app/main.go 31 | 2. 浏览器访问:localhost:8081/hello 32 | 3. 浏览器访问:localhost:16686 查看链路 -------------------------------------------------------------------------------- /sdk/trace/app/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "go-demo/sdk/trace/app/db" 6 | "go-demo/sdk/trace/app/middleware" 7 | "math/rand" 8 | ) 9 | 10 | type User struct { 11 | Id int `json:"id"` 12 | Age int `json:"age"` 13 | UserName string `json:"user_name"` 14 | Password string `json:"password"` 15 | } 16 | 17 | func (*User) TableName() string { 18 | return "user" 19 | } 20 | 21 | func main() { 22 | r := gin.Default() 23 | 24 | // 使用链路追踪 25 | r.Use(middleware.GinTrace()) 26 | r.Use(middleware.DBTrace()) 27 | 28 | r.GET("/hello", func(ctx *gin.Context) { 29 | ctx.String(200, "this is index") 30 | 31 | // 调用数据库 32 | user := &User{ 33 | Id: rand.Intn(100), 34 | Age: rand.Intn(100), 35 | UserName: "test", 36 | Password: "123456", 37 | } 38 | err := db.DB.Create(user).Error 39 | if err != nil { 40 | ctx.String(200, err.Error()) 41 | } 42 | }) 43 | 44 | r.Run(":8081") 45 | } 46 | -------------------------------------------------------------------------------- /sdk/trace/app/middleware/gorm_trace.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "go-demo/sdk/trace/app/db" 6 | ) 7 | 8 | func DBTrace() gin.HandlerFunc { 9 | return func(ctx *gin.Context) { 10 | db.AddContext(ctx) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /sdk/trace/app/middleware/grpc_trace.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/grpc-ecosystem/go-grpc-middleware" 5 | "github.com/grpc-ecosystem/go-grpc-middleware/recovery" 6 | "github.com/grpc-ecosystem/go-grpc-middleware/tags" 7 | "github.com/grpc-ecosystem/go-grpc-middleware/tracing/opentracing" 8 | "github.com/grpc-ecosystem/go-grpc-prometheus" 9 | "google.golang.org/grpc" 10 | "google.golang.org/grpc/reflection" 11 | "net" 12 | ) 13 | 14 | func RunGRPCServe() error { 15 | grpc_prometheus.EnableHandlingTimeHistogram() 16 | 17 | grpcServer := grpc.NewServer( 18 | grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer( 19 | grpc_ctxtags.UnaryServerInterceptor(), 20 | grpc_opentracing.UnaryServerInterceptor(), 21 | grpc_recovery.UnaryServerInterceptor(), 22 | )), 23 | 24 | grpc.StreamInterceptor(grpc_middleware.ChainStreamServer( 25 | grpc_ctxtags.StreamServerInterceptor(), 26 | grpc_opentracing.StreamServerInterceptor(), 27 | )), 28 | ) 29 | 30 | reflection.Register(grpcServer) 31 | 32 | grpc_prometheus.Register(grpcServer) 33 | listener, err := net.Listen("tcp", ":9999") 34 | if err != nil { 35 | panic(err) 36 | } 37 | return grpcServer.Serve(listener) 38 | } 39 | -------------------------------------------------------------------------------- /sdk/trace/demo/server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "go-demo/sdk/trace/demo" 5 | "io" 6 | "net/http" 7 | 8 | "github.com/opentracing/opentracing-go" 9 | "github.com/opentracing/opentracing-go/ext" 10 | ) 11 | 12 | var ( 13 | tracer opentracing.Tracer 14 | closer io.Closer 15 | ) 16 | 17 | func init() { 18 | tracer, closer = demo.NewTracer("hello-server") 19 | } 20 | 21 | func hello(w http.ResponseWriter, req *http.Request) { 22 | spanCtx, _ := tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(req.Header)) 23 | span := tracer.StartSpan("hello", ext.RPCServerOption(spanCtx)) 24 | defer span.Finish() 25 | 26 | io.WriteString(w, "Hello World!") 27 | } 28 | 29 | func main() { 30 | defer closer.Close() 31 | 32 | http.HandleFunc("/hello", hello) 33 | if err := http.ListenAndServe(":8081", nil); err != nil { 34 | panic(err) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /sdk/trace/demo/trace.go: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | import ( 4 | "fmt" 5 | "github.com/opentracing/opentracing-go" 6 | "github.com/uber/jaeger-client-go" 7 | "github.com/uber/jaeger-client-go/config" 8 | "io" 9 | ) 10 | 11 | func NewTracer(serviceName string) (opentracing.Tracer, io.Closer) { 12 | cfg := &config.Configuration{ 13 | ServiceName: serviceName, 14 | Sampler: &config.SamplerConfig{ 15 | Type: "const", 16 | Param: 1, 17 | }, 18 | Reporter: &config.ReporterConfig{ 19 | LocalAgentHostPort: "127.0.0.1:6831", 20 | LogSpans: true, 21 | }, 22 | Headers: &jaeger.HeadersConfig{ 23 | JaegerDebugHeader: "x-debug-id", 24 | JaegerBaggageHeader: "x-baggage", 25 | TraceContextHeaderName: "x-trace-id", 26 | TraceBaggageHeaderPrefix: "x-ctx", 27 | }, 28 | } 29 | 30 | tracer, closer, err := cfg.NewTracer(config.Logger(jaeger.StdLogger)) 31 | if err != nil { 32 | panic(fmt.Sprintf("Init failed: %v\n", err)) 33 | } 34 | 35 | return tracer, closer 36 | } 37 | -------------------------------------------------------------------------------- /sdk/wire/Makefile: -------------------------------------------------------------------------------- 1 | all: build install: @go install github.com/google/wire/cmd/wire build: @wire -------------------------------------------------------------------------------- /spider/README.md: -------------------------------------------------------------------------------- 1 | # Go爬虫项目 2 | 3 | ### gift 4 | > QQ群自动抽礼物爬虫,可部署到服务器中,每天定时抽取群礼物, 5 | Cookie需要使用抓包工具自己获取,这里推荐使用`NetKeeper`,操作非常容易 6 | 执行build.bat脚本可打包成Linux可执行程序 7 | 8 | 9 | ### qq 10 | > QQ快速登录有漏洞,通过分析QQ快速登录协议, 11 | 实现获取QQ的`p_seky` -------------------------------------------------------------------------------- /spider/colly/douban/douban.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/gocolly/colly" 6 | ) 7 | 8 | const URL = "https://movie.douban.com/top250" 9 | 10 | func main() { 11 | c := colly.NewCollector( 12 | colly.Async(true), 13 | colly.IgnoreRobotsTxt(), 14 | colly.UserAgent("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36"), 15 | ) 16 | 17 | c.OnHTML(".hd", func(e *colly.HTMLElement) { 18 | title := e.DOM.Find("span.title").Eq(0).Text() 19 | url, _ := e.DOM.Find("a").Eq(0).Attr("href") 20 | fmt.Println(title, url) 21 | }) 22 | 23 | c.OnHTML(".next", func(e *colly.HTMLElement) { 24 | next, b := e.DOM.Find("a").Attr("href") 25 | if b { 26 | nextURL := fmt.Sprintf("%s%s", URL, next) 27 | e.Request.Visit(nextURL) 28 | } 29 | }) 30 | 31 | c.OnRequest(func(r *colly.Request) { 32 | fmt.Println("Visiting", r.URL) 33 | }) 34 | 35 | c.OnResponse(func(res *colly.Response) { 36 | // 处理Response 37 | }) 38 | 39 | c.OnError(func(res *colly.Response, err error) { 40 | // 处理Error 41 | fmt.Println("err", err.Error()) 42 | }) 43 | 44 | c.Visit(URL) 45 | c.Wait() 46 | } 47 | -------------------------------------------------------------------------------- /spider/gift/auto/build.bat: -------------------------------------------------------------------------------- 1 | SET CGO_ENABLED=0 2 | SET GOOS=linux 3 | SET GOARCH=amd64 4 | go build auto_get_gift.go -------------------------------------------------------------------------------- /spider/gift/cookie: -------------------------------------------------------------------------------- 1 | uin=o0741047261;skey=@l8QyyiX37; -------------------------------------------------------------------------------- /spider/qq/qq_interface.go: -------------------------------------------------------------------------------- 1 | package qq 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "net/http" 7 | "strings" 8 | ) 9 | 10 | func GetAllFriends(user *User) { 11 | client := &http.Client{} 12 | 13 | req, _ := http.NewRequest("POST", "https://qun.qq.com/cgi-bin/qun_mgr/get_friend_list", strings.NewReader(fmt.Sprintf("bkn=%s", user.GTK))) 14 | req.Header.Set("cookie", fmt.Sprintf("uin=%s;p_uin=%s;skey=%s;p_skey=%s", user.Uin, user.Uin, user.Skey, user.PSkey)) 15 | req.Header.Set("Content-Type", "application/x-www-form-urlencoded") 16 | req.Header.Set("referer", "https://qun.qq.com/member.html") 17 | 18 | response, err := client.Do(req) 19 | if err != nil { 20 | fmt.Println(err) 21 | } 22 | bytes, _ := ioutil.ReadAll(response.Body) 23 | fmt.Println(string(bytes)) 24 | } 25 | -------------------------------------------------------------------------------- /spider/qq/qq_login_test.go: -------------------------------------------------------------------------------- 1 | package qq 2 | 3 | import ( 4 | "go-demo/utils/env" 5 | "testing" 6 | ) 7 | 8 | func TestQQLogin(t *testing.T) { 9 | if env.IsCI() { 10 | return 11 | } 12 | 13 | user, err := GetQQInfo(QQZone) 14 | if err != nil { 15 | t.Log(err) 16 | } 17 | t.Logf("%+v \n", user) 18 | 19 | user, err = GetQQInfo(QQFriend) 20 | if err != nil { 21 | t.Log(err) 22 | } 23 | t.Logf("%+v \n", user) 24 | } 25 | 26 | func TestGenderTtk(t *testing.T) { 27 | t.Log(genderGTK("@lasyOoUaj")) 28 | } 29 | -------------------------------------------------------------------------------- /utils/ants/ants.go: -------------------------------------------------------------------------------- 1 | package ants 2 | 3 | import ( 4 | "fmt" 5 | "sync/atomic" 6 | "time" 7 | ) 8 | 9 | // 高性能goroutines池 10 | var sum int32 11 | 12 | func myFunc(i interface{}) { 13 | n := i.(int32) 14 | atomic.AddInt32(&sum, n) 15 | fmt.Printf("run with %d\n", n) 16 | } 17 | 18 | func demoFunc() { 19 | time.Sleep(10 * time.Millisecond) 20 | fmt.Println("Hello World!") 21 | } 22 | -------------------------------------------------------------------------------- /utils/apriori/apriori.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | /** 8 | * @Author: leikewei 9 | * @Date: 2024/8/18 10 | * @Desc: 先验算法 11 | */ 12 | 13 | func main() { 14 | 15 | transactions := [][]string{ 16 | {"豆奶", "莴苣"}, 17 | {"豆奶", "莴苣"}, 18 | {"豆奶", "莴苣"}, 19 | {"豆奶", "莴苣"}, 20 | {"豆奶", "莴苣"}, 21 | {"豆奶", "莴苣"}, 22 | {"莴苣", "尿布", "葡萄酒", "甜菜"}, 23 | {"豆奶", "尿布", "葡萄酒", "橙汁"}, 24 | {"莴苣", "豆奶", "尿布", "葡萄酒"}, 25 | {"莴苣", "豆奶", "尿布", "橙汁"}, 26 | } 27 | // orderedStatistic: 28 | apriori := Apriori.NewApriori(transactions) 29 | // minSupport: 最小支持度 30 | // minConfidence: 最小置信度 31 | results := apriori.Calculate(Apriori.NewOptions(0.5, 1, 0.0, 0)) 32 | 33 | for _, r := range results { 34 | fmt.Printf("%+v, \n", r) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /utils/bar/progress/progress_bar.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/qianlnk/pgbar" 5 | "time" 6 | ) 7 | 8 | // 进度条 9 | func main() { 10 | var i = 0 11 | bar := pgbar.NewBar(0, "下载进度", 100) 12 | for ; i < 100; i++ { 13 | bar.Add() 14 | time.Sleep(time.Millisecond * 30) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /utils/bar/spinner/spinner.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "time" 7 | ) 8 | 9 | // 旋转进度条 10 | var spinChars = `|/-\` 11 | 12 | type Spinner struct { 13 | message string 14 | i int 15 | } 16 | 17 | func NewSpinner(message string) *Spinner { 18 | return &Spinner{message: message} 19 | } 20 | 21 | func main() { 22 | s := NewSpinner("working...") 23 | for i := 0; i < 10; i++ { 24 | if isTTY() { 25 | s.Tick() 26 | } 27 | time.Sleep(100 * time.Millisecond) 28 | } 29 | } 30 | 31 | func (s *Spinner) Tick() { 32 | fmt.Printf("%s %c \r", s.message, spinChars[s.i]) 33 | s.i = (s.i + 1) % len(spinChars) 34 | } 35 | 36 | func isTTY() bool { 37 | fi, err := os.Stdout.Stat() 38 | if err != nil { 39 | return false 40 | } 41 | return fi.Mode()&os.ModeCharDevice != 0 42 | } 43 | -------------------------------------------------------------------------------- /utils/cmp/cmp.go: -------------------------------------------------------------------------------- 1 | package cmp 2 | 3 | import "github.com/google/go-cmp/cmp" 4 | 5 | // 类似于git的diff 6 | func Diff(x, y interface{}, opts ...cmp.Option) string { 7 | return cmp.Diff(x, y, opts...) 8 | } 9 | -------------------------------------------------------------------------------- /utils/cmp/cmp_test.go: -------------------------------------------------------------------------------- 1 | package cmp 2 | 3 | import "testing" 4 | 5 | type User struct { 6 | Name string 7 | Password string 8 | } 9 | 10 | func TestDiff(t *testing.T) { 11 | u1 := &User{ 12 | Name: "派大星", 13 | Password: "123456", 14 | } 15 | 16 | u2 := &User{ 17 | Name: "海绵宝宝", 18 | Password: "123456", 19 | } 20 | 21 | diff := Diff(u1, u2) 22 | t.Log(diff) 23 | } 24 | -------------------------------------------------------------------------------- /utils/code/code_test.go: -------------------------------------------------------------------------------- 1 | package code 2 | 3 | import "testing" 4 | 5 | func TestGenCodeImage(t *testing.T) { 6 | GenCodeImage(6) 7 | } 8 | -------------------------------------------------------------------------------- /utils/code/code_util.go: -------------------------------------------------------------------------------- 1 | package code 2 | 3 | import ( 4 | "github.com/dchest/captcha" 5 | "os" 6 | ) 7 | 8 | func GenCodeImage(length int) error { 9 | f, err := os.Create("code.png") 10 | if err != nil { 11 | return err 12 | } 13 | 14 | return captcha.WriteImage(f, captcha.NewLen(length), length*25, length*10) 15 | } 16 | -------------------------------------------------------------------------------- /utils/copy/copy.go: -------------------------------------------------------------------------------- 1 | package copy 2 | 3 | import ( 4 | "bytes" 5 | "encoding/gob" 6 | "github.com/jinzhu/copier" 7 | ) 8 | 9 | // 基于序列化和反序列化的深度拷贝 10 | func DeepCopy(src, dst interface{}) error { 11 | var buf bytes.Buffer 12 | if err := gob.NewEncoder(&buf).Encode(src); err != nil { 13 | return err 14 | } 15 | return gob.NewDecoder(bytes.NewBuffer(buf.Bytes())).Decode(dst) 16 | } 17 | 18 | // 基于反射的深度拷贝 19 | func Copy(src, dst interface{}) error { 20 | return copier.Copy(dst, src) 21 | } 22 | -------------------------------------------------------------------------------- /utils/copy/copy_test.go: -------------------------------------------------------------------------------- 1 | package copy 2 | 3 | import "testing" 4 | 5 | func TestDeepCopy(t *testing.T) { 6 | type User struct { 7 | Name string 8 | } 9 | user1 := &User{Name: "pibigstar"} 10 | 11 | var user2 User 12 | err := DeepCopy(user1, &user2) 13 | if err != nil { 14 | t.Error(err) 15 | } 16 | t.Log(user2) 17 | 18 | var user3 User 19 | err = Copy(user1, &user3) 20 | if err != nil { 21 | t.Error(err) 22 | } 23 | t.Log(user3) 24 | } 25 | -------------------------------------------------------------------------------- /utils/cron/cron_demo.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/robfig/cron" 5 | "log" 6 | "time" 7 | ) 8 | 9 | /** 10 | 定时任务 11 | */ 12 | func main() { 13 | c := cron.New() 14 | 15 | // 当时间 为 1s时才会执行 16 | c.AddFunc("1 * * * * *", func() { 17 | log.Println("when 1s run ....") 18 | }) 19 | 20 | // 每秒执行一次 21 | c.AddFunc("@every 1s", func() { 22 | log.Println("every 1s run ....") 23 | }) 24 | 25 | // 每秒执行一次 26 | c.AddFunc("*/1 * * * * *", func() { 27 | log.Println("every 1s run ....") 28 | }) 29 | 30 | // 凌晨2点执行 31 | c.AddFunc("0 0 2 * * *", func() { 32 | log.Println("when 2:0:0 执行....") 33 | }) 34 | 35 | // 新建job任务 36 | myJob := new(myJob) 37 | c.AddJob("@every 1s", myJob) 38 | 39 | c.Start() 40 | 41 | time.Sleep(5 * time.Second) 42 | 43 | c.Stop() 44 | } 45 | 46 | type myJob struct{} 47 | 48 | func (myJob) Run() { 49 | log.Println("job run ....") 50 | } 51 | 52 | func init() { 53 | log.Println("initial......") 54 | } 55 | -------------------------------------------------------------------------------- /utils/crypto/aes_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "encoding/hex" 5 | "testing" 6 | ) 7 | 8 | var plaintext = "Hello pibigstar" 9 | var key = "11111111111111111111111111111111" 10 | var iv = "123456789" 11 | 12 | func TestAesCbc(t *testing.T) { 13 | var r, _ = AesCbcEncrypt([]byte(plaintext), []byte(key), []byte(iv)) 14 | t.Log("AES CBC Encrypt: ", hex.EncodeToString(r)) 15 | 16 | r, _ = AesCbcDecrypt(r, []byte(key), []byte(iv)) 17 | t.Log("AES CBC Decrypt: ", string(r)) 18 | } 19 | 20 | func TestAesCfb(t *testing.T) { 21 | encryptStr, _ := AesCfbEncrypt([]byte(plaintext), []byte(key), []byte(iv)) 22 | t.Log("AES CFB Encrypt: ", hex.EncodeToString(encryptStr)) 23 | 24 | decryptStr, _ := AesCfbDecrypt(encryptStr, []byte(key), []byte(iv)) 25 | t.Log("AES CFB Decrypt: ", string(decryptStr)) 26 | } 27 | -------------------------------------------------------------------------------- /utils/crypto/base64.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "encoding/base64" 5 | ) 6 | 7 | // 使用base64 加密 8 | func encrypt(msg string) string { 9 | enctry := base64.StdEncoding.EncodeToString([]byte(msg)) 10 | return enctry 11 | } 12 | 13 | // base64 解密 14 | func UnEncrypt(encrypt string) string { 15 | s, _ := base64.StdEncoding.DecodeString(encrypt) 16 | return string(s) 17 | } 18 | 19 | func Base64Encode(str string) string { 20 | return base64.URLEncoding.EncodeToString([]byte(str)) 21 | } 22 | 23 | func Base64Decode(str string) string { 24 | bytes, _ := base64.URLEncoding.DecodeString(str) 25 | return string(bytes) 26 | } 27 | -------------------------------------------------------------------------------- /utils/crypto/base64_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestBase64(t *testing.T) { 8 | str := "hello world" 9 | encryptStr := encrypt(str) 10 | unEncrypt := UnEncrypt(encryptStr) 11 | if str != unEncrypt { 12 | t.Error("Failed to crypto") 13 | } 14 | } 15 | 16 | func TestBase64Decode(t *testing.T) { 17 | code := Base64Encode("pibigstar") 18 | t.Log(code) 19 | 20 | decode := Base64Decode(code) 21 | t.Log(decode) 22 | } 23 | -------------------------------------------------------------------------------- /utils/crypto/hash.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "crypto/sha256" 5 | "encoding/hex" 6 | "github.com/spaolacci/murmur3" 7 | ) 8 | 9 | // 返回64位字符串 10 | func HashStr(data []byte) string { 11 | bs := sha256.Sum256(data) 12 | return hex.EncodeToString(bs[:]) 13 | } 14 | 15 | // 返回数字Hash值 16 | func HashNum(data []byte) uint64 { 17 | return murmur3.Sum64(data) 18 | } 19 | -------------------------------------------------------------------------------- /utils/crypto/hash_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | var data = []byte("Hello") 8 | 9 | func TestHash(t *testing.T) { 10 | t.Log(HashStr(data)) 11 | t.Log(HashNum(data)) 12 | } 13 | -------------------------------------------------------------------------------- /utils/crypto/md5_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestMd5(t *testing.T) { 8 | t.Log(Md5("pibigstar")) 9 | } 10 | -------------------------------------------------------------------------------- /utils/crypto/md5_util.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "crypto/md5" 5 | "fmt" 6 | ) 7 | 8 | // md5 加密 9 | func Md5(str string) string { 10 | byte := []byte(str) 11 | sum := md5.Sum(byte) 12 | md5 := fmt.Sprintf("%x", sum) 13 | return md5 14 | } 15 | -------------------------------------------------------------------------------- /utils/ctxkit/ctxkit_test.go: -------------------------------------------------------------------------------- 1 | package ctxkit 2 | 3 | import ( 4 | "context" 5 | "github.com/stretchr/testify/assert" 6 | "testing" 7 | ) 8 | 9 | func TestContextValue(t *testing.T) { 10 | ctx := context.Background() 11 | testValue := "派大星" 12 | ctx = SetValue(ctx, "test", testValue) 13 | value := GetValue(ctx, "test") 14 | assert.Equal(t, value, testValue) 15 | } 16 | 17 | func TestContextHeader(t *testing.T) { 18 | ctx := context.Background() 19 | testValue := "派大星" 20 | ctx = AppendHeader(ctx, "test", testValue) 21 | 22 | assert.Equal(t, GetHeader(ctx, "test"), testValue) 23 | } 24 | -------------------------------------------------------------------------------- /utils/disk/disk_test.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package disk 4 | 5 | import ( 6 | "runtime" 7 | "testing" 8 | ) 9 | 10 | func TestGetSystemDisks(t *testing.T) { 11 | sysType := runtime.GOOS 12 | if sysType == "windows" { 13 | GetSystemDisks() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /utils/encode/encode_test.go: -------------------------------------------------------------------------------- 1 | package encode 2 | 3 | import ( 4 | "github.com/axgle/mahonia" 5 | "golang.org/x/text/encoding/simplifiedchinese" 6 | "testing" 7 | ) 8 | 9 | func TestUTF8ToGBK(t *testing.T) { 10 | // 使用第三方转码 11 | enc := mahonia.NewEncoder("gbk") 12 | s := enc.ConvertString("Hello 派大星") 13 | 14 | // 使用golang官方包转码 15 | r, err := simplifiedchinese.GBK.NewEncoder().String("Hello 派大星") 16 | if err != nil { 17 | t.Error(err) 18 | } 19 | 20 | if s != r { 21 | t.Fail() 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /utils/env/env.go: -------------------------------------------------------------------------------- 1 | package env 2 | 3 | import ( 4 | "os" 5 | "runtime" 6 | ) 7 | 8 | func IsCI() bool { 9 | name, _ := os.Hostname() 10 | if name != "pibigstar" && runtime.GOOS == "linux" { 11 | return true 12 | } 13 | return false 14 | } 15 | -------------------------------------------------------------------------------- /utils/fsnotify/fsnotify_test.go: -------------------------------------------------------------------------------- 1 | package fsnotify_test 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "testing" 7 | "time" 8 | 9 | "github.com/fsnotify/fsnotify" 10 | ) 11 | 12 | func TestFileChangeListen(t *testing.T) { 13 | watcher, err := fsnotify.NewWatcher() 14 | if err != nil { 15 | log.Fatal("NewWatcher failed: ", err) 16 | } 17 | defer watcher.Close() 18 | // 监听此目录 19 | err = watcher.Add("./") 20 | if err != nil { 21 | log.Fatal("Add failed:", err) 22 | } 23 | 24 | ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) 25 | defer cancel() 26 | for { 27 | select { 28 | case event, ok := <-watcher.Events: 29 | if !ok { 30 | return 31 | } 32 | log.Printf("%s %s\n", event.Name, event.Op) 33 | case err, ok := <-watcher.Errors: 34 | if !ok { 35 | return 36 | } 37 | log.Println("error:", err) 38 | case <-ctx.Done(): 39 | return 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /utils/goleak/goleak_test.go: -------------------------------------------------------------------------------- 1 | package goleak 2 | 3 | import ( 4 | "testing" 5 | 6 | "go.uber.org/goleak" 7 | ) 8 | 9 | func leak() { 10 | ch := make(chan struct{}) 11 | // goroutine泄漏 12 | go func() { 13 | ch <- struct{}{} 14 | }() 15 | } 16 | 17 | // 直接执行并不能发现goroutine泄漏问题 18 | func TestLeak(t *testing.T) { 19 | leak() 20 | } 21 | 22 | // goroutine 泄漏检测 23 | func TestGoLeak(t *testing.T) { 24 | // 校验goroutine是否有泄漏 25 | defer goleak.VerifyNone(t) 26 | leak() 27 | } 28 | 29 | // 集成到TestMain中 30 | func TestMain(m *testing.M) { 31 | goleak.VerifyTestMain(m) 32 | } 33 | -------------------------------------------------------------------------------- /utils/govaluate/govaluate_test.go: -------------------------------------------------------------------------------- 1 | package govaluate 2 | 3 | import ( 4 | "gopkg.in/Knetic/govaluate.v3" 5 | "testing" 6 | ) 7 | 8 | // 值比较 9 | func TestComparison(t *testing.T) { 10 | expression, err := govaluate.NewEvaluableExpression("foo > 0") 11 | if err != nil { 12 | t.Error(err) 13 | } 14 | params := make(map[string]interface{}) 15 | params["foo"] = 1 16 | 17 | result, err := expression.Evaluate(params) 18 | if err != nil { 19 | t.Error(err) 20 | } 21 | t.Log(result) 22 | } 23 | 24 | // 值计算 25 | func TestCalculate(t *testing.T) { 26 | expression, err := govaluate.NewEvaluableExpression("salary / days") 27 | if err != nil { 28 | t.Error(err) 29 | } 30 | params := make(map[string]interface{}) 31 | params["salary"] = 12000 32 | params["days"] = 30 33 | 34 | result, err := expression.Evaluate(params) 35 | if err != nil { 36 | t.Error(err) 37 | } 38 | t.Log(result) 39 | } 40 | -------------------------------------------------------------------------------- /utils/hystrix/hystrix_test.go: -------------------------------------------------------------------------------- 1 | package hystrix 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "github.com/afex/hystrix-go/hystrix" 7 | "testing" 8 | "time" 9 | ) 10 | 11 | func TestHystrix(t *testing.T) { 12 | hystrix.ConfigureCommand("test", hystrix.CommandConfig{ 13 | MaxConcurrentRequests: 10, // 最大并发量 14 | Timeout: 3000, // 单次请求超时时间,如果超过3s将认为服务不可用 15 | RequestVolumeThreshold: 1, // 10秒内如果出现1次错误就触发熔断 16 | ErrorPercentThreshold: 1, // 按百分比,如果出现1%的错误就触发熔断 17 | SleepWindow: 2000, // 熔断后2秒后再去尝试服务是否可用 18 | }) 19 | 20 | for i := 0; i < 10; i++ { 21 | GetInfo(i) 22 | time.Sleep(1 * time.Second) 23 | } 24 | } 25 | 26 | func GetInfo(i int) { 27 | hystrix.Go("test", func() error { 28 | // 业务逻辑部分 29 | fmt.Println("处理业务逻辑...") 30 | if i%2 == 0 { 31 | return errors.New("this is error") 32 | } 33 | return nil 34 | }, func(err error) error { 35 | // 业务逻辑处理失败, 降级处理 36 | if err == hystrix.ErrCircuitOpen { 37 | fmt.Println("服务处于熔断中...") 38 | } else { 39 | fmt.Println("服务处理失败, err: ", err.Error()) 40 | } 41 | return err 42 | }) 43 | } 44 | -------------------------------------------------------------------------------- /utils/i18n/i18n_test.go: -------------------------------------------------------------------------------- 1 | package i18n 2 | 3 | import ( 4 | "go-demo/utils/i18n/locales" 5 | "io/ioutil" 6 | "testing" 7 | ) 8 | 9 | func TestLoadYaml(t *testing.T) { 10 | LoadYaml() 11 | } 12 | 13 | func TestLoadJson(t *testing.T) { 14 | LoadJson() 15 | } 16 | 17 | func TestLoadJsonFile(t *testing.T) { 18 | filePath := locales.Path("zh-CN.json") 19 | f, _ := ioutil.ReadFile(filePath) 20 | LoadJsonFile(f) 21 | } 22 | -------------------------------------------------------------------------------- /utils/i18n/locales/path.go: -------------------------------------------------------------------------------- 1 | package locales 2 | 3 | import ( 4 | "path/filepath" 5 | "runtime" 6 | ) 7 | 8 | var basePath string 9 | 10 | func init() { 11 | _, currentFile, _, _ := runtime.Caller(0) 12 | basePath = filepath.Dir(currentFile) 13 | } 14 | 15 | func Path(rel string) string { 16 | if filepath.IsAbs(rel) { 17 | return rel 18 | } 19 | 20 | return filepath.Join(basePath, rel) 21 | } 22 | -------------------------------------------------------------------------------- /utils/i18n/locales/zh-CN.json: -------------------------------------------------------------------------------- 1 | { 2 | "200": "操作成功", 3 | "500": "内部错误" 4 | } 5 | -------------------------------------------------------------------------------- /utils/i18n/locales/zh-CN.yaml: -------------------------------------------------------------------------------- 1 | - id: "200" 2 | translation: "操作成功" 3 | 4 | - id: "500" 5 | translation: "操作失败" -------------------------------------------------------------------------------- /utils/images/image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibigstar/go-demo/1b4058972b01ec376d8c2131f8839bdf1fe909ab/utils/images/image.jpg -------------------------------------------------------------------------------- /utils/images/new.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibigstar/go-demo/1b4058972b01ec376d8c2131f8839bdf1fe909ab/utils/images/new.jpg -------------------------------------------------------------------------------- /utils/images/resize_image.go: -------------------------------------------------------------------------------- 1 | package images 2 | 3 | import ( 4 | "image" 5 | "image/jpeg" 6 | "log" 7 | "os" 8 | 9 | "github.com/nfnt/resize" 10 | ) 11 | 12 | // 得到图片的宽和高 13 | func GetImageSize(imagePath string) (width, height int) { 14 | file, err := os.Open(imagePath) 15 | if err != nil { 16 | log.Println("open file failed:", err) 17 | } 18 | 19 | image, _, err := image.DecodeConfig(file) 20 | if err != nil { 21 | log.Println("decode image config failed:", err) 22 | } 23 | return image.Width, image.Height 24 | } 25 | 26 | // 修改图片尺寸 27 | func Resize(imagePath, targePath string, width, height uint) { 28 | file, err := os.Open(imagePath) 29 | if err != nil { 30 | log.Println("open file failed:", err) 31 | } 32 | 33 | img, err := jpeg.Decode(file) 34 | if err != nil { 35 | log.Println("decode file failed:", err) 36 | } 37 | file.Close() 38 | 39 | newImg := resize.Resize(width, height, img, resize.Lanczos3) 40 | 41 | newFile, err := os.Create(targePath) 42 | if err != nil { 43 | log.Println("create file failed:", err) 44 | } 45 | defer newFile.Close() 46 | 47 | jpeg.Encode(newFile, newImg, nil) 48 | 49 | } 50 | -------------------------------------------------------------------------------- /utils/images/resize_image_test.go: -------------------------------------------------------------------------------- 1 | package images 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | const ( 8 | imgPath = "image.jpg" 9 | newPath = "new.jpg" 10 | ) 11 | 12 | func TestResizeImage(t *testing.T) { 13 | width, height := GetImageSize(imgPath) 14 | 15 | t.Logf("width:%d, height:%d", width, height) 16 | 17 | Resize(imgPath, newPath, 100, 100) 18 | } 19 | -------------------------------------------------------------------------------- /utils/ip/address/address.datx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibigstar/go-demo/1b4058972b01ec376d8c2131f8839bdf1fe909ab/utils/ip/address/address.datx -------------------------------------------------------------------------------- /utils/ip/address/get_address_by_ip.go: -------------------------------------------------------------------------------- 1 | package address 2 | 3 | type LocationAddress struct { 4 | Country string `json:"country"` 5 | Province string `json:"province"` 6 | City string `json:"city"` 7 | } 8 | 9 | var cityDB *City 10 | var cityDbError error 11 | 12 | func init() { 13 | cityDB, cityDbError = NewCity("address/address.datx") 14 | } 15 | 16 | func GetAddressByIP(ip string) (*LocationAddress, error) { 17 | if cityDbError != nil { 18 | return nil, cityDbError 19 | } 20 | 21 | locations, err := cityDB.Find(ip) 22 | if err != nil { 23 | return nil, err 24 | } 25 | 26 | address := &LocationAddress{} 27 | if len(locations) >= 3 { 28 | address.Country = locations[0] 29 | address.Province = locations[1] 30 | address.City = locations[2] 31 | } 32 | 33 | return address, nil 34 | } 35 | -------------------------------------------------------------------------------- /utils/ip/ip.go: -------------------------------------------------------------------------------- 1 | package ip 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "net" 7 | "net/http" 8 | "os" 9 | "strings" 10 | ) 11 | 12 | // 获取公网地址 13 | func GetInternetIP() string { 14 | resp, err := http.Get("http://myexternalip.com/raw") 15 | if err != nil { 16 | return "" 17 | } 18 | defer resp.Body.Close() 19 | content, _ := ioutil.ReadAll(resp.Body) 20 | return strings.TrimSpace(string(content)) 21 | } 22 | 23 | // 获取本地IP地址 24 | func GetLocalIP() string { 25 | address, err := net.InterfaceAddrs() 26 | if err != nil { 27 | fmt.Println(err) 28 | os.Exit(1) 29 | } 30 | for _, address := range address { 31 | // 检查ip地址判断是否回环地址 32 | if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { 33 | if ipnet.IP.To4() != nil { 34 | return strings.TrimSpace(ipnet.IP.String()) 35 | } 36 | } 37 | } 38 | return "" 39 | } 40 | -------------------------------------------------------------------------------- /utils/ip/ip_test.go: -------------------------------------------------------------------------------- 1 | package ip 2 | 3 | import ( 4 | "go-demo/utils/ip/address" 5 | "testing" 6 | ) 7 | 8 | func Test_Address(t *testing.T) { 9 | 10 | ip := GetInternetIP() 11 | address, _ := address.GetAddressByIP(ip) 12 | t.Logf("%+v", address) 13 | } 14 | -------------------------------------------------------------------------------- /utils/js/test.js: -------------------------------------------------------------------------------- 1 | function test(name) { 2 | return "Hello: " + name 3 | } 4 | 5 | // 由go进行完成 6 | sayHello("pibigstar"); -------------------------------------------------------------------------------- /utils/jsondiff/jsondiff.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/nsf/jsondiff" 7 | ) 8 | 9 | /** 10 | * @Author: leikewei 11 | * @Date: 2024/9/9 12 | * @Desc: 13 | */ 14 | 15 | func main() { 16 | json1 := `{"a":1,"b":2,"c":3}` 17 | json2 := `{"dd":1,"b":2,"c":5}` 18 | opts := jsondiff.DefaultJSONOptions() 19 | p, str := jsondiff.Compare([]byte(json1), []byte(json2), &opts) 20 | fmt.Println(str) 21 | 22 | fmt.Println(p.String()) 23 | } 24 | -------------------------------------------------------------------------------- /utils/markdown/index.html: -------------------------------------------------------------------------------- 1 |

MarkDown 测试 2 |

3 | 4 |

5 | go 6 | fmt.Println("Hello World") 7 |

8 | -------------------------------------------------------------------------------- /utils/markdown/index.md: -------------------------------------------------------------------------------- 1 | # MarkDown 测试 2 | 3 | ```go 4 | fmt.Println("Hello World") 5 | ``` -------------------------------------------------------------------------------- /utils/markdown/markdown.go: -------------------------------------------------------------------------------- 1 | package markdown 2 | 3 | import ( 4 | "github.com/TruthHun/html2md" 5 | "github.com/microcosm-cc/bluemonday" 6 | "github.com/russross/blackfriday" 7 | "io/ioutil" 8 | ) 9 | 10 | func Parse(input []byte) { 11 | unsafe := blackfriday.Run(input) 12 | html := bluemonday.UGCPolicy().SanitizeBytes(unsafe) 13 | ioutil.WriteFile("index.html", html, 0666) 14 | } 15 | 16 | func htmlToMarkdown(html string) string { 17 | mdStr := html2md.Convert(html) 18 | return mdStr 19 | } 20 | -------------------------------------------------------------------------------- /utils/markdown/markdown_test.go: -------------------------------------------------------------------------------- 1 | package markdown 2 | 3 | import ( 4 | "io/ioutil" 5 | "testing" 6 | ) 7 | 8 | func TestMarkdown(t *testing.T) { 9 | bytes, err := ioutil.ReadFile("index.md") 10 | if err != nil { 11 | t.Error(err) 12 | } 13 | Parse(bytes) 14 | } 15 | 16 | func TestHtmlToMarkdown(t *testing.T) { 17 | bytes, err := ioutil.ReadFile("index.html") 18 | if err != nil { 19 | t.Error(err) 20 | } 21 | md := htmlToMarkdown(string(bytes)) 22 | t.Log(md) 23 | } 24 | -------------------------------------------------------------------------------- /utils/metadata/metadata.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "github.com/dgrijalva/jwt-go" 6 | "go-demo/utils/token" 7 | "google.golang.org/grpc/metadata" 8 | "time" 9 | ) 10 | 11 | // 为Context 设值 和取值 12 | 13 | const ( 14 | ContextMDTokenKey = "token" 15 | ContextMDReqIDKey = "req-id" 16 | ) 17 | 18 | // 将token放到上下文中 19 | func mockTokenContext(tokenKey string) context.Context { 20 | 21 | // 生成token 22 | claims := make(jwt.MapClaims) 23 | claims[tokenKey] = time.Now().Add(time.Hour * time.Duration(1)).Unix() 24 | claims[tokenKey] = "this is user id" 25 | token, err := utils.GenJwtToken(claims) 26 | if err != nil { 27 | return nil 28 | } 29 | 30 | md := metadata.New(map[string]string{ 31 | tokenKey: token, 32 | }) 33 | 34 | return metadata.NewOutgoingContext(context.Background(), md) 35 | } 36 | 37 | // 从上下文中取出token 38 | func GetTokenFromContext(ctx context.Context) string { 39 | // incoming 40 | if md, ok := metadata.FromIncomingContext(ctx); ok { 41 | if md[ContextMDTokenKey] != nil && len(md[ContextMDTokenKey]) > 0 { 42 | return md[ContextMDTokenKey][0] 43 | } 44 | } 45 | 46 | // outcoming 47 | if md, ok := metadata.FromOutgoingContext(ctx); ok { 48 | if md[ContextMDTokenKey] != nil && len(md[ContextMDTokenKey]) > 0 { 49 | return md[ContextMDTokenKey][0] 50 | } 51 | } 52 | return "" 53 | } 54 | -------------------------------------------------------------------------------- /utils/metadata/metadata_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestGetTokenFromContext(t *testing.T) { 8 | tokenContext := mockTokenContext(ContextMDTokenKey) 9 | 10 | token := GetTokenFromContext(tokenContext) 11 | t.Log(token) 12 | } 13 | -------------------------------------------------------------------------------- /utils/mock/README.md: -------------------------------------------------------------------------------- 1 | # 命令生成mock文件 2 | 3 | ``` 4 | mockgen -destination mock/mock_spider.go -package mock go-demo/mock Spider 5 | ``` 6 | mockgen -destination 生成文件位置 -package 包名 要mock的接口的位置 接口名 7 | 8 | 9 | ## 使用go generate 10 | 11 | 在接口文件处添加注释 12 | ```go 13 | //go:generate mockgen -destination mock_spider.go -package mock go-demo/mock Spider 14 | ``` 15 | 这样直接在mock文件夹下面执行 `go generate` 即可 16 | 17 | ## 参数详解 18 | 19 | -source: 指定接口文件 20 | 21 | -destination: 生成的文件名 22 | 23 | -package:生成文件的包名 24 | 25 | -imports: 依赖的需要import的包 26 | 27 | -aux_files:接口文件不止一个文件时附加文件 28 | 29 | -build_flags: 传递给build工具的参数 30 | 31 | ## 更多 32 | 33 | [点击查看更多](https://www.jianshu.com/p/598a11bbdafb) -------------------------------------------------------------------------------- /utils/mock/mock_test.go: -------------------------------------------------------------------------------- 1 | package mock 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/golang/mock/gomock" 8 | ) 9 | 10 | func TestGetGoVersion(t *testing.T) { 11 | 12 | // 1. 新建一个mock控制器 13 | mockCtl := gomock.NewController(t) 14 | defer mockCtl.Finish() 15 | 16 | // 2. 调用mock生成代码里面为我们实现的接口对象 17 | mockSpider := NewMockSpider(mockCtl) 18 | // 3. 调用EXPECT()得到实现的对象 并调用对象的方法 指定其返回值 19 | mockSpider.EXPECT().GetBody().Return("go1.8.3") 20 | // 4. 再实现Init方法,现在mockSpider 就是一个实现了Spider接口的对象 21 | mockSpider.EXPECT().Init() 22 | 23 | // 5. 将mock出的Spider接口对象传递过去 (GetGoVersion 必须要使用到Init和GetBody方法) 24 | goVer := GetGoVersion(mockSpider) 25 | 26 | fmt.Println(goVer) 27 | } 28 | -------------------------------------------------------------------------------- /utils/mock/spider.go: -------------------------------------------------------------------------------- 1 | package mock 2 | 3 | type Spider interface { 4 | Init() 5 | GetBody() string 6 | } 7 | 8 | func GetGoVersion(s Spider) string { 9 | s.Init() 10 | body := s.GetBody() 11 | return body 12 | } 13 | -------------------------------------------------------------------------------- /utils/multiconfig/config.toml: -------------------------------------------------------------------------------- 1 | Name = "koding" 2 | Enabled = false 3 | Port = 8080 4 | Users = ["ankara", "istanbul"] -------------------------------------------------------------------------------- /utils/multiconfig/multiconfig_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/koding/multiconfig" 5 | "testing" 6 | ) 7 | 8 | type Server struct { 9 | Name string 10 | Port int `default:"6060"` 11 | Enabled bool 12 | Users []string 13 | } 14 | 15 | // supports TOML, JSON and YAML 16 | func TestReadConfig(t *testing.T) { 17 | 18 | m := multiconfig.NewWithPath("config.toml") 19 | serverConf := new(Server) 20 | 21 | err := m.Load(serverConf) 22 | if err != nil { 23 | t.Error(err) 24 | } 25 | // Panic's if there is any error 26 | m.MustLoad(serverConf) 27 | 28 | t.Logf("%+v", serverConf) 29 | } 30 | -------------------------------------------------------------------------------- /utils/name/name_test.go: -------------------------------------------------------------------------------- 1 | package name 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestName(t *testing.T) { 8 | t.Log("name:", GenerateUserName(2)) 9 | t.Log("name:", GenerateUserName(3)) 10 | } 11 | -------------------------------------------------------------------------------- /utils/oauth2/README.md: -------------------------------------------------------------------------------- 1 | # oauth2授权 2 | > 文档中心: https://go-oauth2.github.io/zh/ 3 | 4 | - server: 授权中心 5 | - client: 第三方应用 6 | 7 | ## 授权流程 8 | 9 | 1. 用户在client点击通过 `server`登录 10 | 2. 如果用户没有在`server`登录,则用户去登录 11 | 3. 用户已登录,弹出用户授权页面 12 | 4. 授权成功,返回 `code` 13 | 5. 第三方应用通过`code`请求授权中心换取token -------------------------------------------------------------------------------- /utils/oauth2/server/static/auth.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Auth 6 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |
18 |

Authorize

19 |

The client would like to perform actions on your behalf.

20 |

21 | 28 |

29 |
30 |
31 |
32 | 33 | 34 | -------------------------------------------------------------------------------- /utils/oauth2/server/static/auth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibigstar/go-demo/1b4058972b01ec376d8c2131f8839bdf1fe909ab/utils/oauth2/server/static/auth.png -------------------------------------------------------------------------------- /utils/oauth2/server/static/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Login 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |

Login In

15 |
16 |
17 | 18 | 19 |
20 |
21 | 22 | 23 |
24 | 25 |
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /utils/oauth2/server/static/login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibigstar/go-demo/1b4058972b01ec376d8c2131f8839bdf1fe909ab/utils/oauth2/server/static/login.png -------------------------------------------------------------------------------- /utils/oauth2/server/static/token.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibigstar/go-demo/1b4058972b01ec376d8c2131f8839bdf1fe909ab/utils/oauth2/server/static/token.png -------------------------------------------------------------------------------- /utils/ocr/ocr.go: -------------------------------------------------------------------------------- 1 | package ocr 2 | 3 | import "github.com/otiai10/gosseract/v2" 4 | 5 | /** 6 | * @Author: leikewei 7 | * @Date: 2024/2/29 8 | * @Desc: 9 | */ 10 | 11 | func GetImageInfo(img []byte) (string, error) { 12 | client := gosseract.NewClient() 13 | defer client.Close() 14 | client.SetImageFromBytes(img) 15 | text, _ := client.Text() 16 | return text, nil 17 | } 18 | -------------------------------------------------------------------------------- /utils/pinyin/pinyin_test.go: -------------------------------------------------------------------------------- 1 | package pinyin 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/mozillazg/go-pinyin" 8 | ) 9 | 10 | // 汉字转拼音 11 | func TestPinyin(t *testing.T) { 12 | str := "派大星" 13 | // 默认 14 | a := pinyin.NewArgs() 15 | fmt.Println(pinyin.Pinyin(str, a)) 16 | // [[pai] [da] [xing]] 17 | 18 | // 包含声调 19 | a.Style = pinyin.Tone 20 | fmt.Println(pinyin.Pinyin(str, a)) 21 | // [[pài] [dà] [xīng]] 22 | 23 | // 声调用数字表示 24 | a.Style = pinyin.Tone2 25 | fmt.Println(pinyin.Pinyin(str, a)) 26 | // [[pa4i] [da4] [xi1ng]] 27 | } 28 | -------------------------------------------------------------------------------- /utils/qrcode/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibigstar/go-demo/1b4058972b01ec376d8c2131f8839bdf1fe909ab/utils/qrcode/1.jpg -------------------------------------------------------------------------------- /utils/qrcode/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibigstar/go-demo/1b4058972b01ec376d8c2131f8839bdf1fe909ab/utils/qrcode/2.jpg -------------------------------------------------------------------------------- /utils/qrcode/qrcode.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "github.com/skip2/go-qrcode" 5 | ) 6 | 7 | func GenQRCode(content string, level qrcode.RecoveryLevel, size int) ([]byte, error) { 8 | var png []byte 9 | png, err := qrcode.Encode(content, level, size) 10 | if err != nil { 11 | return nil, err 12 | } 13 | return png, err 14 | } 15 | 16 | func GenDefaultQRCode(content string) ([]byte, error) { 17 | return GenQRCode(content, qrcode.Medium, 256) 18 | } 19 | -------------------------------------------------------------------------------- /utils/qrcode/qrcode_test.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "io/ioutil" 5 | "testing" 6 | ) 7 | 8 | func TestGenQRCode(t *testing.T) { 9 | bytes, err := GenDefaultQRCode("Hello World") 10 | if err != nil { 11 | t.Error(err) 12 | } 13 | err = ioutil.WriteFile("1.jpg", bytes, 0666) 14 | 15 | bytes, err = GenDefaultQRCode("http://www.baidu.com") 16 | if err != nil { 17 | t.Error(err) 18 | } 19 | err = ioutil.WriteFile("2.jpg", bytes, 0666) 20 | } 21 | -------------------------------------------------------------------------------- /utils/qrcode/terminal/qr_terminal.go: -------------------------------------------------------------------------------- 1 | package terminal 2 | 3 | import ( 4 | "github.com/mattn/go-colorable" 5 | "github.com/mdp/qrterminal/v3" 6 | "os" 7 | "runtime" 8 | ) 9 | 10 | // 将二维码输出到控制台 11 | func GenQrToTerminal(content string) { 12 | qrterminal.Generate(content, qrterminal.L, os.Stdout) 13 | } 14 | 15 | // 使用简单配置 16 | func GenQrToTerminalWithConfig(content string) { 17 | config := qrterminal.Config{ 18 | Level: qrterminal.M, 19 | Writer: os.Stdout, 20 | BlackChar: qrterminal.WHITE, 21 | WhiteChar: qrterminal.BLACK, 22 | QuietZone: 1, 23 | } 24 | qrterminal.GenerateWithConfig(content, config) 25 | } 26 | 27 | // 复杂点配置 28 | func GenQR(content string) { 29 | qrConfig := qrterminal.Config{ 30 | HalfBlocks: true, 31 | Level: qrterminal.L, 32 | Writer: os.Stdout, 33 | BlackWhiteChar: "\u001b[37m\u001b[40m\u2584\u001b[0m", 34 | BlackChar: "\u001b[30m\u001b[40m\u2588\u001b[0m", 35 | WhiteBlackChar: "\u001b[30m\u001b[47m\u2585\u001b[0m", 36 | WhiteChar: "\u001b[37m\u001b[47m\u2588\u001b[0m", 37 | } 38 | if runtime.GOOS == "windows" { 39 | qrConfig.HalfBlocks = false 40 | qrConfig.Writer = colorable.NewColorableStdout() 41 | qrConfig.BlackChar = qrterminal.BLACK 42 | qrConfig.WhiteChar = qrterminal.WHITE 43 | } 44 | 45 | qrterminal.GenerateWithConfig(content, qrConfig) 46 | } 47 | -------------------------------------------------------------------------------- /utils/qrcode/terminal/qr_terminal_test.go: -------------------------------------------------------------------------------- 1 | package terminal 2 | 3 | import "testing" 4 | 5 | func TestGenQrToTerminal(t *testing.T) { 6 | GenQrToTerminal("http://www.baidu.com") 7 | } 8 | 9 | func TestGenQrToTerminalWithConfig(t *testing.T) { 10 | GenQrToTerminalWithConfig("http://www.baidu.com") 11 | } 12 | 13 | func TestGenQR(t *testing.T) { 14 | GenQR("Hello World") 15 | } 16 | -------------------------------------------------------------------------------- /utils/rand/rand.go: -------------------------------------------------------------------------------- 1 | package rand 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "strings" 7 | "time" 8 | ) 9 | 10 | // 生成随机数验证码 11 | func GenValidateCode(len int) string { 12 | numbers := [10]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} 13 | rand.Seed(time.Now().UnixNano()) 14 | 15 | var sb strings.Builder 16 | for i := 0; i < len; i++ { 17 | fmt.Fprintf(&sb, "%d", numbers[rand.Intn(10)]) 18 | } 19 | return sb.String() 20 | } 21 | -------------------------------------------------------------------------------- /utils/ratelimit/ip_rate.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "golang.org/x/time/rate" 5 | "sync" 6 | ) 7 | 8 | type IPRateLimit struct { 9 | mu *sync.RWMutex 10 | limiter map[string]*rate.Limiter 11 | r rate.Limit 12 | b int 13 | } 14 | 15 | func NewIPRateLimiter(r rate.Limit, b int) *IPRateLimit { 16 | return &IPRateLimit{ 17 | limiter: make(map[string]*rate.Limiter), 18 | mu: &sync.RWMutex{}, 19 | r: r, // 1s创建多少个令牌 20 | b: b, // 最大存储多少个令牌 21 | } 22 | } 23 | 24 | func (i *IPRateLimit) AddIp(ip string) *rate.Limiter { 25 | i.mu.Lock() 26 | defer i.mu.Unlock() 27 | limiter := rate.NewLimiter(i.r, i.b) 28 | i.limiter[ip] = limiter 29 | return limiter 30 | } 31 | 32 | func (i *IPRateLimit) GetLimiter(ip string) *rate.Limiter { 33 | if limiter, ok := i.limiter[ip]; ok { 34 | return limiter 35 | } 36 | return i.AddIp(ip) 37 | } 38 | -------------------------------------------------------------------------------- /utils/ratelimit/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | ) 7 | 8 | // 每秒可接收1个请求,最大可运行5个请求 9 | var limiter = NewIPRateLimiter(1, 5) 10 | 11 | func main() { 12 | mux := http.NewServeMux() 13 | mux.HandleFunc("/hello", helloHandler) 14 | 15 | if err := http.ListenAndServe(":8888", limitMiddleware(mux)); err != nil { 16 | panic(err) 17 | } 18 | } 19 | 20 | func limitMiddleware(next http.Handler) http.Handler { 21 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 22 | // 获取IP限速器 23 | limiter := limiter.GetLimiter(r.RemoteAddr) 24 | if !limiter.Allow() { 25 | http.Error(w, http.StatusText(http.StatusTooManyRequests), http.StatusTooManyRequests) 26 | return 27 | } 28 | 29 | next.ServeHTTP(w, r) 30 | }) 31 | } 32 | 33 | func helloHandler(w http.ResponseWriter, r *http.Request) { 34 | fmt.Fprint(w, "Hello, World") 35 | } 36 | -------------------------------------------------------------------------------- /utils/ratelimit/ratelimit/ratelimit.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "go.uber.org/ratelimit" 8 | ) 9 | 10 | func main() { 11 | // 每秒可以通过100个请求,也就是每个请求间隔10ms 12 | rl := ratelimit.New(100) 13 | 14 | prev := time.Now() 15 | for i := 0; i < 10; i++ { 16 | now := rl.Take() 17 | fmt.Println(i, now.Sub(prev)) 18 | prev = now 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /utils/registry/registry.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | "golang.org/x/sys/windows/registry" 8 | ) 9 | 10 | // 操作注册表 11 | func main() { 12 | // 创建:指定路径的项 13 | // 路径:HKEY_CURRENT_USER\Software\Test 14 | key, exists, _ := registry.CreateKey(registry.CURRENT_USER, `SOFTWARE\Test`, registry.ALL_ACCESS) 15 | defer func() { 16 | _ = key.Close() 17 | }() 18 | 19 | // 判断是否已经存在了 20 | if exists { 21 | fmt.Println(`键已存在`) 22 | } else { 23 | fmt.Println(`新建注册表键`) 24 | } 25 | 26 | // 写入:32位整形值 27 | _ = key.SetDWordValue(`32位整形值`, uint32(123456)) 28 | // 写入:64位整形值 29 | _ = key.SetQWordValue(`64位整形值`, uint64(123456)) 30 | // 写入:字符串 31 | _ = key.SetStringValue(`字符串`, `hello`) 32 | // 写入:字符串数组 33 | _ = key.SetStringsValue(`字符串数组`, []string{`hello`, `world`}) 34 | // 写入:二进制 35 | _ = key.SetBinaryValue(`二进制`, []byte{0x11, 0x22}) 36 | 37 | // 读取:字符串 38 | s, _, _ := key.GetStringValue(`字符串`) 39 | fmt.Println(s) 40 | 41 | // 读取:一个项下的所有子项 42 | keys, _ := key.ReadSubKeyNames(0) 43 | for _, key := range keys { 44 | // 输出所有子项的名字 45 | fmt.Println(key) 46 | } 47 | 48 | // 创建:子项 49 | subKey, _, _ := registry.CreateKey(key, `子项`, registry.ALL_ACCESS) 50 | defer func() { 51 | _ = subKey.Close() 52 | }() 53 | 54 | // 删除:子项 55 | // 该键有子项,所以会删除失败 56 | // 没有子项,删除成功 57 | err := registry.DeleteKey(key, `子项`) 58 | if err != nil { 59 | fmt.Println(err.Error()) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /utils/retry/retry.go: -------------------------------------------------------------------------------- 1 | package retry 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | /** 9 | 最多重试attempts次,如果调用fn返回错误, 10 | 等待sleep的时间,而下次错误重试就需要等待两倍的时间了。 11 | 还有一点是错误的类型,常规错误会重试,而stop类型的错误会中断重试, 12 | 这也提供了一种中断机制。 13 | */ 14 | func Retry(attempts int, sleep time.Duration, fn func() error) error { 15 | if err := fn(); err != nil { 16 | if s, ok := err.(Stop); ok { 17 | return s.error 18 | } 19 | 20 | if attempts--; attempts > 0 { 21 | fmt.Printf("retry func error: %s, attemps: %d, after: %s. \n", err.Error(), attempts, sleep) 22 | time.Sleep(sleep) 23 | return Retry(attempts, 2*sleep, fn) 24 | } 25 | return err 26 | } 27 | return nil 28 | } 29 | 30 | type Stop struct { 31 | error 32 | } 33 | 34 | func NoRetryError(err error) Stop { 35 | return Stop{err} 36 | } 37 | -------------------------------------------------------------------------------- /utils/retry/retry_test.go: -------------------------------------------------------------------------------- 1 | package retry 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | func demo() error { 11 | i := rand.Int31n(5) 12 | if i == 2 { 13 | return NoRetryError(fmt.Errorf("i is 3")) 14 | } 15 | return fmt.Errorf("i = %d", i) 16 | } 17 | 18 | func TestRetry(t *testing.T) { 19 | err := Retry(3, time.Second, demo) 20 | if err != nil { 21 | t.Log(err) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /utils/robot/robot.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibigstar/go-demo/1b4058972b01ec376d8c2131f8839bdf1fe909ab/utils/robot/robot.exe -------------------------------------------------------------------------------- /utils/sentinel/fallback.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import "github.com/gin-gonic/gin" 4 | 5 | // 熔断回调函数 6 | var BreakerFallbackMap = map[string]func(*gin.Context){ 7 | "/ping": func(ctx *gin.Context) { 8 | ctx.AbortWithStatusJSON(403, map[string]interface{}{ 9 | "err": "服务暂时不可用,请稍后重试", 10 | "code": 403, 11 | }) 12 | }, 13 | } 14 | 15 | // 流量控制回调函数 16 | var BlockFallbackMap = map[string]func(*gin.Context){ 17 | "/ping": func(ctx *gin.Context) { 18 | ctx.AbortWithStatusJSON(400, map[string]interface{}{ 19 | "err": "请求太多,来不及处理了,请稍后重试", 20 | "code": 429, 21 | }) 22 | }, 23 | } 24 | -------------------------------------------------------------------------------- /utils/sentinel/middleware.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "net/http" 5 | "strings" 6 | 7 | "github.com/alibaba/sentinel-golang/api" 8 | "github.com/alibaba/sentinel-golang/core/base" 9 | "github.com/gin-gonic/gin" 10 | ) 11 | 12 | func SentinelMiddleware(opts ...Option) gin.HandlerFunc { 13 | options := evaluateOptions(opts) 14 | return func(ctx *gin.Context) { 15 | resource := ctx.FullPath() 16 | 17 | if options.resourcePrefix != nil { 18 | resource = strings.ReplaceAll(resource, options.resourcePrefix(ctx), "") 19 | } 20 | flowResource := FlowPrefix + resource 21 | breakerResource := BreakerPrefix + resource 22 | 23 | // 流量控制 24 | flowEntry, err := api.Entry( 25 | flowResource, 26 | api.WithResourceType(base.ResTypeWeb), 27 | api.WithTrafficType(base.Inbound), 28 | ) 29 | if err != nil { 30 | if fn, ok := options.blockFallbackMap[resource]; ok { 31 | fn(ctx) 32 | } else { 33 | // 默认失败回调 34 | ctx.AbortWithStatus(http.StatusTooManyRequests) 35 | } 36 | 37 | return 38 | } 39 | defer flowEntry.Exit() 40 | 41 | // 熔断检查 42 | breakerEntry, err := api.Entry(breakerResource) 43 | if err != nil { 44 | if fn, ok := options.breakerFallbackMap[resource]; ok { 45 | fn(ctx) 46 | } else { 47 | // 默认失败回调 48 | ctx.AbortWithStatus(http.StatusForbidden) 49 | } 50 | return 51 | } 52 | defer breakerEntry.Exit() 53 | 54 | ctx.Next() 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /utils/sentinel/option.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import "github.com/gin-gonic/gin" 4 | 5 | type ( 6 | Option func(*options) 7 | options struct { 8 | resourcePrefix func(*gin.Context) string 9 | blockFallbackMap map[string]func(*gin.Context) // 流量被限制回调函数 10 | breakerFallbackMap map[string]func(*gin.Context) // 熔断之后的回调函数 11 | } 12 | ) 13 | 14 | func evaluateOptions(opts []Option) *options { 15 | optCopy := &options{} 16 | for _, opt := range opts { 17 | opt(optCopy) 18 | } 19 | 20 | return optCopy 21 | } 22 | 23 | // WithResourcePrefix sets the resource prefix 24 | func WithResourcePrefix(fn func(*gin.Context) string) Option { 25 | return func(opts *options) { 26 | opts.resourcePrefix = fn 27 | } 28 | } 29 | 30 | // WithBlockFallback sets the fallback handler when requests are blocked. 31 | func WithBlockFallback(fnMap map[string]func(ctx *gin.Context)) Option { 32 | return func(opts *options) { 33 | opts.blockFallbackMap = fnMap 34 | } 35 | } 36 | 37 | // WithBreakerFallback sets the fallback handler when requests are breaker. 38 | func WithBreakerFallback(fnMap map[string]func(ctx *gin.Context)) Option { 39 | return func(opts *options) { 40 | opts.breakerFallbackMap = fnMap 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /utils/seq/id.go: -------------------------------------------------------------------------------- 1 | package seq 2 | 3 | import ( 4 | "context" 5 | "github.com/sony/sonyflake" 6 | "google.golang.org/grpc/metadata" 7 | "strconv" 8 | "time" 9 | ) 10 | 11 | var ( 12 | sf *sonyflake.Sonyflake 13 | startTime = time.Date(2019, 7, 28, 0, 0, 0, 0, time.UTC) 14 | ) 15 | 16 | func init() { 17 | var st sonyflake.Settings 18 | st.StartTime = startTime 19 | sf = sonyflake.NewSonyflake(st) 20 | if sf == nil { 21 | panic("sonyflake not created") 22 | } 23 | } 24 | 25 | func NextNumID() uint64 { 26 | id, _ := sf.NextID() 27 | return id 28 | } 29 | 30 | const IdPrefixKey = "p-id" 31 | 32 | func NextID(ctx context.Context) string { 33 | nextId, err := sf.NextID() 34 | if err != nil { 35 | return err.Error() 36 | } 37 | // uit64 转成 str 38 | id := strconv.FormatUint(nextId, 10) 39 | 40 | if ctx == nil { 41 | ctx = context.Background() 42 | } 43 | 44 | if md, ok := metadata.FromIncomingContext(ctx); ok { 45 | if ps := md.Get(IdPrefixKey); len(ps) != 0 { 46 | return ps[0] + id 47 | } 48 | } 49 | 50 | if md, ok := metadata.FromOutgoingContext(ctx); ok { 51 | if ps := md.Get(IdPrefixKey); len(ps) != 0 { 52 | return ps[0] + id 53 | } 54 | } 55 | return id 56 | } 57 | -------------------------------------------------------------------------------- /utils/seq/id_test.go: -------------------------------------------------------------------------------- 1 | package seq 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "github.com/sony/sonyflake" 7 | "google.golang.org/grpc/metadata" 8 | "testing" 9 | ) 10 | 11 | // 利用雪花算法生成不重复ID 12 | func TestID(t *testing.T) { 13 | id := NextNumID() 14 | t.Log(id) 15 | body, err := json.Marshal(sonyflake.Decompose(id)) 16 | if err != nil { 17 | t.Error(err) 18 | } 19 | t.Log(string(body)) 20 | } 21 | 22 | //根据上下文生成带前缀的ID 23 | func TestNextID(t *testing.T) { 24 | ctx := metadata.NewIncomingContext(context.Background(), metadata.Pairs(IdPrefixKey, "P66-")) 25 | id := NextID(ctx) 26 | t.Log(id) 27 | } 28 | -------------------------------------------------------------------------------- /utils/seq/uuid.go: -------------------------------------------------------------------------------- 1 | package seq 2 | 3 | import ( 4 | "github.com/google/uuid" 5 | "strings" 6 | ) 7 | 8 | func UUID() string { 9 | return uuid.New().String() 10 | } 11 | 12 | func UUIDShort() string { 13 | return strings.Replace(UUID(), "-", "", -1) 14 | } 15 | -------------------------------------------------------------------------------- /utils/seq/uuid_test.go: -------------------------------------------------------------------------------- 1 | package seq 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestUUID(t *testing.T) { 8 | uuid := UUID() 9 | t.Log(uuid) 10 | 11 | shortUUID := UUIDShort() 12 | t.Log(shortUUID) 13 | } 14 | -------------------------------------------------------------------------------- /utils/stack/stack.go: -------------------------------------------------------------------------------- 1 | package stack 2 | 3 | import ( 4 | "fmt" 5 | "path/filepath" 6 | "runtime" 7 | "runtime/debug" 8 | ) 9 | 10 | // 获取函数调用栈 11 | func GetStack() string { 12 | var buf [4096]byte 13 | n := runtime.Stack(buf[:], false) 14 | return string(buf[:n]) 15 | } 16 | 17 | // 获取函数调用栈 18 | func GetStackSimple() string { 19 | stack := debug.Stack() 20 | return string(stack) 21 | } 22 | 23 | // 获取函数调用栈 24 | func GetStackFunc() string { 25 | pc, file, line, ok := runtime.Caller(2) 26 | if !ok { 27 | return "" 28 | } 29 | funcName := runtime.FuncForPC(pc).Name() 30 | return fmt.Sprintf("文件: %s, func: %s, 行号: %d", file, funcName, line) 31 | } 32 | 33 | // 获取当前go文件的绝对路径 34 | func GetGoFilePath() string { 35 | _, currentFile, _, _ := runtime.Caller(0) 36 | basePath := filepath.Dir(currentFile) 37 | return basePath 38 | } 39 | -------------------------------------------------------------------------------- /utils/stack/stack_test.go: -------------------------------------------------------------------------------- 1 | package stack 2 | 3 | import "testing" 4 | 5 | func TestPrintStack(t *testing.T) { 6 | stack := GetStack() 7 | t.Log(stack) 8 | } 9 | -------------------------------------------------------------------------------- /utils/timex/timex_test.go: -------------------------------------------------------------------------------- 1 | package timex 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func TestTimeConsuming(t *testing.T) { 10 | defer TimeConsuming()() 11 | 12 | fmt.Println("开始执行....") 13 | time.Sleep(1 * time.Second) 14 | } 15 | 16 | func TestTimeFormat(t *testing.T) { 17 | now := time.Now() 18 | t.Log(Format(now, defaultLayout)) 19 | 20 | t.Log(Format(now, "Y-M-D h:m:s")) 21 | 22 | t.Log(Format(now, "Y-M-D")) 23 | 24 | t.Log(Format(now, "h:m:s")) 25 | 26 | t.Log(FormatTime(now)) 27 | 28 | t.Log(FormatYMD(now)) 29 | 30 | t.Log(FormatMD(now)) 31 | } 32 | 33 | func TestTimeParse(t *testing.T) { 34 | st, err := ParseTime("2020-09-10 15:22:00") 35 | if err != nil { 36 | t.Error(err) 37 | } 38 | t.Log(st) 39 | 40 | st, err = ParseYMD("2020-09-10") 41 | if err != nil { 42 | t.Error(err) 43 | } 44 | t.Log(st) 45 | } 46 | 47 | func TestTimeFirstAndLast(t *testing.T) { 48 | st, err := ParseTime("2020-09-10 15:22:00") 49 | if err != nil { 50 | t.Error(err) 51 | } 52 | t.Log(FirstMonth(st)) 53 | t.Log(FirstMonthUnix(st)) 54 | t.Log(LastMonth(st)) 55 | t.Log(LastMonthUnix(st)) 56 | } 57 | -------------------------------------------------------------------------------- /utils/token/token_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "github.com/dgrijalva/jwt-go" 5 | "go-demo/utils/seq" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | func TestToken(t *testing.T) { 11 | claims := make(jwt.MapClaims) 12 | // 有效期 13 | claims[TokenClaimEXP] = time.Now().Add(time.Hour * time.Duration(1)).Unix() 14 | claims[TokenClaimUID] = seq.UUID() 15 | 16 | token, err := GenJwtToken(claims) 17 | if err != nil { 18 | t.Error(err) 19 | } 20 | 21 | t.Log("token:", token) 22 | 23 | isToken := CheckJwtToken(token) 24 | t.Log("isToken:", isToken) 25 | 26 | if uid, found := GetUIDFromToken(token); found { 27 | t.Log("用户id:", uid) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /utils/walk/walk.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | PerMonitorV2, PerMonitor 12 | True 13 | 14 | 15 | -------------------------------------------------------------------------------- /utils/walk/walk.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/lxn/walk" 5 | . "github.com/lxn/walk/declarative" 6 | ) 7 | 8 | // go build -ldflags="-H windowsgui" 9 | 10 | func main() { 11 | var username, password *walk.TextEdit 12 | 13 | MainWindow{ 14 | Title: "QQ", 15 | Size: Size{Width: 350, Height: 100}, 16 | Layout: VBox{}, 17 | Children: []Widget{ 18 | VSplitter{ 19 | Children: []Widget{ 20 | HSplitter{ 21 | Children: []Widget{ 22 | TextLabel{Text: "用户名"}, 23 | TextEdit{AssignTo: &username}, 24 | }, 25 | }, 26 | HSplitter{ 27 | Children: []Widget{ 28 | TextLabel{Text: "密码"}, 29 | TextEdit{AssignTo: &password}, 30 | }, 31 | }, 32 | }, 33 | }, 34 | PushButton{ 35 | Text: "登陆", 36 | OnClicked: func() { 37 | if username.Text() == "admin" && password.Text() == "admin" { 38 | walk.MsgBox(&walk.MainWindow{}, "成功", "登陆成功: "+username.Text(), walk.MsgBoxOK) 39 | } 40 | }, 41 | }, 42 | }, 43 | }.Run() 44 | } 45 | -------------------------------------------------------------------------------- /utils/word/new.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibigstar/go-demo/1b4058972b01ec376d8c2131f8839bdf1fe909ab/utils/word/new.docx -------------------------------------------------------------------------------- /utils/word/old.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibigstar/go-demo/1b4058972b01ec376d8c2131f8839bdf1fe909ab/utils/word/old.docx -------------------------------------------------------------------------------- /utils/word/word.go: -------------------------------------------------------------------------------- 1 | package word 2 | 3 | import ( 4 | "github.com/nguyenthenguyen/docx" 5 | "github.com/unidoc/unioffice/document" 6 | ) 7 | 8 | // https://github.com/nguyenthenguyen/docx 9 | func ReplaceWord(file string) error { 10 | r, err := docx.ReadDocxFile(file) 11 | if err != nil { 12 | return err 13 | } 14 | docx := r.Editable() 15 | docx.Replace("{name}", "派大星", -1) 16 | docx.Replace("{age}", "20", -1) 17 | docx.WriteToFile("new.docx") 18 | r.Close() 19 | 20 | return nil 21 | } 22 | 23 | // https://github.com/unidoc/unioffice 24 | func ReplaceWithStyle(file string) error { 25 | 26 | doc, err := document.Open(file) 27 | if err != nil { 28 | return err 29 | } 30 | paragraphs := []document.Paragraph{} 31 | paragraphs = append(paragraphs, doc.Paragraphs()...) 32 | 33 | for _, sdt := range doc.StructuredDocumentTags() { 34 | paragraphs = append(paragraphs, sdt.Paragraphs()...) 35 | } 36 | 37 | for _, p := range paragraphs { 38 | for _, r := range p.Runs() { 39 | switch r.Text() { 40 | case "{name}": 41 | r.ClearContent() 42 | r.AddText("派大星") 43 | case "{age}": 44 | r.ClearContent() 45 | r.AddText("20") 46 | default: 47 | continue 48 | } 49 | } 50 | } 51 | 52 | doc.SaveToFile("new.docx") 53 | 54 | return nil 55 | } 56 | -------------------------------------------------------------------------------- /utils/word/word_test.go: -------------------------------------------------------------------------------- 1 | package word 2 | 3 | import "testing" 4 | 5 | func TestReplaceWord(t *testing.T) { 6 | err := ReplaceWord("old.docx") 7 | if err != nil { 8 | t.Error(err) 9 | } 10 | } 11 | 12 | func TestReplaceWithStyle(t *testing.T) { 13 | err := ReplaceWithStyle("old.docx") 14 | if err != nil { 15 | t.Error(err) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /utils/xlsx/test_write.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pibigstar/go-demo/1b4058972b01ec376d8c2131f8839bdf1fe909ab/utils/xlsx/test_write.xlsx -------------------------------------------------------------------------------- /utils/xlsx/xlsx_test.go: -------------------------------------------------------------------------------- 1 | package xlsxkit 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestWriteXlsx(t *testing.T) { 8 | err := WriteXlsx() 9 | if err != nil { 10 | t.Error(err) 11 | } 12 | } 13 | 14 | func TestReadXlsx(t *testing.T) { 15 | rows, err := ReadXlsx("test_write.xlsx") 16 | if err != nil { 17 | t.Error(err) 18 | } 19 | 20 | for _, row := range rows { 21 | for _, colCell := range row { 22 | t.Log(colCell) 23 | } 24 | } 25 | } 26 | 27 | // 反射写Xlsx 28 | func TestRefactorWrite(t *testing.T) { 29 | var records []*Record 30 | records = append(records, &Record{ 31 | Name: "小明", 32 | Age: 11, 33 | }) 34 | records = append(records, &Record{ 35 | Name: "小华", 36 | Age: 12, 37 | }) 38 | 39 | err := RefactorWrite(records) 40 | if err != nil { 41 | t.Error(err) 42 | } 43 | } 44 | 45 | func TestRefactorReadXlsx(t *testing.T) { 46 | var r []Record 47 | ReadToStruct("test_write.xlsx", "Sheet1", &r) 48 | t.Log(r) 49 | } 50 | --------------------------------------------------------------------------------