【从零单排Golang】第八话:通过cache缓存模块示范interface该怎么用
和许多面向对象的编程语言一样,Golang
也存在interface
接口这样的概念。interface
相当于是一个中间层,下游只需要关心interface
实现了什么行为,利用这些行为做些业务级别事情,而上游则负责实现interface
,把这些行为具象化。本文就来通过一个简单的缓存cache
模块的实现,来示范一下Golang
的interface
该怎么用。
首先,从业务service
角度而言,一个cache模块可能需要以下几种方法:
- 获取缓存中的某个值
- 缓存数据,加缓存时效
- 删除缓存内容
那么这些个方法,就可以用一类叫Cache
的interface
来表示:
type Cache interface {Get(key string) (interface{}, bool)Set(key string, value interface{})SetExpire(key string, value interface{}, expire time.Duration)Delete(key string)
}
其中,Get
方法返回一个interface{}
的value
,以及是否存在的bool
标识;Set
跟SetExpire
表示无时限跟有时限的缓存行为;Delete
表示删除缓存内容。整块Cache
的接口定义也非常明显。
这样写有什么好处?如果你是下游业务服务的话,你只需要这样写就可以了。这里给一个同package
下的测试用例代码:
func TestCache(t *testing.T) {k, v := "hello", "world"// Current()的实现,在下文慢慢解释var curCache Cache = Current()// set & get & deletecurCache.Set(k, v)cached, ok := curCache.Get(k)if !ok {t.Fatalf("cannot cache %s:%s", k, v)} else {t.Logf("got cached %s:%v (type: %s)", k, cached, reflect.TypeOf(cached).Name())}curCache.Delete(k)_, ok = curCache.Get(k)if ok {t.Fatalf("cannot delete %s:%s", k, v)} else {t.Logf("delete cached %s:%s", k, v)}// set expirecurCache.SetExpire(k, v, 1*time.Second)cached, ok = curCache.Get(k)if !ok {t.Fatalf("cannot cache %s:%s", k, v)} else {t.Logf("got cached %s:%v (type: %s)", k, cached, reflect.TypeOf(cached).Name())}time.Sleep(3 * time.Second)_, ok = curCache.Get(k)if ok {t.Fatalf("cannot expire %s:%s", k, v)} else {t.Logf("expired %s:%s", k, v)}
}
可以看到,我们指定的缓存对象curCache
标识为一个Cache
,是个接口定义,这样标识起来的话,下面的代码就可以正常使用Get
、Set
之类的方法了。而更重要的是,下面的代码,不会因为Cache
的具体实现变化而有所变化。举个例子,你有10个开源的缓存库,想定时切换Current() Cache
背后的缓存对象实现,就算你再怎么换,只要用到缓存的代码标注缓存对象为Cache
这个interface
,并且interface
的定义没有变化,那么使用缓存的代码就不需要动。这样,就彻底实现了缓存提供方和使用方的解耦,开发效率也会噌噌噌的上去。
既然提到了提供方Provider
的概念,那在缓存的实现上,就可以走依赖注入控制反转的模式。假设某个Web服务有个本地缓存模块,在实现上,就可以考虑提供多个Cache
接口的实现,同时在配置里指定默认的一种。这里,就以go-cache为例,做一个实现案例。
import ("github.com/patrickmn/go-cache""time"
)const (GoCacheDefaultExpiration = 10 * time.MinuteGoCacheCleanupInterval = 15 * time.Minute
)type GoCache struct {c *cache.CachedefaultExpiration time.DurationcleanupInterval time.Duration
}func (g *GoCache) Get(key string) (interface{}, bool) {return g.c.Get(key)
}func (g *GoCache) Set(key string, value interface{}) {g.c.Set(key, value, GoCacheDefaultExpiration)
}func (g *GoCache) SetExpire(key string, value interface{}, expire time.Duration) {if expire < 0 {expire = g.defaultExpiration}if expire > g.cleanupInterval {expire = g.cleanupInterval}g.c.Set(key, value, expire)
}func (g *GoCache) Delete(key string) {g.c.Delete(key)
}func NewGoCache() *GoCache {return &GoCache{c: cache.New(GoCacheDefaultExpiration, GoCacheCleanupInterval),defaultExpiration: GoCacheDefaultExpiration,cleanupInterval: GoCacheCleanupInterval,}
}
当我们定义一个GoCache
的struct
,实现了Cache
接口定义的所有行为,那么GoCache
的实例,在Golang
里,就能够被标识为一个Cache
接口实例。NewGoCache
方法,不仅是提供了一个GoCache
的实例,而在业务层面,更是提供了一个Cache
实例。因此,我们可以简单用一个map
来管理所有的Cache
的构造器,从而标识不同的缓存实现:
func provideGoCache() Cache {return NewGoCache()
}var cacheProviders = map[string]Cache{"go-cache": provideGoCache(),
}const (DefaultCacheProvider = "go-cache"
)func Get(provider string) Cache {c, ok := cacheProviders[provider]if !ok {return nil}return c
}func Default() Cache {return Get(DefaultCacheProvider)
}// 上文提到的样例代码,就用了这个方法拿到go-cache实现的Cache接口实例
func Current() Cache {return Default()
}
显而易见,通过这样的一个代码组织,不论是go-cache
,抑或是其它的Cache
实现,都可以集中管理并灵活取用。这,便是interface
在Golang
编程中给我们带来的便利了。
相关文章:
【从零单排Golang】第八话:通过cache缓存模块示范interface该怎么用
和许多面向对象的编程语言一样,Golang也存在interface接口这样的概念。interface相当于是一个中间层,下游只需要关心interface实现了什么行为,利用这些行为做些业务级别事情,而上游则负责实现interface,把这些行为具象…...

