调整MySQL最大连接数上限

在项目中碰到了MySQL数据库连接限制的问题,导致项目中数据库访问出现问题:

django.db.utils.OperationalError: (1040, 'Too many connections')

查阅了相关资料,MySQL中有max_connections这么一个配置值,用来人为限制同时存在的最多客户端连接数,该数值配置过低会导致如上的问题,调高该数值可以解决。

采取的调整策略,直接修改该值为1500:

set GLOBAL max_connections=1500;

相关参考资料:

Vue的多套环境发布脚本

需求来源

项目需要部署多套环境来发布(测试、生产),对于项目而言目前只有代码中后台接口地址不同,要求项目在build的时候能根据需要build成不同后台接口的版本去发布,目前咱在项目里用到的是一种最简单的办法,针对不同的build需求,写不同的build脚本,以达到build出不同版本的目的。

在Vue项目里具体的实现

Vue项目用过package.json这个文件来定义一组npm脚本:

"scripts": {
  "dev": "node build/dev-server.js",
  "start": "node build/dev-server.js",
  "build": "node build/build.js",
  "unit": "cross-env BABEL_ENV=test karma startest/unit/karma.conf.js --single-run",
  "e2e": "node test/e2e/runner.js",
  "test": "npm run unit && npm run e2e",
  "lint": "eslint --ext .js,.vue srtest/unit/specs test/e2e/specs"
},

scripts字段定义了7个脚本,可以通过npm run build的方式来运行node build/build.js这个部分,nodejs支持从环境变量中读取指定变量的值,关于这点在node命令行下也可以直接体现,通过process.env可以得到类似如下的结果:

╰$ node
> process.env
{ 
  COLORFGBG: '15;0',
  ITERM_PROFILE: 'natas',
  XPC_FLAGS: '0x0',
  LANG: 'zh_CN.UTF-8',
  SHELL: '/bin/zsh'
}

那么开头提到的需求的具体的实现就很简单了,在bash脚本里设置环境变量,在build脚本运行时读取环境变量的值作为build运行时的值,build出符合需求的版本。

具体的代码如下

文件列表

  • build_for_test.sh
  • conf/prod.env.js
  • src/api/api.js

build_for_test.sh

#!/bin/bash
mkdir nginx_web
# set nodeJs ENV
export NODE_ENV_API_ROOT="http://x.x.x.x:8000/panelApi/v1"
export NODE_ENV_NAME="test"

echo "Build for test env"
echo "NODE_ENV_API_ROOT="${NODE_ENV_API_ROOT}
echo "NODE_ENV_NAME="${NODE_ENV_NAME}

