GoLong的学习之路,进阶,Redis

这个redis和上篇rabbitMQ一样,在之前我用Java从原理上进行了剖析,这里呢,我做项目的时候,也需要用到redis,所以这里也将去从怎么用的角度去写这篇文章。

文章目录

  • 安装redis以及原理
  • redis概念
    • redis的应用场景有很多
    • redis常用的数据类型
  • 使用redis
    • 连接
    • 普通连接模式
    • TLS连接模式
    • Redis Sentinel模式
    • Redis Cluster模式
    • 操作数据
      • 特殊
    • 数据结构的处理
    • Pipeline(缓冲读写)
      • 使用`Pipelined` 方法,它会在函数退出时调用` Exec`
    • 事务
      • Watch
    • GET 、SET和WATCH 的示例

安装redis以及原理

安装redis其实很简单,我这里用的是阿里云,所以给出阿里云的安装文件文档,但是在没有用阿里云的时候,其实也有其他的方式。

这里献上,安装redis及其原理的一篇文章,这篇文章是以Java来讲解的,但是本质是没有区别的。想看原理的朋友可以看这里。

redis概念

Redis是一个开源的内存数据库,Redis提供了多种不同类型的数据结构,很多业务场景下的问题都可以很自然地映射到这些数据结构上。除此之外,通过复制、持久化和客户端分片等特性,我们可以很方便地将Redis扩展成一个能够包含数百GB数据、每秒处理上百万次请求的系统。

redis的应用场景有很多

  • 缓存系统,减轻主数据库(MySQL)的压力。
  • 计数场景,比如微博、抖音中的关注数和粉丝数。
  • 热门排行榜,需要排序的场景特别适合使用ZSET。
  • 利用 LIST 可以实现队列的功能。
  • 利用 HyperLogLog 统计UV、PV等数据。
  • 使用 geospatial index 进行地理位置相关查询。
  • 会话存储:保存用户的登录信息,原本的session里存储会话,只支持一次,诺是在分布式下,会使得,用户登录需要将这些分布式服务器都登录才可以,而redis就能很好的解决这个问题。
  • 存储普通缓存:列如详情页等数据的缓存信息存储。
  • 实现分布式锁:redis 可以非常方便的实现微服务选的分布式锁,redis 天然支持分布式服务。也可以使用zookeeper实现分布式锁
  • 简单的消息队列:redis 自身提供的发布订阅模式,可以用来实现简单的消息队列

redis常用的数据类型

  • String:(字符串):
    常见的使用场景是存储session信息,存储缓存信息如详情页缓存,存储整数信息可使用incr实现整数 + 1和使用decr实现整数 - 1。
  • list:(列表类型):
    常见使用场景式简单的消息队列,存储某项列表数据。
  • Hash:(哈希表):
    常见使用场景是存储session信息存储商品的购物车,购物车非常适合用哈希字典表示,使用人员唯一编号作为字典的key value值,可以存储商品的ID和数量等信息,存储详情页信息。
  • Set:(集合):
    一个无序并唯一的兼职集合,它的常见使用场景是实现关注功能,比如关注我的人,我关注的人使用集合存储可以保证人员不重复。
  • Sorted Set :(有序集合):
    相比于set集合类型多了一个排序属性的 score (分值),它的常见使用场景是可以用来存储排名信息,关注列表功能,这样就可以根据关注实现排序展示了。

带范围查询的排序集合(sorted set)、bitmap、hyperloglog、带半径查询的地理空间索引(geospatial index)和流(stream)等数据结构。

使用redis

注意:
redis 7对应 v9redis 6 对应 v8

安装v8版本:

go get github.com/redis/go-redis/v8

导入:

import "github.com/redis/go-redis/v9"

连接

你敢信这东西的链接手法多种多样

普通连接模式

rdb := redis.NewClient(&redis.Options{
	Addr:     "localhost:6379",
	Password: "", // 密码
	DB:       0,  // 数据库
	PoolSize: 20, // 连接池大小
})

使用 redis.ParseURL 函数从表示数据源的字符串中解析得到 Redis 服务器的配置信息

opt, err := redis.ParseURL("redis://<user>:<pass>@localhost:6379/<db>")
if err != nil {
	panic(err)
}
rdb := redis.NewClient(opt)

TLS连接模式

