go语言超时机制 go语言chan

golang之context详解为什么需要context
在go服务器中,对于每个请求的request都是在单独的goroutine中进行的,处理一个request也可能设计多个goroutine之间的交互,使用context可以使开发者方便的在这些goroutine里传递request相关的数据、取消goroutine的signal或截止日期
在并发程序中,由于超时、取消操作或者一些异常情况,往往需要进行抢占操作或者中断后续操作 。熟悉channel的朋友应该都见过使用done channel来处理此类问题 。比如以下这个例子:
上述例子中定义了一个buffer为0的channel done, 子协程运行着定时任务 。如果主协程需要在某个时刻发送消息通知子协程中断任务退出,那么就可以让子协程监听这个done channel,一旦主协程关闭done channel,那么子协程就可以推出了,这样就实现了主协程通知子协程的需求 。这很好,但是这也是有限的 。
如果我们可以在简单的通知上附加传递额外的信息来控制取消:为什么取消,或者有一个它必须要完成的最终期限,更或者有多个取消选项 , 我们需要根据额外的信息来判断选择执行哪个取消选项 。
考虑下面这种情况:假如主协程中有多个任务1, 2, …m , 主协程对这些任务有超时控制;而其中任务1又有多个子任务1, 2, …n,任务1对这些子任务也有自己的超时控制 , 那么这些子任务既要感知主协程的取消信号,也需要感知任务1的取消信号 。
如果还是使用done channel的用法,我们需要定义两个done channel,子任务们需要同时监听这两个done channel 。嗯,这样其实好像也还行哈 。但是如果层级更深,如果这些子任务还有子任务 , 那么使用done channel的方式将会变得非常繁琐且混乱 。
我们需要一种优雅的方案来实现这样一种机制:
上层任务取消后,所有的下层任务都会被取消;中间某一层的任务取消后,只会将当前任务的下层任务取消,而不会影响上层的任务以及同级任务 。
这个时候context就派上用场了 。我们首先看看context的结构设计和实现原理 。
context接口
先看Context接口结构 , 看起来非常简单 。
}
Context接口包含四个方法:
Deadline返回绑定当前context的任务被取消的截止时间;如果没有设定期限 , 将返回ok == false 。
Done 当绑定当前context的任务被取消时,将返回一个关闭的channel;如果当前context不会被取消,将返回nil 。
Err 如果Done返回的channel没有关闭,将返回nil;如果Done返回的channel已经关闭,将返回非空的值表示任务结束的原因 。如果是context被取消,Err将返回Canceled;如果是context超时,Err将返回DeadlineExceeded 。
Value 返回context存储的键值对中当前key对应的值 , 如果没有对应的key,则返回nil 。
可以看到Done方法返回的channel正是用来传递结束信号以抢占并中断当前任务;Deadline方法指示一段时间后当前goroutine是否会被取消;以及一个Err方法,来解释goroutine被取消的原因;而Value则用于获取特定于当前任务树的额外信息 。而context所包含的额外信息键值对是如何存储的呢?其实可以想象一颗树,树的每个节点可能携带一组键值对,如果当前节点上无法找到key所对应的值,就会向上去父节点里找,直到根节点 。
emptyCtx
emptyCtx是一个int类型的变量 , 但实现了context的接口 。emptyCtx没有超时时间,不能取消,也不能存储任何额外信息,所以emptyCtx用来作为context树的根节点 。
Background和TODO只是用于不同场景下: Background通常被用于主函数、初始化以及测试中,作为一个顶层的context,也就是说一般我们创建的context都是基于Background;而TODO是在不确定使用什么context的时候才会使用 。
用法 :
go语言聊天室实现(七)websocket收消息设置上一节中,我们为每个连接都创建了一个goroutine来读取其中的消息,现在我们将这个读取消息的方法实现一下 。
【go语言超时机制 go语言chan】我们在application目录下新建controllers目录,并在其中创建一个MessageController.go文件 。
首先我们新建一个MessageController的结构体,内容如下
这个结构体包括两个内容 , 一个是我们将连接放在数组之后 , 返回的索引 , 另一个是连接本身.
这个是具体的方法 。
我们首先设置了一下读消息的大小、超时时间以及超时后需要的操作 。
超时时间如果设置为0,那么就是永不超时 。之前在这里直接写0,被告知需要传一个time.Time类型的数据 。最终谷歌后才得到了这个值time.Time{}为"0001-01-01 00:00:000000 UTC" 。
我们将用户手法消息的内容定义为一个结构体,然后将用户的订阅信息的json通过json.unmarshal转换成这个结构体 。
之后的switch操作与我们在Swoole中的操作基本雷同,在查询到login之后,调用service中 的login方法来进行注册 。
下一节中我们再介绍具体的注册逻辑 。
golang使用Nsq1. 介绍
最近在研究一些消息中间件,常用go语言超时机制的MQ如RabbitMQ,ActiveMQ,Kafka等 。NSQ是一个基于Go语言go语言超时机制的分布式实时消息平台 , 它基于MIT开源协议发布 , 由bitly公司开源出来的一款简单易用的消息中间件 。
官方和第三方还为NSQ开发了众多客户端功能库 , 如官方提供的基于HTTP的nsqd、Go客户端go-nsq、Python客户端pynsq、基于Node.js的JavaScript客户端nsqjs、异步C客户端libnsq、Java客户端nsq-java以及基于各种语言的众多第三方客户端功能库 。
1.1 Features
1). Distributed
NSQ提供了分布式的 , 去中心化 , 且没有单点故障的拓扑结构 , 稳定的消息传输发布保障,能够具有高容错和HA(高可用)特性 。
2). Scalable易于扩展
NSQ支持水平扩展 , 没有中心化的brokers 。内置的发现服务简化了在集群中增加节点 。同时支持pub-sub和load-balanced 的消息分发 。
3). Ops Friendly
NSQ非常容易配置和部署,生来就绑定了一个管理界面 。二进制包没有运行时依赖 。官方有Docker image 。
4.Integrated高度集成
官方的 Go 和 Python库都有提供 。而且为大多数语言提供了库 。
1.2 组件
1.3 拓扑结构
NSQ推荐通过他们相应的nsqd实例使用协同定位发布者,这意味着即使面对网络分区,消息也会被保存在本地,直到它们被一个消费者读取 。更重要的是,发布者不必去发现其他的nsqd节点,他们总是可以向本地实例发布消息 。
NSQ
首先,一个发布者向它的本地nsqd发送消息 , 要做到这点 , 首先要先打开一个连接,然后发送一个包含topic和消息主体的发布命令,在这种情况下,go语言超时机制我们将消息发布到事件topic上以分散到我们不同的worker中 。
事件topic会复制这些消息并且在每一个连接topic的channel上进行排队,在我们的案例中,有三个channel,它们其中之一作为档案channel 。消费者会获取这些消息并且上传到S3 。
nsqd
每个channel的消息都会进行排队 , 直到一个worker把他们消费 , 如果此队列超出了内存限制,消息将会被写入到磁盘中 。Nsqd节点首先会向nsqlookup广播他们的位置信息,一旦它们注册成功,worker将会从nsqlookup服务器节点上发现所有包含事件topic的nsqd节点 。
nsqlookupd
2. Internals
2.1 消息传递担保
1)客户表示已经准备好接收消息
2)NSQ 发送一条消息,并暂时将数据存储在本地(在 re-queue 或 timeout)
3)客户端回复 FIN(结束)或 REQ(重新排队)分别指示成功或失败 。如果客户端没有回复, NSQ 会在设定的时间超时,自动重新排队消息
这确保了消息丢失唯一可能的情况是不正常结束 nsqd 进程 。在这种情况下,这是在内存中的任何信息(或任何缓冲未刷新到磁盘)都将丢失 。
如何防止消息丢失是最重要的,即使是这个意外情况可以得到缓解 。一种解决方案是构成冗余 nsqd对(在不同的主机上)接收消息的相同部分的副本 。因为你实现的消费者是幂等的,以两倍时间处理这些消息不会对下游造成影响,并使得系统能够承受任何单一节点故障而不会丢失信息 。
2.2 简化配置和管理
单个 nsqd 实例被设计成可以同时处理多个数据流 。流被称为“话题”和话题有 1 个或多个“通道” 。每个通道都接收到一个话题中所有消息的拷贝 。在实践中,一个通道映射到下行服务消费一个话题 。
在更底的层面,每个 nsqd 有一个与 nsqlookupd 的长期 TCP 连接,定期推动其状态 。这个数据被 nsqlookupd 用于给消费者通知 nsqd 地址 。对于消费者来说,一个暴露的 HTTP /lookup 接口用于轮询 。为话题引入一个新的消费者,只需启动一个配置了 nsqlookup 实例地址的 NSQ 客户端 。无需为添加任何新的消费者或生产者更改配置,大大降低了开销和复杂性 。
2.3 消除单点故障
NSQ被设计以分布的方式被使用 。nsqd 客户端(通过 TCP )连接到指定话题的所有生产者实例 。没有中间人,没有消息代理,也没有单点故障 。
这种拓扑结构消除单链,聚合,反馈 。相反 , 你的消费者直接访问所有生产者 。从技术上讲 , 哪个客户端连接到哪个 NSQ 不重要,只要有足够的消费者连接到所有生产者,以满足大量的消息,保证所有东西最终将被处理 。对于 nsqlookupd , 高可用性是通过运行多个实例来实现 。他们不直接相互通信和数据被认为是最终一致 。消费者轮询所有的配置的 nsqlookupd 实例和合并 response 。失败的,无法访问的,或以其他方式故障的节点不会让系统陷于停顿 。
2.4 效率
对于数据的协议,通过推送数据到客户端最大限度地提高性能和吞吐量的,而不是等待客户端拉数据 。这个概念,称之为 RDY 状态 , 基本上是客户端流量控制的一种形式 。
efficiency
2.5 心跳和超时
组合应用级别的心跳和 RDY 状态,避免头阻塞现象,也可能使心跳无用(即,如果消费者是在后面的处理消息流的接收缓冲区中,操作系统将被填满 , 堵心跳)为了保证进度,所有的网络 IO 时间上限势必与配置的心跳间隔相关联 。这意味着,你可以从字面上拔掉之间的网络连接 nsqd 和消费者,它会检测并正确处理错误 。当检测到一个致命错误 , 客户端连接被强制关闭 。在传输中的消息会超时而重新排队等待传递到另一个消费者 。最后,错误会被记录并累计到各种内部指标 。
2.6 分布式
因为NSQ没有在守护程序之间共享信息,所以它从一开始就是为了分布式操作而生 。个别的机器可以随便宕机随便启动而不会影响到系统的其余部分,消息发布者可以在本地发布 , 即使面对网络分区 。
这种“分布式优先”的设计理念意味着NSQ基本上可以永远不断地扩展,需要更高的吞吐量?那就添加更多的nsqd吧 。唯一的共享状态就是保存在lookup节点上,甚至它们不需要全局视图 , 配置某些nsqd注册到某些lookup节点上这是很简单的配置,唯一关键的地方就是消费者可以通过lookup节点获取所有完整的节点集 。清晰的故障事件——NSQ在组件内建立了一套明确关于可能导致故障的的故障权衡机制,这对消息传递和恢复都有意义 。虽然它们可能不像Kafka系统那样提供严格的保证级别 , 但NSQ简单的操作使故障情况非常明显 。
2.7 no replication
不像其他的队列组件,NSQ并没有提供任何形式的复制和集群,也正是这点让它能够如此简单地运行 , 但它确实对于一些高保证性高可靠性的消息发布没有足够的保证 。我们可以通过降低文件同步的时间来部分避免,只需通过一个标志配置,通过EBS支持我们的队列 。但是这样仍然存在一个消息被发布后马上死亡,丢失了有效的写入的情况 。
2.8 没有严格的顺序
虽然Kafka由一个有序的日志构成,但NSQ不是 。消息可以在任何时间以任何顺序进入队列 。在我们使用的案例中,这通常没有关系,因为所有的数据都被加上了时间戳 , 但它并不适合需要严格顺序的情况 。
2.9 无数据重复删除功能
NSQ对于超时系统 , 它使用了心跳检测机制去测试消费者是否存活还是死亡 。很多原因会导致我们的consumer无法完成心跳检测,所以在consumer中必须有一个单独的步骤确保幂等性 。
3. 实践安装过程
本文将nsq集群具体的安装过程略去,大家可以自行参考官网 , 比较简单 。这部分介绍下笔者实验的拓扑,以及nsqadmin的相关信息 。
3.1 拓扑结构
topology
实验采用3台NSQD服务,2台LOOKUPD服务 。
采用官方推荐的拓扑,消息发布的服务和NSQD在一台主机 。一共5台机器 。
NSQ基本没有配置文件,配置通过命令行指定参数 。
主要命令如下:
LOOKUPD命令
NSQD命令
工具类,消费后存储到本地文件 。
发布一条消息
3.2 nsqadmin
对Streams的详细信息进行查看 , 包括NSQD节点,具体的channel,队列中的消息数 , 连接数等信息 。
nsqadmin
channel
列出所有的NSQD节点:
nodes
消息的统计:
msgs
lookup主机的列表:
hosts
4. 总结
NSQ基本核心就是简单性,是一个简单的队列 , 这意味着它很容易进行故障推理和很容易发现bug 。消费者可以自行处理故障事件而不会影响系统剩下的其余部分 。
事实上,简单性是我们决定使用NSQ的首要因素,这方便与我们的许多其他软件一起维护 , 通过引入队列使我们得到了堪称完美的表现,通过队列甚至让我们增加了几个数量级的吞吐量 。越来越多的consumer需要一套严格可靠性和顺序性保障,这已经超过了NSQ提供的简单功能 。
结合我们的业务系统来看,对于我们所需要传输的发票消息,相对比较敏感,无法容忍某个nsqd宕机,或者磁盘无法使用的情况,该节点堆积的消息无法找回 。这是我们没有选择该消息中间件的主要原因 。简单性和可靠性似乎并不能完全满足 。相比Kafka,ops肩负起更多负责的运营 。另一方面,它拥有一个可复制的、有序的日志可以提供给我们更好的服务 。但对于其他适合NSQ的consumer,它为我们服务的相当好,我们期待着继续巩固它的坚实的基础 。
关于go语言超时机制和go语言chan的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站 。

    推荐阅读