分类 Linux 下的文章

python解析配置文件

项目中经常需要解析配置文件,最简单的就是有很多个section,然后每个section里面都是很多option,每一项option的组成都是key=value,怎么解析这个配置文件?python有内置的库可以解决,比如下面是一个配置文件的demo

#WatchDir 配置文件
#配置的value的时候除了第一项就先别加单引号或者双引号了

[monitor]
#监控路径
WatchPath='/var/log/'

#数据写到本地磁盘cache时间间隔,单位:S
Interval=10


#是否以守护进程方式运行
Daemon=False

#mysql配置
[mysql]
mysql_host = 127.0.0.1
mysql_username = root
mysql_password = 12345678
mysql_port = 3306
mysql_dbname = db_a
mysql_table = tb_b

- 阅读剩余部分 -

Ganglia监控Hadoop

$ vim hadoop-metrics2.properties

#*.sink.file.class=org.apache.hadoop.metrics2.sink.FileSink
# default sampling period, in seconds
#*.period=10

*.sink.ganglia.class=org.apache.hadoop.metrics2.sink.ganglia.GangliaSink31  
*.sink.ganglia.period=10  
  
*.sink.ganglia.slope=jvm.metrics.gcCount=zero,jvm.metrics.memHeapUsedM=both  
*.sink.ganglia.dmax=jvm.metrics.threadsBlocked=70,jvm.metrics.memHeapUsedM=40  
  
namenode.sink.ganglia.servers=hadoop_master_ip:8649  
resourcemanager.sink.ganglia.servers=hadoop_master_ip:8649  
  
datanode.sink.ganglia.servers=hadoop_master_ip:8649    
nodemanager.sink.ganglia.servers=hadoop_master_ip:8649    

maptask.sink.ganglia.servers=hadoop_master_ip:8649    
reducetask.sink.ganglia.servers=hadoop_master_ip:8649 

然后分发配置文件,接可以ganglia监控HDFS等基本情况
ganglia2hadoop

Ganglia配置

ganglia工作原理 ganglia主要有两个角色,gmond(ganglia monitor daemons)和gmetad(ganglia metadata daemons)。gmond是agent,需要在被监控的每台机器上部署,负责采集所在机器的系统状态,信息都是存储在内存里面的。

ganglia-icm ganglia有一种工作模式是组播,顾名思义,以组播的形式发出自己采集到的信息。这时候集群内所有配置成组播的都可以接收数据,也就是说在组播的情形下,集群内的数据都是共享并且一致的(和路由协议很像),gmetad的功能就是从采集集群内所有系统状态信息,在组播的工作模式下,gmetad可以从任一台gmond上采集集群信息。但是组播的局限性就是在于集群要在一个网段内,并且网络负载提高。 ganglia还有一种工作模式是单播,每个agent上的gmond采集好各自的信息,然后通过udp汇总到一台gmond上,然后这台gmond汇总所有来自其他gmond的信息并且联合本机信息也发送给ganglia,单播的模式就是push,gmetad等待从gmond中心节点上过来的信息。 gmetad会把从gmond收集到的信息写入rrdtool里面,rrdtool是一个环形数据库,用来存储集群信息,然后在ganglia-web可以去读取rrdtool,并且绘图呈现给前端。

- 阅读剩余部分 -

Shell单引号和双引号

问题一:

/etc/hosts
192.168.1.1 www.firefoxbug.net

$ awk '{print $1}' /etc/hosts ==>> 打印IP
$ awk "{print $1}" /etc/hosts ==>> 打印整行

这两个打印出来是不一样的,同样是在一个bash下执行。因为$1在双引号里面,是属于弱引用,$1被shell解析了,解析出来是空,可以看看下面的实验。
$ echo $1
$ awk "{print }" /etc/hosts

问题二:

echo '\'' ==>> 等待输入
echo "\"" ==>> 输出"

在单引号的强引用下,所有的转义都已经失效了,所以第一个echo在等待第二个单引号的匹配。

resolv.conf中search作用

reslov.conf中的search主要是用来补全hostname的,有时候域名太长,可以做一个短域名做主机名字,但是DNS解析需要的是FQDN,而在resolv.conf中设置search能进行补全。

vim /etc/hosts
42.120.7.71 www

ping www能通,返回就是42.120.7.71,ping会首先解析hosts
vim /etc/resolv.conf
search firefoxbug.net
nameserver 114.114.114.114

这时候nslookup www
Server:		114.114.114.114
Address:	114.114.114.114#53

Non-authoritative answer:
Name:	www.firefoxbug.net
Address: 42.120.7.71

看到没,search的作用就是补全要访问的短域名

正确的域名解析顺序是:
1. 查找/etc/hosts
2. 根据nameserver查找域名
3. 如果在nameserver查找不到域名就进行search补全,重新走1~2步

