tcp编程、socket编程、redis

Easter79
• 阅读 688

tcp编程(需要建立连接,三次握手,四次挥手,然后发送信息流,数据包是有序的)

udp编程(知道IP、端口直接发送数据,数据包可能是无序的)

1、客户端和服务器客

tcp编程、socket编程、redis

socket编程

1.服务端的处理流程
  a.监听端口
  b.接收客户端的链接
  c.创建goroutine,处理该链接

2.客户端的处理流程
  a.建立与服务端的链接
  b.进行数据收发
  c.关闭链接

3.服务端代码

tcp编程、socket编程、redis tcp编程、socket编程、redis

package main

import (
    "fmt"
    "net"//导入socket的包
)

func main() {
    fmt.Println("start server...")
    listen, err := net.Listen("tcp", "0.0.0.0:50000")//监听50000端口
    if err != nil {
        fmt.Println("listen failed, err:", err)
        return
    }

    for {
        conn, err := listen.Accept()//等待客户端连接
        if err != nil {
            fmt.Println("accept failed, err:", err)
            continue
        }
        go process(conn)
    }

}

func process(conn net.Conn) {
    defer conn.Close()
    for {
        buf := make([]byte, 512)
        _, err := conn.Read(buf)//读取客户端传输过来的数据
        if err != nil {
            fmt.Println("read err:", err)
            return
        }
        fmt.Println("read: ", string(buf))
    }
}

View Code

4.客户端代码

tcp编程、socket编程、redis tcp编程、socket编程、redis

package main

import (
    "bufio"
    "fmt"
    "net"
    "os"
    "strings"
)

func main() {
    conn, err := net.Dial("tcp", "localhost:50000")   //建立链接
    if err != nil {
        fmt.Println("Error dialing", err.Error())
        return
    }

    defer conn.Close()
    inputReader := bufio.NewReader(os.Stdin)  //从终端读取数据
    for {
        input, _ := inputReader.ReadString('\n') //从终端读取一行数据
        trimmedInput := strings.Trim(input, "\r\n")//去掉字符串\r\n
        if trimmedInput == "Q" {
            return
        }

        _, err = conn.Write([]byte(trimmedInput))//将信息发给服务端
        if err != nil {
            return
        }
    }
}

View Code

5.发送http请求

tcp编程、socket编程、redis tcp编程、socket编程、redis

package main

import (
    "fmt"
    "io" "net" ) func main() { conn, err := net.Dial("tcp", "www.baidu.com:80") if err != nil { fmt.Println("Error dialing", err.Error()) return } defer conn.Close() msg := "GET / HTTP/1.1\r\n" msg += "Host: www.baidu.com\r\n" msg += "Connection: close\r\n" msg += "\r\n\r\n" _, err = io.WriteString(conn, msg) if err != nil { fmt.Println("write string failed, ", err) return } buf := make([]byte, 4096) for { count, err := conn.Read(buf) if err != nil { break } fmt.Println(string(buf[0:count])) } }

View Code

redis

redis是个开源的高性能的key-value的内存数据库,可以把它当成远程的数据结构。
支持的value类型 非常多, 比如string、list(链表)、set(集合)、hash表等等
redis性能非常高,单机能够达到15w qps,通常适合做缓存。

PV=page view      是指页面被浏览的次数,比如你打开一网页,那么这个网站的pv就算加了一次;
TPS=transactions per second   是每秒内的事务数
QPS=queries per second    是指每秒内查询次数,比如执行了select操作,相应的qps会增加。RPS=requests per second  是指每秒请求数

1、redis使用

安装使用第三方开源的redis库: github.com/garyburd/redigo/redis
go get github.com/garyburd/redigo/redis
import(
    “github.com/garyburd/redigo/redis"
)

2、连接redis

package main

import (
    "fmt"
    "github.com/garyburd/redigo/redis"
)


func main() {
    c, err := redis.Dial("tcp", "localhost:6379")  //连接redis    if err != nil {
        fmt.Println("conn redis failed,", err)
        return
    }
    defer c.Close()
}

