nsq的目录锁,源码分析

news2024/12/24 10:57:00

文章目录

        前言

        nsqd启动加锁流程及源码分析

        总结


前言

前面几篇博客我们讲了nsq是什么,nsq的安装等,大家想过下面这样的问题没有,就是

问题:一个目录下能启动多个nsqd进程吗?

答案:不能

问题:为什么不能呢?它是怎么实现的呢?

答案:nsqd需要保存topic,channel等数据到文件中,文件名都是nsqd.dat。如果启动多个nsqd进程,这些进程保存的文件会冲突或相互覆盖。所以一个nsqd进程启动时会对启动目录加锁,其他nsqd进程若再加锁该目录就会失败,最终启动失败

问题:一个目录下能启动多个nsqlookupd,nsqadmin吗?

答案:可以,因为这些进程没有读取,保存文件的需求,各进程绑定的端口不一致即可

这一篇博客我们就讲nsqd进程启动时加锁目录的实现原理

nsqd启动加锁流程及源码分析

先看下nsqd进程启动的步骤,nsqd启动的main函数在文件 nsq\apps\nsqd\main.go,main函数源码如下(已加注释)

// nsqd的启动入口
func main() {
	prg := &program{}

	// Run内部会调用Init(),Start(),监听到这两个信号时会调用Stop()
	if err := svc.Run(prg, syscall.SIGINT, syscall.SIGTERM); err != nil {
		logFatal("%s", err)
	}
}

要注意的是:nsqdnsqlookupdnsqadmin等所有进程都是使用了go-svc包,这个包很简单,就是三个函数 Init(), Start(), Stop(),各个使用者实现了这三个接口就行

上面的main()里面调用了svc.Run(),这个函数内部会调用实现者的Init()函数

nsqd实现的Init()源码如下(已加注释)

// nsqd的初始化,主要是配置及检测相关,最后会创建nsqd实例
func (p *program) Init(env svc.Environment) error {
	// 生成nsqd的默认配置
	opts := nsqd.NewOptions()
    ......
	// 重点:根据opts新建一个nsqd实例
	nsqd, err := nsqd.New(opts)
    ......
}

省略了一些不重要的代码,nsqd的Init函数主要就是生成配置opts,然后使用opts来创建nsqd实例

重点来了:nsqd.New(opts),源码如下(已加注释)

// 根据配置opts新建一个nsqd对象
func New(opts *Options) (*NSQD, error) {
	var err error

	// 没有指定运行路径,就使用当前目录
	dataPath := opts.DataPath
	if opts.DataPath == "" {
		cwd, _ := os.Getwd()
		dataPath = cwd
	}
    ......
	n := &NSQD{
		startTime:            time.Now(),
		topicMap:             make(map[string]*Topic),
		exitChan:             make(chan int),
		notifyChan:           make(chan interface{}),
		optsNotificationChan: make(chan struct{}, 1),
		dl:                   dirlock.New(dataPath),	// 创建目录锁
	}
    ......
	// 加锁运行的目录
	err = n.dl.Lock()
	if err != nil {
		return nil, fmt.Errorf("failed to lock data-path: %v", err)
	}
    ......
}

上面的代码,我们主要关注的几个点

1. 如果启动配置中没有指定data目录,nsqd会以当前目录为运行目录,赋给dataPath

2. 创建nsqd实例时,会以dataPth来创建一个目录锁dl,即dl : dirlock.New(dataPath)

3. 创建nsqd实例后,会调用dl.Lock()对目录进行加锁,加锁失败就返回了,也就是nsqd进程启动失败

核心:dl.Lock()这里真正实现了目录的加锁

有意思的是,dirlock包对不同的系统实现的加锁方式也不同,但是只有linux下dirlock.go真正有实现,另外的illumos.go,dirlock_windows.go的实现都是空的。

dirlock包位置在nsq/internal/dirlock,如下图

 我们只看linux下dirlock.go即可,代码如下(已加注释)

// 目录锁(内部靠文件锁来实现)
type DirLock struct {
	dir 	string		// 要锁定的目录(全路径),锁定时会把这个目录当成文件来锁定
	f   	*os.File	// 以该目录创建的文件对象
}
// 加锁
func (l *DirLock) Lock() error {
	f, err := os.Open(l.dir) // linux下目录也是文件,可以像文件一样打开
	if err != nil {
		return err
	}
	l.f = f

	// 锁定文件(LOCK_EX表放置互斥锁, LOCK_NB表非阻塞锁)
	err = syscall.Flock(int(f.Fd()), syscall.LOCK_EX|syscall.LOCK_NB)
	if err != nil {
		return fmt.Errorf("cannot flock directory %s - %s (possibly in use by another instance of nsqd)", l.dir, err)
	}
	return nil
}
// 解锁
func (l *DirLock) Unlock() error {
	defer l.f.Close() // 关闭文件
	return syscall.Flock(int(l.f.Fd()), syscall.LOCK_UN) // LOCK_UN表解锁
}

