golang工程组件——redigo使用(redis协议,基本命令,管道,事务,发布订阅,stream)

news2024/11/20 9:47:22

redisgo

redis 与 client 之间采用请求回应模式,一个请求包对应一个回应包;但是也有例外,pub/sub 模 式下,client 发送 subscribe 命令并收到回应包后,之后被动接收 redis 的发布包;所以若需要使 用 pub/sub 模式,那么需要在 client 下创建额外一条连接与 redis 交互;

在这里插入图片描述

Redis 协议图

在这里插入图片描述

redis 协议采用特殊字符( \r\n )来分割有意义的数据,redis 使用的是二进制安全字符串(用长 度来标识字符串的长度),所以 redis 可以用来存储二进制数据(比如经过压缩算法加密的数 据);

例如

set key val
# "*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$3\r\nval\r\n"

命令执行

Do(commandName string, args ...interface{}) (reply interface{}, err error)

redisgo参数转换

Redis TypeGo Type
errorredis.Error
integerint64
simple stringstring
bulk string[]byte or nil if value not present
array[]interface{} or nil if value not present

参数转换处理

  • 单个string[]byte直接传递;
  • intfloat需要转成 string
  • 也可用redis.Args类型来处理,提供了方法 Add 添加单个元素;提供了方法 AddFlat添加了 对 map[interface{}]interface{}以及结构体的处理;

返回值转换处理

redisgo返回值有多种

  • 接收单个返回值处理
  • 接收多个不同类型的返回值处理

redis.Values+redis.Scan

  • 接收单个结构体

redis.Values+redis.ScanStruct

  • 接收多个结构体

redis.Values+redis.ScanSlice

案例

链接认证

package main