使用的是 TLS 连接方式,则需要使用 tls.Config 配置

rdb := redis.NewClient(&redis.Options{
	TLSConfig: &tls.Config{
		MinVersion: tls.VersionTLS12,
		// Certificates: []tls.Certificate{cert},
    	// ServerName: "your.domain.com",
	},
})

Redis Sentinel模式

搭建模式:哨兵模式

下面的命令连接到由Redis Sentinel管理的 Redis 服务器

rdb := redis.NewFailoverClient(&redis.FailoverOptions{
    MasterName:    "master-name",
    SentinelAddrs: []string{":9126", ":9127", ":9128"},
})

在这里插入图片描述

Redis Cluster模式

搭建模式:集群模式

命令连接到 Redis Clustergo-redis 支持按延迟或随机路由命令。

rdb := redis.NewClusterClient(&redis.ClusterOptions{
    Addrs: []string{":7000", ":7001", ":7002", ":7003", ":7004", ":7005"},

    // 若要根据延迟或随机路由命令,请启用以下命令之一
    // RouteByLatency: true,
    // RouteRandomly: true,
})

在这里插入图片描述

例子:

package main

import (
	"fmt"
	"github.com/go-redis/redis"
)

// 声明一个全局的redisDb变量
var redisDb *redis.Client

// 根据redis配置初始化一个客户端
func initClient() (err error) {
	redisDb = redis.NewClient(&redis.Options{
		Addr:     "localhost:6379", // redis地址
		Password: "",               // redis密码,没有则留空
		DB:       0,                // 默认数据库,默认是0
	})

	//通过 *redis.Client.Ping() 来检查是否成功连接到了redis服务器
	_, err = redisDb.Ping().Result()
	if err != nil {
		return err
	}
	return nil
}

func main() {
	err := initClient()
	if err != nil {
		//redis连接错误
		panic(err)
	}
	fmt.Println("Redis连接成功")
}

Options参数详解

  • Network :网络类型 tcp 或者 unix, 默认是 tcp。
  • Addr :redis地址,格式 host:port
  • OnConnect :新建一个redis连接的时候,会回调这个函数
  • Password : redis密码,redis server没有设置可以为空
  • DB : redis数据库,序号从0开始,默认是0,可以不用设置
  • MaxRetries :redis操作失败最大重试次数,默认不重试。
  • MinRetryBackoff :最小重试时间间隔,默认是 8ms ; -1 表示关闭
  • MaxRetryBackoff:最大重试时间间隔,默认是 512ms; -1 表示关闭.
  • DialTimeout :redis连接超时时间,默认是 5 秒.
  • ReadTimeout :socket读取超时时间,默认 3 秒.
  • WriteTimeout :socket写超时时间
  • PoolSize :redis连接池的最大连接数,默认连接池大小等于 cpu个数 * 10
  • MinIdleConns :redis连接池最小空闲连接数。
  • MaxConnAge :redis连接最大的存活时间,默认不会关闭过时的连接.
  • PoolTimeout :从redis连接池获取一个连接之后,连接池最多等待这个拿出去的连接多长时间,默认是等待 ReadTimeout + 1 秒.
  • IdleTimeout :edis连接池多久会关闭一个空闲连接,默认是 5 分钟. -1 则表示关闭这个配置项
  • IdleCheckFrequency :多长时间检测一下,空闲连接,默认是 1 分钟. -1 表示关闭空闲连接检测
  • readOnly :只读设置,如果设置为true, redis只能查询缓存不能更新。

操作数据

redis基本的key/value操作,指的是针对value值的类型为字符串或者数字类型的读写操作

  • Set:给数据库中名称为key的string赋予值value,并设置失效时间,0为永久有效
  • Get:查询数据库中名称为key的value值
  • GetSet:设置一个key的值,并返回这个key的旧值
  • SetNX:如果key不存在,则设置这个key的值,并设置key的失效时间。如果key存在,则设置不生效
  • MGet:批量查询key的值。比如redisDb.MGet(“name1”,“name2”,“name3”)
  • MSet:批量设置key的值。redisDb.MSet(“key1”, “value1”,“key2”, “value2”,“key3”, “value3”)
  • Incr:Incr函数每次加一,key对应的值必须是整数或nil,否则会报错incr key1: ERR value is not an integer or out of range
  • ncrBy:IncrBy函数,可以指定每次递增多少,key对应的值必须是整数或nil
  • IncrByFloat:IncrByFloat函数,可以指定每次递增多少,跟IncrBy的区别是累加的是浮点
  • Decr:Decr函数每次减一,key对应的值必须是整数或nil,否则会报错
  • DecrBy:DecrBy,可以指定每次递减多少,key对应的值必须是整数或nil
  • Del:删除key操作,支持批量删除 redisDb.Del(“key1”,“key2”,“key3”)
  • Expire:设置key的过期时间,单位秒
  • Append:给数据库中名称为key的string值追加value

