Leo的技术分享

记录开发的点滴

官方推荐使用 yum 源来安装 MySQL,本文讲述如何使用 yum 源来安装 MySQL。

一、添加 MySQL yum 源

可以采用yum localinstall命令来添加 MySQL 源。在 https://dev.mysql.com/downloads/repo/yum/ 页面选择合适 rpm 包,这些 rpm 包的作用就是添加 MySQL yum 源的。笔者下载的是 mysql80-community-release-el7-1.noarch.rpm。下载后,然后执行命令:

1
sudo yum localinstall mysql80-community-release-el7-1.noarch.rpm

执行yum localinstall命令后,可以看到/etc/yum.repos.d目录多了两个文件:

  • mysql-community.repo
  • mysql-community-source.repo

有兴趣的同学可以打开来看下里面的内容。

安装

添加好 yum 源后,就可以执行安装命令了。假设我们安装的是最新版本 MySQL ,执行以下命令:

1
sudo yum install mysql-community-server

不出意外的话,会成功安装 MySQL。

阅读全文 »

最近生产环境上发现有服务器进程出现任务堆积的情况,由于一时无法定位出原因,故对堆积的任务数量进行监控。进程日志中已有任务数量的输出,故只需要编写一个脚本读取日志中的任务数量,发现任务数量超过某个阈值就发送告警短信即可。
本想使用 Shell 脚本来实现,没想到 Shell 的语法实在不好掌握,赋值语法,数值比较语法,字符串与数字的转换,等等,这些语法的问题经过了多次 google 和百度后,还是没能解决:(。一气之下,决定还是用回了自己熟悉的 Python 来实现。

Python 中执行 Shell 命令有多种方法,stackoverflow 上有对这些方法进行比较的讨论,Calling an external command in Python 指出使用subprocess模块来实现更优。因此,本文说明如何使用subprocess模块来实现 Shell 脚本的功能。
subprocess模块提供多种方法来实现执行 Linux 的命令,例如subprocess.call()方法,subprocess.check_call()方法,等。这些方法都是对Popen类的封装,故本文着重讲述Popen类的使用。

执行 Shell 命令

可以通过向Popen()传递需要执行的命令来创建一个Popen对象,这样,便会创建一个子进程来执行命令。例如:

1
child = subprocess.Popen(["ping","-c","5","leehao.me"])

上面的代码会创建一个子进程来执行ping -c 5 leehao.me命令,这个命令采用列表的形式传递给Popen()方法。如果我们想直接采用ping -c 5 leehao.me字符串形式,可以添加shell=True来实现:

1
child = subprocess.Popen("ping -c 5 leehao.me", shell=True)

官方文档指出由于安全原因故不建议使用shell=True,详细说明可以参考官方文档的描述。

阅读全文 »

在 Python 中,实例方法(instance method),类方法(class method)与静态方法(static method)经常容易混淆。本文通过代码例子来说明它们的区别。

实例方法

Python 的实例方法用得最多,也最常见。我们先来看 Python 的实例方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
class Kls(object):
def __init__(self, data):
self.data = data

def printd(self):
print(self.data)


ik1 = Kls('leo')
ik2 = Kls('lee')

ik1.printd()
ik2.printd()

输出:

leo
lee

上述例子中,printd为一个实例方法。实例方法第一个参数为self,当使用ik1.printd()调用实例方法时,实例ik1会传递给self参数,这样self参数就可以引用当前正在调用实例方法的实例。利用实例方法的这个特性,上述代码正确输出了两个实例的成员数据。

阅读全文 »

原来的个人网站 leehao.me 使用了阿里云的负载均衡服务,因此 HTTPS 证书也是部署在负载均衡服务器上。文章《在阿里云部署 Hexo 网站》介绍了采用负载均衡方式来部署网站的方法。
考虑到网站访问流量不高且为了减少费用,决定不再使用负载均衡服务,而是直接采用单个 ECS 服务器的方式来部署网站。采用新的方式部署网站后,为了同样让网站支持 https,需要重新对 https 证书进行配置。

购买免费的阿里云证书

阿里云提供免费的证书服务,购买路径为,首页- 产品 - 安全 - CA 证书服务,选择免费型 DV SSL 证书类型,如下图所示:

图:在阿里云购买免费的证书服务

阅读全文 »

Python 装饰器是 Python 中常常使用到的一种语法糖,它可以大幅度减少重复代码,使用起来十分方便。另一方面,装饰器的定义往往会导致出现函数重重嵌套的情况,这会给装饰器的实现者带来阅读代码的困难。

本文剖析 Python 装饰器的定义与用法。

不带参数的装饰器

我们先来看一下不带参数的装饰器的实现,这种情况比较简单。以下是一个不带参数的装饰器的使用例子:

1
2
3
@decorator
def fun():
print('fun() is called')

前面提到,装饰器只是 Python 提供的语法糖,使用@decorator作为装饰器相当于:

1
2
3
def fun():
print('fun() is called')
fun = decorator(fun)

我们再来看一下装饰器decorator本身的定义。装饰器可以使用函数实现,也可以使用类来实现,这里我们介绍函数的实现方式。

阅读全文 »

这篇文章介绍 Python 的名称空间以及变量的作用域。

Python 的名称

Python 的名称(Name)是对象的一个标识(Identifier)。我们知道,在 Python 里面一切皆对象,名称就是用来引用对象的。说得有点玄乎,我们以例子说明。

例如,在a = 2这个语句中,2是个存储在内存中的一个对象,名称a则会引用2这个对象,“引用”的含义是指可以通过名称a来使用2这个对象。我们可以使用id()函数来获取对象的地址。

1
2
3
a = 2
print(id(2))
print(id(a))

输出:

43547988
43547988

可以看到,两都均指向同一个对象。我们再来看复杂一点的代码。

1
2
3
4
5
6
7
8
a = 2
print 'id(a) = %s' % id(a)
a = a + 1
print 'id(a) = %s' % id(a)
print 'id(3) = %s' % id(3)
b = 2
print 'id(2) = %s' % id(2)
print 'id(b) = %s' % id(b)

输出:

id(a) = 55606612
id(a) = 55606600
id(3) = 55606600
id(2) = 55606612
id(b) = 55606612

上述的代码发生了什么呢?我们以图1来说明。


图1:Python 的名称与对象

阅读全文 »

最近生产上上线了一个新的功能,在业务人员使用过程中,通过查看日志我们发现一个诡异的问题。这篇文章记录一下这个问题的排查过程以及解决方法。

存在问题的程序使用了 C 语言的 Redis 客户端 hiredis 来连接和操作 Redis。由于处于分布式的环境,所以我们使用了 Redis 的事务的特性,以避免多个客户端同时对 Redis 数据进行更改导致数据错乱的情况出现。有关 hiredis 的使用以及 Redis 事务特殊的介绍,可以参考文章《Redis C 语言客户端 hiredis 的使用》,以及《Hiredis 实现 Redis 流水线》

程序原始代码如下:

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
void GFRedisClient::QueryRowsByFields(const char *key,
map<string, string> &mapField2Value,
vector<map<string, string> > &outRecords)
{
if (!key || strlen(key) <= 0) {
KSBLog(WARN, "传入的服务代码为空");
return;
}
// 获取Redis连接
redisContext *ctx = CreateContext();
if (ctx == NULL) {
KSBLog(WARN, "查询二维表出错,无法获取Redis连接,服务:%s", key);
return;
}
// 监控二维表数据
SendWatch(ctx, key);
// 取出指定记录
vector<string> vecRecordRowIds;
GetSpecRecords(ctx, key, mapField2Value, vecRecordRowIds);
if (vecRecordRowIds.empty()) {
KSBLog(INFO, "从Redis查找不到指定条件的记录,服务:%s", key);
ReleaseContext(ctx);
return;
}
// 以事务实现
int pipelineCount = 0;
SendMulti(ctx, pipelineCount);
// 取出字段名称
SendQueryFields(ctx, key, pipelineCount);
// 取出指定记录
SendQuerySpecRecords(ctx, key, vecRecordRowIds, pipelineCount);
// 执行事务
SendExec(ctx, pipelineCount);
// 处理返回结果
ProposeQueryRows(ctx, key, pipelineCount, outRecords);
// 归还连接
KSBLog(INFO, "查询二维表归还Redis连接,服务:%s", key);
ReleaseContext(ctx);
}
阅读全文 »

文章《搭建 Elastic Stack 日志系统》 描述了如何利用 Kibana,Elasticsearch,Filebeat 来搭建日志系统。本文在上一篇 Elastic Stack 文章的基础上,说明如何将 Nginx 的日志接入 Elastic Stack 日志系统。

为了更新在 Kibana 上展示 Nginx 的日志,可以先将 Nginx 的日志改成 json 格式,如何更改 Nginx 的日志格式,可以参考文章 《Nginx 日志改成 JSON 格式》

假设我们的 Nginx 访问日志路径为 /usr/local/nginx/logs/access.log ,错误日志路径为 /usr/local/nginx/logs/error.log,那么我们的 Filebeat 配置可以这样写:

1
2
3
4
5
6
7
8
9
10
11
filebeat.prospectors:

- input_type: log
paths:
- /usr/local/nginx/logs/access.log
json.keys_under_root: true
json.message_key:

- input_type: log
paths:
- /usr/local/nginx/logs/error.log
阅读全文 »

Nginx 日志默认为普通文本的格式,例如,下面是 Nginx 的一行访问日志:

1
10.88.122.105 - - [02/Dec/2017:09:15:04 +0800] "GET /js/pagination.js HTTP/1.1" 304 0 "http://10.88.105.20:8063/stockrecommand.html" "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/7.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)" "-" 0.000

为了便于利用 Elastic Stack 日志平台收集展示 Nginx 的日志,可以将 Nginx 的日志改成 json 的格式。修改后的 json 日志格式如下所示:

1
{ "@timestamp": "12/Dec/2017:14:30:40 +0800", "remote_addr": "10.88.122.108", "referer": "-", "request": "GET / HTTP/1.1", "status": 304, "bytes":0, "agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36", "x_forwarded": "-", "up_addr": "-","up_host": "-","up_resp_time": "-","request_time": "0.000" }

为了修改 Nginx 的日志格式改成 json,需要修改 Nginx 的配置文件,笔者 Nginx 的配置文件为 /usr/local/nginx/conf/nginx.conf。

阅读全文 »

Node.js 是一个开源的项目,可以让 JavaScript 的代码在浏览器之外运行。
我们知道,JavaScript 代码可以在浏览器中运行,它可以访问 window 和 document 这样的全局对象以及其他 API 和库函数。
为了支持 JavaScrip 在浏览器以外的环境运行,谷歌工程师 Ryan Dahl 基于 Chrome V8 JavaScript 引擎开发了 Node.js。Node.js 是一个 JavaScript 运行环境,在这个环境下,JavaScript 代码能够访问硬盘驱动器,数据库,网络等。使用 Node.js 可以创建各种应用,下至命令行工具,上至 Web 服务器。
图 1 展示了浏览器环境的 JavaScript 代码的运行环境;图 2 展示了 Node.js 环境的 JavaScript 代码的运行环境。


图1:在浏览器运行的 JavaScript 代码


图2:在 Node.js 运行的 JavaScript 代码

阅读全文 »
0%