3、Set 接 口

tcp编程、socket编程、redis tcp编程、socket编程、redis

package main

import (
    "fmt"
    "github.com/garyburd/redigo/redis"
)

func main() {
    c, err := redis.Dial("tcp", "localhost:6379")
    if err != nil {
        fmt.Println("conn redis failed,", err)
        return
    }
    defer c.Close()
    _, err = c.Do("Set", "abc", 100)   //设置值
    if err != nil {
        fmt.Println(err)
        return
    }

    r, err := redis.Int(c.Do("Get", "abc"))  //获取设置的值
    if err != nil {
        fmt.Println("get abc failed,", err)
        return
    }
    fmt.Println(r)
}

View Code

4、Hash表

tcp编程、socket编程、redis tcp编程、socket编程、redis

package main

import (
    "fmt"
    "github.com/garyburd/redigo/redis"
)

func main() {
    c, err := redis.Dial("tcp", "localhost:6379")
    if err != nil {
        fmt.Println("conn redis failed,", err)
        return
    }
    defer c.Close()
    _, err = c.Do("HSet", "books", "abc", 100)   //设置hash表的名字books
    if err != nil {
        fmt.Println(err)
        return
    }

    r, err := redis.Int(c.Do("HGet", "books", "abc"))  //因为读取出来是字符串,所以redis.Int转换成整数
    if err != nil {
        fmt.Println("get abc failed,", err)
        return
    }
    fmt.Println(r)
}

View Code

5、批量Set

tcp编程、socket编程、redis tcp编程、socket编程、redis

package main

import (
    "fmt"
    "github.com/garyburd/redigo/redis"
)

func main() {
    c, err := redis.Dial("tcp", "localhost:6379")
    if err != nil {
        fmt.Println("conn redis failed,", err)
        return
    }

    defer c.Close()
    _, err = c.Do("MSet", "abc", 100, "efg", 300)  //批量设置
    if err != nil {
        fmt.Println(err)
        return
    }

    r, err := redis.Ints(c.Do("MGet", "abc", "efg"))   //读取出来是一个切片
    if err != nil {
        fmt.Println("get abc failed,", err)
        return
    }
    for _, v := range r {
        fmt.Println(v)
    }
}

View Code

6、过期时间

tcp编程、socket编程、redis tcp编程、socket编程、redis

package main

import ( 
    "fmt" 
    "github.com/garyburd/redigo/redis"
)

func main() {
    c, err := redis.Dial("tcp", "localhost:6379")
    if err != nil {
        fmt.Println("conn redis failed,", err)
        return
    }

    defer c.Close()
    _, err = c.Do("expire", "abc", 10)  
    //设置过期时间,abc为key值,10为超时时间,插入的时候不能设置超时时间,因为插入和设置超时时间是两个不同的接口
    if err != nil {
        fmt.Println(err)
        return
    }
}

View Code

7、队列操作

tcp编程、socket编程、redis tcp编程、socket编程、redis

package main

import (
    "fmt"
    "github.com/garyburd/redigo/redis"
)

func main() {

    c, err := redis.Dial("tcp", "localhost:6379")
    if err != nil {
        fmt.Println("conn redis failed,", err)
        return
    }
    defer c.Close()
    _, err = c.Do("lpush", "book_list", "abc", "ceg", 300)  //book_list队列的名字
    if err != nil {
        fmt.Println(err)
        return
    }
    r, err := redis.String(c.Do("lpop", "book_list"))     //从队列里面取值
    if err != nil {
        fmt.Println("get abc failed,", err)
        return
    }
    fmt.Println(r)
}

View Code

8、连接池