上面代码,主要就两个函数

Lock() 加锁内部调用syscall.Flock(fd, syscall.LOCK_EX|syscall.LOCK_NB)

fd是文件打开后的fd

LOCK_EX :加锁标记。只有一个进程能加锁成功,其他进程再尝试加锁时会阻塞,等同于我们常用的写锁

LOCK_NB :不阻塞标记。如果其他进程已加锁成功,自己去尝试加锁时就不再阻塞,而是直接返回错误

Unlock() 解锁内部调用 syscall.Flock(int(l.f.Fd()), syscall.LOCK_UN)

LOCK_UN :解锁标记。如果自己已经加锁成功,可以用此标记去解锁

nsqd进程在关闭的时候,会调用Stop()函数,函数内部会调用dl.Unlock()对目录进行解锁,代码如下(已加注释)

func (n *NSQD) Exit() {
    ......
	// 解锁目录
    n.dl.Unlock()
	n.logf(LOG_INFO, "NSQ: bye")
	n.ctxCancel()
}

总结

1. nsqd进程之所以要对目录加锁是因为nsqd需要保存topic,channel等数据到文件,且文件名字固定为nsqd.dat。nsqd进程启动时需要加载文件以恢复数据。不加锁的话,多个nsqd进程会导致文件冲突,相互覆盖

2. nsqd进程在启动时,创建nsqd对象后就进行了加锁目录,加锁失败则进程启动失败

3. nsqd进程在退出时,会解锁目录

4. nsqd进程加锁,解锁目录用的是syscall包,最终使用的是函数syscall.Flock(),其中LOCK_EX标记用来加锁,LOCK_UN标记用来解锁

如果大家想对go语言的syscall包有更详细地了解,可以参考我的这篇博客

golang文件锁,目录锁,syscall包的使用_YZF_Kevin的博客-CSDN博客

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

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

相关文章

Maven项目解决cannot resolve plugin maven-deploy-plugin:2.7