shell数组和字典

#!/bin/bash

echo "shell定义字典"
#必须先声明
declare -A dic
dic=([key1]="value1" [key2]="value2" [key3]="value3")

#打印指定key的value
echo ${dic["key1"]}
#打印所有key值
echo ${!dic[*]}
#打印所有value
echo ${dic[*]}

#遍历key值
for key in $(echo ${!dic[*]})
do
        echo "$key : ${dic[$key]}"
done

echo "shell定义数组"

#数组
list=("value1" "value2" "value3")
#打印指定下标
echo ${list[1]}
#打印所有下标
echo ${!list[*]}
#打印数组下标
echo ${list[*]}
#数组增加一个元素
list=("${list[@]}" "value3")

pssh使用

pssh安装

wget http://parallel-ssh.googlecode.com/files/pssh-2.3.1.tar.gz
tar zxvf pssh-2.3.1.tar.gz 
cd pssh-2.3.1/ 
python setup.py install 

远程命令执行

pssh -A -i -H 'username@ip:port' 'execute_cmd'
其中username默认是root,port默认是22,可以省略

$ pssh -A -i -H 'root@117.135.139.195:22' 'ls /' 参数 -A:ssh手动输入登入密码,不然没法登入,除非是免登入机制 参数 -i:把标准输出和错误输出都返回

- 阅读剩余部分 -

Linux read函数深入

之前Linux read函数浅析-高速缓存说到Linux下读写文件都是通过高速缓存区实现的,应用程序要和底层的设备打交道,首先是通过高速缓存区,要是所要读的数据不存在,高速缓存区会帮应用程序代理去“拿”数据,高速缓存区的处理程序会向设备的驱动程序发出读请求,等待数据返回,这里就说说高速缓存区怎么实现和底层设备驱动函数交互。

- 阅读剩余部分 -

Linux read函数浅析-高速缓存

最近一直在看Linux下read()函数的实现,发现确实是复杂,几次想写篇博客来把read()函数讲清楚,可是写着写着就断了思绪,有些地方不是很懂,有些概念太过于模糊。今天还是决定写下,只能大概讲讲read()函数。

高速缓存


首先引入的就是高速缓存,什么是高速缓存?众所周知,磁盘的I/O和内存的I/O两者访问是相差几个数量级的,A进程访问了X文件,Linux为了加速A进程或者其他进程再次访问X文件的时候不再千里迢迢到去磁盘上寻找,于是就设计了高速缓存,把A进程访问过的X文件缓存在内存里面。这么说高速缓存就是内存了?对,高速缓存就是属于物理内存的一部分!Linux设计思想就是尽可能地发挥物理内存的使用。

Linux读写机制


好,我们看看高速缓存怎么发挥作用,A进程要去磁盘上访问磁盘上的X文件,首先会在高速缓存区里面找,找到了就直接返回对应的内容,找不到呢?没办法,得再去磁盘上找,通过设备驱动程序把相应的文件内容读取到高速缓存里,然后A进程从高速缓存里得到数据,返回给用户空间。那么某个进程再去请求X文件的时候就会直接从高速缓存区里返回了,效率就会大大提高。
那么对于写文件呢?A进程要写磁盘文件,那么先把数据写到高速缓存区里面,并不是写了小部分数据就会立马同步到磁盘上的。内核会有一组回写线程,一段时间去把高速缓存区上“脏”的逻辑块写到磁盘上,这里“脏”的意思和虚拟内存页差不多,最简单定义就是内存中某个块和磁盘上对应块不一样就回写(不是页)。

高速缓存实现


对于块设备而言,比如磁盘,最小的访问单位都是扇区,一般一个扇区是512字节,Linux抽象了一层块的概念,平时说的I/O块和逻辑块都是这个意思,一个块必然是扇区的整数倍,一般都是1K。在内核0.11里面,高速缓存区都是这些块组成的,统一用buffer_head进行描述,所有的块都组成双向链表。每个设备号+逻辑块号还通过指定的HASH算法构成自己的链表,访问X文件会在inode节点里提供设备号,接下来需要的是逻辑块号的获取是通过提供的文件偏移地址算出来的,就可以在对应的设备号+逻辑块号HASH队列里找到相应缓存块,找不到就向底层的驱动程序发出读磁盘请求。块的概念都是面向设备的,也就是驱动程序和设备打交道的最基本单位都是块。而对于应用程序和高速缓存区打交道都是基于页的概念,页是在块纸上又一层抽象,一个页一般是4K,也就是4个块。下面这张图可以详细说明这些概念之间的关系。

buffer

总结


