2014年10月31日星期五

程序员的智囊库系列之---序 - FingerLiu

本邮件内容由第三方提供,如果您不想继续收到该邮件,可 点此退订
程序员的智囊库系列之---序 - FingerLiu  阅读原文»

平时写程序,上网的过程中,总会有意识、无意识的学到各种各样的知识,接触到各种各样的工具。

这些知识和工具可能本身与编程无关,但它们绝对能够提高你编程、工作的效率。

写这个系列的目的是为了记录并积累这些知识、工具,以免以后时间长了忘记掉。

这个系列会涉及网页前端,linux服务器维护,数据分析,数据挖掘等等等等。

将会介绍以下内容(还会有补充):

前端:

  d3js

  scrapy

  bootstrap

  diffbot

  importio

数据分析

  r语言

  rcharts

  shiny

  slidify

数据挖掘

  predictionio

  mahout

服务器维护

  salt

  lnmp

分布式存储

  ceph

  glusterfs


本文链接:程序员的智囊库系列之---序,转载请注明。

Android中XML解析-Dom解析 - Fly_Elephant  阅读原文»

Android中需要解析服务器端传过来的数据,由于XML是与平台无关的特性,被广泛运用于数据通信中,有的时候需要解析xml数据,格式有三种方式,分别是DOM、SAX以及PULL三种方式,本文就简单以Dom解析为例,解析XML, DOM方式解析xml是先把xml文档都读到内存中,然后再用DOM API来访问树形结构,并获取数据的,但是这样一来,如果xml文件很大,手机CPU处理能力比PC差,因此在处理效率方面就相对差了,使用Dom解析就不是太合适了。

基础维护

首先下assets目录下新建一个Book.xml文件:

<?xml version="1.0" encoding="utf-8"?>
<Books>

<Book name="康师傅的亿万帝国" >

<Title>
 据周元根的小学同学回忆,大约7岁那年,周元根开始读小学,由于和别人重名,于是改了名字。但他在村里一直沿用“周元根”这个名字,周家祖坟的5块墓碑上,刻的也是“周元根”这个名字。

</Title>

<Picture>
http://p.qpic.cn/ninja/0/ninja1406636943/0

</Picture>
</Book>

<Book name="徐才厚受贿额特别巨大" >

<Title>
根据最高人民检察院授权,军事检察院对中央军委原副主席徐才厚以涉嫌受贿犯罪立案侦查。2014年10月27日,对该案侦查终结,移送审查起诉。

</Title>

<Picture>
http://www.sinaimg.cn/dy/slidenews/1_img/2014_44/2841_506865_709392.jpg

</Picture>
</Book>
<Book name="发改委副司长魏鹏远" >

<Title>
最高人民检察院反贪污贿赂总局局长徐进辉今日表示,煤炭司副司长魏鹏远家中搜查发现现金折合人民币2亿余元,成为建国以来检察机关一次起获赃款现金数额最大的案件。

</Title>

<Picture>
http://img1.cache.netease.com/catchpic/D/DC/DCB2315FD0F50C665BB1474768192642.jpg

</Picture>
</Book>

</Books>

XML存储的方式其实和类存储的是类似的,这个时候建一个对应的Book类:

public class Book {
private String title;
private String picture;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getPicture() {
return picture;
}
public void setPicture(String picture) {
this.picture = picture;
}
}

 Book布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >

<ImageView
android:id="@+id/itemImage"
android:layout_width="40dp"
android:layout_height="60dp"
android:src="@drawable/fight"
/>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="10dp"
>

<TextView
android:id="@+id/itemTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="标题"
android:textSize="18sp" />

<TextView
android:id="@+id/itemText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="12sp"
android:text="xxxxxxxxx" />
</LinearLayout>

</LinearLayout>

Demo实现

现在最主要的是将XML的Book转换成一个Book集合:

public List<Book> getBooks(InputStream stream) {
List<Book> list = new ArrayList<Book>();
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder builder = factory.newDocumentBuilder();
// 获取XML文档结构
Document document = builder.parse(stream);
// 获取根节点
Element rootElement = document.getDocumentElement();
NodeList nodeList = rootElement.getElementsByTagName("Book");
for (int i = 0; i < nodeList.getLength(); i++) {
Book book = new Book();
// Node转成Element
Element element = (Element) nodeList.item(i);
book.setName(element.getAttribute("name"));
Element eleTitlElement = (Element) element
.getElementsByTagName("Title").item(0);
String title = eleTitlElement.getFirstChild().getNodeValue();
Element elePicElement = (Element) element.getElementsByTagName(
"Picture").item(0);
String picString = elePicElement.getFirstChild().getNodeValue();
book.setTitle(title);
book.setPicture(picString);
list.add(book);
}
} catch (ParserConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return list;

}

 1.通过DocumentBuilderFactory工厂类实例化一个工厂,通过工厂的newDocumentBuilder()方法实例化一个DocumentBuilder .通过创建好的 DocumentBuilder 对象的 parse(InputStream) 方法就可以解析我们的xml文档,然后返回的是一个Document的对象,这个Document对象代表的就是我们的整个xml文档。

2.获取整个xml的Document对象后,我们可以获得其下面的各个元素节点(Element),同样每个元素节点可能又有多个属性(Attribute),根据每个元素节点我们又可以遍历该元素节点下面的子节点等等。

MainActivity启动方法中的调用:

InputStream inputStream = null;
try {
inputStream = inputStream = MainActivity.this.getResources().getAssets().open("Book.xml");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

list = getBooks(inputStream);
View view = getLayoutInflater().inflate(R.layout.activity_main, null);
ListView listView = (ListView) findViewById(R.id.list_dom);
listView.setAdapter(new testAdapter());

testAdapter的写法:

class testAdapter extends BaseAdapter {

@Override
public int getCount() {
// TODO Auto-generated method stub
return list.size();
}

@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return null;
}

@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return 0;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
Book book = (Book) list.get(position);
View view = null;
if (convertView == null) {
LayoutInflater layoutInflater = getLayoutInflater();
view = layoutInflater.inflate(R.layout.book, null);
} else {
view = convertView;
}
// ImageView imageView = (ImageView)
// view.findViewById(R.id.itemImage);

// imageView.setImageResource(BIND_ABOVE_CLIENT);
TextView titleView = (TextView) view.findViewById(R.id.itemTitle);
titleView.setText(book.getName());

TextView contentView = (TextView) view.findViewById(R.id.itemText);
contentView.setText(book.getTitle());
return view;
}

}

周末看博客的都是好孩子,祝大家周末愉快~


本文链接:Android中XML解析-Dom解析,转载请注明。

阅读更多内容

2014年10月30日星期四

我们是怎么管理QQ群的 - 豪情

本邮件内容由第三方提供,如果您不想继续收到该邮件,可 点此退订
我们是怎么管理QQ群的 - 豪情  阅读原文»