type Pool struct {
    //Dial 是创建链接的方法
    Dial func() (Conn, error)

    //TestOnBorrow 是一个测试链接可用性的方法
    TestOnBorrow func(c Conn, t time.Time) error

    // 最大的空闲连接数,表示即使没有redis连接时依然可以保持N个空闲的连接,而不被清除,随时处于待命状态
    MaxIdle int

    // 最大的激活连接数,表示同时最多有N个连接 ,为0事表示没有限制
    MaxActive int

    //最大的空闲连接等待时间,超过此时间后,空闲连接将被关闭
    IdleTimeout time.Duration

    // 当链接数达到最大后是否阻塞,如果不的话,达到最大后返回错误
    Wait bool

}

tcp编程、socket编程、redis tcp编程、socket编程、redis

package main

import (
    "encoding/json"
    "flag"
    "fmt"
    "time"

    "github.com/garyburd/redigo/redis"
)

//声明一些全局变量
var (
    pool          *redis.Pool
    redisServer   = flag.String("redisServer", ":6379", "") 
    //flag对命令参数进行解析是常见的需求
    //go run main.go -redisServer "8000",启动程序是给redisServer赋值,第3个参数是使用说明
    //如果执行程序不带有-fredisServer 那么flag.Sring()的第2个参数则为默认值
    redisPassword = flag.String("redisPassword", "123456", "")
)

//初始化一个连接池pool
func newPool(server, password string) *redis.Pool {
    return &redis.Pool{
        MaxIdle:     64,                //空闲连接数
        MaxActive:   1000,              //活跃连接数
        IdleTimeout: 240 * time.Second, //240s超时时间,连接池没有使用,会关闭
        Dial: func() (redis.Conn, error) {
            c, err := redis.Dial("tcp", server)
            if err != nil {
                return nil, err
            }
            /*
               //密码权限验证
               if _, err := c.Do("AUTH", password); err != nil {
                   c.Close()
                   return nil, err
               }*/
            return c, err
        },
        //从连接池获取一个链接,测试连接是否可用
        TestOnBorrow: func(c redis.Conn, t time.Time) error {
            if time.Since(t) < time.Minute {
                return nil
            }
            _, err := c.Do("PING")
            return err
        },
    }
}

type Student struct {
    Id    int     `json:"id"`
    Name  string  `json:"name"`
    Age   int     `json:"age"`
    Score float32 `json:"score"`
}

func main() {
    flag.Parse()
    pool = newPool(*redisServer, *redisPassword)

    conn := pool.Get()
    defer conn.Close()

    var stu Student = Student{
        Id:    1000,
        Name:  "abc",
        Age:   89,
        Score: 99.2,
    }

    data, _ := json.Marshal(stu)
    //redis操作结构体,需要转成json格式
    v, err := conn.Do("SET", 1000, string(data))
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println(v)
    ret, err := redis.String(conn.Do("GET", 1000))
    if err != nil {
        fmt.Println(err)
        return
    }

    var stu01 Student
    json.Unmarshal([]byte(ret), &stu01)
    fmt.Printf("stu01:%#v\n", stu01)

}

View Code

socket练习:

client/main.go

tcp编程、socket编程、redis tcp编程、socket编程、redis

package main

import (
    "bufio"
    "encoding/json"
    "fmt"
    "net"
    "os"
    "github.com/sherlockhua/goproject/day9/proto"
)

var recvMsg chan interface{}

func main() {
    conn, err := net.Dial("tcp", "192.168.14.200:18080")
    if err != nil {
        fmt.Printf("dial server failed, err:%v\n", err)
        return
    }

    fmt.Fprintf
    recvMsg = make(chan interface{}, 1000)
    defer conn.Close()
    go read(conn)

    err = login(conn)
    if err != nil {
        fmt.Printf("login failed, err:%v\n", err)
        return
    }

    msg := <-recvMsg
    loginResp, ok := msg.(*proto.LoginResponse)
    if !ok {
        fmt.Printf("unexpect msg:%T, %+v\n", msg, msg)
        return
    }

    if loginResp.Errno != 0 {
        fmt.Printf("login failed, err:%v\n", loginResp.Message)
        return
    }

    fmt.Printf("login succ\n")
    for {
        var data string
        reader := bufio.NewReader(os.Stdin)  //在shell输入
        data, err := reader.ReadString('\n') //从shell读取一行数据
        if err != nil {
            continue
        }

        err = sendMessage(conn, data) //开始发消息
        if err != nil {
            fmt.Printf("send message failed, err:%v\n", err)
            return
        }
    }
}