进程要和块设备打交道都是需要经过高速缓存的(也有特殊情况)进程发起读请求,首先会有缓存区页面管理程序接收,它会根据逻辑块号和设备号去高速缓存区找请求的buffer,找到就直接返回;如果找不到,缓存区页面管理程序会向底层的驱动程序发出读指令,等其放回后缓存到高速缓存里,然后返回给应用进程。至于高速缓存区的维护则是由内核维护,淘汰算法也是LRU。

Linux虚拟内存概述

为什么需要虚拟内存?


程序是一系列代码段,数据段的集合,而程序要运行必须是加载到内存里的,但是物理内存就那么大,如何能保证很多个程序都装载进去呢?这里就引进了虚拟内存的概念,虚拟内存基本思想就是,给每个程序都分配一个4G的虚拟的内存,但这部分内存占用的不是物理内存,而是磁盘空间,这部分叫做虚拟存储器,就是安装Linux系统时候的SWAP空间。而对应的物理内存就是物理存储器。
有没有觉得整个过程像开了“空头支票”一样?程序要跑起来,操作系统许诺它给你4G的空间,但却是不能用的,那真要执行的时候怎么办呢?操作系统会把磁盘上的程序代码数据“移”到内存里,把不需要的还会“移”出去到磁盘上,这样看上去就好像可以跑很多进程了。

- 阅读剩余部分 -

Linux系统调用

最近重新开始看操作系统,看得我云里雾里,经典的操作系统书又很难,看不大懂。学校的操作系统课,说实在的,真的好难和自己真正编程结合起来,里面的概念很多,而且很多都是很老的,历史总是这么蛋疼。没办法,只能硬着头皮去看。这次先讲讲系统调用,因为没看过里面的具体源码,但是尽可能让文章偏向实际编程中的应用。

什么叫系统调用


计算机有很多的硬件,比如键盘,鼠标,网卡,磁盘,声卡这些硬件都是我们实际可以触碰到的,但是对于用户应用程序而言是没办法直接操作这些设备的。为什么呢?一是硬件涉及到很多电路实现,让用户自己编写程序去操作是很困难和复杂的。二是编程的人直接操作硬件会有很大的安全隐患,因为这些资源都是很多程序共享的,万一我们写的程序能够直接操作硬件,安全性就不能保障了。所以所有这些设备的上面抽象了一层驱动程序,这些驱动程序会实现很多硬件相关操作,也就是说每个硬件发布商都得提供各自的硬件的驱动程序用于操作硬件。那么应用程序怎么去调用这些驱动程序呢?答案就是通过操作系统的系统调用,我们告诉操作系统我们要对某个硬件做某个操作,操作系统来帮你完成,然后把得到的结果返回给我们。

- 阅读剩余部分 -

grep 动态文件输出

tail -f access.log | grep "www.firefoxbug.net" 在因为access.log是动态变化的,随时都可能增加,因为管道的作用是在输入端完成或者buffer满了才会到输出端过滤,所以grep出来的结果是无的。

加 --line-buffered 参数可以动态查看文件。
tail -f access.log | grep --line-buffered "www.firefoxbug.net"

git入门二(新建分支)

这一节讲下git的分支功能,分支功能是什么呢?其实这才是真正的软件版本管理核心,比如你的项目想添加一个的功能,但是这个版本是测试的,不一定上线,再或者想开发一个2.0版本专门用于某个场合。传统的做法就是把原来的项目代码,全部都复制一份。然后还傻乎乎地为了区别,把文件夹的名字分成1.0版本和2.0版本,别笑华哥,我之前开发OpenCDN就是这样的。到后来以至于我根本搞不清楚各个版本了,而且还浪费磁盘空间。总之看起来就是很不爽,但是用git可以解决版本控制的问题。

git通过增加分支的方法来实现,git默认的主分支就是master,可以用下面的命令查看。

git branch #查看分支
git branch new_branch #创建一个新的分支
git checkout new_branch #切换到新的分支
git checkout -d new_branch #删除分支

- 阅读剩余部分 -

git入门一(一次commit)

网上git的资料已经很多了,可git这个东西一开始还是比较难理解,以至于我周围的朋友都说这东西太难用了。我自己也玩了两年的linux,但是接触git这个东西一开始还是挺难理解的。其实主要是不像其他工具那样,git还是得静下心来好好看看的。自己对git也不是特别熟悉,所以不能深入解释里面的工作细节,只解释一些常用到的。
在一个项目下面初始化一个git仓库,主要会分成三个部分。一部分是你的工作目录(working),说白了就是你的项目代码。一部分是索引文件(staged file),这里就是你项目的tmp文件。还有一部分是仓库(History),这里才是git真正的数据库,存放你的项目记录的地方。举个例子,我们通用的vim编辑一个文件,一旦开始编辑,目录下就会产生一个.swp文件,然后你编辑完成后按下wq退出,才会真正存到编辑的文件里面。