以上是主要的方法。

例子:

可以尝试先填写数据然后读取。

// doCommand go-redis基本使用示例
func doCommand() {
	ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
	defer cancel()

	// 执行命令获取结果
	val, err := rdb.Get(ctx, "key").Result()
	fmt.Println(val, err)

	// 先获取到命令对象
	cmder := rdb.Get(ctx, "key")
	fmt.Println(cmder.Val()) // 获取值
	fmt.Println(cmder.Err()) // 获取错误

	// 直接执行命令获取错误
	err = rdb.Set(ctx, "key", 10, time.Hour).Err()

	// 直接执行命令获取值
	value := rdb.Get(ctx, "key").Val()
	fmt.Println(value)
}

特殊

任意方法:Do方法
go-redis 还提供了一个执行任意命令或自定义命令的 Do 方法,特别是一些 go-redis 库暂时不支持的命令都可以使用该方法执行。

这个方法的作用是向Redis服务器发送一个命令并返回执行结果。

源码接口

func (c *Client) Do(ctx context.Context, cmd string, args ...interface{}) *Cmd
// doDemo rdb.Do 方法使用示例
func doDemo() {
	ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
	defer cancel()

	// 直接执行命令获取错误
	err := rdb.Do(ctx, "set", "key", 10, "EX", 3600).Err()
	fmt.Println(err)

	// 执行命令获取结果
	val, err := rdb.Do(ctx, "get", "key").Result()
	fmt.Println(val, err)
}

其中:

  • ctx是上下文,用于控制请求的生命周期。
  • cmd是要执行的Redis命令,比如"GET"、"SET"等。
  • args是传递给Redis命令的参数,比如键名、数值等。

Do方法的返回值是一个*Cmd类型,它代表了一个异步执行的命令,并且可以用来获取执行结果。

使用Do方法,你可以向Redis发送各种命令,并通过返回的*Cmd对象获取执行结果,比如获取值、处理错误等。这使得在Golang中使用Redis变得非常方便和灵活。

上面这个例子我是用的官方文档中的。如果有朋友试了,就会发现,这东西只能指向第一个key:value,第二个看存不存在,存在就不报错,不存在就报错,然后第二个直接无法写入。这个主打的就是一个扯淡。真不知这玩意出来时干啥的。

redis.Nil

go-redis 库提供了一个 redis.Nil 错误来表示 Key 不存在的错误。因此在使用go-redis时需要注意对返回错误的判断。在某些场景下我们应该区别处理redis.Nil和其他不为 nil 的错误。

// getValueFromRedis redis.Nil判断
func getValueFromRedis(key, defaultValue string) (string, error) {
	ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
	defer cancel()

	val, err := rdb.Get(ctx, key).Result()
	if err != nil {
		// 如果返回的错误是key不存在
		if errors.Is(err, redis.Nil) {
			return defaultValue, nil
		}
		// 出其他错了
		return "", err
	}
	return val, nil
}

数据结构的处理

剩下的其实也就是对整这个数据结构的处理,包括string,hash,等集合类的操作。需要详细的说明的可以看这篇:非常详细的方法使用文章

Pipeline(缓冲读写)

Redis Pipeline 允许通过使用单个 client-server-client 往返执行多个命令来提高性能。区别于一个接一个地执行100个命令,你可以将这些命令放入 pipeline 中,然后使用1次读写操作像执行单个命令一样执行它们。这样做的好处是节省了执行命令的网络往返时间(RTT)。

	pipe := rdb.Pipeline()
	//添加计数器
	incr := pipe.Incr(cxt, "pipeline_counter")
	//设置过期时常
	pipe.Expire(cxt, "pipeline_counter", time.Hour)
	//Exec是将管道中缓冲的所有命令发送到redis-server。
	cmds, err := pipe.Exec(cxt)
	if err != nil {
		panic(err)
	}
	fmt.Println(cmds)

	fmt.Println(incr.Val())