文章背景:腾讯平台上的qq群数以千万百万计,但99%的在吹水扯蛋,从早上的问好开始,到晚上的晚安,无一不浪费青春之时间,看之痛心,无力改变,只好自己建了一个,希望能以此来改变群内交流的氛围或环境。
以下是我群的一些约定分享一下,给其它行业同仁一点借鉴,以此也侧面推动行业阶梯式的进步,让入门级初中级找到一个适合的学习群。
以下为具体内容:
群号:159758989
人的存在是为了创建价值,群的存在是为了解决问题,你的存在是为了问题快速解决。 为了使你的问题得到快速的解决,请抽空阅读以下约定:
如果你暂时没有问题,可直接进入解决问题环节:
一. 如何提问:
1. 请提供能重现问题的url或资源demo文件包。(url:是以www开头的一级域名或二级域名,demo文件是未上线的html静态页面,带图片的请上传至群共享。提供的资源文件,必须是换个环境之后完整的能重现所说的bug,而不是只有html没有关键css,图片等一个单独文件,如果你所属团队对前端代码有特殊的保密要求,请自行找其它解决办法。)
2. 文字描述不清的bug,用截图示意,且要说明两种状态:现状或期望的状态。
3. 没有图片的代码发
优点:国内访问稍快,发表简单,缺点:功能简单
优点:功能强大,历史记录给力,缺点:访问速度慢
有图片的代码发群共享或申请免费的测试空间:
推荐使用intellij idea / phpstorm / webstorm来管理 svn/git 代码的提交与更新。
Intellij idea群: 244908708 群公告: http://t.cn/8kZZ1Uy
参考:
X-Y 问题
向别人求助时有哪些「潜」规则?
如何在知乎提一个好问题?
如何优雅地向前辈或者高手请教技术问题?
二. 禁 问:
有没有人在?
谁能给我解决一个问题?
我的XXXX位置不对了怎么解决?
群里有人做过XX的东西吗?等等类似的问题。
建议的问法是:
1. 有问题直接问。比如:前端的岗位特点是什么?
2. 直接说场景:我在做xx端东西的时候,在window 7平台的IE7版本下遇到了左右不对齐问题,具体如图所示,代码地址:http://www.jsbin.com/xxxx,在百度中找到的答案,试了之后还是有同样的问题。请有空的同学帮我看看是什么问题?
如果你的问题暂时没人理,那表示有空的同学都未涉猎,赶紧找其它办法。
三. 禁 发:
早上问好,打招呼。
广告,求职(群邮件仅发一次),推广,八卦,
超过行距的自定义表情,约束这个的目的在于:
1. 回查记录的时候不要因为过多的无意义的内容而频繁点击翻页键。
2. 或者有经典的教程资源内容时,方便白天不上网的同学一次性复制记录。
所以建议:不要发超过行距的表情,或自定义qq表情,
3. 禁止在工作时间内利用qq提供的@别人的功能,建议进行手动@[ 昵称 ],这样就不会进行特别提醒,也利于问题的一对一交流。
4. 禁止在群邮件中回复招聘或其它通知邮件。因为你的每一次回复将会有1000多人同时收到,而这条回复对其它人是没有任何意义的。
四. 警告:
字体在10-12之间,
禁止闲聊,无意义回复。
问问题之前最好自己百度google查过,思考过,都不能解决的再问。
禁止把群当手册用。
问题在群里问,不建议单M,不要指代问题只让某人回答,
有些基础不要完全不会,不要打算直接要简单代码。不要做伸手党
不要想着问题提出就立马有人回答。
五. 群约定 :
最近经历了一个人数的增长期或交流的蜜月期,为了使此群航向不发生偏离,也为了使开放,互助,共赢的互联网精神走得更远,在重复一下以前的约定:
1. 此群原则是:高效,高质量的交流,禁止无意义的闲扯,话题内容尽量保持在技术与职业相关范围内。
2. 为了使此群的聊天记录能有效的回溯可查,禁止发与内容无关的或超过文本行距的图片,建议qq默认表情。
3. 有问题代码不建议截图(直接发jsbin.com,有图片的bug直接发群共享),如明显强调代码片段,建议直接发文本代码。为了保护眼睛少受刺激,聊天字体颜色禁用红,紫,等饱和度比较高的颜色,另外字体不建议强调加粗,加粗之后发出来的代码片段很占空间,阅读体验很不友好。建议默认设置,微软雅黑,10号-12号字大小。另外,再强调我群有从事10年及以上的it资深人士,这些人的工作强度是很饱和的,难度是很大,在工作之外参与分享与探讨,尽可能的提供友好的交流体验。搞前端玩的就是体验,建议注重细节。
4. 为了保持高效有力的交流原则,此群不建议问早,提问时问:有人在吗?有人用过xx吗?
有问题建议直接提问,提问之前请组织好文字,换角度看看你的问题是否被别人能容易看懂。
提倡主动学习,自己动手的学习方法及生活风格,有问题先百度,百度之后有问题,在提问。
从长远的角度一般培养的是一种学习,生活的能力,不是一个答案。答案会随着时代与客观因素的变化而变化,方法与能力是永恒不变的。
so,保持清醒的头脑,没有方向时要驻足思考,没有目标时要抬头寻路。庆幸的时,我们一直在路上。
最后谦和,尊重,互助,感恩是此群的四大交流基础。
技术之外完整的人格与良好的人性修养是职业发展的垫脚石。
六. 资源:

前端入门教程

前端入门视频

前端入门资料

http://www.cnblogs.com/sb19871023/p/3894452.html

前端知识体系

七. API手册:

1. API集合

百度网盘离线众多chm

在线api大全

2. Ecmascript 5

es5 en

ECMAScript 6入门

3. 各开发中心

mozilla js参考

safari开发中心

chrome开发中心(chrome的内核已转向blink)

microsoft js参考

js秘密花园

4. jQuery

http://learn.jquery.com/

学习jquery

Hemin在线版jquery 1.11.0手册,适合mac用户

http://jquery.bootcss.com/

jquery 中文api

css88 jq api

5. js template

6. 弹出

artDialog 最新版

模型评估与模型选择 - ☆Ronny丶  阅读原文»

机器学习算法 原理、实现与实战——模型评估与模型选择

1. 训练误差与测试误差

机器学习的目的是使学习到的模型不仅对已知数据而且对未知数据都能有很好的预测能力。

假设学习到的模型是$Y = \hat{f}(X)$,训练误差是模型$Y = \hat{f}(X)$关于训练数据集的平均损失:

$$R_{emp}(\hat{f}) = \frac{1}{N}\sum_{i=1}^NL(y_i,\hat{f}(x_i))$$

其中$N$是训练样本容量。

测试误差是模型$Y = \hat{f}(X)$关于测试数据集的平均损失:

$$e_{test}(\hat{f}) = \frac{1}{N’}\sum_{i=1}^NL(y_i,\hat{f}(x_i))$$

其中$N’$是测试样本容量。

当损失函数是0-1损失时,测试误差就变成了常见的测试数据集上的误差率(预测错误的个数除以测试数据的总个数)。

训练误差的大小,对判定给定问题是不是一个容易学习的问题是有意义的,但本质上不重要。测试误差反映了学习方法对未知数据集的预测能力,是学习中的重要概念。显然,给定两种学习方法,测试误差小的方法具有更好的预测能力,是更有效的方法。通常将学习方法对未知数据的预测能力称为泛化能力(generalization ability)。

2. 过拟合与模型选择

我们知道假设空间理论上有无限个模型,它们有着不同的复杂度(一般表现为参数个数的多少),我们希望选择或学习一个合适的模型。

如果一味提高对训练数据的预测能力,所选的模型的复杂度则往往会比真实模型更高。这种现象称为过拟合。过拟合是指学习时选择的模型所包含的参数过多,以致于出现这一模型对已知数据预测很好,但对未知数据预测很差的现象。

下面,以多项式函数拟合问题为例,说明过拟合与模型选择,这是一个回归问题。

现在给定一个训练数据集:

$$T=\{(x_1,y_1),(x_2,y_2),\dots,(x_n,y_n)\}$$

其中,$x_i\in R$是输入$x$的观测值,$y_i\in R$是相应的输出$y$的观测值。多项式函数拟合的任务是假设给定数据由M次多项式函数生成,选择最有可能产生这些数据的M次多项式函数,即在M次多项式函数中选择一个对已知数据以及未知数据都有很多预测能力的函数。

我们用$y = sin(x)$生成10个数据点,并适当的在$y$值上加了一些误差,下面我们分别用0~9次多项式对数据进行拟合。

image image image

上图给出了$M=1,M=3,M=9$时多项式拟合的情况。当$M=1$时多项式曲线是一条直线,数据拟合效果很差。相反,如果$M=9$,多项式曲线通过每个数据点,训练误差为0。从对给定的训练数据拟合的角度来说,效果是最好的。但是因为训练数据本身存在噪声,这种拟合曲线对未知数据的预测能力往往并不是最好的,这时过拟合现象就会发生。