git1
图片from http://blog.jobbole.com/tag/git/page/2/

git的工作流程也是这样,你初始化一个git仓库后,git监控要指定的文件,你修改了文件之后会暂时存到staged里面去,也就是tmp的部分。当你commit的时候就会真正把tmp里面的文件提交到git仓库里面,这才是真正的一次操作。

- 阅读剩余部分 -

Hadoop HDFS和MapReduce测试

Hadoop的MapReduce应该是属于核心的编程框架,到目前为止我还不是特别懂,只能按照网上的步骤一点实践,就和学一门新语言第一个程序是HelloWorld一样。前文已经介绍了Hadoop的安装,这里就HDFS和MapReduce的测试。

HDFS测试


在HDFS中新建一个文件夹,并把本地文件copy到HDFS新建的文件夹
cd hadoop路径
bin/hadoop dfs -mkdir /tmp/firefoxbug
bin/hadoop dfs -put /opt/run.log /tmp/firefoxbug
bin/hadoop dfs -ls /tmp/firefoxbug

- 阅读剩余部分 -

Elasticsearch集群搭建

之前对于CDN的日志处理模型是从
logstash agent==>>redis==>>logstash index==>>elasticsearch==>>kibana3,对于elasticsearch集群搭建,可以把索引进行分片存储,一个索引可以分成若干个片,分别存储到集群里面,而对于集群里面的负载均衡,副本分配,索引动态均衡(根据节点的增加或者减少)都是elasticsearch自己内部完成的,一有情况就会重新进行分配。
下面先是介绍几个关于elasticsearch的几个名词

- 阅读剩余部分 -

Hadoop安装

听了这么久的Hadoop,终于要开始玩了,好幸福的说。不水了,Hadoop到底能怎么用我还没研究,只是安装了下。这里大概介绍下Hadoop,它是一组项目,在下面有好多个开源的牛逼组件。HDFS是下面的一个分布式存储项目,主要是文件存储,它设计就是来存储大文件,整个HDFS就是一套文件系统,但是它的Block特别大,默认是64M,HDFS会有自己的Backup。HDFS主要分成两部分,一部分是管理中心,叫做NameData,这类似linux文件系统的super blocks,用来管理所有的DataNode,DataNode就是真正用来存储实际的数据内容的。而NameData会管理好文件的索引,存储,还有backup。

- 阅读剩余部分 -

logstash kibana3显示坐标

按照之前logstash+kibana3 GeoIP地理位置那样配置出来的geo地理位置里有经度和纬度,但是是独立开来的。在官网的demo上可以有coordinates的字段,只要是以数组的形式显示经纬度。下面是logstash的配置,在kibana3上呈现坐标形式。

filter {
geoip {
   source => "source_ip"
   type => "linux-syslog"
   add_tag => [ "geoip" ]
   add_field => [ "[geoip][coordinates]", "%{[geoip][longitude]}" ]
   add_field => [ "[geoip][coordinates]", "%{[geoip][latitude]}"  ]
}

mutate {
    convert => [ "[geoip][coordinates]", "float" ]
}

下面是显示后的效果

- 阅读剩余部分 -

Python redis操作

首先安装python redis扩展模块 yum install python-redis

#!/usr/bin/env python2.6
#-*- coding: utf-8 -*-

# Author : firefoxbug
# E-Mail : wanghuafire@gmail.com
# Blog   : www.firefoxbug.net
# Function : put or get log dictionary from redis-server

import redis  

global redis_q

class RedisQueue(object):
    """Simple Queue with Redis Backend"""
    def __init__(self,**redis_kwargs):
        """The default connection parameters are: host='localhost', port=6379, db=0"""
        self.__db= redis.Redis(**redis_kwargs)
#       self.key = '%s:%s' %(namespace, name)  

    def qsize(self,key):
        """Return the approximate size of the queue."""
        return self.__db.llen(key)

    def empty(self):
        """Return True if the queue is empty, False otherwise."""
        return self.qsize() == 0  

    def put(self,key,item):
        """Put item into the queue."""
        self.__db.rpush(key, item)  

    def get(self,key,block=True, timeout=None):
        """Remove and return an item from the queue.  

        If optional args block is true and timeout is None (the default), block
        if necessary until an item is available."""
        if block:
            item = self.__db.blpop(key, timeout=timeout)
        else:
            item = self.__db.lpop(key)  

        if item:
            item = item[1]
        return item  

    def get_nowait(self):
        """Equivalent to get(False)."""
        return self.get(False) 

def connect2redis(host='127.0.0.1'):
    global redis_q
    redis_q = RedisQueue()

if __name__ == '__main__':
    redis_q = RedisQueue(host='127.0.0.1')
    i = 0
    while True:
                redis_q.put('test2','hello world')
        print redis_q.get('test2')