Exec :Exec是将管道中缓冲的所有命令发送到redis-server。
Expire:设置过期时常
Incr:计数器
Discard:表示丢弃缓存中所有尚未执行的命令
Process:把要执行的命令放入流水线缓冲区中
Do:如果某个Redis命令还不支持,你可以使用Do来执行它。执行任何命令的API
Len:获取管道中尚未执行的命令的数量

使用Pipelined 方法,它会在函数退出时调用 Exec

var incr *redis.IntCmd

cmds, err := rdb.Pipelined(ctx, func(pipe redis.Pipeliner) error {
	incr = pipe.Incr(ctx, "pipelined_counter")
	pipe.Expire(ctx, "pipelined_counter", time.Hour)
	return nil
})
if err != nil {
	panic(err)
}

// 在pipeline执行后获取到结果
fmt.Println(incr.Val())

我们可以遍历pipeline命令的返回值依次获取每个命令的结果,示例代码中使用pipiline一次执行了100个 Get 命令,在pipeline 执行后遍历取出100个命令的执行结果。

cmds, err := rdb.Pipelined(ctx, func(pipe redis.Pipeliner) error {
	for i := 0; i < 100; i++ {
		pipe.Get(ctx, fmt.Sprintf("key%d", i))
	}
	return nil
})
if err != nil {
	panic(err)
}

for _, cmd := range cmds {
    fmt.Println(cmd.(*redis.StringCmd).Val())
}

一次性执行多个命令的场景下,就可以考虑使用 pipeline 来优化。

事务

Redis 是单线程执行命令的,因此单个命令始终是原子的,但是来自不同客户端的两个给定命令可以依次执行,例如在它们之间交替执行。但是,Multi/exec能够确保在multi/exec两个语句之间的命令之间没有其他客户端正在执行命令。

在这种场景我们需要使用 TxPipelineTxPipelined 方法将 pipeline 命令使用 MULTI EXEC包裹起来。

// TxPipeline demo
pipe := rdb.TxPipeline()
incr := pipe.Incr(ctx, "tx_pipeline_counter")
pipe.Expire(ctx, "tx_pipeline_counter", time.Hour)
_, err := pipe.Exec(ctx)
fmt.Println(incr.Val(), err)

// TxPipelined demo
var incr2 *redis.IntCmd
_, err = rdb.TxPipelined(ctx, func(pipe redis.Pipeliner) error {
	incr2 = pipe.Incr(ctx, "tx_pipeline_counter")
	pipe.Expire(ctx, "tx_pipeline_counter", time.Hour)
	return nil
})
fmt.Println(incr2.Val(), err)

相当于

MULTI
INCR pipeline_counter
EXPIRE pipeline_counts 3600
EXEC

Watch

搭配 WATCH命令来执行事务操作

从使用WATCH命令监视某个 key 开始,直到执行EXEC命令的这段时间里,如果有其他用户抢先对被监视的 key 进行了替换、更新、删除等操作,那么当用户尝试执行EXEC的时候,事务将失败并返回一个错误,用户可以根据这个错误选择重试事务或者放弃事务

源码接口

Watch(fn func(*Tx) error, keys ...string) error

Watch 方法搭配 TxPipelined 的使用示例:

// watchDemo 在key值不变的情况下将其值+1
func watchDemo(ctx context.Context, key string) error {
	return rdb.Watch(ctx, func(tx *redis.Tx) error {
		n, err := tx.Get(ctx, key).Int()
		if err != nil && err != redis.Nil {
			return err
		}
		// 假设操作耗时5秒
		// 5秒内我们通过其他的客户端修改key,当前事务就会失败
		time.Sleep(5 * time.Second)
		_, err = tx.TxPipelined(ctx, func(pipe redis.Pipeliner) error {
			pipe.Set(ctx, key, n+1, time.Hour)
			return nil
		})
		return err
	}, key)
}

将上面的函数执行并打印其返回值,如果我们在程序运行后的5秒内修改了被 watch 的 key 的值,那么该事务操作失败,返回redis: transaction failed错误。

GET 、SET和WATCH 的示例

