上一篇文章主要是关于整体架构以及用到的软件的一些介绍,这一篇文章是对各个软件的使用介绍,当然这里主要是关于架构中我们 agent 的实现用到的内容关于zookeeper+kafka 我们需要先把两者启动,先启动 zookeeper, 再启动 kafka, 启动 ZooKeeper:./bin/zkServer.sh start, 启动 kafka:./bin/kafka-server-start.sh ./config/server.properties 。 操作 kafka 需要安装一个包: go get github.com/Shopify/sarama 写一个简单的代码,通过 go 调用往 kafka 里扔数据:来源
https://www.cnblogs.com/zhaof/p/8673420.html
package mainimport ( "github.com/Shopify/sarama" "fmt")func main() {
config := sarama.NewConfig() config.Producer.RequiredAcks = sarama.WaitForAll config.Producer.Partitioner = sarama.NewRandomPartitioner config.Producer.Return.Successes = true msg := &sarama.ProducerMessage{} msg.Topic = "nginx_log" msg.Value = sarama.StringEncoder("this is a good test,my message is zhaofan") client,err := sarama.NewSyncProducer([]string{
"192.168.0.118:9092"},config) if err != nil{
fmt.Println("producer close err:",err) return } defer client.Close() pid,offset,err := client.SendMessage(msg) if err != nil{
fmt.Println("send message failed,",err) return } fmt.Printf("pid:%v offset:%v\n",pid,offset)}
config.Producer.RequiredAcks = sarama.WaitForAll 这里表示是在给 kafka 扔数据的时候是否需要确认收到 kafka 的 ack 消息。 msg.Topic = "nginx_log" 因为 kafka 是一个分布式系统,假如我们要读的是 nginx 日志,apache 日志,我们可以根据 topic 做区分,同时也是我们也可以有不同的分区。 我们将上述代码执行一下,就会往 kafka 中扔一条消息,可以通 过 kakfa 中自带的消费者命令查看: ./bin/kafka-console-consumer.sh --zookeeper localhost:2181 --topic nginx_log --from-beginning
我们可以将最后的代码稍微更改一下,更改为循环发送:
for{
pid,offset,err := client.SendMessage(msg) if err != nil{
fmt.Println("send message failed,",err) return } fmt.Printf("pid:%v offset:%v\n",pid,offset) time.Sleep(2*time.Second)}
这样当我们再次执行的程序的时候,我们可以看到客户端在不停的消费到数据:
这样我们就实现一个 kakfa 的生产者的简单的 demo 接下来我们还需要知道一个工具的使用 tailftailf
package mainimport ( "github.com/hpcloud/tail" "fmt" "time")func main() {
filename := "/Users/zhaofan/go_project/src/go_dev/13/tailf/my.log" tails,err := tail.TailFile(filename,tail.Config{
ReOpen:true, Follow:true, Location:&tail.SeekInfo{Offset:0,Whence:2}, MustExist:false, Poll:true, }) if err !=nil{
fmt.Println("tail file err:",err) return } var msg *tail.Line var ok bool for true{
msg,ok = if !ok{
fmt.Printf("tail file close reopen,filenam:%s\n",tails,filename) time.Sleep(100*time.Millisecond) continue } fmt.Println("msg:",msg.Text) }}
我们通过下面一个例子对这个包进行一个基本的使用,更详细的 api 说明看: https://godoc.org/github.com/hpcloud/tail 最终实现的效果是当你文件里面添加内容后,就可以不断的读取文件中的内容日志库的使用
package mainimport ( "github.com/astaxie/beego/logs" "encoding/json" "fmt")func main() {
config := make(map[string]interface{}) config["filename"] = "/Users/zhaofan/go_project/src/go_dev/13/log/logcollect.log" config["level"] = logs.LevelTrace configStr,err := json.Marshal(config) if err != nil{
fmt.Println("marshal failed,err:",err) return } logs.SetLogger(logs.AdapterFile,string(configStr)) logs.Debug("this is a debug,my name is %s","stu01") logs.Info("this is a info,my name is %s","stu02") logs.Trace("this is trace my name is %s","stu03") logs.Warn("this is a warn my name is %s","stu04")}
简单版本logagent的实现
├── conf│ └── app.conf├── config.go├── kafka.go├── logs│ └── logcollect.log├── main.go└── server.go
app.conf : 配置文件
config.go: 用于初始化读取配置文件中的内容,这里的配置文件加载是通过之前自己实现的配置文件热加载包处理的,博客地址:http://www.cnblogs.com/zhaof/p/8593204.html
logcollect.log: 日志文件
kafka.go: 对 kafka 的操作,包括初始化 kafka 连接,以及给 kafka 发送消息
server.go: 主要是 tail 的相关操作,用于去读日志文件并将内容放到 channel 中
// 这里主要是kafak的相关操作,包括了kafka的初始化,以及发送消息的操作package mainimport ( "github.com/Shopify/sarama" "github.com/astaxie/beego/logs")var ( client sarama.SyncProducer kafkaSender *KafkaSender)type KafkaSender struct {
client sarama.SyncProducer lineChan chan string}// 初始化kafkafunc NewKafkaSender(kafkaAddr string)(kafka *KafkaSender,err error){
kafka = &KafkaSender{
lineChan:make(chan string,100000), } config := sarama.NewConfig() config.Producer.RequiredAcks = sarama.WaitForAll config.Producer.Partitioner = sarama.NewRandomPartitioner config.Producer.Return.Successes = true client,err := sarama.NewSyncProducer([]string{kafkaAddr},config) if err != nil{
logs.Error("init kafka client failed,err:%v\n",err) return } kafka.client = client for i:=0;i // 根据配置文件循环开启线程去发消息到kafka go kafka.sendToKafka() } return}func initKafka()(err error){
kafkaSender,err = NewKafkaSender(appConfig.kafkaAddr) return}func (k *KafkaSender) sendToKafka(){
//从channel中读取日志内容放到kafka消息队列中 for v := range k.lineChan{
msg := &sarama.ProducerMessage{} msg.Topic = "nginx_log" msg.Value = sarama.StringEncoder(v) _,_,err := k.client.SendMessage(msg) if err != nil{
logs.Error("send message to kafka failed,err:%v",err) } }}func (k *KafkaSender) addMessage(line string)(err error){
//我们通过tailf读取的日志文件内容先放到channel里面 k.lineChan return}
server.go的代码为:
package mainimport ( "github.com/hpcloud/tail" "fmt" "sync" "github.com/astaxie/beego/logs" "strings")type TailMgr struct {
//因为我们的agent可能是读取多个日志文件,这里通过存储为一个map tailObjMap map[string]*TailObj lock sync.Mutex}type TailObj struct {
//这里是每个读取日志文件的对象 tail *tail.Tail offset int64 //记录当前位置 filename string}var tailMgr *TailMgrvar waitGroup sync.WaitGroupfunc NewTailMgr()(*TailMgr){
tailMgr = &TailMgr{
tailObjMap:make(map[string]*TailObj,16), } return tailMgr}func (t *TailMgr) AddLogFile(filename string)(err error){
t.lock.Lock() defer t.lock.Unlock() _,ok := t.tailObjMap[filename] if ok{
err = fmt.Errorf("duplicate filename:%s\n",filename) return } tail,err := tail.TailFile(filename,tail.Config{
ReOpen:true, Follow:true, Location:&tail.SeekInfo{Offset:0,Whence:2}, MustExist:false, Poll:true, }) tailobj := &TailObj{
filename:filename, offset:0, tail:tail, } t.tailObjMap[filename] = tailobj return}func (t *TailMgr) Process(){
//开启线程去读日志文件 for _, tailObj := range t.tailObjMap{
waitGroup.Add(1) go tailObj.readLog() }}func (t *TailObj) readLog(){
//读取每行日志内容 for line := range t.tail.Lines{
if line.Err != nil {
logs.Error("read line failed,err:%v",line.Err) continue } str := strings.TrimSpace(line.Text) if len(str)==0 || str[0] == '\n'{
continue } kafkaSender.addMessage(line.Text) } waitGroup.Done()}func RunServer(){
tailMgr = NewTailMgr() // 这一部分是要调用tailf读日志文件推送到kafka中 for _, filename := range appConfig.LogFiles{
err := tailMgr.AddLogFile(filename) if err != nil{
logs.Error("add log file failed,err:%v",err) continue } } tailMgr.Process() waitGroup.Wait()}
可以整体演示一下代码实现的效果,当我们运行程序之后我配置文件配置的目录为: log_files=/app/log/a.log,/Users/zhaofan/a.log 我通过一个简单的代码对对a.log循环追加内容,你可以从kafka的客户端消费力看到内容了:
完成的代码地址: https://github.com/pythonsite/logagent小结
51Reboot 将在 2020.1.16日 21:00 为您带来分享主题
《大佬教你如何从 ES 初学者到 ES专家》
参与方式:
51Reboot 课程信息最新一期 Golang 课程开课时间:2020.2.16
1、GO 并发
goroutine
channel
有缓存 chan 和无缓存 chan,以及通信的一些细节
常见并发模式
锁与条件竞争
实战
并发 Web 爬虫
2、Beego 实战
Go Web 浅析
HTTP 协议
net/http
Gin/Beego 框架对比介绍
请求校验包的使用
中间件 middleware ORM
日志模块
3、堡垒机权限管理系统
架构设计
HTTP 协议 加密传输
分布式 Agent/Server 开发
Beego 可视化 Web 界面统管
思考:如何对接服务树系统
4、分布式监控
架构设计
GRPC 传输
Etcd 服务注册发现
Influxdb 存储
Granfan 展示
实战项目一:SQL 自动化上线平台
手动 VS 自动的现状对比
Mysql、Inception、SQLAdvisor 讲解
用户权限设计、执行流程梳理、平台登录双因子安全认证
敏感配置加解密实现、人员/数据库配置设计,整体代码实现
通过 API 对[Aws、阿里云、腾讯云、青云、百度云]管理
公有云账单、比价、资源管理
Ansible 简介及常用场景分析
Ansible API 二次开发入门
基于 Ansible Playbook API 快速实现任务管理系统
工作中流程规范的设计思路
典型工单系统的实现原理-状态机
基于 Gitlab + Jenkins + DevOps 平台实现 CI/CD 的设计思路
结合运维平台流程规范实现持续集成与交付
结合 ELK 的日志分析平台,实现代码上线运维无人值守
第一阶段:Docker 基础与进阶
第二阶段:Kubernetes 基础
第三阶段:Pod 与生命周期管理
Kubernetes 集群资源管理与调度管理
Kubernetes 控制器和常用资源对象
第四阶段:Kubernetes 服务发现
持久化存储
第五阶段:Helm 包管理工具
第六阶段:Kubernetes 集群网络
Kubernetes 集群网络常用方案比较及选型建议
Flannel 网络组件详解
Flannel 网络组件配置及应用
Flannel 生产环境应用经验
Calico 网络组件详解
Calico 网络组件配置及应用
......
第七阶段:Kubernetes 集群监控
Prometheus 介绍
部署 Prometheus
监控 Kubernetes 集群及应用
NodeExporter 的安装使用
Prometheus的自动发现
Kubernetes常用资源对象监控
Grafana的安装与使用
Grafana的插件与监控
.......
第八阶段:日志收集
日志收集架构
Elasticsearch 集群
Kibana 可视化组件
Fluentd 采集组件
生产环境采集日志方案详解
第九、十阶段:DevOps
动态 Jenkins Slave
Jenkins Pipeline
Jenkins Blue Ocean
Harbor 详解
Gitlab 安装与使用
Gitlab CI Runner
Gitlab CI 示例
Kubernetes 开源管理平台
完整 devops 项目实例
关注我们 ,一起成长吧!