func sendMessage(conn net.Conn, data string) (err error) {
    var message proto.MessageRequest
    message.Message = data
    message.Username, _ = os.Hostname() //获取主机名

    body, err := json.Marshal(message)
    if err != nil {
        fmt.Printf("marshal failed, err:%v\n", err)
        return
    }

    err = proto.WritePacket(conn, proto.CmdSendMessageRequest, body) //将数据包发送给服务端
    if err != nil {
        fmt.Printf("send to server failed, err:%v\n", err)
        return
    }
    return
}

func login(conn net.Conn) (err error) {
    var loginReq proto.LoginRequest
    loginReq.Password = "admin"
    loginReq.Username = "admin"

    body, err := json.Marshal(loginReq)
    if err != nil {
        fmt.Printf("marshal failed, err:%v\n", err)
        return
    }

    err = proto.WritePacket(conn, proto.CmdLoginRequest, body)
    if err != nil {
        fmt.Printf("send to server failed, err:%v\n", err)
        return
    }
    return
}

func read(conn net.Conn) {
    for {
        body, cmd, err := proto.ReadPacket(conn)
        if err != nil {
            fmt.Printf("read from server failed, err:%v\n", err)
            return
        }

        switch cmd {
        case proto.CmdLoginResponse:
            err = processLoginResponse(conn, body)
        case proto.CmdSendMessageResponse:
            err = processSendMsgResponse(conn, body)
        case proto.CmdBroadMessage:
            err = processBroadMessage(conn, body)
        default:
            fmt.Printf("unsupport cmd[%v]\n", cmd)
            return
        }
    }
}

func processLoginResponse(conn net.Conn, body []byte) (err error) {
    //登录返回
    var loginResponse proto.LoginResponse
    err = json.Unmarshal(body, &loginResponse)
    if err != nil {
        fmt.Printf("unmarshal failed, err:%v\n", err)
        return
    }

    recvMsg <- &loginResponse
    return
}

func processSendMsgResponse(conn net.Conn, body []byte) (err error) {
    var messageResp proto.MessageResponse
    err = json.Unmarshal(body, &messageResp)
    if err != nil {
        fmt.Printf("unmarshal failed, err:%v\n", err)
        return
    }

    if messageResp.Errno != 0 {
        fmt.Printf("消息发送失败:%v\n", messageResp.Message)
        return
    }
    return
}

func processBroadMessage(conn net.Conn, body []byte) (err error) {

    var msg proto.BroadMessage
    err = json.Unmarshal(body, &msg)
    if err != nil {
        fmt.Printf("unmarshal failed, err:%v\n", err)
        return
    }

    fmt.Printf("%s:\n   %s\n\n", msg.Username, msg.Message)
    return
}

main.go

server/

tcp编程、socket编程、redis tcp编程、socket编程、redis

package main

import (
    "fmt"
    "net"
)

var (
    clientMgr *ClientMgr
)

func startServer(addr string) (l net.Listener, err error) {
    l, err = net.Listen("tcp", addr) //监听一个地址
    if err != nil {
        fmt.Printf("listen addr:%s failed, err:%v\n", addr, err)
        return
    }

    return
}

func main() {

    clientMgr = NewClientMgr(200)
    fmt.Printf("start server...\n")
    l, err := startServer("0.0.0.0:18080")
    if err != nil {
        fmt.Println("start server failed, err:", err)
        return
    }

    err = runServer(l)
    if err != nil {
        fmt.Println("run server failed, err:", err)
        return
    }

    fmt.Println("server is exied")
}

main.go

tcp编程、socket编程、redis tcp编程、socket编程、redis