import numpy as np
import matplotlib.pyplot as plt
import random
x
= np.linspace(0,1,10)
y
= np.sin(2*np.pi*x)
for i in range(0,10):
y
= y + random.uniform(-0.4,0.4)
p
= np.polyfit(x,y,9)
t
= np.linspace(0,1.0,100)
plt.plot(x,y,
'o')
plt.plot(t,np.sin(np.pi
*2*t),label='$y=sin(x)$');
plt.plot(t,np.polyval(p,t),label
='$y = \sum_{i=0}^Mw_ix_i,M=9,x_0=0$');
plt.legend()
plt.show()

3. 正则化与交叉验证

3.1 正则化

前面文章在介绍机器学习策略的时候,已经提到过结构风险最小化的概念。结构风险最小化正是为了解决过拟合问题来提出来的策略,它在经验风险上加一个正则化项。正则化项一般是模型复杂度的单调递增函数,模型越复杂,正则化值就越大。比如,正则化项可以是模型参数的向量的范数。

正则化项可以取不同的形式。例如,回归问题中,损失函数是平方损失,正则化项可以是参数向量的$L_2$范数:

$$L(w) = \frac{1}{N}\sum_{i=1}^N(f(x_i;w)-y_i)^2+\frac{\lambda}{2}||w||^2$$

这里,$||w||$表示参数向量$w$的$L_2$范数。

正则化项也可以是参数向量的$L_1$范数:

$$L(w) = \frac{1}{N}\sum_{i=1}^N(f(x_i;w)-y_i)^2+\lambda||w||_1$$

这里,$||w||_1$表示参数向量$w$的$L_1$范数。

正则化符合奥卡姆剃刀(Occam’s razor)原理。奥卡姆剃刀应用在模型选择时想法是:在所有可能选择的模型中,能够很好地解释已知数据并且十分简单才是最好的模型,也就是应该选择的模型。从贝叶斯估计的角度来看,正则化项对应于模型的先验概率。可以假设复杂模型有较小的先验概率,简单的模型有较大的先验概率。

3.2 交叉验证

如果给定的样本数据充足,进行模型选择的一种简单方法是随机地将数据集切分成三部分,分别为训练集、验证集和测试集。训练集用来训练模型,验证集用于模型的选择,而测试集用于最终对学习方法的评估。在学习到的不同复杂度的模型中,选择对验证集有最小预测误差的模型。

但是在许多实际应用中数据是不充分的。为了选择好的模型,可以采用交叉验证方法。交叉验证的基本想法是重复地使用数据;把给定的数据进行切分,将切分的数据集组合为训练集与测试集,在此基础上反复地进行训练、测试以及模型选择。

1. 简单交叉验证

首先随机地将已给数据分为两部分,一部分作为训练集,另一部分作为测试集;然后用训练集在各种条件下训练模型,从而得到不同的模型;在测试集上评价各个模型的测试误差,选出测试误差最小的模型。

2. S折交叉验证

这种方法应用最多。首先随机地将已给的数据切分为S个互不相交的大小相同的子集;然后利用其中的S-1个子集的数据训练模型,然后用余下的子集测试模型;将这一过程对可能的S种选择重复进行;最后选出S次评测中平均测试误差最小的模型。

3. 留一交叉验证

S折交叉验证的特征情形是$S=N$,称为留一交叉验证,往往在数据缺乏的情况下使用。这里,N是给定数据集的容量。

4. 泛化能力

学习方法的泛化能力是指由该方法学习到的模型对未知数据的预测能力,是学习方法本质上重要的性质。现实中采用最多的办法是通过测试数据集的误差来评价学习方法的泛化能力。但是因为数据是有限的,并不能代表全体未知样本,所以很有可能按照这样评价到得的结果是不可靠的。

下面我们从理论上对学习方法的泛化能力进行分析。

首先给出泛化误差的定义。如果学习到的模型是$\hat{f}$,那么用这个模型对未知数据预测的误差即为泛化误差(generalization error)

$$R_{exp}(\hat{f}) = E_P[L(Y,\hat{f}(X))] = \int_{\mathcal{X}\times\mathcal{Y}}L(y,\hat{f}(x))P(x,y)dxdy$$

泛化误差反映了学习方法的泛化能力,如果一种方法学习的模型比另一种方法学习的模型具有更小的泛化误差,那么这种方法就更有效。事实上,泛化误差就是所学习到的模型的期望风险。


本文链接:模型评估与模型选择,转载请注明。

阅读更多内容

系统性能排查命令及优化思路

本邮件内容由第三方提供,如果您不想继续收到该邮件,可 点此退订
系统性能排查命令及优化思路  阅读原文»

用户名:兰心hui性
文章数:18
评论数:17
访问量:8990
无忧币:1351
博客积分:286
博客等级:2
注册日期:2013-07-06

系统性能排查命令及优化思路

最近笔者经常处理了一些线上的问题机器。特抽空写一篇文章将处理系统性能问题和优化思路进行总结,方便后续工作中系统故障的排查。作为运维,收到网管系统性能报警应该是常有的事情。而快速进行问题定位并解决则是工作的关键。我们在排查或者优化一个系统的时候无外乎从以下几个方面考虑:

1.CPU的使用率异常,如某个核心的使用率过高,而其它核心则处于空闲的状况。

2.内存的使用状况:通常需要注意是否程序内存泄漏导致的。

3.磁盘IO性能:通常磁盘IO不正常时我们需要判断程序是否处在顺序读写的状态。

4.网络IO负载:如web服务要考虑time_wait状态的连接数;如代理服务器,检查系统打开的socket连接数。

5.程序性能问题:缓存和DB类注关注写的性能,web服务类关注内存的使用

6.系统BUG:一般都是在某些高并发场景才体现出来问题。

常用的判断工具和命令的用法总结如下:

1.系统性能综合判断工具vmstat

当系统出现性能瓶颈后可以使用vmstat命令简单判断一下进程的运行状况及系统资源的使用率,详细输入情况如下:

wKiom1RPu8-SG-2EAAESfgFVKJs023.jpg

注意事项:

1.rb的数量,一般r小于CPU的核心数,b表示阻塞的队列长度。如果r并未达到最大值而b的数量较多,我们就需要观察进程所需要的资源状况。

2.注意incs的比例和数量,in表示中断的请求数,而cs才代表CPU对中断的处理状况。如果有较多的上下文切换则可以结合top命令观察CPU性能。

选项解释:

rrunning状态进程个数,小于CPU核心数(CPU时间片+所需要的资源)
b被阻塞的进程队列的长度(等待I/O完成即进程所需要的资源未到位或者未拿到CPU时间片)
swpd从物理内存交换至交换分区的数据量
buffbuffercache的空间大小,即IO处理时的缓冲。(结果)
cachepagecache的空间大小,缓存的是linux中的具体文件。进程运行时所需要的资源。(过程)
si从内存转到swap分区
so从swap到内存(kb/s)
bi从块设备读入内存的数据量(kb/s),如select
bo从内存保存至块设备的数据量(kb/s),如update,insert
in中断发生的速率,通常意味每秒多少次中断请求发生产生原因:时间片轮转,IO(磁盘/网络)
cs对中断的响应,即上下文切换(进程切换)个数

2.LinuxCPU使用状况排查命令top(htop)

wKioL1RPvsORaZj4AAFs9qsfxy8951.jpg

注意事项:

1.键入1观察各CPU使用状况,可能会出现某些核心使用率较高而某些核心却处于空闲状态。如果运行类似于nginx这样的web应用可以将核心与进程绑定,这样既能将用户请求负载均衡也能减小CPU上下文切换的消耗。

2.使用该命令一般会查看每个CPU的使用率。如果发现id资源较少,也可以观察wa状态百分比,该状态高百分比也有可能是程序未能拿到运行时所需要的资源或者时间片。所以有的时候我们发现系统load较高,而cpu use较少的情况。都是因为进程在等待所运行资源的到位。

3.可以键入M基于内存使用大小排序观察各进程内存使用大小。

选项解释:

wKiom1RPvvbwtaZzAAEFtv95sVQ827.jpg

3.内存信息展示free

keepalived+amoeba+mysql-mmm+mysql实现mysql读写分离及高可用  阅读原文»