import (
    "fmt"
    "reflect"
    _ "reflect"

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

func main() {
    conn, err := redis.Dial("tcp", fmt.Sprintf("%s:%d", "10.65.143.2", 31923))
    if err != nil {
        panic(err)
    }
    defer (func() {
        fmt.Println("connection close")
        conn.Close()
    })()
    ///Sangfor-paas.237
    // 密码认证
    if _, authErr := conn.Do("AUTH", "paas.ss"); authErr != nil {
        fmt.Println("Redis auth error", authErr)
        return
    }
    
}

set,get,list操作

package main


import (
    "fmt"
    "reflect"
    _ "reflect"

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

func main() {
    conn, err := redis.Dial("tcp", fmt.Sprintf("%s:%d", "10.65.143.2", 31923))
    if err != nil {
        panic(err)
    }
    defer (func() {
        fmt.Println("connection close")
        conn.Close()
    })()
    ///Sangfor-paas.237
    // 密码认证
    if _, authErr := conn.Do("AUTH", "paas.ss"); authErr != nil {
        fmt.Println("Redis auth error", authErr)
        return
    }
    
    if false {
        conn.Do("set", "test_hello", 1)
        rpy, err := redis.Int(conn.Do("get", "test_hello"))
        if err != nil {
            panic(err)
        }
        fmt.Println(rpy, reflect.TypeOf(rpy))
    }

    if false {
        args := redis.Args{"test_list"}.Add("test1").Add("test2")
        conn.Do("lpush", args...)
        res, _ := redis.Strings(conn.Do("lrange", "test_list", 0, -1))
        fmt.Println(res, reflect.TypeOf(res))
    }


    if false {
        //conn.Do("del", "test_list")
        // 阻塞pop
        vals, err := redis.Strings(conn.Do("brpop", redis.Args{}.Add("test_list").Add(20)...))
        if err != redis.ErrNil {
            fmt.Println(vals, err)
        }
    }


    if false {
        // 按map形式返回
        vals, err := redis.StringMap(conn.Do("brpop", redis.Args{}.Add("test_list").Add(20)...))
        if err != redis.ErrNil {
            fmt.Println(vals, err)
        }
    }
}

变量读取

package main


import (
    "fmt"
    "reflect"
    _ "reflect"

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

func main() {
    conn, err := redis.Dial("tcp", fmt.Sprintf("%s:%d", "10.65.143.2", 31923))
    if err != nil {
        panic(err)
    }
    defer (func() {
        fmt.Println("connection close")
        conn.Close()
    })()
    ///Sangfor-paas.237
    // 密码认证
    if _, authErr := conn.Do("AUTH", "paas.ss"); authErr != nil {
        fmt.Println("Redis auth error", authErr)
        return
    }
    
    // 取元素到对应的变量上
    if false { // redis.Values + redis.Scan
        conn.Do("del", "list")
        conn.Do("lpush", "list", "aaabb", 100)
        vals, _ := redis.Values(conn.Do("lrange", "list", 0, -1))
        var name string
        var score int
        redis.Scan(vals, &score, &name)
        fmt.Println(name, score)
    }
    
}

结构体读取

package main


import (
    "fmt"
    "reflect"
    _ "reflect"

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

func main() {
    conn, err := redis.Dial("tcp", fmt.Sprintf("%s:%d", "10.65.143.2", 31923))
    if err != nil {
        panic(err)
    }
    defer (func() {
        fmt.Println("connection close")
        conn.Close()
    })()
    ///Sangfor-paas.237
    // 密码认证
    if _, authErr := conn.Do("AUTH", "paas.ss"); authErr != nil {
        fmt.Println("Redis auth error", authErr)
        return
    }
    
    if true {
        var p1, p2 struct {
            Name string `redis:"name"`
            Age  string `redis:"age"`
            Sex  string `redis:"sex"`
        }
        p1.Age = "18"
        p1.Name = "chaochaoyu"
        p1.Sex = "male"
        // age 18 name mark sex male

        args1 := redis.Args{}.Add("role:10001").AddFlat(&p1)
        if _, err := conn.Do("hmset", args1...); err != nil {
            fmt.Println(err)
            return
        }

        m := map[string]string{
            "name": "quxiansen",
            "age":  "20",
            "sex":  "female",
        }
        args2 := redis.Args{}.Add("role:10002").AddFlat(m)
        if _, err := conn.Do("hmset", args2...); err != nil {
            fmt.Println(err)
            return
        }


        for _, id := range []string{"role:10001", "role:10002"} {

            v, err := redis.Values(conn.Do("HGETALL", id))
            if err != nil {
                fmt.Println(err)
                return
            }

            if err := redis.ScanStruct(v, &p2); err != nil {
                fmt.Println(err)
                return
            }

            fmt.Printf("%+v\n", p2)
        }
    
}

reidsgo—管道以及事务

管道

redis pipeline 是一个客户端提供的,而不是服务端提供的;一次发送多条命令,减少与 redis-server 之间的网络交互;

在这里插入图片描述

type Conn interface {
    // Close closes the connection.
    Close() error
    // Err returns a non-nil value when the connection is not usable.
    Err() error
    // Do sends a command to the server and returns the received reply.
    Do(commandName string, args ...interface{}) (reply interface{}, err
    error)
    // Do = Send + Flush + Receive
    // Send writes the command to the client's output buffer.
    Send(commandName string, args ...interface{}) error
    // Flush flushes the output buffer to the Redis server.
    Flush() error
    // Receive receives a single reply from the Redis server
    Receive() (reply interface{}, err error)
}
代码使用
// 批量发送,批量接收
c.Send(cmd1, ...)
c.Send(cmd2, ...)
c.Send(cmd3, ...)
c.Flush() // 将上面的三个命令发送出去
c.Receive() // cmd1 的返回值
c.Receive() // cmd2 的返回值
c.Receive() // cmd3 的返回值
// 如果不需要关注返回值
c.Send(cmd1, ...)
c.Send(cmd2, ...)
c.Send(cmd3, ...)
c.Do("")
// 如果只关注最后一个命令的返回值
c.Send(cmd1, ...)
c.Send(cmd2, ...)
c.Do(cmd3, ...)

reids 网络事件处理

r在这里插入图片描述
edis 是单线程处理逻辑;网络事件处理以及命令处理都是在这个线程当中进行的; 每条连接都对应着一个读缓冲区,线程需要轮询每条连接,从连接的读缓冲区中分割出一个个有意义的数据包,每条连接的读缓冲区相当于一个队列;线程会交错执行活跃连接的命令

客户端批量发送测试
package main

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

func main() {
    c, err := redis.Dial("tcp", fmt.Sprintf("%s:%d", "10.65.143.2", 31923))
    if err != nil {
        panic(err)
    }
    defer (func() {
        fmt.Println("connection close")
        c.Close()
    })()
    ///paas.237
    // 密码认证
    if _, authErr := c.Do("AUTH", "paas.237"); authErr != nil {
        fmt.Println("Redis auth error", authErr)
        return
    }

    if false {
        c.Send("del", "set", "list", "zset")
        c.Send("sadd", "set", "aa", "bb", "cc")
        c.Send("lpush", "list", 10001, 10002, 10003)
        c.Send("smembers", "set")
        c.Send("lrange", "list", 0, -1)
        c.Flush()
        c.Receive() // del
        c.Receive() // sadd
        c.Receive() // lpush
        mbrs, err := redis.Strings(c.Receive()) // smembers
        if err != redis.ErrNil {
            fmt.Println(mbrs)
        }
        lsts, err := redis.Ints(c.Receive()) // lrange
        if err != redis.ErrNil {
            fmt.Println(lsts)
        }
    }

    if false {
        c.Send("del", "set", "list", "zset")
        c.Send("sadd", "set", "aa", "bb", "cc")
        c.Send("lpush", "list", 10001, 10002, 10003)
        // do里面有flush和所有receive
        c.Do("")
    }

    if true {
        rand.Seed(time.Now().UnixNano())
        c.Send("del", "set", "list", "zset")
        c.Send("sadd", "set", "aa", "bb", "cc")
        {
            args := redis.Args{}.Add("zset")
            args = args.Add(rand.Intn(100)).Add("xiaoming")
            args = args.Add(rand.Intn(100)).Add("xiaohong")
            args = args.Add(rand.Intn(100)).Add("xiaohuang")
            c.Send("zadd", args...)
        }
        {
            args := redis.Args{}.Add("zset")
            args = args.Add(0).Add(-1).Add("withscores")
            // 只关注最后一个的返回值
            vals, err := redis.Values(c.Do("zrange", args...))
            fmt.Printf("vals:%v\n", vals)
            if err != nil {
            panic(err)
        }
        //返回值是反过来的,name要放前面
            var rets []struct {
            Name  string
            Score int
        }
            if err = redis.ScanSlice(vals, &rets); err != nil {
            panic(err)
        }
            fmt.Println(rets)
        }
    }
}

事务

虽然redis是单线程,对于一条链接请求队列是线性执行的,如果有多条链接,那么redis线程执行命令的顺序是根据各个队列中先后顺序来的。如果要实现事务,需要保证执行事务包含的命令中间时不插入别的会干扰需要操作数据的命令。

redis事务操作

MULTI 开启事务,事务执行过程中,单个命令是入队列操作,直到调用 EXEC 才会一起执行;

MULTI

开启事务

EXEC

提交事务

DISCARD

取消事务

WATCH

检测key的变动,若在事务请求时,key变动则取消事务;在事务开启前调用,乐观锁实现(cas); 若被取消则事务返回 nil ;

WATCH score:10001
val = GET score:10001
MULTI
SET score:10001 val*2
EXEC
reids ACID特性
  • 原子性:事务是一个不可分割的工作单位,事务中的操作要么全部成功,要么全部失败;redis 不支持回滚;即使事务队列中的某个命令在执行期间出现了错误,整个事务也会继续执行下去,直 到将事务队列中的所有命令都执行完毕为止。
  • 一致性:事务使数据库从一个一致性状态到另外一个一致性状态;这里的一致性是指预期的一致性(有命令出错后续命令也会执行)而不是异常后的一致性;所以redis也不满足;
  • 隔离性:事务的操作不被其他用户操作所打断;redis命令执行是串行的,redis事务天然具备隔 离性;
  • 持久性:redis只有在 aof 持久化策略的时候,并且需要在 redis.conf 中 appendfsync=always 才具备持久性;实际项目中几乎不会使用 aof 持久化策略;

lua实现原子性操作

lua 脚本实现原子性;

redis中加载了一个 lua 虚拟机;用来执行 redis lua 脚本;redis lua 脚本的执行是原子性的;当 某个脚本正在执行的时候,不会有其他命令或者脚本被执行;

lua 脚本当中的命令会直接修改数据状态;

注意:如果项目中使用了 lua 脚本,不需要使用上面的事务命令;

# 从文件中读取 lua脚本内容
cat test1.lua | redis-cli script load --pipe
# 加载 lua脚本字符串 生成 sha1
> script load 'local key = KEYS[1];local s = redis.call("get",key);redis.call("set", key, s*2);return s*2'
"8f7d021dcc386a422e0febe38befdc6084357610"
# 检查脚本缓存中,是否有该 sha1 散列值的lua脚本
> script exists "8f7d021dcc386a422e0febe38befdc6084357610"
1) (integer) 1
# 清除所有脚本缓存
> script flush
OK
# 如果当前脚本运行时间过长,可以通过 script kill 杀死当前运行的脚本
> script kill
(error) NOTBUSY No scripts in execution right now.
执行lua脚本
EVAL
# 测试使用
EVAL script numkeys key [key ...] arg [arg ...]
EVALSHA
# 线上使用
EVALSHA sha1 numkeys key [key ...] arg [arg ...]

eg:

script load 'local key = KEYS[1];local s = redis.call("get",key);redis.call("set", key, s*2);return s*2'
"dbb7d4a2a615df353820f35ffa710a45fa1c4ec0"
set score 100
# 有一个参数
evalsha dbb7d4a2a615df353820f35ffa710a45fa1c4ec0 1 score
应用
# 1: 项目启动时,建立redis连接并验证后,先加载所有项目中使用的lua脚本(script load);
# 2: 项目中若需要热更新,通过redis-cli script flush;然后可以通过订阅发布功能通知所有服务器重新加载lua脚本;
# 3:若项目中lua脚本发生阻塞,可通过script kill暂停当前阻塞脚本的执行;

事务建议用lua脚本

go操作reids执行lua脚本

double.lua

local key = KEYS[1]
local default = ARGV[1]
if redis.call("exists", key) == 0 then
    redis.call("set", key, default)
end
local val = redis.call("get", key)
redis.call("set", key, val*2)
return val*2

script.go

package main

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

func main() {
    c, err := redis.Dial("tcp", fmt.Sprintf("%s:%d", "10.65.143.2", 31923))
    if err != nil {
        panic(err)
    }
    defer (func() {
        fmt.Println("connection close")
        c.Close()
    })()
    ///ssr-paas.237
    // 密码认证
    if _, authErr := c.Do("AUTH", "sss-paas.237"); authErr != nil {
        fmt.Println("Redis auth error", authErr)
        return
    }
    var data []byte
    data, err = ioutil.ReadFile("script/double.lua")
    if err != nil {
        fmt.Println("load double.lua error")
        return
    }
    // 设置一个参数
    script := redis.NewScript(1, string(data))
    script.Load(c)

    if true {
        c.Send("set", "score", 1000)
        rpy, _ := redis.Int(script.Do(c, "score"))
        fmt.Println(rpy)
    }
    if true {
        rpy, _ := redis.Int(script.Do(c, "bbb", 500))
        fmt.Println(rpy)
    }

}

发布订阅

为了支持消息的多播机制,redis 引入了发布订阅模块;

生产者生产一次消息,由redis负责将消息复制到多个消息队列中,每个消息队列由相应的消费者进行消费;

它是分布式系统中常用的一种解耦方式,用于将多个消费者的逻辑进行拆分。多个消费者的逻辑就可以放到不同的子系统中完成;

在这里插入图片描述

# 订阅频道
subscribe 频道
psubscribe new.car
# 订阅模式频道
psubscribe 频道
psubscribe new.*
# 取消订阅频道
unsubscribe 频道
# 取消订阅模式频道
punsubscribe 频道
# 发布具体频道或模式频道的内容
publish 频道 内容
# 客户端收到具体频道内容
message 具体频道 内容
# 客户端收到模式频道内容
pmessage 模式频道 具体频道 内容
subscribe news.it news.showbiz news.car
psubscribe news.*
publish new.showbiz 'aaa bbb ccc'

go订阅频道实现

package main

import (
	"fmt"

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

func main() {
	sp, err := redis.Dial("tcp", fmt.Sprintf("%s:%d", "127.0.0.1", 6379))
	if err != nil {
		return
	}
	defer sp.Close()
	spc := redis.PubSubConn{Conn: sp}
	if false {
		// c.Do  =  c.Send + c.Flush + c.Receive
		spc.Subscribe("news.it")  // send + flush
		for {
			switch v := spc.Receive().(type) {
			case redis.Message:
				fmt.Printf("%s: message: %s\n", v.Channel, v.Data)
			case redis.Subscription: // 是否注册成功的消息
				fmt.Printf("%s: %s %d\n", v.Channel, v.Kind, v.Count)
			case error:
				return
			}
		}
	}
	if false {
		// it showbz cars
		spc.PSubscribe("news.*")
		for {
			switch v := spc.Receive().(type) {
			case redis.PMessage:
				fmt.Printf("%s: pmessage: %s\n", v.Channel, v.Data)
			case redis.Subscription: // 是否注册成功的消息
				fmt.Printf("%s: %s %d\n", v.Channel, v.Kind, v.Count)
			case error:
				return
			}
		}
	}
}

注意

发布订阅功能一般要区别命令连接重新开启一个连接;因为命令连接严格遵循请求回应模式;而pubsub能收到redis主动推送的内容;所以实际项目中如果支持pubsub的话,需要另开一条连接用于处理发布订阅;

在这里插入图片描述

缺点

发布订阅的生产者传递过来一个消息,redis会直接找到相应的消费者并传递过去;假如没有消费者,消息直接丢弃;假如开始有2个消费者,一个消费者突然挂掉了,另外一个消费者依然能收到消息,但是如果刚挂掉的消费者重新连上后,在断开连接期间的消息对于该消费者来说彻底丢失了;

另外,redis停机重启,pubsub的消息是不会持久化的,所有的消息被直接丢弃;

stream

*在这里插入图片描述
多播可持久化队列。

  • 一个消息链表将加入的消息都串起来,每个消息都有一个唯一的消息ID和对应的内容;消息都是持久化的,redis 重启后,内容还在;

  • 每个 stream 对象通过一个 key 来唯一索引;每个 stream 都可以挂多个消费组(consumergroup),每个消费组会有个游标 last_delivered_id 在 stream 数组之上往前移动,表示当前消费组已经消费到哪条消息了。

  • stream 在第一次使用 xadd 命令后自动创建;而消费组不会自动创建,需要通过命令 xgroup create 进行创建,并且需要指定从 stream 的某个消息 ID 开始消费;

  • 每个消费组都是相互独立的,互相不受影响;也就是同一份 stream 内部的消息会被每个消费组都消费到;

  • 同一个消费组可以挂接多个消费者,这些消费者之间是竞争关系,任意一个消费者读取了消息都会使游标往前移动;

  • 消费者内部会有一个状态变量 pending_ids,它记录了当前已经被客户端读取,但是还没有 ack 的消息。当客户端 ack 一条消息后,pending_ids 将会删除该消息 ID;它用来确保客户端至少消费了消息一次,而不会在网络传输的中途丢失了而没有被处理;

命令
# 向 stream 追加消息, ID可以填* 让redis生成全局唯一
XADD key ID field string [field string ...]
# 从 stream 中删除消息
XDEL key ID [ID ...]
# 获取 stream 中消息列表,会自动过滤已经删除的消息
XRANGE key start end [COUNT count]
# 获取 stream 消息长度
XLEN key
# 删除 stream 中所有消息
DEL key
# 独立消费
XREAD [COUNT count] [BLOCK milliseconds] STREAMS key [key ...] ID [ID ...]
# 创建消费者
XGROUP [CREATE key groupname id-or-$] [SETID key id-or-$] [DESTROY key groupname] [DELCONSUMER key groupname consumername]
# 消费消息
XREADGROUP GROUP group consumer [COUNT count] [BLOCK milliseconds] STREAMS key [key ...] ID [ID ...]
# > 意味着消费者希望只接收从未发送给任何其他消费者的消息。最新的消息
# 任意其他id 发送待处理的消息
# 确认消费消息
XACK key group ID [ID ...]
#创建stream_test stream 并由redis生成id
XADD stream_test * message "hello word"
"1626511386012-o"
#创建消费组g1 从0开始消费
XGROUP CREATE stream g1 0-0

XLEN stream
(integer) 1
xrange stream - +

#生成一个消费者,消费一个
xreadgroup group g1 consumer1 count 1 streams stream_test 0 # > 是获取最新消息, 0代表是任意ID

# ACK该消息
xack stream_test g1 "1626511386012-o"

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1195838.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ROS 学习应用篇(三)话题Topic学习之自定义话题消息的类型的定义与调用

自定义消息类型的定义 Person.msg文件的定义(数据接口文件的定义) 创建msg文件 首先在功能包下新建msg文件夹,接着在该文件夹下创建文件。 定义msg文件内容 一个消息最重要的就是数据结构类型。这就需要引入一个msg文件,用于…

FM9918R系列-副边同步整流芯片

产品描述: FM9918R 系列是集成了 MOSFET 的同步整流二极管,用于替换反激式转换器的整流二极管,能够显著减少发热,提升系统的转换效率。IC 通过检测集成 MOSFET 的源漏电压来决定其开关状态。 FM9918R 系列能够兼容连续模式、非连续…

积分上限函数

定积分的形式 a:积分下限 b:积分上限 定积分的值与积分变量无关 积分上限函数的形式 x:自变量 t:积分变量 积分上限是变量,积分下限是常数 定积分的几何意义 x轴所围成面积 x轴以上面积为正 x轴以下面积为负 积分…

【华为数据通信】BFD是什么?

一、概述 BFD提供了一个通用的、标准化的、介质无关的、协议无关的快速故障检测机制,有以下两大优点: 对相邻转发引擎之间的通道提供轻负荷、快速故障检测。用单一的机制对任何介质、任何协议层进行实时检测。 BFD是一个简单的“Hello”协议。两个系统…

k8s系列-kuboard 该操作平台的使用操作

文章目录 一、相关平台,以及账号和密码镜像打包服务器仓库地址K8s平台数据库mysql 二、平台概述1.集群导入2.集群管理3.名称空间4.访问控制授权5.集群用户操作审计 三、kuboard平台操作手册一、部署服务操作1.名称空间部署2.工作负载部署 一、相关平台,以…

【中国知名企业高管团队】系列64:燕京啤酒

昨天,华研荟介绍了中国可能是最大的啤酒企业:青岛啤酒。接下来介绍总部位于北京的另一家啤酒企业——燕京啤酒。 当我想寻找官方的信息时发现坏了——燕京啤酒的官方PC网站竟然无法打开!倒是设立了一系列的微信公众号,看第一个官…

cmake_install.cmake这个文件有什么用

2023年11月11日,周六上午 目录 简介 举例说明 简介 cmake_install.cmake是由 CMake 自动生成的一个脚本文件,用于在安装过程中执行各种安装操作。 请注意,cmake_install.cmake件是自动生成的,无需手动编辑或修改它。如果需要自…

postman 参数化使用csv导入外部数据

一、参数化脚本入参 postman中变量用{{变量名}}表示变量 二、创建外部数据文件 csv文件逗号分割多个变量和对应值注意编码格式必须为utf-8 三、run collection导入数据文件 四、设置运行参数run 浏览数据 可调试设置迭代次数:防止批量出错,可先设定…

响应式理工实验外语学校学院网站模板源码

模板信息: 模板编号:11862 模板编码:UTF8 模板颜色:蓝色 模板分类:学校、教育、培训、科研 适合行业:学校类企业 模板介绍: 本模板自带eyoucms内核,无需再下载eyou系统&#xff0c…

KCC@广州与 TiDB 社区联手—广州开源盛宴

10月21日,KCC广州与 TiDB 社区联手,在海珠区保利中悦广场 29 楼召开了一次难忘的开源盛宴。这不仅仅是 KCC广州的又一次线下见面,更代表着与 TiDB 社区及广州技术社区的首次深度合作。 活动的策划与组织由 KCC广州负责人 - 惠世冀、PingCAP 的…

spring boot configuration annotation processor notconfigured解决方法

spring boot configuration annotation processor notconfigured解决方法 一、问题描述二、解决方法 一、问题描述 我在使用ConfigurationProperties注解的时候idea出现提示信息spring boot configuration annotation processor notconfigured,但是却不影响程序的运…

Clickhouse学习笔记(4)—— Clickhouse SQL

insert insert操作和mysql一致 标准语法:insert into [table_name] values(…),(….)从表到表的插入:insert into [table_name] select a,b,c from [table_name_2] update 和 delete ClickHouse 提供了 Delete 和 Update 的能力,这类操作…

CodeWhisperer 史上最强大的 AI 编程助手!!

最近用了一个叫 CodeWhisperer 的插件,这个软件对于来说开发人员,插件有好多实用的功能,能有效减少我们的重复性工作,让编码更高效,代码质量也提升了很多。 CodeWhisperer 简介 CodeWhisperer 是亚⻢逊出品的一款基于…

GZ038 物联网应用开发赛题第3套

2023年全国职业院校技能大赛 高职组 物联网应用开发 任 务 书 (第3套卷) 工位号:______________ 第一部分 竞赛须知 一、竞赛要求 1、正确使用工具,操作安全规范; 2、竞赛过程中如有异议,可向现场考评…

C++---类的优化构造

首先,先介绍以下拷贝构造和构造的区别。 拷贝构造Date(Date& d)初始化:用一个实例对象去初始化一个未初始化的对象, 例:如果d1未被实例化,则Date d1 d2; 也会被编译器认为拷贝构造&#…

Springboot+vue的企业资产管理系统(有报告)。Javaee项目,springboot vue前后端分离项目。

演示视频: Springbootvue的企业资产管理系统(有报告)。Javaee项目,springboot vue前后端分离项目。 项目介绍: 本文设计了一个基于Springbootvue的前后端分离的企业资产管理系统,采用M(model&a…

Shopee店铺支付方式有哪些? Shopee自养号测评提高产品曝光率的有效方法

Shopee作为一家领先的电子商务平台,如何优化你的Shopee店铺商品再结合自养号测评,并提高曝光率和销售能力,其中支付是至关重要一环之一。 虾皮为卖家提供了多种收款方式,包括在线支付、虚拟账户余额和线下支付。市场适应性也是卖…

2023最新版本 从零基础入门C++与QT(学习笔记) -2- 命名空间的使用

🎏在不同的命名空间变量名可相同 创建(如下方代码块) 🎄分析一下构成 🎈-1- namespace 关键字命名空间 🎈-2- wm9 空间名称 🎈-3-括号里边正常定义变量即可 namespace wm9 {int a 99;char b A;float c 9.99;char…

概念解析 | 认知战:21世纪的新战争形式

注1:本文系“概念解析”系列之一,致力于简洁清晰地解释、辨析复杂而专业的概念。本次辨析的概念是:认知战 概念解析 | 认知战:21世纪的新战争形式 背景介绍 随着科技的飞速发展,信息技术深深渗透到各个领域,信息已经成为一种重要的战略资源。传统的战争形式主要依靠武力进攻,…

短视频矩阵seo系统源码搭建----技术定制化开发

一、需要遵循一下技术开发步骤: 1. 确定需求和功能:明确系统的主要目标和需要实现的功能,包括关键词研究、短视频制作、外链建设、数据分析、账号设置优化等方面。 2. 设计系统架构:根据需求和功能确定系统的架构,包…