P158内容回顾
TODO,比如有些函数包里定义的必须要传context.context对象,这时候你不知道传什么,这是你的模块不需要conetxt的一些超时或者deadline的控制,那就传一个TODO即可,方便 向后兼容。
background会提高一个根节点,往下传递。
withcancel,返回一个ctx对象和一个取消函数,把它当参数传到goroutine的一个执行的函数里,cancel就进行 在外面goroutine控制子goroutine的退出。
withTimeout设置一个相对时间
witthDeadline设置一个绝对的时间。
context.withvalue(),传递上下文相关的value
把配置文件读出来映射成go的结构体,cfg是需要修改的,传一个指针的
只能读的单向通道
P159 今日内容概要
P160 etcd介绍
etcd是go语言开发的一个开源高可用分布式kv存储,用于配置共享和服务注册发现
etcd具有以下特点:
完全复制:集群中的每个节点都可以使用完整的存档
高可用性: Etcd可用于避免硬件的单点故障或网络问题
一致性:每次读取都会返回跨多主机的最新写入
简单:包括一个定义良好、面向用户的API (gRPC)
安全:实现了带有可选的客户端证书身份验证的自动化TLS
快速:每秒10000次写入的基准速度
可靠:使用Raft算法实现了强一致、高可用的服务存储目录。
raft算法区别于zookeeper用的帕克索斯
可以在etcd上注册watcher并等待,每次配置有更新的时候,etcd会通知订阅者,实现热加载
**将所有分布式锁的机制交给etcd来控制,它从底层的raft原理实现了锁机制,要么取成功,要么不成功。
**
etcd里创建了一个key,lock,三个服务取抢,只有一个人能成功,其他都失败,谁抢到了value就是谁的。
为什么用etcd而不用ZooKeeper?
etcd实现的这些功能, Zookeeper都能实现。那么为什么用etcd而非直接使用Zookeeper呢?
相较之下, ZooKeeper有如下缺点
1,复杂。ZooKeeper的部署维护复杂,管理员需要掌握一系列的知识和技能;而Paxos强一致性算法也是素来以复杂难懂而闻名于世;另外, ZooKeeper的使用也比较复杂,需要安装客户端,官方只提供了Java和C两种语言的接口。
2. Java编写。这里不是对Java有偏见,而是Java本身就偏向于重型应用,它会引入大量的依赖。而运维人员则普遍希望保持强一致、高可用的机器集群尽可能简单,维护起来也不易出错。
3. 3、发展缓慢. Apache基金会项目特有的Apache way在开源界饱受争议,其中一大原因就是由于基金会庞大的结构以及松散的管理导致项目发展缓慢。
etcd部署比较方便,二进制文件,直接启动,是http的接口,数据默认已更新就持久化
etcd架构,httpserver相当于代理(v3,是grpc,二进制的协议),raft算法,
从etcd的架构图中我们可以看到, etcd主要分为四个部分。
HTTP Server:用于处理用户发送的API请求以及其它etcd节点的同步与心跳信息请求。
Store:用于处理etcd支持的各类功能的事务,包括数据索引、节点状态变更、监控与反馈、事件处理与执行等等,是etcd对用户提供的大多数API功能的具体实现。
Raft: Raft强一致性算法的具体实现,是etcd的核心。
WAL: Write Ahead Log (预写式日志) ,是etcd的数据存储方式。除了在内存中存有所有数据的状态以及节点的索引以外, etcd就通过WAL进行持久化存储。WAL中,所有的数据提交前都会事先记录日志。Snapshot是为了防止数据过多而进行的状态快照; Entry表示存储的具体日志内容。
etcd是作为一个高可用的支持项目,天生为了集群而配置的。
raft算法带一个任期的索引,还有一个日志的索引,不同的节点是有优先级的,任期最新和日志最新才能被选举做leader
token随便起一个,不同集群不一样就行,cluster_state=new 创建新的集群,如果是新节点加入老集群就不是new了,
cluster相当于集群里面有几个节点。
依次启动二进制文件,-- data-dir数据存在哪里,–name节点名称,–listen-peer-urls集群通信节点,
advertise-client-urls 客户端2379.
–initial-cluster初始化集群,这个节点属于哪个集群。
–initial-cluster-state状态,上面的状态变量
–initial-cluster-token上面token变量
官网也提高了一个方式,指向公网的一个地址,就认为节点在一个集群里
P161 etcd搭建
最新的默认已经是v3版本了
设置环境变量,现在用etcdctl就是v3版本接口
put就是设置键值对
没有put命令就是环境变量没有设置v3版本,ok就是设置成功
get获取值
del删除key
先下载包
字符串切片可以传多个,5秒超时时间
延时关闭连接
写一个watch,观察值的变化
下面需要连接etcd,然后做watch操作,只要看到值变化,就通过通道通知,
ch 派一个哨兵去监控etcd的key变化,key发生变化,就会发消息给通道,从通道里尝试去取值,值是一个结构体,
cancel是立即释放掉context的资源
这样就不退出了
这样可以做一个配置热更新,服务不重启
P162 etcd操作
可以在之前的logagent里修改,之前是从配置文件里读,配置项可以不读,放在etcd里
先加载配置文件,初始化kafka,初始化etcd
建立一个etcd模块并配上超时时间
初始化etcd
先启动zookeeper
启动kafka
P163 logagent从etcd加载收集项配置
第一步初始化kafka,第二步初始化etcd,下面开启日志收集,从etcd中获取日志收集项的配置信息,指定一个哨兵取监视日志收集项目的变化
从etcd里收集获取配置
这样就可以直接序列化
watch etcd 的key ‘’/xxx‘
传一个指针进来
这样就有值了
P164 logagent上午内容回顾
这是新增的部分,初始化需要etcd的地址和超时的时间
main函数这样就不需要写死了
需要收集的日志的配置信息
etcd put直接把数据推进去
这个变量是日志收集的slice
现在有三项
etcd里的key收集项,logagent是可以收集到的
P165 logagent根据etcd的配置创建
现在for循环里有几个配置项造几个配置项
对tailobj一行一行读取数据
taltask代替tailobj,代表是一个日志收集的任务收集的路径,收集的日志放到哪个topic下,具体的tailf对象
构造函数,返回tailObj对象
这样每一个tailtask都有readchan
现在做一个具体收集,下面的readchan就不需要了
开启一个gorutine去跑run方法,去instance里去读,赋值到line里
可以把tailllogmgr单独拿出来
main函数里就可以直接这么写
先把日志数据放到一个通道中,kafka那个包中有单独的goroutine去取日志数据发的kafka
定义了一个全局变量,但是没有初始化,不能往里放
通道大小可以配置
新增一个参数
提供外部的一个函数,这个函数只把日志数据发送到一个内部的channel
从通道取数据,不满足分支,就写一个default,真正往kafka发送日志的函数
从通道取数据发往kafka
把kafka这个配置传进去
退出是因为主程序没有run
P166 logagent实现watch新配置
当etcd配置项进行变化的时候,因为调整tailtask任务,原来3个任务,新增一个配置项,就需要新创建一个tailtask去收集日志;
2.原来有,现在没有了,把对应的tailtask停止;
3.有关闭的,有重新创建的。
写taillogmgr就是用map的方式把有的tailtask都存起来。
其实就是派一个哨兵去观察配置的变化,获得到配置,想办法传递到真正用这个配置的地方。
单独拎出来,做一个函数
watchconf,这个哨兵拿到配置后,需要去通知别人
写一个等待新配置来的通道
通道建立好后,需要make做初始化
从newconfchan一直去读,然后对配置进行新增,删除,变更
所有init之后,开始run
写一个函数,向外暴露tskMgr的newConfChan,这就是go语言写代码常用场景,给内部私有对象字段向外暴露
上面会卡住,但是监听操作时异步的,放在监听里就可以,如果删除操作,就可以返回空
从taillog包中获取对外暴露的通道,下面是哨兵,发现最新的配置信息,会通知上面的通道
这么写,可能因为下面的原因报空指针
一定要先调用init,再执行
传递新配置
这里就拿到配置了
如果删除,会手动传一个空的slice
现在是空的对象
设置key为空
当一个新的配置文件再etcd设置的时候,应该通知taillog模块,做监听是在etcd模块,(初始化etcd后,第一次拿到配置后,就派小弟监听)
如何watch(拿到新配置还要通知taillog模块,利用管道通知,把配置放到管道里),但tailog模块才是真正去收集日志的模块
taillogmgr,从管道里拿最新配置
P167 logagent实现新增收集任务
**拿到新配置后就需要进行日志收集了,如何判断是不是新的配置,不同的配置项,肯定路径不可能一致。
**
topic和路径连成一个key
如果配置存在及continue,如果不存在就新增
之前已经把配置文件存起来过
找出原来t.logEntry有的,但是newconf中没有的,要删掉
把mysql.log删除,到时候新增一个
消费者现在能收到更新的日志
增加一个mysql.log的配置
新配置来了,等待对应日志的出现
新建mysql日志
现在就检测出来日志了
现在就能收集到最新的日志
P168 logagent删除新配置中没有的那个任务
第一层循环拿到最先的配置项,然后跟 新的watch进行比较,要看看有没有删除掉的配置,就需要topic和path都进行比较,如果满足if条件,说明原来有现在也有,如果没找到说明,原来有现在没有了。
如果有删除的 配置项,就需要想办法把tailobj里的给停掉了
就需要把run 给停掉
在上下文中把在执行的tailtask停掉,为了实现退出t.run(),需要加两个字段
在初始化对象的时候加上去
go一个函数,当函数退出的时候,goroutine就结束了
这里根据对象拿到tailobj
现在有三个配置项
把mysql的删除掉
etcd的watch,底层是基于socket建立连接发送消息的
P169 logagent根据IP拉取自己的配置
不通服务线的机器,拉取的服务器环境都不一样,应该每个log agent拉取不同的服务器位置上的日志
可以通过服务器的ip来区分
拿到本机的对外ip
把这个代码放到logagent包里,相当于logagent启动后,先去加载本机ip地址,根据ip地址去替换本机%s,这样再去拉配置,就是去拉自己ip的配置
建立一个utils
实现每个logagent都拉取自己独有的配置,所以要以自己的ip地址作为区分
这里别忘记改