go-redis 官方文档中使用 GETSETWATCH命令实现一个 INCR 命令的完整示例

// 此处rdb为初始化的redis连接客户端
const routineCount = 100

// 设置5秒超时
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

// increment 是一个自定义对key进行递增(+1)的函数
// 使用 GET + SET + WATCH 实现,类似 INCR
increment := func(key string) error {
	txf := func(tx *redis.Tx) error {
		// 获得当前值或零值
		n, err := tx.Get(ctx, key).Int()
		if err != nil && err != redis.Nil {
			return err
		}

		// 实际操作(乐观锁定中的本地操作)
		n++

		// 仅在监视的Key保持不变的情况下运行
		_, err = tx.TxPipelined(ctx, func(pipe redis.Pipeliner) error {
			// pipe 处理错误情况
			pipe.Set(ctx, key, n, 0)
			return nil
		})
		return err
	}

	// 最多重试100次
	for retries := routineCount; retries > 0; retries-- {
		err := rdb.Watch(ctx, txf, key)
		if err != redis.TxFailedErr {
			return err
		}
		// 乐观锁丢失
	}
	return errors.New("increment reached maximum number of retries")
}

// 开启100个goroutine并发调用increment
// 相当于对key执行100次递增
var wg sync.WaitGroup
wg.Add(routineCount)
for i := 0; i < routineCount; i++ {
	go func() {
		defer wg.Done()

		if err := increment("counter3"); err != nil {
			fmt.Println("increment error:", err)
		}
	}()
}
wg.Wait()
n, err := rdb.Get(ctx, "counter3").Int()
fmt.Println("最终结果:", n, err)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

第二证券:股票几点到几点开盘?

作为股民或许投资者&#xff0c;我们都知道股票是每天都有开盘和收盘时间的。但是&#xff0c;关于股票的开盘时间&#xff0c;很多人并不是很清楚&#xff0c;特别是初学者。在本文中&#xff0c;我们将从多个视点分析股票开盘时间&#xff0c;并为大家供给一些有用的信息。 …

深度学习毕设项目 基于深度学习的植物识别算法 - cnn opencv python

文章目录 0 前言1 课题背景2 具体实现3 数据收集和处理3 MobileNetV2网络4 损失函数softmax 交叉熵4.1 softmax函数4.2 交叉熵损失函数 5 优化器SGD6 最后 0 前言 &#x1f525; 这两年开始毕业设计和毕业答辩的要求和难度不断提升&#xff0c;传统的毕设题目缺少创新和亮点&a…

科聪停车移动机器人整体解决方案!

停车机器人是用于在停车场或车库等场所中进行车辆的自动停放和取车操作。随着智能交通和智能城市的发展&#xff0c;停车机器人在解决停车难、提高停车效率和减少停车空间浪费等方面具有广阔的应用前景。 科聪停车机器人解决方案&#xff1a; 本方案中核心部分采用的是科聪通用…

kibana安装

kibana安装下载注意事项 地址&#xff1a;curl -O https://artifacts.elastic.co/downloads/kibana/kibana-7.16.3-linux-x86_64.tar.gz 下载后直接解压启动即可 1. 但需要使用非root用户启动 &#xff0c;root用户启动会报错 2. kibana需要和elasticsearch版本一致 不然…

CRM系统的数据分析和报表功能对企业重要吗?

竞争日益激烈&#xff0c;企业需要更加高效地管理客户关系&#xff0c;以获取更多的商机。为此&#xff0c;许多企业选择使用CRM系统。在CRM中&#xff0c;数据分析功能扮演着重要的角色。下面就来详细说说&#xff0c;CRM系统数据分析与报表功能对企业来说重要吗&#xff1f; …

20. Matplotlib 数据可视化

目录 1. 简介2. Matplotlib 开发环境2.1 画图2.2 画图接口2.4 线形图2.5 散点图2.6 等高线图2.7 直方图 1. 简介 Matplotlib网址&#xff1a;https://matplotlib.org/ 数据可视化是数据分析中最重要的工作之一。Matploblib是建立在Numpy数组基础上的多平台数据可视化程序库&a…

计算机毕业设计 基于Web的课程设计选题管理系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

【Windows】解决Windows11错误0x80190001