package main

import (
    "encoding/json"
    "errors"
    "fmt"
    "net"

    "github.com/sherlockhua/goproject/day9/proto"
)

func runServer(l net.Listener) (err error) {
    fmt.Println("run server succ")
    for {
        conn, err := l.Accept()
        if err != nil {
            fmt.Println("accept failed, err:", err)
            continue
        }

        clientMgr.newClientChan <- conn
        go process(conn)
    }
}

func process(conn net.Conn) {

    defer func() {
        clientMgr.closeChan <- conn
        conn.Close()
    }()

    for {
        body, cmd, err := proto.ReadPacket(conn)
        if err != nil {
            fmt.Printf("read from conn failed, err:%v\n", err)
            return
        }

        err = processRequest(conn, body, cmd)
        if err != nil {
            fmt.Printf("processRequest[%v] failed, err:%v\n", cmd, err)
            return
        }
        /*
            var buf []byte = make([]byte, 512)
            n, err := conn.Read(buf)
            if err != nil {
                fmt.Printf("read from conn failed, err:%v\n", err)
                return
            }

            buf = buf[0:n]
            clientMgr.addMsg(buf)
        */
    }

}

func processRequest(conn net.Conn, body []byte, cmd int32) (err error) {
    switch cmd {
    case proto.CmdLoginRequest:
        err = processLogin(conn, body)
    case proto.CmdRegisterRequest:
        err = processRegister(conn, body)
    case proto.CmdSendMessageRequest:
        err = processMessage(conn, body)
    default:
        fmt.Printf("unsupport cmd[%v]\n", cmd)
        err = errors.New("unsupport cmd")
        return
    }

    return
}

func processLogin(conn net.Conn, body []byte) (err error) {

    fmt.Printf("begin process login request\n")
    var loginRequest proto.LoginRequest
    err = json.Unmarshal(body, &loginRequest)
    if err != nil {
        fmt.Printf("Unmarshal failed[%v]\n", err)
        return
    }

    fmt.Printf(" process login request:%+v\n", loginRequest)
    var loginResp proto.LoginResponse
    loginResp.Errno = 100
    loginResp.Message = "username or password not right"

    if loginRequest.Username == "admin" && loginRequest.Password == "admin" {
        loginResp.Errno = 0
        loginResp.Message = "success"
    }

    data, err := json.Marshal(loginResp)
    if err != nil {
        fmt.Printf("Marshal failed[%v]\n", err)
        return
    }

    fmt.Printf(" write login response %+v\n", loginResp)
    return proto.WritePacket(conn, proto.CmdLoginResponse, data) //将登录响应信息传递给客户端
}

func processRegister(conn net.Conn, body []byte) (err error) {
    return
}

//将消息广播出去
func processMessage(conn net.Conn, body []byte) (err error) {

    fmt.Printf("begin process login request\n")
    var messageReq proto.MessageRequest
    err = json.Unmarshal(body, &messageReq)
    if err != nil {
        fmt.Printf("Unmarshal failed[%v]\n", err)
        return
    }

    var broadMessage proto.BroadMessage
    broadMessage.Message = messageReq.Message
    broadMessage.Username = messageReq.Username

    body, err = json.Marshal(broadMessage)
    if err != nil {
        fmt.Printf("marshal failed, err:%v\n", err)
        return
    }

    packet := &proto.Packet{
        Cmd:  proto.CmdBroadMessage,
        Body: body,
    }

    clientMgr.addMsg(packet)
    return
}

server.go

tcp编程、socket编程、redis tcp编程、socket编程、redis

package main

import (
    "errors"
    "fmt"
    "net"
    "sync"
    "time"

    "github.com/sherlockhua/goproject/day9/proto"
)

type ClientMgr struct {
    //clientsMap维护所有客户端连接
    clientsMap    map[net.Conn]int
    maxClientNums int
    //msgChan用来保存客户端发送过来的消息
    msgChan       chan *proto.Packet
    newClientChan chan net.Conn
    closeChan     chan net.Conn
    lock          sync.RWMutex //读写锁
}

