January 14, 2020
golang中有关select的几个知识点
"\u003cp\u003egolang中的select语句格式如下\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eselect {\n case \u0026lt;-ch1:\n // 如果从 ch1 信道成功接收数据,则执行该分支代码\n case ch2 \u0026lt;- 1:\n // 如果成功向 ch2 信道成功发送数据,则执行该分支代码\n default:\n // 如果上面都没有成功,则进入 default 分支处理流程\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e可以看到select的语法结构有点类似于switch,但又有些不同。\u003c/p\u003e\n\u003cp\u003eselect里的case后面并不带判断条件,而是一个信道的操作,不同于switch里的case,对于从其它语言转过来的开发者来说有些需要特别注意的地方。\u003c/p\u003e\n\u003cp\u003egolang 的 select 就是监听 IO 操作,当 IO 操作发生时,触发相应的动作每个case语句里必须是一个IO操作,确切的说,应该是一个面向channel的IO操作。\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e注:Go 语言的 \u003ccode\u003eselect\u003c/code\u003e 语句借鉴自 Unix 的 \u003ccode\u003eselect()\u003c/code\u003e 函数,在 Unix 中,可以通过调用 \u003ccode\u003eselect()\u003c/code\u003e 函数来监控一系列的文件句柄, …\u003c/p\u003e\u003c/blockquote\u003e"
January 11, 2020
golang性能调优工具
"\u003cp\u003e\u003ca href=\"https://cloud.tencent.com/developer/article/1478198\"\u003eGODEBUG\u003c/a\u003e, 输出结果以gc 开头的表示进行了gc垃圾回收操作,后面的数字表示gc 的次数\u003cimg src=\"https://blog.haohtml.com/wp-content/uploads/2020/01/golang_gc.jpg\" alt=\"\"\u003e \u003ca href=\"https://www.jianshu.com/p/ba9f07a346d5\"\u003epprof\u003c/a\u003e\u003c/p\u003e"
January 11, 2020
golang中的sync.Pool对象缓存
"\u003ch2 id=\"参考文章\"\u003e参考文章\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://juejin.im/post/5b7678f451882533110e8948\"\u003eGolang 的 协程调度机制 与 GOMAXPROCS 性能调优\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://www.cnblogs.com/sunsky303/p/9706210.html\"\u003e深入Golang之sync.Pool详解\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://www.jianshu.com/p/494cda4db297\"\u003egolang sync.Pool 分析\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://juejin.im/post/5d006254e51d45776031afe3\"\u003e[译] Go: 理解 Sync.Pool 的设计\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e视频 \u003ca href=\"https://time.geekbang.org/course/detail/160-87731\"\u003esync.pool对象缓存\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"知识点\"\u003e知识点\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003ePool只是一个缓存,一个缓存,一个缓存。由于生命周期受GC的影响,一定不要用于数据库连接池这类的应用场景,它只是一个缓存。\u003c/li\u003e\n\u003cli\u003egolang1.13版本对 \u003ca href=\"https://golang.org/pkg/sync/#Pool\"\u003ePool\u003c/a\u003e 进行了优化,结构体添加了两个字段 victim 和 victimSize。\u003c/li\u003e\n\u003cli\u003e适应于通过复用,降低复杂对象的创建和GC代价的场景\u003c/li\u003e\n\u003cli\u003e因为init()的时候会注册一个PoolCleanup函数,他会在gc时清除掉sync.Pool中的所有的缓存的对象。所以每个sync.Pool的生命周期为两次GC中间时段才有效,可以手动进行gc操作 \u003cstrong\u003eruntime.GC()\u003c/strong\u003e\u003c/li\u003e\n\u003cli\u003e由于要保证协程安全,所以会有锁的开销\u003c/li\u003e\n\u003cli\u003e每个Pool都有一个私有池(协程安全)和共享池(\u003cstrong\u003e协程不安全\u003c/strong\u003e),其中私有池只有存放一个值。\u003c/li\u003e\n\u003c/ul\u003e\n\u003col\u003e\n\u003cli\u003e每次Get()时会先从当前P的私有池private中获取( \u003ca href=\"https://studygolang.com/articles/11825\"\u003e类似MPG模型中的G\u003c/a\u003e)\u003c/li\u003e\n\u003cli\u003e如果获 …\u003c/li\u003e\u003c/ol\u003e"
January 11, 2020
golang 的编程模式之“功能选项”
"\u003cp\u003e最近在用go重构iot中的一个服务时,发现库 \u003ca href=\"https://github.com/apache/rocketmq-client-go/releases/tag/v2.0.0-rc1\"\[email protected]\u003c/a\u003e 在初始化消费客户端实现时,实现的极其优雅,代码见 \u003ca href=\"https://github.com/apache/rocketmq-client-go/blob/v2.0.0-rc1/examples/consumer/simple/main.go#L32\"\u003ehttps://github.com/apache/rocketmq-client-go/blob/v2.0.0-rc1/examples/consumer/simple/main.go#L32\u003c/a\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ec, _ := rocketmq.NewPushConsumer(\n consumer.WithGroupName(\u0026#34;testGroup\u0026#34;),\n consumer.WithNameServer([]string{\u0026#34;127.0.0.1:9876\u0026#34;}),\n)\nerr := c.Subscribe(\u0026#34;test\u0026#34;, consumer.MessageSelector{}, func(ctx context.Context,\n msgs ...*primitive.MessageExt) (consumer.ConsumeResult, error) {\n for i := …\u003c/code\u003e\u003c/pre\u003e"
January 4, 2020
MySQL中的 InnoDB Buffer Pool
"\u003ch2 id=\"一innodb-buffer-pool简介\"\u003e一、InnoDB Buffer Pool简介\u003c/h2\u003e\n\u003cp\u003eBuffer Pool是InnoDB引擎内存中的一块区域,主要用来缓存表和索引数据使用。我们知道从内存读取数据要比磁盘读取效率要高的多,这也正是buffer pool发挥的主要作用。一般配置值都比较大,在专用数据库服务器上,大小为物理内存的80%左右。\u003c/p\u003e\n\u003ch2 id=\"二buffer-pool-lru-算法\"\u003e二、Buffer Pool LRU 算法\u003c/h2\u003e\n\u003cp\u003eBuffer Pool 链表使用\u003cstrong\u003e优化改良后LRU\u003c/strong\u003e(最近最少使用)算法进行管理。\u003c/p\u003e\n\u003cp\u003e整个LRU链表可分为两个子链表,一个是New Sublist,也称为Young列表或新生代,另一个是Old Sublist ,称为Old 列表或老生代。每个子链表都有一个Head和Tail,中间部分是存储Page数据的地方。\u003c/p\u003e\n\u003cp\u003e当新的Page放入 Buffer Pool 缓存池的时候,会交其Page插入就是两个子链表的交界处,称为midpoint,同时就会有旧的Page被淘汰,整个操作过程都需要对链接进行维护。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eYoung 链表区存放的数据是经常访问的数据;\nOld 链表区存放是即将被淘汰的数据;\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e一个新数据先被插入到midpoint 位置,根据LRU算法访问频率高 …\u003c/p\u003e"
December 31, 2019
使用Dockerfile 多阶段构建Golang 应用
"\u003cp\u003edocker在开发和运维中使用的场景越来越多,作为开发人员非常有必要了解一些docker的基本知识,而离我们工作中最近的也就是对应用的docker部署编排了,小到一个dockerfile, docker-compse文件的编写,大到k8s的管理。这里我们以 golang应用为例讲解一些Dockerfile的基本用法,在ci/cd中经常用到这些知识。\u003c/p\u003e\n\u003ch1 id=\"前提\"\u003e前提\u003c/h1\u003e\n\u003cp\u003e\u003cstrong\u003e项目清单:\u003c/strong\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003edrwxr-xr-x 9 sxf staff 288 12 31 16:13 .\ndrwx------@ 17 sxf staff 544 12 31 14:59 ..\n-rw-r--r-- 1 sxf staff 14 12 31 16:09 .dockerignore\ndrwxr-xr-x 14 sxf staff 448 12 31 16:21 .git\n-rw-r--r-- 1 sxf staff 467 12 31 16:08 Dockerfile\n-rw-r--r-- 1 sxf staff 11 12 31 15:01 README.md …\u003c/code\u003e\u003c/pre\u003e"
November 8, 2019
Golang中的goroutine泄漏问题
"\u003cp\u003egoroutine作为Go中开发语言中的一大利器,在高并发中发挥着无法忽略的作用。但东西虽好,真正做到用好还是有一些要注意的地方,特别是对于刚刚接触这门开发语言的新手来说,稍有不慎,就极有可能导致goroutine 泄漏。\u003c/p\u003e\n\u003ch2 id=\"什么是goroutine-leak\"\u003e什么是goroutine Leak\u003c/h2\u003e\n\u003cp\u003egoroutine leak 的意思是go协程泄漏,那么什么又是协程泄漏呢?我们知道每次使用go关键字开启一个gorountine任务,经过一段时间的运行,最终是会结束,从而进行系统资源的释放回收。而如果由于操作不当导致一些goroutine一直处于阻塞状态或者永远运行中,永远也不会结束,这就必定会一直占用系统资源。最球的情况下是随着系统运行,一直在创建此类goroutine,那么最终结果就是程序崩溃或者系统崩溃。这种情况我们一般称为goroutine leak。\u003c/p\u003e\n\u003ch2 id=\"出现的问题\"\u003e出现的问题\u003c/h2\u003e\n\u003cp\u003e先看一段代码:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epackage main\n\nimport (\n \u0026#34;fmt\u0026#34;\n \u0026#34;math/rand\u0026#34;\n \u0026#34;runtime\u0026#34;\n \u0026#34;time\u0026#34;\n)\n\nfunc query() …\u003c/code\u003e\u003c/pre\u003e"
October 25, 2019
MySQL8.0中的跳跃范围扫描优化Skip Scan Range Access Method介绍
"\u003cp\u003e在MySQL8.0以前,索引使用规则有一项是索引左前缀,假如说有一个索引idx_abc(a,b,c),能用到索引的情况只有查询条件为a、ab、abc、ac这四种,对于只有字段b的where条件是无法用到这个idx_abcf索引的。这里再强调一下,这里的顺序并不是在where中字段出现的顺序,where b=2 and 1=1 也是可以利用到索引的,只是用到了(a,b)这两个字段\u003c/p\u003e\n\u003cp\u003e针对这一点, 从MySQL 8.0.13开始引入了一种新的优化方案,叫做 \u003cstrong\u003eSkip Scan Range\u003c/strong\u003e,翻译过来的话是\u003cstrong\u003e跳跃范围扫描\u003c/strong\u003e。如何理解这个概念呢?我们可以拿官方的SQL示例具体讲一下()\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eCREATE TABLE t1 (f1 INT NOT NULL, f2 INT NOT NULL, PRIMARY KEY(f1, f2));\nINSERT INTO t1 VALUES\n (1,1), (1,2), (1,3), (1,4), (1,5),\n (2,1), (2,2), (2,3), (2,4), (2,5);\nINSERT INTO t1 SELECT f1, f2 + 5 FROM t1; …\u003c/code\u003e\u003c/pre\u003e"
October 18, 2019
一致性哈希算法及其在分布式系统中的应用(推荐)
"\u003ch1 id=\"摘要\"\u003e摘要\u003c/h1\u003e\n\u003cp\u003e本文将会从实际应用场景出发,介绍一致性哈希算法(Consistent Hashing)及其在分布式系统中的应用。首先本文会描述一个在日常开发中经常会遇到的问题场景,借此介绍一致性哈希算法以及这个算法如何解决此问题;接下来会对这个算法进行相对详细的描述,并讨论一些如虚拟节点等与此算法应用相关的话题。\u003c/p\u003e\n\u003ch1 id=\"分布式缓存问题\"\u003e分布式缓存问题\u003c/h1\u003e\n\u003cp\u003e假设我们有一个网站,最近发现随着流量增加,服务器压力越来越大,之前直接读写数据库的方式不太给力了,于是我们想引入Memcached作为缓存机制。现在我们一共有三台机器可以作为Memcached服务器,如下图所示。\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"https://blogstatic.haohtml.com//uploads/2023/09/1.png\" alt=\"\"\u003e\u003c/p\u003e\n\u003cp\u003e很显然,最简单的策略是将每一次Memcached请求随机发送到一台Memcached服务器,但是这种策略可能会带来两个问题:一是同一份数据可能被存在不同的机器上而造成数据冗余,二是有可能某数据已经被缓存但是访问却没有命中,因为无法保证对相同key的所有访问都被发送到相同的服务器。因此,随机策略无论是时间效率还是空间效率都非常不好。\u003c/p\u003e\n\u003cp\u003e要解决上述问题只需做到如下一点:保证对相同key的访问会被发送到相同的服务器。很多方法可以实现这一点,最常用的方法是计算哈希。例 …\u003c/p\u003e"
August 31, 2019
Redis 选择hash还是string 存储数据?
"\u003cp\u003e在Redis中存储数据时,经常使用string和hash这两种类型,至于这两者有什么不同,底层实现有何区别,推荐参考: \u003ca href=\"https://segmentfault.com/a/1190000019552836\"\u003ehttps://segmentfault.com/a/1190000019552836\u003c/a\u003e\u003c/p\u003e"
August 13, 2019
一文理解MySQL中的page页
"\u003cp\u003e在介绍InnoDB中的页的时候,很有必要先让大家了解一下InnoDB中的存储结构\u003cimg src=\"https://blogstatic.haohtml.com//uploads/2023/09/innodb_engine_struct.png\" alt=\"\"\u003e\u003c/p\u003e\n\u003cp\u003e从InnoDB存储引擎的逻辑结构看,所有数据都被逻辑地存放在一个空间内,称为表空间(tablespace),而表空间由段(sengment)、区(extent)、页(page)组成。 在一些文档中extend又称块(block)。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e一、表空间(table space)\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e表空间(Tablespace)是一个逻辑容器,表空间存储的对象是段,在一个表空间中可以有一个或多个段,但是一个段只能属于一个表空间。数据库由一个或多个表空间组成,表空间从管理上可以划分为系统表空间、用户表空间、撤销表空间、临时表空间等。\u003c/p\u003e\n\u003cp\u003e在 InnoDB 中存在两种表空间的类型:共享表空间和独立表空间。如果是共享表空间就意味着多张表共用一个表空间。如果是独立表空间,就意味着每张表有一个独立的表空间,也就是数据和索引信息都会保存在自己的表空间中。独立的表空间可以在不同的数据库之间进行迁移。可通过命令\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003emysql \u0026gt; show variables like \u0026#39;innodb_file_per_table\u0026#39;;\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e查看当前系统启用 …\u003c/p\u003e"
August 12, 2019
一列说明数组与Hash效率的区别到底多大
"\u003cp\u003e在数组中添加 10000 个元素,然后分别对这 10000 个元素进行检索,最后统计检索的时间。\u003c/p\u003e\n\u003cp\u003e数组Array\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eimport time\n# 插入数据,数组\nresult = []\nfor i in range(10000):\n result.append(i)\n# 检索数据\ntime_start=time.time()\nfor i in range(10000):\n temp = result.index(i)\ntime_end=time.time()\nprint(\u0026#39;检索时间\u0026#39;, time_end-time_start)\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e运行结果:\u003c/p\u003e\n\u003cp\u003e检索时间为 \u003cstrong\u003e1.2436728477478027\u003c/strong\u003e 秒。\u003c/p\u003e\n\u003cp\u003eHash哈希\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eimport time\n# 插入数据\nresult = {}\nfor i in range(1000000):\n result[i] = i\n# 检索数据\ntime_start=time.time()\nfor i in range(10000):\n temp = result[i]\ntime_end=time.time() …\u003c/code\u003e\u003c/pre\u003e"
July 29, 2019
kubernetes dashboard向外网提供服务
"\u003cp\u003e目前新版本的 kubernetes dashboard ()安装了后,为了安全起见,默认情况下已经不向外提供服务,只能通过 \u003ca href=\"http://localhost:8001/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/\"\u003e\u003ccode\u003ehttp://localhost:8001/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/\u003c/code\u003e\u003c/a\u003e 本机访问。在我们学习过程中,总有些不方便,这时我们可以利用 \u003ccode\u003ekubectl proxy\u003c/code\u003e 命令来实现。\u003c/p\u003e\n\u003cp\u003e首先我们看一下此命令的一些想着参数\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e➜ ~ kubectl proxy -h\nTo proxy all of the kubernetes api and nothing else, use:\n\n $ kubectl proxy --api-prefix=/\n\nTo proxy only part of the kubernetes api and also some static files:\n\n $ kubectl proxy --www=/my/files --www-prefix=/static/ --api-prefix=/api/\n\nThe above …\u003c/code\u003e\u003c/pre\u003e"
July 28, 2019
RabbitMQ常见面试题
"\u003col\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"https://www.rabbitmq.com/getstarted.html\"\u003eRabbitMq的消息类型(6种)\u003c/a\u003e、 \u003ca href=\"https://blog.haohtml.com/archives/15285\"\u003e消息确认机制\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eRabbitMq中的概念及解释\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003cul\u003e\n\u003cli\u003e \u003ccode\u003eServer(Broker)\u003c/code\u003e: 接收客户端连接,实现AMQP协议的消息队列和路由功能的进程;\u003c/li\u003e\n\u003cli\u003e \u003ccode\u003eVirtual Host\u003c/code\u003e:虚拟主机的概念,类似权限控制组,一个Virtual Host里可以有多个Exchange和Queue。\u003c/li\u003e\n\u003cli\u003e \u003ccode\u003eExchange\u003c/code\u003e: 交换机,接收生产者发送的消息,并根据Routing Key将消息路由到服务器中的队列Queue。\u003c/li\u003e\n\u003cli\u003e \u003ccode\u003eExchangeType\u003c/code\u003e: 交换机类型决定了路由消息行为,RabbitMQ中有四种类型Exchange,分别是\u003ccode\u003efanout\u003c/code\u003e、\u003ccode\u003edirect\u003c/code\u003e、\u003ccode\u003etopic\u003c/code\u003e 和 \u003ccode\u003eheaders\u003c/code\u003e;\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eQueue\u003c/code\u003e:消息队列,用于存储还未被消费者消费的消息;\u003c/li\u003e\n\u003cli\u003e \u003ccode\u003eMessage\u003c/code\u003e:由Header和body组成,Header是由生产者添加的各种属性的集合,包括Message是否被持久化、优先级是多少、由哪个Message Queue接收等;body是真正需要发送的数据内容;\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eBindingKey\u003c/code\u003e:绑定关键字,将一个特定的 \u003ccode\u003eExchange\u003c/code\u003e 和一个特定的 \u003ccode\u003eQueue\u003c/code\u003e 绑定起来。\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e2. …\u003c/p\u003e"
July 28, 2019
rabbitmq消息队列的消息持久化机制
"\u003cp\u003e首先阅读这篇文章: \u003ca href=\"https://blog.csdn.net/yongche_shi/article/details/51500534\"\u003ehttps://blog.csdn.net/yongche_shi/article/details/51500534\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e之前其实已经写过一篇关于RabbitMQ持久化的 \u003ca href=\"http://jzhihui.iteye.com/blog/1642324\"\u003e文章\u003c/a\u003e ,但那篇文章侧重代码层面的写入流程,对于持久化操作何时发生以及什么时候会刷新到磁盘等问题其实都没有搞清楚,这篇文章着重于关注这些问题。\u003c/p\u003e\n\u003ch2 id=\"消息什么时候需要持久化\"\u003e消息什么时候需要持久化?\u003c/h2\u003e\n\u003cp\u003e根据 \u003ca href=\"http://www.rabbitmq.com/blog/2011/01/20/rabbitmq-backing-stores-databases-and-disks/\"\u003e官方博文\u003c/a\u003e 的介绍,RabbitMQ在两种情况下会将消息写入磁盘:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e消息本身在 \u003ccode\u003epublish\u003c/code\u003e 的时候就要求消息写入磁盘;\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e内存紧张\u003c/code\u003e 需要将部分内存中的消息转移到磁盘;\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 id=\"消息什么时候会刷到磁盘\"\u003e消息什么时候会刷到磁盘?\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003e写入文件前会有一个Buffer,大小为1M(1048576),数据在写入文件时,首先会写入到这个Buffer,如果Buffer已满,则会将Buffer写入到文件(未必刷到磁盘);\u003c/li\u003e\n\u003cli\u003e有个固定的刷盘时间:25ms,也就是不管Buffer满不满,每隔25ms,Buffer里的数据及未刷新到磁盘的文件内容必定会刷到磁盘;\u003c/li\u003e\n\u003cli\u003e每次消息写入后,如果没有后续写入请求,则会直接将已写入的消息刷到磁盘:使用Erlang的receive x after 0 …\u003c/li\u003e\u003c/ol\u003e"