1. 安装Fiddler网络调试工具 下载链接&#xff1a;Fiddler Classic 注&#xff1a;获取安装包的过程中可能要获取邮箱信息&#xff0c;但不用验证邮箱&#xff0c;大概是给你的邮箱发广告信息&#xff0c;问题不大。 在“开始”界面找到Fiddler Classic&#xff0c;点击运行…

如何修改.exe文件的修改时间,亲测有效

&#x1f482; 个人网站:【 海拥】【神级代码资源网站】【办公神器】&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交流的小伙伴&#xff0c;请点击【全栈技术交流群】 演示视频&#xff1a; 10秒钟实现将文件的修改…

【Spring系列】DeferredResult异步处理

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

【Node.js】解决npm报错:RequestError: unable to verify the first certificate

1. 问题简述 帖主从nodejs官网下载安装nodejs后&#xff0c;发现使用以下命令安装electron会报错&#xff1a; npm install electron 报错信息如下&#xff1a; npm ERR! RequestError: unable to verify the first certificate 2. 解决方案 网上列举的方案&#xff0c;无…

uniapp项目打包h5,支持文件协议,使用vconsole调试移动端

uniapp项目需要打包h5&#xff0c;并且需要嵌套到一个原生的移动端项目中&#xff0c;需要支持文件协议能直接访问 打包设置 设置./基础路径 引入vconsole调试移动端 <script src"https://unpkg.com/vconsole/dist/vconsole.min.js"></script>可以将…

数字系列——数字经济(2)​

上次呢&#xff0c;已经为大家捋了什么是数字经济&#xff1f;、数字经济的特点有哪些&#xff1f;和数字经济的构成&#xff0c;对于数字经济有了基础性的了解&#xff0c;今天继续为大家捋一捋。 数字经济的发展 1.互联网的普及 互联网作为数字经济的坚实基础&#xff0c;其…

猫头虎分享ubuntu20.04下VSCode无法输入中文解决方法

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

dbCAN碳水化合物酶基因数据库及run_dbCAN4工具安装配置及使用

dbCAN&#xff08;碳水化合物酶基因数据库&#xff09;是一个专门用于在基因组中预测碳水化合物酶基因的在线工具。它基于隐马尔可夫模型&#xff08;HMM&#xff09;和BLAST搜索&#xff0c;能够在蛋白质序列中识别和注释不同类型的碳水化合物酶基因&#xff0c;包括纤维素酶、…

logcat日志的使用——Qt For Android

前言 最近一直用qt开发安卓app&#xff0c;一直无法用真机调试&#xff0c;可能是缺什么东西。但是如果通过Qt Creator在真机上运行&#xff0c;可以在电脑控制台看打印&#xff08;安卓本身的日志、qDebug之类的打印&#xff09;&#xff0c;所以我是通过打印猜测问题所在&am…

Flutter 控件查阅清单

为了方便记录和使用Flutter中的各种控件&#xff0c;特写此博客以记之&#xff0c;好记性不如烂笔头嘛&#xff1a;&#xff09; 通过控件的首字母进行查找&#xff0c;本文会持续更新 控件目录 AAppBar BCContainerColumn &#xff08;列&#xff09; DDivider (分割线) EElev…

基于安卓的2048益智游戏的设计与实现

基于安卓的2048益智类游戏的设计与实现 摘要&#xff1a;现如今随着社会日新月异&#xff0c;人们越来越离不开智能手机所提供的灵活性与便携性。安卓系统是在这股手机发展迅猛的潮流中其市场占有率过半的手机平台&#xff0c;基于安卓系统的游戏开发有着不可估量的前景。 本论…

pip安装python包(pytorch)时遇到超时现象的通用解决方案

最近在使用服务器配置pytorch环境的时候&#xff0c;遇到了极为恼火的事情&#xff0c;使用pytorch官方的命令来下载GPU版本的pytorch总会是不是下载到一半就会崩溃&#xff0c;然而pip下载并不会断点续传&#xff08;什么时候能出这个功能啊喂&#xff01;&#xff09;。每次下…

Android中实现RecyclerView,并对item及其多个子控件的点击事件监听

目录 背景 实现RecyclerView 第一步、 新建item的xml 第二步、在activity的布局中引入 RecyclerView 第三步、新建一个adapter 第四步、在activity中初始化绑定adapter即可 实现item及其多个子组件点击事件监听 第一步、 适配器中创建监听对象 第二步、适配器中绑定监听…