导入maven项目后,编辑的时候提示一些插件加载失败!大概率是你的网络有问题,插件下载失败。 如下图:(网络突然好了,我想截图但是没有复现,用网上找到的截图代替,明白意思就行&#x…

Docker Compose 安装与使用(常用指令)

一、简介 Docker Compose 是一个编排多容器分布式部署的工具,提供命令集管理容器化应用的完整开发周期,包括服务构建、启动和停止。使用步骤:1. 利用 Dockerfile 定义运行环境镜像 2. 使用 docker-compose.yml 家义组成应用的各服务 3. 运行 …

python制作超高难度走迷宫游戏,你要来挑战嘛~(赶紧收藏)

前言 嗨喽~大家好呀,这里是魔王呐 ❤ ~! 走迷宫,是一项充满智慧的挑战~ 作为经常刷短视频的我们,见识过不少迷宫小游戏 当然印象深刻的当然是小动物走迷宫 这里有几组挑战走迷宫的小可爱。先来看看吧! (1&#xff…

Java Maven 构建项目里面有个聚合的概念

Java 项目里面有个聚合的概念,它没有.net里面解决方案(solution)的能力,可以统一的编译项目下的所有包,或设置统一的打包路径,使用maven编译后的产物也不会像.net那样编译到当前项目的bin文件夹下面,而是统一的生成到配…

MySQL 其他数据库日志

我们了解数据库事务时,知道两种日志:重做日志,回滚日志。 对于线上数据库应用系统,突然遭遇 数据库宕机 怎么办?在这种情况下,定位宕机的原因 就非常关键。我们可以查看数据库的 错误日志。因为日志中记录…

保修管理系统

保修管理系统密切关注IT资产的保修信息,以便在发生任何故障或损坏时,供应商可以及时更换和修复任何IT硬件。自动化保修管理软件跟踪各种供应商提供的维修和服务期限的有效性,以便任何硬件都可以在保修到期之前升级,从而降低成本并…

企业项目中md-loader项目组件文档实现

背景需求: 随着业务增多,公共组件变多,无法直观知道其中的方法使用和业务场景,轻量级不需要重新新建一个项目 技术基础: 1.仿造element-ui md-loader 需求场景: 当前项目公共组件比较多,需…

在AndroidStudio中如何查看Gradle的版本

以Android Studio Giraffe | 2022.3.1为例 File -> Project Structure -> Project Android Gradle Plugin Version - Android Gradle插件版本号 Gradle Version - Gradle的版本号 Gradle 版本 (gradle version): Gradle 是一种通用的构建工具,用于构建各种类…

蓝牙、GPS定位学习

启动状态(APP) 冷启动 指在启动应用时,后台没有应用的进程或者进程被杀死的情况下,系统会重新创建一个新的进程,并按照一定的顺序创建和初始化Application类和MainActivity类,最后显示在界面上。这个过程需…

vue3使用iframe引入其他网站,vue-router路由跳转后页面空白,刷新之后才展示页面内容乌龙事件

问题描述:vue3项目的页面A跳转到页面B时,页面B页面是空白的,需要手动刷新一下才能恢复正常,在页面A中用iframe引入了别的网站(后续事实证明,跟iframe没一毛钱的关系)。着急的童鞋可以直接拉到底…

【C++】开源:Eigen3矩阵与线性代数库配置使用

😏★,:.☆( ̄▽ ̄)/$:.★ 😏 这篇文章主要介绍Eigen3矩阵与线性代数库配置使用。 无专精则不能成,无涉猎则不能通。——梁启超 欢迎来到我的博客,一起学习,共同进步。 喜欢的朋友可以关注一下&…

机柜PDU的选购也有大学问——与机柜PDU相关的那些事儿

在各行各业数据中心机房供配电建设过程中,机柜专用PDU电源插座看似是一个较为简单的用电设备,事实上又不那么简单。机柜PDU,是为安装在机柜内部的IT设备提供电源分配、管理的末端配电设备,在不同的工作场合对于PDU的规格要求也是不…

HackSudo2靶机 通关详解

环境配置 发现vmWare的kali扫不到virtualbox的靶机 网上找了挺久资料都没解决 索性全桥接上物理机了 信息收集 漏洞发现 扫个目录 都看了一眼 没什么有用的 然后回到file.php 感觉之前做过类似的靶场,猜测存在文件包含 随便传个file试试 确实有 考虑日志文件包含 之前看…

C++---list常用接口和模拟实现

list---模拟实现 list的简介list函数的使用构造函数迭代器的使用list的capacitylist element accesslist modifiers list的模拟实现构造函数,拷贝构造函数和迭代器begin和endinsert和eraseclear和析构函数 源码 list的简介 list是用双向带头联表实现的一个容器&…

[用go实现解释器]笔记1-词法分析

本文是《用go实现解释器》的读书笔记 ​ https://malred-blog​malred.github.io/2023/06/03/ji-suan-ji-li-lun-ji-shu-ji/shi-ti/go-compile/yong-go-yu-yan-shi-xian-jie-shi-qi/go-compiler-1/#toc-heading-6http://个人博客该笔记地址 ​github.com/malred/malanghttp:/…

入行软件测试的一些工作感悟

成为xx一员测试已经有1年半了,一直没有真正坐下来花些时间将自己的思路理清一下。刚好近期公司落地了OKR,给自己制定了OKR之后思路终于开始清晰起来,朦朦胧胧地开始看清了远方的路,麻着胆子分析一下自己,毕竟摸黑走路的…

Screens 4 for mac VNC客户端 强大的远程控制工具

Screens 4 for Mac 是一款功能强大的 VNC 客户端软件,为 Mac 用户提供了便捷的远程访问和控制解决方案。无论您是需要远程管理服务器、办公电脑,还是需要远程协助他人解决问题,Screens 4 都是您的理想选择。 Screens 4 for Mac具备简洁直观的…

小白到运维工程师自学之路 第六十二集 (docker持久化与数据卷容器)

一、概述 Docker持久化是指将容器中的数据持久保存在主机上,以便在容器重新启动或迁移时不丢失数据。由于Docker容器是临时和可变的,它们的文件系统默认是易失的,这意味着容器中的任何更改或创建的文件都只存在于此容器的生命周期内。但是&a…

基于低代码平台快速搭建应用

一、前言 近年来,SaaS行业的迅猛发展带动了低代码领域的快速兴起。国外的低代码创业公司如Mendix、Outsystems,以及国内的软件业巨头如华为、用友、金蝶等和小众高性价比的引迈JNPF都已经纷纷涉足低代码市场。根据Transparency Market Research的报告&am…

独立站运营要做哪些工作?包含哪些模块?

在电商行业中,广告投放和设计等岗位的招聘可能会相对容易,但真正理解并有效执行独立站运营的人员却十分稀少。因此,今天将聚焦于独立站的运营,特别是针对精品垂直站和品牌站的运营。 首先,我们需要了解“运营”的含义…