前言
静态化处理提高运行效率,可以编译go文件到二进制脚本。进阶使用
1.安装golang环境
a.本机测试
wget -O /tmp/go.tgz "https://dl.google.com/go/go1.12.7.src.tar.gz"
tar -C /usr/local -xzf /tmp/go.tgz && rm -f /tmp/go.tgz
ln -s /usr/local/go/bin/* /usr/local/bin/
[]:~/tmp/dk/golang# vim test.go
package main
import "fmt"
func main() {
fmt.Println("hello go!")
}
可以直接go run test.go测试效果,使用go build test.go生成二进制代码:test文件,看到权限自动调整好了不用再chmod,运行./test 得到hello go!。
b.docker容器
docker-hub的golang官方示例。
# -w:指定工作目录
docker run --name golang -v ~/tmp/dk/golang:/usr/src/app -w /usr/src/app -itd golang
进入容器中使用的一个编译环境。go代码的编译环境齐全,移植方便。
2.连接数据库
参考《go语言之行--golang操作redis、mysql大全》
go get "github.com/go-redis/redis"
go get "github.com/go-sql-driver/mysql"
go get "github.com/jmoiron/sqlx"
go get "github.com/mikemintang/go-curl"
redis集群操作官方参考\
使用win10 go get超时的解决参考百度 “git clone超时”。
数据库MySQL的操作,代码参考下篇。
a.mysql读写
mysql.go,参考官方,比php使用不够方便:
package main
import (
"crypto/md5"
"crypto/sha1"
"encoding/hex"
"fmt"
"log"
"strconv"
"time"
"database/sql"
_"github.com/go-sql-driver/mysql"
)
type User struct {
id int `db:"id"`
username string `db:"username"`
last_login string `db:"last_login"`
}
func md5V(str string) string {
h := md5.New()
h.Write([]byte(str))
return hex.EncodeToString(h.Sum(nil))
}
func sha1V (str string) string {
hs := sha1.New()
hs.Write([]byte(str))
return hex.EncodeToString((hs.Sum(nil)))
}
var mysqlDB *sql.DB
func main() {
t1 := time.Now()
//https://github.com/jmoiron/sqlx postgres
dsn := "root:123456@tcp(172.1.11.11:3306)/test?charset=utf8"
mysqlDB, err := sql.Open("mysql", dsn)
//fmt.Println(mysqlDB, err)
var (
id int
username string
passwd string
last_login string
sum int
)
//id, username, passwd, last_login
err = mysqlDB.QueryRow("select count(id) from user").Scan(&sum)
if err != nil {
log.Fatal(err)
}
fmt.Println(`目前行数:`, sum)
if sum<10 {
stmt, err := mysqlDB.Prepare("INSERT INTO user (username,passwd,last_login) VALUES(?,?,?)")
if err != nil {
log.Fatal(err)
}
for i:=0; i<20; i++ {
username = "Dolly"+strconv.Itoa(i)
last_login = "2000-01-01 10:00:00"
passwd = sha1V(md5V(username + last_login))
_, err := stmt.Exec(username, passwd, last_login)
if err != nil {
log.Fatal(err)
}
}
err = mysqlDB.QueryRow("select count(id) from user").Scan(&sum)
if err != nil {
log.Fatal(err)
}
fmt.Println(`行数=`, sum)
}else {
rows, _ := mysqlDB.Query(`SELECT * FROM user ORDER BY id ASC`)
users := []User{}
for rows.Next() {
err := rows.Scan(&id, &username, &passwd, &last_login)
users = append(users, User{id, username, last_login})
if err != nil {
log.Fatalln(err)
}
}
log.Println("\n", users)
}
t2 := time.Now()
fmt.Println()
fmt.Println(t2.Sub(t1))
fmt.Printf("main OK!\n")
}
b.redis集群操作
redis.go,主要参考官方,pipeline参考《golang. 批量获取redis中的缓存值》:
package main
import (
"encoding/hex"
"crypto/md5"
"fmt"
"log"
"strconv"
"strings"
"github.com/go-redis/redis"
"reflect"
"time"
)
func redisClient(addr string) {
client := redis.NewClient(&redis.Options{
Addr: addr,
})
pong, err := client.Ping().Result()
fmt.Println(pong, err)
// Output: PONG <nil>
}
func clusterClient(addr []string) {
fmt.Println("type:", reflect.TypeOf(redisCsDB))
redisCsDB = redis.NewClusterClient(&redis.ClusterOptions{
Addrs: addr,
})
fmt.Println("type:", reflect.TypeOf(redisCsDB))
// Output: PONG <nil>
pong, err := redisCsDB.Ping().Result()
fmt.Println(pong, err)
if err != nil {
panic(err)
}
//fmt.Println(redisCsDB)
}
func md5V(str string) string {
h := md5.New()
h.Write([]byte(str))
return hex.EncodeToString(h.Sum(nil))
}
var redisDB *redis.Client
var redisCsDB *redis.ClusterClient
func main() {
var servers string = "172.1.50.11:6379,172.1.50.12:6379,172.1.50.13:6379,172.1.30.11:6379,172.1.30.12:6379,172.1.30.13:6379"
serverInfo := strings.Split(servers, ",")
fmt.Println(serverInfo)
clusterClient(serverInfo)
//设置时区
l,_ := time.LoadLocation("Asia/Shanghai")
fmt.Println(time.Now().In(l))
val, err := redisCsDB.Get("set-10").Result()
fmt.Println(val)
fmt.Println(err)
if len(val) < 1 {
//清空当前的
redisCsDB.FlushDB()
for i:=0; i<20000; i++ {
//fmt.Println("set-"+ strconv.Itoa(i))
key := "set-"+ strconv.Itoa(i)
str := time.Now().Format("2006-01-02 15:04:05 ") + strconv.Itoa(i)
val, err = redisCsDB.Set(key, md5V(str), 0).Result()
fmt.Println(str)
fmt.Println(val)
}
}
t1 := time.Now()
for i:=10; i<20000; i++ {
key := "set-"+ strconv.Itoa(i)
_, err := redisCsDB.Get(key).Result()
if err != nil {
panic(err)
}
//fmt.Println("set-"+ strconv.Itoa(i), val)
}
t2 := time.Now()
fmt.Println()
fmt.Println(t2.Sub(t1))
//pipeline
t1 = time.Now()
pipe := redisCsDB.Pipeline()
for i:=10; i<20000; i++ {
key := "set-"+ strconv.Itoa(i)
pipe.Get(key).Result()
}
_, err = pipe.Exec()
t2 = time.Now()
fmt.Println(t2.Sub(t1))
fmt.Println()
log.Println(err == redis.Nil)
//执行命令
slotsInfo, err := redisCsDB.Do("cluster", "slots").Result()
fmt.Println("slotsInfo type:", reflect.TypeOf(slotsInfo), "\n", slotsInfo, err)
info, err := redisCsDB.Do("cluster", "info").Result()
fmt.Println("cluster info:", reflect.TypeOf(info), "\n", info, err)
//这里被for阻塞
fmt.Printf("main OK!\n")
}
c.curl请求
参考 IT无崖子《深入浅出Golang的协程池设计》:
package main
import (
"fmt"
"math/rand"
"strconv"
//"sync"
"time"
"github.com/mikemintang/go-curl"
)
/* 有关Task任务相关定义及操作 */
//定义任务Task类型,每一个任务Task都可以抽象成一个函数
type Task struct {
f func() error //一个无参的函数类型
}
//通过NewTask来创建一个Task
func NewTask(f func() error) *Task {
t := Task{
f: f,
}
return &t
}
//执行Task任务的方法
func (t *Task) Execute(work_ID string, p *Pool) {
p.CurJobs += 1
fmt.Println(p, " >> ", work_ID)
t.f() //调用任务所绑定的函数
fmt.Println("worker: ", work_ID, " 执行完毕")
p.CurJobs -= 1
p.Dealed += 1
}
/* 有关协程池的定义及操作 */
//定义池类型
type Pool struct {
//对外接收Task的入口
EntryChannel chan *Task
//协程池最大worker数量,限定Goroutine的个数
workerNum int
//协程池内部的任务就绪队列
JobsChannel chan *Task
CurJobs int
Dealed int
}
//创建一个协程池
func NewPool(max int) *Pool {
p := Pool{
EntryChannel: make(chan *Task),
workerNum: max,
JobsChannel: make(chan *Task),
CurJobs: 0,
Dealed: 0,
}
return &p
}
//协程池创建一个worker并且开始工作
func (p *Pool) worker(work_ID string) {
//worker不断的从JobsChannel内部任务队列中拿任务
for task := range p.JobsChannel {
//如果拿到任务,则执行task任务
fmt.Println("______task:", p.Dealed, "", p.CurJobs, "/", p.workerNum, p)
task.Execute(work_ID, p)
fmt.Println("----第 ", p.Dealed, "个任务 执行完毕")
}
}
//让协程池Pool开始工作
func (p *Pool) Run() {
//1,首先根据协程池的worker数量限定,开启固定数量的Worker,
// 每一个Worker用一个Goroutine承载
for i := 1; i <= p.workerNum; i++ {
go p.worker(time.Now().Format("2006.01.02 15:04:05 ")+ strconv.Itoa(i))
}
//2, 从EntryChannel协程池入口取外界传递过来的任务
// 并且将任务送进JobsChannel中
for task := range p.EntryChannel {
p.JobsChannel <- task
}
//3, 执行完毕需要关闭JobsChannel
close(p.JobsChannel)
//4, 执行完毕需要关闭EntryChannel
close(p.EntryChannel)
}
func curlV (url string) string {
headers := map[string]string{
"User-Agent": "Chrome/63.0.3239.132 (Windows NT 10.0; WOW64)",
"Authorization": "Bearer access_token",
"Content-Type": "application/octet-stream",
}
// 链式操作
req := curl.NewRequest()
req.SetResponseTimeOut(10)
resp, err := req.SetUrl(url).SetHeaders(headers).Get()
if resp != nil {
if resp.Raw.StatusCode == 200 {
//fmt.Println(url, `StatusCode =`, resp.Raw.StatusCode)
return resp.Body
} else {
fmt.Println(err, resp.Raw.StatusCode)
return ""
}
}else{
fmt.Println(err)
return ""
}
}
func main() {
//curl
urls := []string {"https://www.baidu.com", "https://www.php.net", "https://www.runoob.com"}
fmt.Println()
fmt.Println(urls)
t := NewTask(func() error {
fmt.Print("__")
t1 := time.Now()
r := rand.Int() % len(urls)
curlV(urls[r])
t2 := time.Now()
fmt.Println(time.Now().Format("-- 2006-01-02 15:04:05"), t2.Sub(t1))
return nil
})
//创建一个协程池,最大开启10个协程worker
max := 10
p := NewPool(max)
//开一个协程 不断的向 Pool 输送打印一条时间的task任务
go func() {
for {
p.EntryChannel <- t
//time.Sleep(time.Duration(time.Microsecond * 200))
}
}()
//启动协程池p
p.Run()
fmt.Printf("main OK!\n")
}
运行可能会报错:__panic: read tcp xx: i/o timeout,这里猜想是随机的、部分请求被拒绝,调长时间会缓解,暂无法解决。
3.使用小结
与php+swoole对比:
go变量作用域--很大:各种函数能使用公共变量、不用php的传参,好处坏处都很明显,基本上一个协程环境内(包括主进程的一个)公共变量是通用的,指针类型数据传参效率很高。
协程数据/事件使用指针:代码难以理解。
有疑问加站长微信联系(非本文作者)