go语言select的作用
Go里面提供了一个关键字select,通过select可以监听channel上的数据流动。
创新互联公司始终坚持【策划先行,效果至上】的经营理念,通过多达十余年累计超上千家客户的网站建设总结了一套系统有效的全网整合营销推广解决方案,现已广泛运用于各行各业的客户,其中包括:成都石雕等企业,备受客户赞誉。
select的用法与switch语言非常类似,由select开始一个新的选择块,每个选择条件由case语句来描述。
与switch语句相比, select有比较多的限制,其中最大的一条限制就是每个case语句里必须是一个IO操作,大致的结构如下:
在一个select语句中,Go语言会按顺序从头至尾评估每一个发送和接收的语句。
如果其中的任意一语句可以继续执行(即没有被阻塞),那么就从那些可以执行的语句中任意选择一条来使用。
如果没有任意一条语句可以执行(即所有的通道都被阻塞),那么有两种可能的情况:
如果给出了default语句,那么就会执行default语句,同时程序的执行会从select语句后的语句中恢复。
如果没有default语句,那么select语句将被阻塞,直到至少有一个通信可以进行下去
有时候会出现goroutine阻塞的情况,那么我们如何避免整个程序进入阻塞的情况呢?我们可以利用select来设置超时,通过如下的方式实现:
select总结:
作用: 用来监听 channel 上的数据流动方向。 读?写?
select实现fibonacci数列:
golang实现本地延迟队列
有个服务会大量使用延迟消息,进行事件处理。随着业务量不断上涨。在晚间、节假日等流量高峰期消息延迟消息队列限流会导致事件丢失,影响业务。与下游沟通后给上调到了最大限流值,问题依然存在,于是决定自己搞一套降级方案。
下游服务触发限流时,能降级部分流量到本地延迟队列,把业务损失降到最低。
本地延迟队列承接部分mq流量
流程如下:
1. 使用zset 存储延迟消息,其中:score为执行时间,value为消息体
2. 启动协程轮询zset,获取score最小的10条数据,协程执行间隔时间xs
如果最小分值小于等于当前时间戳,则发送消息
若最小分值大于当前时间戳,sleep等待执行
需要对key进行hash,打散到多个分片中,避免大key和热key问题,官方大key定义
因此,需保证每个key中value数量n5000,单个value大小不超过 10240/n kb
假设承接10w qps,如何处理?
10w qps延迟120s时,最开始消息队列会积累100000*120=12000000条消息
假如每条消息大小500b,需占用存储6000000kb = 6000Mb = 6GB
为避免大key问题,每个zset存放4000个元素,需要哈希到3000(3000是key的数量,可配置)个zset中。
整个集群假设500台实例,每个处理qps平均在200左右。
单实例消费能力计算:
遍历每个zset,针对每个zset起goroutine处理,此示例中需要 起3000个
但是每秒能处理成功的只有200个,其他都在空跑
综上:
将redis key分片数n和每次处理的消息数m进行动态配置,便于调整
当流量上涨时,调大分片数n和单实例单分片并发数m即可,假如消费间隔200ms,集群处理能力为n*m*5 qps
n = (qps * 120) / 4000
若qps=q,则计算公式如下
zadd = q
zRange = 500 * 5 * n / 500
zRemove = q
setNx = 500 * 5 * n
若10w qps,则
读qps = 15000 + 500*3000*5 =7515000,写 20w
pros
redis 读写性能好,可支持较大并发量,zrange可直接取出到达执行时间的消息
cons
redis 大key问题导致对数据量有一定的限制
分片数量扩缩容会漏消费,会导致事件丢失,业务有损
key分片数量过多时,redis读写压力较大
机器资源浪费,3000个协程,单实例同一秒只有200个针对处理,其他都在空跑
流程如下:
使用带缓冲的channel来实现延迟队列,channel中存放的数据为消息体(包括执行时间),channel能保证先进先出
从channel中取出数据后,判断是否到达执行时间
到达,同步发送mq
未到达,sleep 剩余执行时间,然后再次执行
从channel读出的数据如果未到达执行时间,无法再次放入channel中,需要协程sleep(执行时间-当前时间)
10w qps延迟120s时,最开始消息队列会积累100000*120=12000000条消息,假设每条消息大小500b,需要6G存储空间
channel 大小 = (qps*120)/ c , c=集群实例数,c=500 = channel大小为24000,占用12M内存
要处理10w qps,分摊到每个机器的处理速度为 100000/500 = 200,假设单协程处理10qps,开20个即可。
pros:
本地存储,相比redis,读写速度更快;协程数量少,开销低;资源利用率较方案一高
cons:
稳定性不如redis,实例故障可能导致数据丢失;worker池和channel扩缩容依赖服务重启,成本高速度慢
综上,我们以10w qps为例,对比两种方案在以下指标差异,选择方案二。
附上demo
Go时区设置
全球以英国伦敦格林威治作为零度经线的起点,每隔15经度为一个时区,15度经线为该时区的中央经线,共分为24个时区。由西向东每隔15经度增加一个时区,相反的,每向西15经度减少一个时区。中国所在时区为东8区。
当前时间 time.Now() 返回的是当地时区的时间:
CST可以代表如下四个不同的时区:
time.Now() 返回的 +0800 CST 表示的就是中国标准时间,与UTC时间有如下的转化:
Wall Clocks表示挂钟时间,存储的是自1970 年 1 月 1 日 0 时 0 分 0 秒以来的时间戳,当系统和授时服务器进行校准时间时间操作时,有可能造成这一秒是2018-1-1 00:00:00,而下一秒变成了2017-12-31 23:59:59的情况。
Monotonic Clocks,意思是单调时间的,所谓单调,就是只会不停的往前增长,不受校时操作的影响,这个时间是自进程启动以来的秒数。
time.Now() 返回的 m=+0.004002201 就是表示Monotonic Clocks
go语言中如果不设置指定的时区,通过 time.Now() 获取到的就是本地时区
设置时区有两种方式:
固定时区到东八区。但这种不是对程序的全局设置,每次获取时都需要固定时区
加载指定时区。但如果没有go环境使用这种方式就会加载失败,因为时区信息是放在go的安装包中的。
如果你用第二种方式加载时区,在打docker镜像时就需要进行时区相关的配置,配置文件如下:
参考文章:
golang 读取服务器时间 延迟问题怎么解决
简单减少slave同步延案架构做优化尽量让主库DDL快速执行主库写数据安全性较高比sync_binlog=1innodb_flush_log_at_trx_commit = 1 类设置slave则需要高数据安全完全讲sync_binlog设置0或者关闭binloginnodb_flushlog设置0提高sql执行效率另外使用比主库更硬件设备作slave
mysql-5.6.3已经支持线程主复制原理丁奇类似丁奇表做线程Oracle使用数据库(schema)单位做线程同库使用同复制线程
sync_binlog=1
This makes MySQL synchronize the binary log’s contents to disk each time it commits a transaction
默认情况并每写入都binlog与硬盘同步操作系统或机器(仅仅MySQL服务器)崩溃能binlog语句丢 失要想防止种情况使用sync_binlog全局变量(1安全值慢)使binlog每Nbinlog写入与硬盘 同步即使sync_binlog设置1,现崩溃能表内容binlog内容间存致性使用InnoDB表MySQL服务器 处理COMMIT语句整事务写入binlog并事务提交InnoDB两操作间现崩溃重启事务InnoDB滚仍 存binlog用--innodb-safe-binlog选项增加InnoDB表内容binlog间致性(注释:MySQL 5.1需要--innodb-safe-binlog;由于引入XA事务支持该选项作废)该选项提供更程度安全使每事务 binlog(sync_binlog =1)(默认情况真)InnoDB志与硬盘同步该选项效崩溃重启滚事务MySQL服务器binlog剪切滚 InnoDB事务确保binlog反馈InnoDB表确切数据等并使服务器保持与主服务器保持同步(接收 滚语句)
innodb_flush_log_at_trx_commit (管用)
抱怨Innodb比MyISAM慢 100倍概忘调整值默认值1意思每事务提交或事务外指令都需要志写入(flush)硬盘费特别使用电 池供电缓存(Battery backed up cache)设2于运用特别MyISAM表转意思写入硬盘写入系统缓存志仍每秒flush硬 盘所般丢失超1-2秒更新设0更快点安全面比较差即使MySQL挂能丢失事务数据值2整操作系统 挂才能丢数据
文章名称:go语言设置延时 go语言 效率
转载来源:http://scgulin.cn/article/docgggp.html