解析从Linux零拷贝深入了解Linux-I/O(上)
本文将从文件传输场景以及零拷贝技术深究 Linux I/O 的发展过程、优化手段以及实际应用。前言 存储器是计算机的核心部件之一,在完全理想的状态下,存储器应该要同时具备以下三种特性: 速度足够快:存储器的存取速度应当快于 CPU …...
JavaScript系列之公有、私有和静态属性和方法
文章の目录一、公有属性、公有方法1、定义2、理解3、ES54、ES6二、私有属性、私有方法1、定义2、理解3、ES54、ES6三、静态属性、静态方法1、定义2、理解3、ES54、ES6写在最后一、公有属性、公有方法 1、定义 指的是所属这个类的所有对象都可以访问的属性,叫做公有…...

过滤器与拦截器
文章目录一、前言1、概述2、过滤器与拦截器异同2.1 简介2.2 异同2.3 总结3、Filters vs HandlerInterceptors二、过滤器1、概述2、生命周期2.1 生命周期概述2.2 基于函数回调实现原理3、自定义过滤器两种实现方式3.1 WebFilter注解注册3.2 过滤器(配置类注册过滤器&…...
spring boot 和cloud 版本升级
spring boot 和cloud 版本对应 背景:原来一直用的版本是Hoxton.SR12 2.3.10.RELEASE(SR12一路升,几乎没有影响,不需要测试,但是换大版本就有点担心) 去年2022年底黑鸭子报漏洞把springboot,clou…...

untiy 录制网络摄像头视频并保存到本地文件
网络摄像头使用的是海康威视的,关于如何使用Ump插件播放海康威视rtsp视频流,请参考我的这篇文章 内部有ump插件的下载链接 untiy接入 海康威视网络摄像头 录屏使用的插件是 AVPro movieCapture 4.6.3版, 插件和完整工程的下载链接放在本文的…...
微服务架构设计模式-(15)部署
关联概念 流程 将软件投入到生产环境 架构 软件运行的环境结构 生产环境四个关键功能 服务管理接口 使开发人员能够创建、更新和配置服务 运行时服务管理 确保始终运行一定数量的服务实例非中断更新 监控 让开发人员了解服务情况,包括日志文件和各种应用指标可观…...

Redis:数据结构
简单动态字符串SDS Redis没有直接使用C语言传统的字符串表示(以空字符结尾的字符数组,以下简称C字符串),而是自己构 建了一种名为简单动态字符串(simple dynamic string, SDS)的抽象类型,并将SDS用作Redis的默认字符 串表示。 SDS 的实现…...

2.18 设置language和中文输入法
文章目录一:设置language二:设置中文输入法一:设置language nvidia的开发板上默认只有English,需要点击如下管理: 接着进入如下界面: 此时图中的“汉语(中国)”应该是没有的&…...

图解LeetCode——剑指 Offer 28. 对称的二叉树
一、题目 请实现一个函数,用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。 二、示例 2.1> 示例 1: 【输入】root [1,2,2,3,4,4,3] 【输出】true 2.2> 示例 2: 【输入】root [1,2,2,nul…...

Qt Desginer布局方法
首先将我们需要的控件拖拽到一个合适的位置,该例子中用到了两个label,两个lineEdit和两个pushButton。 然后我们需要利用弹簧来控制控件到控件之间的距离以及控件到窗体边界的距离,因为这里只有一组控件(两个label,两个…...
C/C++、Java、Python的比较及学习(3)
函数间的值传递与地址传递 值传递方式:指主调函数把实参的值赋给形参。 在这种传递方式下,主调函数中的实参地址与被调函数中的形参地址是相互独立的。 函数被调用时,系统为形参变量分配内存单元,并将实参的值存入到对应形参的内存…...
智慧校园建设方案
第一章、 智慧教学 6.1. 校本资源库 提供校本资源管理功能,实现学校内的教学资源的共建共享,促进教师之间的交流学习,提升学校的整体教学水平。在本系统中学校可以统一采购资源接入到校本资源库中供教师下载使用,教师也可以将…...

ARM uboot 源码分析5 -启动第二阶段
一、start_armboot 解析6 1、console_init_f (1) console_init_f 是 console(控制台)的第一阶段初始化。_f 表示是第一阶段初始化,_r 表示第二阶段初始化。有时候初始化函数不能一次一起完成,中间必须要夹杂一些代码,…...

【ip neigh】管理IP邻居( 添加ARP\NDP静态记录、删除记录、查看记录)
一、邻居管理存在状态 1、NUD_NONE: 初始状态。当一个新的路由缓存条目被创建时,arp_bind_neighbour()函数被调用.如果找不到相匹配的ARP缓存条目, neigh_alloc()将创建一个新的ARP缓存条目并设置状态为NUD_NONE. 2、NUD_INCOMPLETE:未完成状…...

Java程序员线上排查问题神器-Arthas
文章目录前言一、Arthas是什么?二、快速入门1.下载2.如何运行三、常用命令1.dashboard2.trace总结前言 最近公司项目版本迭代升级,在开发新需求导致没什么时间写博客。 在开发需求的过程中,我写了一个接口,去批量调内部已经写好…...

上市公司企业持续创新能力、创新可持续性(原始数据+计算代码+计算结果)(2008-2021年)
数据来源:自主计算 时间跨度:2008-2021年 区域范围:沪深A股上市公司 指标说明: 参考何郁冰(2017)[1]的做法,将持续创新作为独立研究变量,同时采用创新投入指标(研发经费) 和创新…...
华为OD机试 - 考古学家(JS)
考古学家 题目 有一个考古学家发现一个石碑 但是很可惜 发现时其已经断成多段 原地发现N个断口整齐的石碑碎片 为了破解石碑内容 考古学家希望有程序能帮忙计算复原后的石碑文字组合数 你能帮忙吗 备注: 如果存在石碑碎片内容完全相同,则由于碎片间的顺序不影响复原后的碑…...
Leetcode.2100 适合打劫银行的日子
题目链接 Leetcode.2100 适合打劫银行的日子 Rating : 1702 题目描述 你和一群强盗准备打劫银行。给你一个下标从 0开始的整数数组 security,其中 security[i]是第 i天执勤警卫的数量。日子从 0开始编号。同时给你一个整数 time。 如果第 i天满足以下所…...

linux ubuntu查日志信息以及错误排查
目录 一、linux的日志文件 1、常用日志文件 2、其他日志文件 二、历史日志的查看 1、查看Logrotate的配置信息 2、查看日志配置 一、linux的日志文件 Linux系统中最有趣的(可能也是最重要的)目录之一是/var/log。根据文件系统层次结构标准,在系统中运行的大多数…...

Docker 离线安装指南
参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性,不同版本的Docker对内核版本有不同要求。例如,Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本,Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...

Docker 运行 Kafka 带 SASL 认证教程
Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明:server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...
Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器
第一章 引言:语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域,文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量,支撑着搜索引擎、推荐系统、…...

【Oracle】分区表
个人主页:Guiat 归属专栏:Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...

springboot整合VUE之在线教育管理系统简介
可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生,小白用户,想学习知识的 有点基础,想要通过项…...

LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf
FTP 客服管理系统 实现kefu123登录,不允许匿名访问,kefu只能访问/data/kefu目录,不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...

Python Ovito统计金刚石结构数量
大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...

Windows安装Miniconda
一、下载 https://www.anaconda.com/download/success 二、安装 三、配置镜像源 Anaconda/Miniconda pip 配置清华镜像源_anaconda配置清华源-CSDN博客 四、常用操作命令 Anaconda/Miniconda 基本操作命令_miniconda创建环境命令-CSDN博客...

wpf在image控件上快速显示内存图像
wpf在image控件上快速显示内存图像https://www.cnblogs.com/haodafeng/p/10431387.html 如果你在寻找能够快速在image控件刷新大图像(比如分辨率3000*3000的图像)的办法,尤其是想把内存中的裸数据(只有图像的数据,不包…...
HTML前端开发:JavaScript 获取元素方法详解
作为前端开发者,高效获取 DOM 元素是必备技能。以下是 JS 中核心的获取元素方法,分为两大系列: 一、getElementBy... 系列 传统方法,直接通过 DOM 接口访问,返回动态集合(元素变化会实时更新)。…...