# re build
npm run build && cp -R dist/* nginx_web/ && chmod -R 755 nginx_web

conf/prod.env.js

exports.exports = {
  NODE_ENV: '"' + process.env.NODE_ENV_NAME + '"',
  API_ROOT: '"' + process.env.NODE_ENV_API_ROOT + '"'
}

src/api/api.js

let baseUrl = process.env.API_ROOT
console.log('baseUrl', baseUrl)
export let axiosIns = axios.create({
  baseURL: baseUrl
})

这样一来,在运行build_for_test.sh时,环境变量NODE_ENV_API_ROOT被设置为期望的值,在build的过程中,require('conf/prod.env.js')时可以从process.env.NODE_ENV_API_ROOT中获取到环境变量的值,build完成后发布的版本中,src/api/api.js文件里的baseUrl值便可以从process.env.API_ROOT中获取到。

python用队列实现多线程间的通信

在写Python网络爬虫的时候,因为网络延迟的问题,单线程的爬虫效率往往不尽人意,最简单的办法是使用多线程,并发地去发送请求,多线程间的通信也是挺有意思的,下面是自己写的一段练习的代码

# coding:utf-8
import Queue
import threading
import time
import random

q = Queue.Queue()  # 线程间通信的队列
quit_flage = False  # 一个全局变量,标记程序是否该退出了

pq = Queue.Queue()  # 用来存放print需要输出的消息


# 模拟工作线程
def worker(id):
    while not (quit_flage and q.empty()):  # 当队列为空且退出标记为True时结束循环
        time.sleep(random.random())  # 暂停一个随机的时间
        try:
            item = q.get(True, 0.1)  # 从队列中取出一个元素,最多阻塞0.1秒
        except Queue.Empty:  # 队列空,进入下一个循环
            continue
        que_print('%s get %s' % (id, item))
        q.task_done()  # 队列任务计数器减一,和join()函数对应
    que_print('%s stop' % id)


# 向队列q中加入元素
def add_item(item):
    q.put(item)


# 向打印队列加入消息,用来print
def que_print(msg):
    pq.put(msg)


# 从打印队列中取出待打印的消息
def que_print_thread():
    print 'start pq'
    while not (quit_flage and pq.empty()):  # 当队列为空且退出标记为True时结束循环
        try:
            msg = pq.get(True, 0.1)  # 从队列中取出一个元素,最多阻塞0.1秒
        except Queue.Empty:  # 队列空,进入下一个循环
            continue
        print msg


[add_item(i) for i in 'qwertyuiopasdfghjkl']  # 向队列中加些东西

[threading.Thread(target=worker, kwargs={'id': i}).start() for i in range(5)]  # 开启5个woker线程
threading.Thread(target=que_print_thread).start()  # 开启print队列
print 'wating job...'
q.join()  # 和task_done()对应,阻塞,直到队列中的任务计数器归零,计数器在put()后+1,和task_done()后-1
print 'job finished'
quit_flage = True  # 设置退出标记为True,各个线程会在标记为True的时候退出

程序运行结果

wating job...
start pq

1 get q
4 get w
3 get e
4 get r
2 get t
1 get y
0 get u
3 get i
2 get o
1 get p
4 get a
4 get s
3 get d
3 get f
0 get g
4 get h
1 get j
2 get k
job finished
1 get l

Process finished with exit code 0

程序中为什么要加一个打印队列呢?因为因为多线程同时向stdout输出信息的话,信息流看起来会是下面这个样子,所以还是写了一个用来输出消息的队列。

start pq
wating job...

1 get q
1 get w
4 get e3 get r
0 get t
2 get y2 get u
 4 get i
2 get o
1 get p
1 get a
4 get s0 get d
3 get f
0 get g
1 get h2 get j
2 get k
2 get l
job finished

Process finished with exit code 0

终端下来自FBI的问候

先上效果图

在猥琐同学的启发下,打算把爱情动作大片的片头放到终端的字符界面下,233,便简单写了一个bash脚本,放到~/.zshrc里面便可,每次打开终端都会显示出来一个FBI WARNING,哈哈。

附上代码

    function bash_center_print {
        strlen=${#1}
        blanklen=$(((COLUMNS - strlen) / 2))
        echo "$(tput cuf $blanklen) $1"
    }

    function bash_center_print_red {
        strlen=${#1}
        blanklen=$(((COLUMNS - strlen) / 2))
        echo "$(tput cuf $blanklen) $(tput setab 1)$1$(tput sgr 0)"
    }

    bash_center_print_red "FBI WARNING"
    echo ""
    bash_center_print "Federal Law provides severe civil and criminal penalties for "
    bash_center_print "the unauthorized reproduction, distribution, or exhibition of"
    bash_center_print "copyrighted motion pictures (Title 17, United States Code,   "
    bash_center_print "Sections 501 and 508). The Federal Bureau of Investigation   "
    bash_center_print "investigates allegations of criminal copyright infringement  "
    bash_center_print "(Title 17, United States Code, Section 506)."

END

其中用COLUMNS来获取到字符终端的宽度,计算一下居中的留白部分长度,用tput cuf来实现居中,关于FBI WARNING那一行用红色背景打印用到了tput setab来设置了背景色。

Github https://github.com/zhaohui8969/FBI_WARNING

如何用Python快速撸出一个贴吧图片下载器

这是一篇关于自己写贴吧图片下载器的总结。
 
起初是室友说要做一个贴吧的信息收集爬虫,于是打算先从简单一点的开始,先做一个贴吧的图片下载吧,之后再把获取图片改成获取背的内容不就好了嘛😄,然后就开始写了这个爬虫。现在这个Python脚本主要用到了下面的一些东西。  

用到的Python模块

  • requests(Python下的HTTP客户端实现)
  • BeautifulSoup(一个很方便的html语法分析工具)
  • colorama(在控制台输出彩色的
  • threading(我用到了多线程的一些东西)
  • signal(Python下和信号相关的东西)
  • OptionParser(很方便的命令行参数配置工具)
  • threadpool(一个菊苣写的Python线程池实现)

原理的部分

其实思路很简单,随便找一个帖子,比如这个http://tieba.baidu.com/p/2545680017,这是一个图楼,然后就需要分析页面的HTM代码内容,我用的是Chrome浏览器,那就右键选择一个楼层的图片,审查元素,然后发现HTML代码中是这样写的:  

<img class="BDE_Image" src="http://imgsrc.baidu.com/forum/w%3D580/sign=198d73cadc54564ee565e43183dc9cde/857f1f1f95cad1c8f75fb3fe7e3e6709c83d5130.jpg" pic_ext="jpeg" width="550" height="600">

 
可以看到里面src部分直接是图片的地址,再观察楼层图片和其他无关紧要的图片(比如层主头像、签名档)的写法区别,发现楼层图片都会有一个BDE_Imageclass,那么这就好办了,等会直接筛选出来就好。

然后翻到帖子的第二页,发现URL变成了http://tieba.baidu.com/p/2545680017?pn=2,多了一个?pn=2,这个pn参数也就表示了当前查看的页数位置,那么我们只需要知道帖子总共有多少页,对pn做一个遍历就可以获得一个帖子中所有页面了,现在需要知道怎么获取帖子的总页面数。直接来到帖子的最底部,看到了这样的内容62回复贴,共3页,对应的HTML代码为:  

<li class="l_reply_num" style="margin-left:8px"><span class="red" style="margin-right:3px">62</span>回复贴,共<span class="red">3</span>页</li>

 
特征很明显,有一个叫做l_reply_num的class,总回帖数和总页数都各用一个sapn标签包起来了,等会直接过滤出来就可以。

现在对网页部分的分析算是结束了,到了代码实现的部分了。

这里贴出部分代码

# 用requests模块直接获取网页的HTML内容
getparams = {'pn': 1}
result_ = requests.get('http://tieba.baidu.com/p/2545680017?', params=getparams).content
# 用BeautifulSoup模块做HTML内容的分析,获取总页数
soup = BeautifulSoup(result_, 'lxml')
page_list = soup.find('li', attrs={'class': 'l_reply_num'})
max_page_number = int(page_list.contents[2].string)  # 总页数
# 在pn处做一个遍历,获取到帖子的所有页面
for pageNum in range(start, end):
    getparams = {'pn': pageNum}
    html_cont = requests.get(self.pURL, params=getparams).content
    soup = BeautifulSoup(html_cont, 'lxml')
    # 用BeautifulSoup模块做HTML内容的分析,获取到所有楼层图片的下载地址
    # 并发送到下载队列
    for item in soup.findAll('img', attrs={'class': "BDE_Image"}):
        srcurl = item.get('src')  # 图片地址
        if not self._is_Exit:
            self.downloader.add_task(srcurl)
# 下载器里用threadpool模块做多线程的并发下载,这样速度比单线程快很多
def add_task(self, url):
    with self.lock:
        self._is_alive = True
    reqs = threadpool.makeRequests(self._download, [url, ], self._callback)
    [self.threadPool.putRequest(req, timeout=1) for req in reqs]
# 直接用requests模块完成图片的下载,并保存到文件
def _download(self, url):
    if self.is_alive():
        r = requests.request('GET', url)
        if r:
            filename = os.path.join(self.save_directory, os.path.basename(url))  # 拼接文件名
            if not os.path.exists(filename):
                with open(filename, 'wb') as f:
                    f.write(r.content)  # 保存图片
                return 1
            else:
                return -1
    else:
        return None

速度还不错

 

END

到这里其实关键的部分也就结束了,剩下的一些微小的工作,比如输出统计结果(下了多少图,用了多少时间,支持设置保存目录。。。),各位看官感兴趣的话可以直接去我的Github上看源码。

Github https://github.com/zhaohui8969/tiebaImageGet

在OSX上用automator实现VPN断线自动重连

  由于某些原因,平时需要使用到VPN,但是和这个服务器之间的链路呢总是有些不稳定,经常会断掉,然而OSX网络设置面板没有找到自动重播的选项,而且有时这个连接只是假死,就是还显示状态是连接上的,但是就是没有流量,得重播。可是手工去重播的话很麻烦,之前有试过在路由器上写过python脚本来实现链路连通状况检测自动重播的脚本,不过后来呢,那个路由器退役了,现在在OSX平台下想寻找到类似的解决办法,Google了解到OSX下有automator这样的神器。倒腾了一会解决了问题,在这里记录下来。

  首先打开automator,他是这个样子的:  

然后我们要制作一个应用程序,就像这样:

在“资料库”中选择“实用工具”分栏,找到“运行 AppleScript”这个东西,拖到右边,然后我们的代码编辑区就出现了,之后的所有代码都写在这里:

然后想想我需要实现什么,首先需要实现控制VPN连接的动作,Google到相关的资料,代码如下

tell application "System Events"
    tell network preferences
        disconnect service "L2BF"
        connect service "L2BF"
    end tell
end tell

这段意思呢就是告诉”System Events模块”(我叫他模块,不知道对不对)里面的”network preferences模块”:我要断开一个叫L2BF的连接,然后重连一下,这里测试结果没有问题。

那么下一个部分就是网络的状态监测部分,这里呢我想到的最简单的办法就是用ping命令检测,然后我就找到一个在AppleScript里运行python程序的办法,代码如下:

do shell script "ping -c 1 -W 3000 baidu.com"

这样就完成了一条python”语句.”执行,对baidu.com发一个ping包,3000秒超时。然后就是关于逻辑判断的部分了,找到了这些我需要的部分:

Try catch语句

try
    {"语句."}
on error
    {"语句."}
end try

循环语句

repeat
    {"语句."}
end repeat

当然,这里也可以有循环附加条件,比如这样:

repeat 2 times
repeat with counter from 1 to 3
repeat while counter ≠ 10
repeat until counter = 10
repeat with 单个元素 in 集合

判断语句

也就类似这样

if flag = 0 then
    {"语句."}
else if flag = -1 then
    {"语句."}
end if

变量什么的

set flag to 0

然后代码就这样写出来了:

on run {input, parameters}

    set flag to 0
    repeat
        try
            do shell script "ping -c 1 -W 3000 baidu.com"
            delay 30
        on error
            beep
            tell application "System Events"
                tell network preferences
                    disconnect service "L2BF"
                    connect service "L2BF"
                end tell
            end tell
            delay 5
        end try
    end repeat

    return input
end run

每隔30秒检测一下连通性,出现问题就重拨一下,看起来很不错,点一下这个按钮测试一下:

结果很不错,至少我把连接断掉他能自动给我连上。到这里就功德圆满了吗?不不不,在查找资料的过程中我看到了这个逼格很高的东西,有人在代码里写了一句这个:

say "Good~"

我也试了一下,当时就懵逼了,这还能语音说话。。。然后就打算提高一下这个程序的逼格,加入语音的部分,还去系统语音里面选了一个不错的女声Vicki:

加了语音和提示音后的程序代码:

on run {input, parameters}

    say "Demon started."
    set flag to 0
    repeat
        try
            do shell script "ping -c 1 -W 3000 baidu.com"
            if flag = 0 then
                say "Connected."
            else if flag = -1 then
                say "Reconnected!"
            end if
            set flag to 1
            delay 10
        on error
            beep
            set flag to -1
            say "Connection error! Try reconnect..."
            tell application "System Events"
                tell network preferences
                    disconnect service "L2BF"
                    connect service "L2BF"
                end tell
            end tell
            delay 5
        end try
    end repeat

    return input
end run

运行之后还有语音提示,666.

标准结局

Good!然后在Spotlight里搜索程序便可以直接打开:

运行之后在顶栏会有这样一个小齿轮,点击可以选择退出:

最后附上一些资料站:

Over