用户名:quenlang 文章数:5
评论数:2
访问量:636无忧币:130:102:1 注册日期:2012-04-10

keepalived+amoeba+mysql-mmm+mysql实现mysql读写分离及高可用

最近尝试了一下mysql的读写分离和高可用的搭建。搭好之后体验了一下,效果还不错。这里跟大家分享一下。

1、首先介绍一下mysql-mmm这个工具是干嘛使的?

众所周知,mysql自身提供了AB复制。我们也可以很轻松的实现master-master双向复制,同时再为其中的一个master节点搭建一个slave库。这样就实现了master1与master2之间的双向复制,同时master1与slave1之间主从复制这样的架构。这样整个体系中就存在两个master,正常情况下只有一个master对外提供服务。如果对外提供服务的master意外宕机了,这时mysql本身并不具备failover切换的能力,这样尽管系统中还有一个正常的master节点,但应用仍不可用,这个正常的master尽管存在,但无疑是个摆设。mysql-mmm就是在这样的条件下诞生的。

Mysql-MMM是Master-Master Replication Manager for MySQL(mysql主主复制管理器)的简称,该项目来自于Google,旨在用来监控mysql主主复制和做失败转移。其原理是将真实数据库节点的IP映射为虚拟IP集,在这个虚拟的IP集中,有一个用于write的IP,多个用于read的IP,这个用于write的虚拟IP映射着数据库集群中的两台master的真实IP,以此来实现failover的切换,如果觉得不是很明白,没有关系,后边具体配置部分还会再做说明。

Mysql-MMM是一个开源的项目,官网:http://mysql-mmm.org

2、接着来说amoeba是个什么物件?

可能您听说过mysql-proxy,这个mysql官方维护的一个实现mysql读写分离的工具,曾经测试使用过,但没有在生产中使用。网上大家讨论比较多的是mysql-proxy的配置比较麻烦,其实不是的,单说mysql-proxy的配置的话是比较简单的,不比amoeba麻烦多少,主要是mysql-proxy自身不带有启动脚本,如果你想实现像mysql服务那样的启动方式就需要自己来编写服务脚本。这里实现mysql读写分离,使用淘宝开源出来的amoeba,amoeba是用java开发出来的一款软件,其配置文件为xml格式。选择amoeba是因为amoeba是淘宝在生产环境中使用过的,经过实践测试的,相比mysql-proxy来说,风险性要小一些。

3、最后来说keepalived

keeplived是用来实现服务的高可用的一款优秀的工具,需要说明的是keepalived会为代理的服务虚拟一个IP,用于外部访问,正常情况下,这个虚拟IP是绑定在master上的。master通过脚本来周期性判断服务是否正常运行,如果发现服务异常,就会停掉keepalived服务,这时原本绑定在master上的虚拟IP就会浮动到backup上,由于这个虚拟IP仍然存在,所以外部仍旧可以访问这个服务。

实验环境:

hadoop0.updb.com192.168.0.100

hadoop1.updb.com192.168.0.101

hadoop2.updb.com192.168.0.102

hadoop3.updb.com192.168.0.103

hadoop4.updb.com192.168.0.104

hadoop5.updb.com192.168.0.105

mysql 5.6

所有节点的系统均为centos,使用自带网络yum源,扩展epel源,保证你的各节点均能访问公网,因为我们的mysql-mmm和keepalived均使用epel源进行网络安装。

最终架构:

wKiom1RPUDaj0rJ7AAHVNrZd8pM728.jpg

为了尽可能简洁而清楚的表达,上图中并没有显示mysql-mmm的部署规划。mysql-mmm分为monitor端和agent端,实验中在所有的mysql节点(192.168.0.102-192.168.0.105)上安装agent端,在192.168.0.101上安装monitor端。

好了,到这里相信你的心中已经有了丘壑,下面我们将一步一步来实现

1、搭建mysql集群,基本的mysql安装这里不再介绍(这里主主复制、主从复制的搭建是在全新安装的数据库的基础上,所以在设置同步参数时,binlog为mysql-bin.000001

a、mysql 主主复制

首先停掉hadoop2、hadoop3上的mysql服务,修改配置文件,hadoop2配置如下:

[root@hadoop2~]#cat/etc/my.cnf
log-bin=mysql-bin.log
log-slave-updates
innodb_buffer_pool_size=512M
innodb_flush_log_at_trx_commit=1
sql_mode=STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION,NO_AUTO_VALUE_ON_ZERO
lower_case_table_names=1
log_bin_trust_function_creators=1
character-set-server=utf8
default-character-set=utf8

hadoop3配置文件,注意server-id不能重

[root@hadoop3~]#cat/etc/my.cnf
log-bin=mysql-bin.log
log-slave-updates
innodb_buffer_pool_size=512M
innodb_flush_log_at_trx_commit=1
sql_mode=STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION,NO_AUTO_VALUE_ON_ZERO
lower_case_table_names=1
log_bin_trust_function_creators=1
character-set-server=utf8
default-character-set=utf8

重启hadoop2、hadoop3上的mysql服务

hadoop2、hadoop3上都执行添加同步用户的操作

mysql>grantreplicationslaveon*.*to'rep'@'192.168.0.%'identifiedby'123456';

hadoop3上设置同步参数

2014年10月29日星期三

实现一个简单的语音聊天室(源码) - zhuweisky

本邮件内容由第三方提供,如果您不想继续收到该邮件,可 点此退订
实现一个简单的语音聊天室(源码) - zhuweisky  阅读原文»

  语音聊天室,或多人语音聊天,是即时通信应用中常见的功能之一,比如,QQ的语音讨论组就是我们用得比较多的。

这篇文章将实现一个简单的语音聊天室,让多个人可以进入同一个房间进行语音沟通。先看运行效果截图:

  

   从左到右的三张图分别是:登录界面、语音聊天室的主界面、标注了各个控件的主界面。

  (如果觉得界面太丑,没关系,后面下载源码后,你可以自己美化~~)

一. C/S结构

  很明显,我这个语音聊天室采用的是C/S结构,整个项目结构相对比较简单,如下所示:

   该项目的底层是基于OMCS构建的。这样,服务端就基本没写代码,直接把OMCS服务端拿过来用;客户端就比较麻烦些,下面我就重点讲客户端的开发。

二. 客户端控件式开发

  客户端开发了多个自定义控件,然后将它们组装到一起,以完成语音聊天室的功能。为了便于讲解,我主界面的图做了标注,以指示出各个自定义控件。  

  现在我们分别介绍各个控件:

1. 分贝显示器

  分贝显示器用于显示声音的大小,比如麦克风采集到的声音的大小,或扬声器播放的声音的大小。如上图中3标注的。

(1)傅立叶变换

  将声音数据转换成分贝强度使用的是傅立叶变换。其对应的是客户端项目中的FourierTransformer静态类。源码比较简单,就不贴出来了,大家自己去看。

(2)声音强度显示控件 DecibelDisplayer

  DecibelDisplayer 使用的是PrograssBar来显示声音强度的大小。

  每当有声音数据交给DecibelDisplayer显示时,首先,DecibelDisplayer会调用上面的傅立叶变换将其转换为分贝,然后,将其映射为PrograssBar的对应的Value。

2.发言者控件 SpeakerPanel

  SpeakerPanel 用于表示聊天室中的一个成员,如上图中1所示。它显示了成员的ID,成员的声音的强度(使用DecibelDisplayer控件),以及其麦克风的状态(启用、引用)。

  这个控件很重要,我将其源码贴出来:

public partial class SpeakerPanel : UserControl ,IDisposable
{
private ChatUnit chatUnit;

public SpeakerPanel()
{
InitializeComponent();
this.SetStyle(ControlStyles.ResizeRedraw, true);//调整大小时重绘
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);// 双缓冲
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);// 禁止擦除背景.
this.SetStyle(ControlStyles.UserPaint, true);//自行绘制
this.UpdateStyles();
}

public string MemberID
{
get
{
if (this.chatUnit == null)
{
return null;
}

return this.chatUnit.MemberID;
}
}