func NewClientMgr(maxClients int) *ClientMgr {
    mgr := &ClientMgr{
        clientsMap:    make(map[net.Conn]int, 1024),   //连接的客户端的map
        maxClientNums: maxClients,                     //最大连接客户端数
        msgChan:       make(chan *proto.Packet, 1000), //消息管道
        newClientChan: make(chan net.Conn, 1000),      //新连接的客户端
        closeChan:     make(chan net.Conn, 1000),      //关闭客户端连接
    }

    go mgr.run() //遍历所有客户端发送过来的消息,并广播到所有的其他客户端
    go mgr.procConn()
    return mgr
}

//监测新连接的客户端管道和需要关闭的客户端管道
func (c *ClientMgr) procConn() {
    for {
        select {
        case conn := <-c.newClientChan: //监测新连接的客户端的管道
            c.lock.Lock()
            c.clientsMap[conn] = 0 //将新连接的客户端写入处于连接的客户端的map
            c.lock.Unlock()
        case conn := <-c.closeChan: //监测需要关闭的客户端的管道
            c.lock.Lock()
            delete(c.clientsMap, conn) //从客户端连接的map中删除需要断开连接的客户端
            c.lock.Unlock()
        }
    }
}

//遍历所有客户端发送过来的消息,并广播到所有的其他客户端
func (c *ClientMgr) run() {
    for msg := range c.msgChan { //遍历消息管道
        c.transfer(msg) //广播消息
    }
}

//广播消息,将消息发送给当前在线的客户端
func (c *ClientMgr) transfer(msg *proto.Packet) {

    c.lock.RLock()
    defer c.lock.RUnlock()
    for client, _ := range c.clientsMap {
        err := c.sendToClient(client, msg) //将消息发送给当前在线的客户端
        if err != nil {
            continue
        }
    }

}

//发送消息给指定客户端
func (c *ClientMgr) sendToClient(client net.Conn, msg *proto.Packet) (err error) {
    return proto.WritePacket(client, msg.Cmd, msg.Body)
    /*
        var n int
        var sendBytes int
        msgLen := len(msg)
        for {
            n, err = client.Write(msg)
            if err != nil {
                fmt.Printf("send to client:%v failed, err:%v\n", client, err)
                client.Close()
                delete(c.clientsMap, client)
                return
            }
            sendBytes += n
            if sendBytes >= msgLen {
                break
            }
            msg = msg[sendBytes:]
        }
        return
    */
}

//将消息广播出去
func (c *ClientMgr) addMsg(msg *proto.Packet) (err error) {

    ticker := time.NewTicker(time.Millisecond * 10) //设置一个超时时间
    defer ticker.Stop()

    select {
    case c.msgChan <- msg:
        fmt.Printf("send to chan succ\n")
    case <-ticker.C:
        fmt.Printf("add msg timeout\n")
        err = errors.New("add msg timeout")
    }
    return
}

conn_mgr.go

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Java修道之路,问鼎巅峰,我辈代码修仙法力齐天
<center<fontcolor00FF7Fsize5face"黑体"代码尽头谁为峰,一见秃头道成空。</font<center<fontcolor00FF00size5face"黑体"编程修真路破折,一步一劫渡飞升。</font众所周知,编程修真有八大境界:1.Javase练气筑基2.数据库结丹3.web前端元婴4.Jav
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
3年前
34.TCP取样器
阅读文本大概需要3分钟。1、TCP取样器的作用   TCP取样器作用就是通过TCP/IP协议来连接服务器,然后发送数据和接收数据。2、TCP取样器详解!(https://oscimg.oschina.net/oscnet/32a9b19ba1db00f321d22a0f33bcfb68a0d.png)TCPClien
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
Easter79
Easter79
Lv1
今生可爱与温柔,每一样都不能少。
文章
2.8k
粉丝
5
获赞
1.2k