public void Initialize(ChatUnit unit)
{
this.chatUnit = unit;
this.skinLabel_name.Text = unit.MemberID;

this.chatUnit.MicrophoneConnector.ConnectEnded += new CbGeneric<OMCS.Passive.ConnectResult>(MicrophoneConnector_ConnectEnded);
this.chatUnit.MicrophoneConnector.OwnerOutputChanged += new CbGeneric(MicrophoneConnector_OwnerOutputChanged);
this.chatUnit.MicrophoneConnector.AudioDataReceived += new CbGeneric<byte[]>(MicrophoneConnector_AudioDataReceived);
this.chatUnit.MicrophoneConnector.BeginConnect(unit.MemberID);
}

public void Initialize(string curUserID)
{
this.skinLabel_name.Text = curUserID;
this.skinLabel_name.ForeColor = Color.Red;
this.pictureBox_Mic.Visible = false;
this.decibelDisplayer1.Visible = false;
}

void MicrophoneConnector_AudioDataReceived(byte[] data)
{
this.decibelDisplayer1.DisplayAudioData(data);
}

void MicrophoneConnector_OwnerOutputChanged()
{
if (this闲谈神经网络--写给初学者(三) - 吴广磊  阅读原文»

  接上篇。

  前面两篇讲解了神经网络就是一个黑箱,里面有一个一个的小圆球(神经元)连接而成,通过改变神经元的连接方式及各个参数,就可以实现一个符合要求的神经网络。接下来我们来举一个BP神经网络的例子,以加深理解。

  在讲解这个例子之前,大概说一下一个神经网络解决问题的思路:

  1.对要解决的问题进行理解,找到输入输出数据。

  2.把已知的输入输出数据分为两部分:一部分用来训练网络;另外一部分来验证训练的网络到底好不好用。

  3.对输入数据进行预处理及归一化处理。

  4.创建合适的网络,如BP、感知器、RBF、Hopfield等等。(其实是先建立一个神经网络的黑箱子)

  5.设置网络训练参数,例如设置训练函数,学习函数、激活函数等等。

  6.使用输入输出训练网络。(其实是把黑箱子里面的神经元的各个参数给训练和学习合适了)

  7.使用验证数据验证网络。

  8.验证后觉得网络还不错,就可以实际应用,在实际应用过程中,还可以再对模型进行优化、重构等等。

下面开始我们例子的说明,用一个传统的花分类的例子(http://en.wikipedia.org/wiki/Iris_flower_data_set ):大概意思是:给你一朵花的四个特征,神经网络输出它的品种(共有三个品种)。

所以每一个样本的输入:四个数据,代表花的四个特征;输出:一个数据,代表花的品种,分别为1,2,3代表三个不同的品种。

样本数据为:http://files.cnblogs.com/wuguanglei/%E6%95%B0%E6%8D%AE.rar

其中trainData.txt用作训练数据,testData.txt用作验证数据。Matlab程序代码如下:

%0.操作前
clear;
close all;
clc;
%1.读入训练数据
[f1,f2,f3,f4,class] = textread('trainData.txt' , '%f%f%f%f%f',150);
input=[f1,f2,f3,f4]';%神经网络是一列算做一个样本输入,因此矩阵要转置一下
output=class';
%2.输入数据归一化
= mapminmax(input) ;
minI=Se.xmin;
maxI=Se.xmax;
%3.构造输出数据矩阵
s = length( class) ;%输出为1,2,3,这用1 0 0表示1;0 1 0表示2;0 0 1表示3
output = zeros( 3, s ) ;
for i = 1 : s
output( class( i ),i ) = 1 ;
end

%4.创建神经网络
net = newff( minmax(input) , [70 3] , { 'logsig' 'logsig' } , 'traingdx' ) ; %其中70表示第一层70个神经元,第二层30个神经元,后面几个参数分别设置的是激活函数和训练函数。

%5.设置训练参数
net.trainparam.show = 40 ;
net.trainparam.epochs = 600 ;
net.trainparam.goal = 0.01 ;
net.trainParam.lr = 0.01 ;

%6.开始训练
[net,tr,e] = train( net, input , output ) ;

%7.测试网络
[t1 t2 t3 t4 c] = textread('testData.txt' , '%f%f%f%f%f',150);
testInput=[t1,t2,t3,t4]';
testInput = mapminmax (testInput ) ;
Y = sim( net , testInput ) ;

%8.统计结果
[s1 , s2] = size( Y ) ;
hitNum = 0 ;
for i = 1 : s2
[m , Index] = max( Y( : , i ) )
if( Index == c(i) )
hitNum = hitNum + 1 ;
end
end
sprintf('识别率是 %0.3f%%',100 * hitNum / s2 )

详细讲解看代码注释吧。

就先写到这儿吧,对于入门,应该够了。

============================

最近帮朋友弄了微信公众号,每天早上6点30分,发送一个60秒的语音,有兴趣的朋友不妨关注一下。

微信号:歪理邪说(wailixs)

二维码:


本文链接:闲谈神经网络--写给初学者(三),转载请注明。

阅读更多内容

2014年10月27日星期一

在 CentOS 6.x 上安装最新版本的 git - make_dream

本邮件内容由第三方提供,如果您不想继续收到该邮件,可 点此退订
在 CentOS 6.x 上安装最新版本的 git - make_dream  阅读原文»

在 CentOS 的默认仓库中有git,所以最简单的方法是:

$ sudo yum install git

这种方法虽然简单,但是一般仓库里的版本更新不及时,比如 CentOS 仓库中的 git 最新版是1.7.1(今天是2014-10-28),但是 git 官方已经到2.x 的版本了。

这时,就不得不动用最终的大杀器了,通过自己编译源码安装。

步骤如下:
1. 需要给 CentOS 下载安装编译工具。

$ sudo yum groupinstall “Development Tools”

2. 安装一些 git 构建或执行时需要的其他依赖。

$ sudo yum install zlib-devel perl-ExtUtils-MakeMaker asciidoc xmlto openssl-devel

3. 下载 git 最新版本的源代码

$ cd ~
$
wget -O git.zip https://github.com/git/git/archive/master.zip

4. 解压源文件

$ unzip git.zip
$ cd git
-master


5. 构建并安装

$ autoconf
$ .
/configure
$
make && make install


6. 创建link

$ ln -s /usr/local/bin/git /usr/bin/


如果系统中已经安装过旧的版本,步骤6可能会报文件已存在的错误,这个时候需要把旧的 link 删掉再重新 link。

7. 检查 git 版本

$ git --version


此时,应该显示为 git 的最新版本。


本文链接:在 CentOS 6.x 上安装最新版本的 git,转载请注明。

Android输出日志Log类 - 宇少095  阅读原文»

android.util.Log常用的方法有以下5个:


Log.v() Log.d() Log.i() Log.w() 以及 Log.e()。根据首字母分别对应VERBOSE,DEBUG,INFO,WARN,ERROR。


1、Log.v 的调试颜色为黑色的,任何消息都会输出,这里的v代表verbose�嗦的意思,平时使用就是Log.v("","");


2、Log.d的输出颜色是蓝色的,仅输出debug调试的意思,但他会输出上层的信息,过滤起来可以通过DDMS的Logcat标签来选择。


3、Log.i的输出为绿色,一般提示性的消息information,它不会输出Log.v和Log.d的信息,但会显示i、w和e的信息。


4、Log.w的意思为橙色,可以看作为warning警告,一般需要我们注意优化Android代码,同时选择它后还会输出Log.e的信息。


5、Log.e为红色,可以想到error错误,这里仅显示红色的错误信息,这些错误就需要我们认真的分析,查看栈的信息了。

1 public class MyLog {
2 private static Boolean MYLOG_SWITCH = true; // 日志文件总开关
3 private static Boolean MYLOG_WRITE_TO_FILE = true;// 日志写入文件开关
4 private static char MYLOG_TYPE = 'v';// 输入日志类型,w代表只输出告警信息等,v代表输出所有信息
5 private static String MYLOG_PATH_SDCARD_DIR = "/sdcard/";// 日志文件在sdcard中的路径
6 private static int SDCARD_LOG_FILE_SAVE_DAYS = 0;// sd卡中日志文件的最多保存天数
7 private static String MYLOGFILEName = "Log.txt";// 本类输出的日志文件名称
8 private static SimpleDateFormat myLogSdf = new SimpleDateFormat(
9 "yyyy-MM-dd HH:mm:ss");// 日志的输出格式
10 private static SimpleDateFormat logfile = new SimpleDateFormat("yyyy-MM-dd");// 日志文件格式
11 public Context context;
12
13 public static void w(String tag, Object msg) { // 警告信息
14 log(tag, msg.toString(), 'w');
15 }
16
17 public static void e(String tag, Object msg) { // 错误信息
18 log(tag, msg.toString(), 'e');
19 }
20
21 public static void d(String tag, Object msg) {// 调试信息
22 log(tag, msg.toString(), 'd');
23 }
24
25 public static void i(String tag, Object msg) {//
26 log(tag, msg.toString(), 'i');
27 }
28
29 public static void v(String tag, Object msg) {
30 log(tag, msg.toString(), 'v');
31 }
32
33 public static void w(String tag, String text) {
34 log(tag, text, 'w');
35 }
36
37 public static void e(String tag, String text) {
38 log(tag, text, 'e');
39 }
40
41 public static void d(String tag, String text) {
42 log(tag, text, 'd');
43 }
44
45 public static void i(String tag, String text) {
46 log(tag, text, 'i');
47 }
48
49 public static void v(String tag, String text) {
50 log(tag, text, 'v');
51 }
52
53 /**
54 * 根据tag, msg和等级,输出日志
55 *
56 * @param tag
57 * @param msg
58 * @param level
59 * @return void
60 * @since v 1.0
<

阅读更多内容

2014年10月25日星期六

用Linux Shell脚本轻松管理Radius服务器

本邮件内容由第三方提供,如果您不想继续收到该邮件,可 点此退订
用Linux Shell脚本轻松管理Radius服务器  阅读原文»

公司的无线环境采用mac地址认证的方式,mac地址被绑定到Radius的users配置文件中,将注册了的mac地址作为用户名和密码。为了方便的管理这些mac地址,自己写了一个shell脚本来管理。

shell脚本所特有的强大文本处理能力和各种命令函数的组合,使得管理员的工作能轻松不少。

下面就列出该脚本的功能以示参考:

  1. 添加mac地址

  2. 删除mac地址

  3. 查找mac地址

  4. 去除重复mac地址

  5. 检查mac地址合法性

  6. TODO,导入导出mac地址,添加注释

其中用到的Shell脚本技术包括但不限于:

  1. 文本文件的列处理和行处理,如sed、awk等命令

  2. 字符串查找、过滤、大小写转换,bash和grep等命令

  3. 获取、计算、比较字符串长度,bash和wc等命令

  4. mac地址正则表达式的处理和类型转换

  5. shell编程操作、包括文件包含、函数、参数传递、返回值等

  6. 其他

代码示例:

  #!/bin/bash  #  # Source function library.  . /etc/rc.d/init.d/functions  RADIUSD=/usr/sbin/radiusd  LOCKF=/var/lock/subsys/radiusd  CONFIG=/etc/raddb/radiusd.conf  USERCONFIG=/etc/raddb/users  [ -f $RADIUSD ] || exit 0  [ -f $CONFIG ] || exit 0  [ -f $USERCONFIG ] || exit 0  RETVAL=0  OPERATION=$1  MACADDRESS=$2  function help()  {          clear          echo $""          echo $"===================================================================================="          echo $"For Radius on Fedora/CentOS/RadHat Linux Server, Written by Chris"          echo $"===================================================================================="          echo $"A tool to manage Radius server"          echo $""          echo $"Usage: $0 {find|add|modify|delete|check|remove|start|stop|status|restart|reload} mac"          #TODO          echo $"Usage: $0 {import|export|debug}"          echo $""          echo $"For more information please contract dgdenterprise@gmail.com"          echo $"===================================================================================="          echo $""          exit 1  }  function mac()  {          if [ -z $MACADDRESS ];then                  echo $"no mac address is signed! "                  echo $"\$2 is $MACADDRESS"                  exit 1          else                  if [[ "${#MACADDRESS}" != "12" ]] && [[ "${#MACADDRESS}" != "17" ]] ;then                          echo "mac length is ${#MACADDRESS}"                          echo "mac address is illegal! "                          exit 1  #                else  #                        echo $"mac which you input is $MACADDRESS"                  fi                  #echo $MACADDRESS | sed -nr '/[A-Fa-f0-9]{2}:[A-Fa-f0-9]{2}:[A-Fa-f0-9]{2}:[A-Fa-f0-9]{2}:[A-Fa-f0-9]{2}:[A-Fa-f0-9]{2}/p'                  #echo $MACADDRESS | sed -nr '/[A-Fa-f0-9]{2}-[A-Fa-f0-9]{2}-[A-Fa-f0-9]{2}-[A-Fa-f0-9]{2}-[A-Fa-f0-9]{2}-[A-Fa-f0-9]{2}/p'                  #echo $MACADDRESS | sed -nr '/[A-Fa-f0-9]{12}/p'                  if [[ `echo $MACADDRESS | grep -` ]];then                          PROMAC=`echo $MACADDRESS | sed -nr '/[A-Fa-f0-9]{2}-[A-Fa-f0-9]{2}-[A-Fa-f0-9]{2}-[A-Fa-f0-9]{2}-[A-Fa-f0-9]{2}-[A-Fa-f0-9]{2}/p' | tr '[:upper:]' '[:lower:]' | sed 's/-//g'`                  elif [[ `echo $MACADDRESS | grep :` ]];then                          PROMAC=`echo $MACADDRESS | sed -nr '/[A-Fa-f0-9]{2}:[A-Fa-f0-9]{2}:[A-Fa-f0-9]{2}:[A-Fa-f0-9]{2}:[A-Fa-f0-9]{2}:[A-Fa-f0-9]{2}/p' | tr '[:upper:]' '[:lower:]' | sed 's/://g'`                  else                          PROMAC=`echo $MACADDRESS | tr '[:upper:]' '[:lower:]'`                  fi                  echo $PROMAC          fi  }  function find()  {          MAC=`mac`          echo $"accepted mac is $MAC"          if [[ `grep $MAC $USERCONFIG` ]]; then                  MACLINE=`grep -n $MAC $USERCONFIG | awk -F ':' '{print $1}'`                  #echo $MACLINE                  MACLINECOUNT=$(echo $MACLINE | wc -w)                  #echo $MACLINECOUNT                  if [[ "$MACLINECOUNT" != "1" ]];then                          echo $"ERROR, this mac $MAC has duplicate record, you should use $0 remove $MAC to remove duplicate record"                          exit 1                  fi                  echo $"Successfully find $MAC in $MACLINE line of file $USERCONFIG! "                  echo                  REVAL=$?          else                  echo $"Can not find $MAC in file $USERCONFIG! "                  echo                  exit 1                  REVAL=$?          fi  }  function add()  {          MAC=`mac`          echo $"accepted mac is $MAC"          #find $MAC          LINENUM=`grep -n "Cleartext-Password :='" users | grep -v \# | head -n1 | awk -F ":" '{print $1}'`          SEDOPERATION=$LINENUM"a"          sed -i "$SEDOPERATION $MAC    Cleartext-Password :='$MAC'" $USERCONFIG          find $MAC          restart  }  function modify()  {          MAC=`mac`          find $MAC          #TODO  }  function delete()  {          MAC=`mac`          echo $"accepted mac is $MAC"          if [[ `grep $MAC $USERCONFIG` ]]; then                  MACLINE=`grep -n $MAC $USERCONFIG | awk -F ':' '{print $1}'`                  ##echo $MACLINE                  #MACLINECOUNT=$(echo $MACLINE | wc -w)                  ##echo $MACLINECOUNT                  #if [[ "$MACLINECOUNT" != "1" ]];then                  #        echo $"ERROR, this mac $MAC has duplicate record, you should use $0 remove $MAC to remove duplicate record"                  #        exit 1                  #fi                  echo $"Successfully find $MAC in $MACLINE line of file $USERCONFIG! "                  echo $"It will be deleted! "                  sed -i "$MACLINE d" $USERCONFIG                  #TODO                  echo $"If you see 'Can not find $MAC in file $USERCONFIG! ', it means successfully! "                  find $MAC                  echo                  REVAL=$?          else                  echo $"Can not find $MAC in file $USERCONFIG! "                  echo                  REVAL=$?          fi  }  function check()  {          MAC=`mac`          find $MAC          remove $MAC  }  function remove()  {          MAC=`mac`          echo $"accepted mac is $MAC"          #TODO          #echo $"backuped file to file $FILENAME"          if [[ `grep $MAC $USERCONFIG` ]]; then                  MACLINE=`grep -n $MAC $USERCONFIG  awk -F ':' '{print $1}'`                  #echo $MACLINE                  MACLINECOUNT=$(echo $MACLINE  wc -w)                  #echo $MACLINECOUNT                  if [[ "$MACLINECOUNT" == "1" ]];then                          echo $"WARNNING, this mac $MAC is good record, no duplicate record has found! "                          exit 0                  fi          TOREMOVE="$MAC    Cleartext-Password :='$MAC'"          sed -i "/^$TOREMOVE$/d" $USERCONFIG          add $MAC          fi  }  function restart()  {          service radiusd restart  }  function reload()  {          service radiusd reload  }  function status()  {          service radiusd status  }  case "$1" in          find)                  find                  RETVAL=$?          ;;          add)                  add                  RETVAL=$?          ;;          modify)                  modify                  RETVAL=$?          ;;          delete)                  delete                  RETVAL=$?          ;;          check)                  check                  RETVAL=$?          ;;          remove)                  remove                  RETVAL=$?          ;;          start)                  start                  RETVAL=$?          ;;          stop)                  stop                  RETVAL=$?          ;;          status)                  status                  RETVAL=$?          ;;          restart)                  restart                  RETVAL=$?          ;;          reload)                  reload                  RETVAL=$?          ;;          *)                  help                  exit 1          ;;  esac  

其中有一些可以改进的地方,比如换一种方法或者增强用户的使用体验都是可以的,欢迎大家提出意见。

本文出自 "通信,我的最爱" 博客,请务必保留此出处http://dgd2010.blog.51cto.com/1539422/1567085

sudo bug导致的zabbix断图问题  阅读原文»

sudo bug导致的zabbix断图问题

线上使用zabbix的host update来监测监控值是否完整(关于host update的实现请参考:

http://caiguangguang.blog.51cto.com/1652935/1345789)

一直发现有机器过一段时间update值就会莫名其妙变低,之前一直没有找到rc,只是简单通过重启agent来进行修复,最近同事细心地发现可能是和sudo的bug有关系。

回过头再来验证下整个的排查过程。

1.通过zabbix 数据库获取丢失数据的item,拿出缺失的(20分钟没有更新的)值的item列表

  select b.key_,b.lastvalue,from_unixtime(b.lastclock) from hosts a,   items b where a.hostid=b.hostid and a.host='xxxxxx' and   b.lastclock < (unix_timestamp() - 1200) limit 10;  

比如这里看agent.ping:

观察监控图,发现在18点20分之后数据丢失:

wKiom1RH0cKSp6HeAAJbnn76bS0678.jpg

2.分析zabbix agent端的日志

发现在18点24粉左右出现下面的日志,没有看到正常的获取值和发送值的情况,只有大量的update_cpustats状态,同时发现有一行kill command 失败的日志:

  27589:20141021:182442.143 In zbx_popen() command:'sudo hadoop_stats.sh nodemanager StopContainerAvgTime'  27589:20141021:182442.143 End of zbx_popen():5  48430:20141021:182442.143 zbx_popen(): executing script  27585:20141021:182442.284 In update_cpustats()  27585:20141021:182442.285 End of update_cpustats()  27585:20141021:182443.285 In update_cpustats()  27585:20141021:182443.286 End of update_cpustats()  27585:20141021:182444.286 In update_cpustats()  27585:20141021:182444.287 End of update_cpustats()  27585:20141021:182445.287 In update_cpustats()  27585:20141021:182445.287 End of update_cpustats()  27585:20141021:182446.288 In update_cpustats()  27585:20141021:182446.288 End of update_cpustats()  ..........  27585:20141021:182508.305 In update_cpustats()  27585:20141021:182508.305 End of update_cpustats()  27585:20141021:182509.306 In update_cpustats()  27585:20141021:182509.306 End of update_cpustats()  27585:20141021:182510.306 In update_cpustats()  27585:20141021:182510.307 End of update_cpustats()  27585:20141021:182511.307 In update_cpustats()  27585:20141021:182511.308 End of update_cpustats()  27589:20141021:182512.154 failed to kill [sudo hadoop_stats.sh nodemanager StopContainerAvgTime]: [1] Operation not permitted  27589:20141021:182512.155 In zbx_waitpid()  27585:20141021:182512.308 In update_cpustats()  27585:20141021:182512.309 End of update_cpustats()  27585:20141021:182513.309 In update_cpustats()  27585:20141021:182513.309 End of update_cpustats()  

对比正常的日志:

  27589:20141021:180054.376 In zbx_popen() command:'sudo hadoop_stats.sh nodemanager StopContainerAvgTime'  27589:20141021:180054.377 End of zbx_popen():5  18798:20141021:180054.377 zbx_popen(): executing script  27589:20141021:180054.384 In zbx_waitpid()  27589:20141021:180054.384 zbx_waitpid() exited, status:1  27589:20141021:180054.384 End of zbx_waitpid():18798  27589:20141021:180054.384 Run remote command [sudo  hadoop_stats.sh nodemanager StopContainerAvgTime] Result [2] [-1]...  27589:20141021:180054.384 For key [hadoop_stats[nodemanager,StopContainerAvgTime]] received value [-1]  27589:20141021:180054.384 In process_value() key:'gd6g203s80-hadoop-datanode.idc.vipshop.com:hadoop_stats[nodemanager,StopContainerAvgTime]' value:'-1'  27589:20141021:180054.384 In send_buffer() host:'10.200.100.28' port:10051 values:37/50  27589:20141021:180054.384 Will not send now. Now 1413885654 lastsent 1413885654 < 1  27589:20141021:180054.385 End of send_buffer():SUCCEED  27589:20141021:180054.385 buffer: new element 37  27589:20141021:180054.385 End of process_value():SUCCEED  

可以看到正常情况下脚本会有返回值,而出问题的时候,脚本是没有返回值的,并且由于是使用sudo 运行脚本,导致以普通用户启动的zabbix在超时时没有办法杀掉这个command(Operation not permitted错误)

3.假设这里启动zabbix agent的普通用户为apps用户,我们看下这个脚本目前的状态

  ps -ef|grep hadoop_stats.sh  root     34494 31429  0 12:54 pts/0    00:00:00 grep 48430  root     48430 27589  0 Oct21 ?        00:00:00 sudo hadoop_stats.sh nodemanager StopContainerAvgTime  root     48431 48430  0 Oct21 ?        00:00:00 [hadoop_stats.sh] <defunct>  

可以看到,这里产生了一个僵尸进程(关于僵尸进程可以参考:http://en.wikipedia.org/wiki/Zombie_process)

僵尸进程是由于子进程运行完毕之后,发送SIGCHLD到父进程,而父进程没有正常处理这个信号导致。

  You have killed the process, but a dead process doesn't disappear from the process table  until its parent process performs a task called "reaping" (essentially calling wait(3)   for that process to read its exit status). Dead processes that haven't been reaped are    called "zombie processes."  The parent process id you see for 31756 is process id 1, which always belongs to init.  That process should reap its zombie processes periodically, but if it can't, they will   remain zombies in the process table until you reboot.  

正常的进程情况下,我们使用strace attach到父进程,然后杀掉子进程后可以看到如下信息:

  Process 3036 attached - interrupt to quit  select(6, [5], [], NULL, NULL  )          = ? ERESTARTNOHAND (To be restarted)  --- SIGCHLD (Child exited) @ 0 (0) ---  rt_sigreturn(0x11)                      = -1 EINTR (Interrupted system call)  wait4(3037, [{WIFSIGNALED(s) && WTERMSIG(s) == SIGTERM}], WNOHANG|WSTOPPED, NULL) = 3037  exit_group(143)                         = ?  Process 3036 detached  

产生僵尸进程之后,可以通过杀掉父进程把僵尸进程变成孤儿进程(父进程为init进程)

但是这里因为是用sudo启动的脚本,导致启动用户都是root,apps用户就没有权限杀掉启动的命令,进而导致子进程一直是僵尸进程的状态存在

4.来看一下zabbix agent端启动的相关进程情况

  ps -ef|grep zabbix  apps     27583     1  0 Sep09 ?        00:00:00 /apps/svr/zabbix/sbin/zabbix_agentd -c /apps/conf/zabbix_agentd.conf  apps     27585 27583  0 Sep09 ?        00:33:25 /apps/svr/zabbix/sbin/zabbix_agentd -c /apps/conf/zabbix_agentd.conf  apps     27586 27583  0 Sep09 ?        00:00:14 /apps/svr/zabbix/sbin/zabbix_agentd -c /apps/conf/zabbix_agentd.conf  apps     27587 27583  0 Sep09 ?        00:00:14 /apps/svr/zabbix/sbin/zabbix_agentd -c /apps/conf/zabbix_agentd.conf  apps     27588 27583  0 Sep09 ?        00:00:14 /apps/svr/zabbix/sbin/zabbix_agentd -c /apps/conf/zabbix_agentd.conf  apps     27589 27583  0 Sep09 ?        02:28:12 /apps/svr/zabbix/sbin/zabbix_agentd -c /apps/conf/zabbix_agentd.conf  root     34207 31429  0 12:54 pts/0    00:00:00 grep zabbix  root     48430 27589  0 Oct21 ?        00:00:00 sudo /apps/sh/zabbix_scripts/hadoop/hadoop_stats.sh nodemanager StopContainerAvgTime  

通过strace我们发现27589的进程一直在等待48430的进程

  strace  -p 27589  Process 27589 attached - interrupt to quit  wait4(48430, ^C <unfinished ...>  Process 27589 detached  

阅读更多内容

Scala中Stream的应用场景及其实现原理 - 崔鹏飞

本邮件内容由第三方提供,如果您不想继续收到该邮件,可 点此退订
Scala中Stream的应用场景及其实现原理 - 崔鹏飞  阅读原文»

欢迎访问我的独立博客:http://cuipengfei.me/blog/2014/10/23/scala-stream-application-scenario-and-how-its-implemented/

假设一个场景

需要在50个随机数中找到前两个可以被3整除的数字。

听起来很简单,我们可以这样来写:

1
2
3
4
5
6
7
8
9
def randomList = (1 to 50).map(_ => Random.nextInt(100)).toList

def isDivisibleBy3(n: Int) = {
val isDivisible = n % 3 == 0
println(s"$n $isDivisible")
isDivisible
}

randomList.filter(isDivisibleBy3).take(2)

一个产生50个随机数的函数;

一个检查某数字是否能被3整除的函数;

最后,对含有50个随机数的List做filter操作,找到其中所有能够被3整除的数字,取其中前两个。

把这段代码在Scala的console里面跑一下,结果是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
scala> randomList.filter(isDivisibleBy3).take(2)
31 false
71 false
95 false
7 false
38 false
48 true
88 false
52 false
2 false
27 true
90 true
55 false
96 true
91 false
82 false
83 false
8 false
51 true
96 true
27 true
12 true
76 false
17 false
53 false
54 true
70 false
29 false
49 false
12 true
83 false
18 true
6 true
7 false
76 false
51 true
95 false
76 false
85 false
87 true
84 true
44 false
44 false
89 false
84 true
42 true
44 false
0 true
23 false
35 false
55 false
res34: List[Int] = List(48, 27)
我的 Sublime Text 2 笔记 - 颜海镜  阅读原文»

作为aptana死忠粉的我,最近由于工作需要最近开始使用sublime,初次使用,就被其秒开的启动速度,简洁的界面设计,无干扰的信息提示所这幅。

俗话说,工欲善其事必先利其器,作为码农,在开始编码之前,必须要对自己的工具熟悉,才能事半功倍,所以开始了一番折腾,下面记录下一些笔记。

快捷键

作为码农,很多时间都是在敲键盘的,所以快捷键是非常重要的,sublime的快捷键非常非常多,很难都记住,按照80/20原则,只有20%是常用的,下面是我常用的快捷键:

快捷键功能
ctrl + p搜索项目中的文件
ctrl+k, ctrl+b切换侧边栏显示状态
ctrl+shift+backspace左侧全部删除
ctrl+y重做或重复
shift+方向键移动并选择
ctrl+[|]缩进|取消缩紧
ctrl+l选择行,重复可依次增加选择下一行
ctrl+m跳转到对应括号
alt+.close tag
ctrl+shift+p打开命令面板
ctrl+r前往Method
ctrl+g跳转到第几行
ctrl+/当前行注释状态切换
ctrl+shift+[|]折叠|展开(代码)
ctrl+h替换

插件

sublime的功能已经很满足大部分需求了,但还是有个别差异化的需求,无法满足,这时候sublime的插件派上用场,先来晒下我的插件。

由于sublime 2本身不带插件,所以要先安装插件管理器(package control),首先打开控制台,点击sublime的菜单栏 view->show console(或者使用快捷键 ctrl+`)。

现在打开了控制台, 这个控制台有上下两栏, 上面一栏会实时显示sublime执行了什么插件,输出执行结果, 如果你安装的某个插件不能正常运行,应该先在这里看看有没有报错。下面栏是一个输入框,可以运行python代码。

我们输入下面的代码点击回车运行, 就能安装好package control了。

import urllib2,os,hashlib; h = '7183a2d3e96f11eeadd761d777e62404' + 'e330c659d4bb41d3bdf022e94cab3cd0'; pf = 'Package Control.sublime-package'; ipp = sublime.installed_packages_path(); os.makedirs( ipp ) if not os.path.exists(ipp) else None; urllib2.install_opener( urllib2.build_opener( urllib2.ProxyHandler()) ); by = urllib2.urlopen( 'http://sublime.wbond.net/' + pf.replace(' ', '%20')).read(); dh = hashlib.sha256(by).hexdigest(); open( os.path.join( ipp, pf), 'wb' ).write(by) if dh == h else None; print('Error validating download (got %s instead of %s), please try manual install' % (dh, h) if dh != h else 'Please restart Sublime Text to finish installation')

运行结束以后,记得重启编辑器,就能在Preferences中看到 package control了。

然后我们按住 ctrl+shift+p。此时会输出一个输入框,即可安装,删除,更新插件了。

LESS

这是一个非常棒的插件,可以让sublime支持less的语法高亮和语法提示,对于搞less的同学灰常重要,不过多解释。

Emmet

Emmet的前身是大名鼎鼎的Zen coding,如果你从事Web前端开发的话,对该插件一定不会陌生。它使用仿CSS选择器的语法来生成代码,大大提高了HTML/CSS代码编写的速度。

SublimeLinter

可以验证各种语法错误,不多解释。

DocBlockr

DocBlockr 可以使你很方便地对代码建立文档。它会解析函数,变量,和参数,根据它们自动生成文档范式,你的工作就是去填充对应的说明。

JsFormat

专门用来格式化js的工具,非常给力。

BracketHighlighter

像这些符号是成对的:花括号{}, 中括号[],括号:() ,引号“” 等。 这些符号当我们鼠标放在开始符号的位置的时候, 希望能明显看到结尾符号在哪儿sublime默认是下划线,很不明显, 想要明显一点,可以安装插件 BracketHighlighter。


本文链接:我的 Sublime Text 2 笔记,转载请注明。

阅读更多内容