Thursday, May 12, 2016

redis的使用

前言
Redis是常用基于内存的Key-Value数据库,比Memcache更先进,支持多种数据结构,高效,快速。用Redis可以很轻松解决高并发的数据访问问题;做为时时监控信号处理也非常不错。
目录
  1. Redis在Windows中安装
  2. Redis在Linux Ubuntu中安装
  3. 通过命令行客户端访问Redis
  4. 修改Redis的配置

1. Redis在Windows中安装

在Windows系统上安装Redis数据库是件非常简单的事情,下载可执行安装文件(exe),双击安装即可。下载地址:https://github.com/rgl/redis/downloads
  • Redis服务器运行命令:Redis安装目录/redis-server.exe
  • Redis客户端运行命令:Redis安装目录/redis-cli.exe

2. Redis在Linux Ubuntu中安装

本文使用的Linux是Ubuntu 12.04.2 LTS 64bit的系统,安装Redis数据库软件包可以通过apt-get实现。
在Linux Ubuntu中安装Redis数据库

#安装Redis服务器端
~ sudo apt-get install redis-server
安装完成后,Redis服务器会自动启动,我们检查Redis服务器程序

# 检查Redis服务器系统进程
~ ps -aux|grep redis
redis     4162  0.1  0.0  10676  1420 ?        Ss   23:24   0:00 /usr/bin/redis-server /etc/redis/redis.conf
conan     4172  0.0  0.0  11064   924 pts/0    S+   23:26   0:00 grep --color=auto redis

# 通过启动命令检查Redis服务器状态
~ netstat -nlt|grep 6379
tcp        0      0 127.0.0.1:6379          0.0.0.0:*               LISTEN

# 通过启动命令检查Redis服务器状态
~ sudo /etc/init.d/redis-server status
redis-server is running

3. 通过命令行客户端访问Redis

安装Redis服务器,会自动地一起安装Redis命令行客户端程序。
在本机输入redis-cli命令就可以启动,客户端程序访问Redis服务器。

~ redis-cli
redis 127.0.0.1:6379>

# 命令行的帮助
redis 127.0.0.1:6379> help
redis-cli 2.2.12
Type: "help @" to get a list of commands in 
      "help " for help on 
      "help " to get a list of possible help topics
      "quit" to exit


# 查看所有的key列表
redis 127.0.0.1:6379> keys *
(empty list or set)
基本的Redis客户端命令操作
增加一条字符串记录key1

# 增加一条记录key1
redis 127.0.0.1:6379> set key1 "hello"
OK

# 打印记录
redis 127.0.0.1:6379> get key1
"hello"
增加一条数字记录key2

# 增加一条数字记录key2
set key2 1
OK

# 让数字自增
redis 127.0.0.1:6379> INCR key2
(integer) 2
redis 127.0.0.1:6379> INCR key2
(integer) 3

# 打印记录
redis 127.0.0.1:6379> get key2
"3"
增加一条列表记录key3

# 增加一个列表记录key3
redis 127.0.0.1:6379> LPUSH key3 a
(integer) 1

# 从左边插入列表
redis 127.0.0.1:6379> LPUSH key3 b
(integer) 2

# 从右边插入列表
redis 127.0.0.1:6379> RPUSH key3 c
(integer) 3

# 打印列表记录,按从左到右的顺序
redis 127.0.0.1:6379> LRANGE key3 0 3
1) "b"
2) "a"
3) "c"
增加一条哈希表记录key4

# 增加一个哈希记表录key4
redis 127.0.0.1:6379> HSET key4 name "John Smith"
(integer) 1

# 在哈希表中插入,email的Key和Value的值
redis 127.0.0.1:6379> HSET key4 email "abc@gmail.com"
(integer) 1

# 打印哈希表中,name为key的值
redis 127.0.0.1:6379> HGET key4 name
"John Smith"

# 打印整个哈希表
redis 127.0.0.1:6379> HGETALL key4
1) "name"
2) "John Smith"
3) "email"
4) "abc@gmail.com"
增加一条哈希表记录key5

# 增加一条哈希表记录key5,一次插入多个Key和value的值
redis 127.0.0.1:6379> HMSET key5 username antirez password P1pp0 age 3
OK

# 打印哈希表中,username和age为key的值
redis 127.0.0.1:6379> HMGET key5 username age
1) "antirez"
2) "3"

# 打印完整的哈希表记录
redis 127.0.0.1:6379> HGETALL key5
1) "username"
2) "antirez"
3) "password"
4) "P1pp0"
5) "age"
6) "3"
删除记录

# 查看所有的key列表
redis 127.0.0.1:6379> keys *
1) "key2"
2) "key3"
3) "key4"
4) "key5"
5) "key1"

# 删除key1,key5
redis 127.0.0.1:6379> del key1
(integer) 1
redis 127.0.0.1:6379> del key5
(integer) 1

# 查看所有的key列表
redis 127.0.0.1:6379> keys *
1) "key2"
2) "key3"
3) "key4"

4. 修改Redis的配置

4.1 使用Redis的访问账号
默认情况下,访问Redis服务器是不需要密码的,为了增加安全性我们需要设置Redis服务器的访问密码。设置访问密码为redisredis。
用vi打开Redis服务器的配置文件redis.conf

~ sudo vi /etc/redis/redis.conf

#取消注释requirepass
requirepass redisredis
4.2 让Redis服务器被远程访问
默认情况下,Redis服务器不允许远程访问,只允许本机访问,所以我们需要设置打开远程访问的功能。
用vi打开Redis服务器的配置文件redis.conf

~ sudo vi /etc/redis/redis.conf

#注释bind
#bind 127.0.0.1
修改后,重启Redis服务器。

~ sudo /etc/init.d/redis-server restart
Stopping redis-server: redis-server.
Starting redis-server: redis-server.
未使用密码登陆Redis服务器

~ redis-cli

redis 127.0.0.1:6379> keys *
(error) ERR operation not permitted
发现可以登陆,但无法执行命令了。
登陆Redis服务器,输入密码

~  redis-cli -a redisredis

redis 127.0.0.1:6379> keys *
1) "key2"
2) "key3"
3) "key4"
登陆后,一切正常。
我们检查Redis的网络监听端口

检查Redis服务器占用端口
~ netstat -nlt|grep 6379
tcp        0      0 0.0.0.0:6379            0.0.0.0:*               LISTEN
我们看到从之间的网络监听从 127.0.0.1:3306 变成 0 0.0.0.0:3306,表示Redis已经允许远程登陆访问。
我们在远程的另一台Linux访问Redis服务器

~ redis-cli -a redisredis -h 192.168.1.199

redis 192.168.1.199:6379> keys *
1) "key2"
2) "key3"
3) "key4"
远程访问正常。通过上面的操作,我们就把Redis数据库服务器,在Linux Ubuntu中的系统安装完成

Saturday, May 7, 2016

ubuntu 14 建 vsftpd ftp服务器

How to setup FTP server on ubuntu 14.04 ( VSFTPD )


FTP is used to transfer files from one host to another over TCP network. This article explains how to setup FTP server on ubuntu 14.04 .
There are 3 popular FTP server packages available PureFTPD, VsFTPD and ProFTPD. Here i’ve used VsFTPD which is lightweight and less Vulnerability.

Setup FTP server on Ubuntu 14.04

Step 1 » Update repositories .
krizna@leela:~$ sudo apt-get update
Step 2 » Install VsFTPD package using the below command.
krizna@leela:~$ sudo apt-get install vsftpd
Step 3 » After installation open /etc/vsftpd.conf file and make changes as follows.
Uncomment the below lines (line no:29 and 33).
write_enable=YES
local_umask=022
» Uncomment the below line (line no: 120 ) to prevent access to the other folders outside the Home directory.
chroot_local_user=YESand add the following line at the end.
allow_writeable_chroot=YES» Add the following lines to enable passive mode.
pasv_enable=Yes
pasv_min_port=40000
pasv_max_port=40100

Step 4 » Restart vsftpd service using the below command.
krizna@leela:~$ sudo service vsftpd restart
Step 5 » Now ftp server will listen on port 21. Create user with the below command.Use /usr/sbin/nologin shell to prevent access to the bash shell for the ftp users .
krizna@leela:~$ sudo useradd -m john -s /usr/sbin/nologin
krizna@leela:~$ sudo passwd john

Step 6 » Allow login access for nologin shell . Open /etc/shells and add the following line at the end.
/usr/sbin/nologin
Now try to connect this ftp server with the username on port 21 using winscp orfilezilla client and make sure that user cannot access the other folders outside the home directory.

Monday, April 25, 2016

python 多线程爬虫

本文希望达到的目标:
  1. 学习Queue模块
  2. 将Queue模块与多线程编程相结合
  3. 通过Queue和threading模块, 重构爬虫, 实现多线程爬虫,
  4. 通过以上学习希望总结出一个通用的多线程爬虫小模版

1. Queue模块


Queue模块实现了多生产者多消费者队列, 尤其适合多线程编程.Queue类中实现了所有需要的锁原语(这句话非常重要), Queue模块实现了三种类型队列:
  • FIFO(先进先出)队列, 第一加入队列的任务, 被第一个取出
  • LIFO(后进先出)队列,最后加入队列的任务, 被第一个取出(操作类似与栈, 总是从栈顶取出, 这个队列还不清楚内部的实现)
  • PriorityQueue(优先级)队列, 保持队列数据有序, 最小值被先取出(在C++中我记得优先级队列是可以自己重写排序规则的, Python不知道可以吗)

1.1. 类和异常

import Queue

#类
Queue.Queue(maxsize = 0)  #构造一个FIFO队列,maxsize设置队列大小的上界, 如果插入数据时, 达到上界会发生阻塞, 直到队列可以放入数据. 当maxsize小于或者等于0, 表示不限制队列的大小(默认)

Queue.LifoQueue(maxsize = 0)  #构造一LIFO队列,maxsize设置队列大小的上界, 如果插入数据时, 达到上界会发生阻塞, 直到队列可以放入数据. 当maxsize小于或者等于0, 表示不限制队列的大小(默认)

Queue.PriorityQueue(maxsize = 0)  #构造一个优先级队列,,maxsize设置队列大小的上界, 如果插入数据时, 达到上界会发生阻塞, 直到队列可以放入数据. 当maxsize小于或者等于0, 表示不限制队列的大小(默认). 优先级队列中, 最小值被最先取出

#异常
Queue.Empty  #当调用非阻塞的get()获取空队列的元素时, 引发异常
Queue.Full  #当调用非阻塞的put()向满队列中添加元素时, 引发异常

1.2. Queue对象

三种队列对象提供公共的方法
Queue.empty()  #如果队列为空, 返回True(注意队列为空时, 并不能保证调用put()不会阻塞); 队列不空返回False(不空时, 不能保证调用get()不会阻塞)
Queue.full()  #如果队列为满, 返回True(不能保证调用get()不会阻塞), 如果队列不满, 返回False(并不能保证调用put()不会阻塞)

Queue.put(item[, block[, timeout]])  #向队列中放入元素, 如果可选参数block为True并且timeout参数为None(默认), 为阻塞型put(). 如果timeout是正数, 会阻塞timeout时间并引发Queue.Full异常. 如果block为False为非阻塞put
Queue.put_nowait(item)  #等价于put(itme, False)

Queue.get([block[, timeout]])  #移除列队元素并将元素返回, block = True为阻塞函数, block = False为非阻塞函数. 可能返回Queue.Empty异常
Queue.get_nowait()  #等价于get(False)

Queue.task_done()  #在完成一项工作之后,Queue.task_done()函数向任务已经完成的队列发送一个信号
Queue.join()  #实际上意味着等到队列为空,再执行别的操作
下面是官方文档给多出的多线程模型(官方文档果然是个好东西):
def worker():
    while True:
        item = q.get()
        do_work(item)
        q.task_done()

q = Queue()
for i in range(num_worker_threads):
     t = Thread(target=worker)
     t.daemon = True
     t.start()

for item in source():
    q.put(item)

q.join()       # block until all tasks are done

2. Queue模块与线程相结合


简单写了一个Queue和线程结合的小程序
#!/usr/bin/env python
# -*- coding:utf-8 -*-

import threading
import time
import Queue

SHARE_Q = Queue.Queue()  #构造一个不限制大小的的队列
_WORKER_THREAD_NUM = 3   #设置线程个数

class MyThread(threading.Thread) :

    def __init__(self, func) :
        super(MyThread, self).__init__()
        self.func = func

    def run(self) :
        self.func()

def worker() :
    global SHARE_Q
    while not SHARE_Q.empty():
        item = SHARE_Q.get() #获得任务
        print "Processing : ", item
        time.sleep(1)

def main() :
    global SHARE_Q
    threads = []
    for task in xrange(5) :  #向队列中放入任务
        SHARE_Q.put(task)
    for i in xrange(_WORKER_THREAD_NUM) :
        thread = MyThread(worker)
        thread.start()
        threads.append(thread)
    for thread in threads :
        thread.join()

if __name__ == '__main__':
    main()

3. 重构爬虫


主要针对之间写过的豆瓣爬虫进行重构:

3.1. 豆瓣电影爬虫重构

通过对Queue和线程模型进行改写, 可以写出下面的爬虫程序 :
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# 多线程爬取豆瓣Top250的电影名称

import urllib2, re, string
import threading, Queue, time
import sys

reload(sys)
sys.setdefaultencoding('utf8')
_DATA = []
FILE_LOCK = threading.Lock()
SHARE_Q = Queue.Queue()  #构造一个不限制大小的的队列
_WORKER_THREAD_NUM = 3  #设置线程的个数

class MyThread(threading.Thread) :

    def __init__(self, func) :
        super(MyThread, self).__init__()  #调用父类的构造函数
        self.func = func  #传入线程函数逻辑

    def run(self) :
        self.func()

def worker() :
    global SHARE_Q
    while not SHARE_Q.empty():
        url = SHARE_Q.get() #获得任务
        my_page = get_page(url)  #爬取整个网页的HTML代码
        find_title(my_page)  #获得当前页面的电影名
        time.sleep(1)
        SHARE_Q.task_done()
完整代码请查看Github豆瓣多线程爬虫
完成这个程序后, 又出现了新的问题:
无法保证数据的顺序性, 因为线程是并发的, 思考的方法是: 设置一个主线程进行管理, 然后他们的线程工作

4. 通用的多线程爬虫小模版


下面是根据上面的爬虫做了点小改动后形成的模板
#!/usr/bin/env python
# -*- coding:utf-8 -*-

import threading
import time
import Queue

SHARE_Q = Queue.Queue()  #构造一个不限制大小的的队列
_WORKER_THREAD_NUM = 3  #设置线程的个数

class MyThread(threading.Thread) :
    """
    
    doc of class
    
    Attributess:
        func: 线程函数逻辑
    """
    def __init__(self, func) :
        super(MyThread, self).__init__()  #调用父类的构造函数
        self.func = func  #传入线程函数逻辑

    def run(self) :
        """
        重写基类的run方法
        
        """
        self.func()

def do_something(item) :
    """
    运行逻辑, 比如抓站
    """
    print item

def worker() :
    """
    主要用来写工作逻辑, 只要队列不空持续处理
    队列为空时, 检查队列, 由于Queue中已经包含了wait,
    notify和锁, 所以不需要在取任务或者放任务的时候加锁解锁
    """
    global SHARE_Q
    while True : 
        if not SHARE_Q.empty():
            item = SHARE_Q.get() #获得任务
            do_something(item)
            time.sleep(1)
            SHARE_Q.task_done()


def main() :
    global SHARE_Q
    threads = []
    #向队列中放入任务, 真正使用时, 应该设置为可持续的放入任务
    for task in xrange(5) :   
        SHARE_Q.put(task)
    #开启_WORKER_THREAD_NUM个线程
    for i in xrange(_WORKER_THREAD_NUM) :
        thread = MyThread(worker)
        thread.start()  #线程开始处理任务
        threads.append(thread)
    for thread in threads :
        thread.join()
    #等待所有任务完成
    SHARE_Q.join()

if __name__ == '__main__':
    main()

5. 思考更高效的爬虫方法


  • 使用twisted进行异步IO抓取
  • 使用Scrapy框架(Scrapy 使用了 Twisted 异步网络库来处理网络通讯)

Saturday, April 9, 2016

java read json

{
   "pageInfo": {
         "pageName": "abc",
         "pagePic": "http://example.com/content.jpg"
    },
    "posts": [
         {
              "post_id": "123456789012_123456789012",
              "actor_id": "1234567890",
              "picOfPersonWhoPosted": "http://example.com/photo.jpg",
              "nameOfPersonWhoPosted": "Jane Doe",
              "message": "Sounds cool. Can't wait to see it!",
              "likesCount": "2",
              "comments": [],
              "timeOfPost": "1234567890"
         }
    ]
}

Java code :

JSONObject obj = new JSONObject(responsejsonobj);
String pageName = obj.getJSONObject("pageInfo").getString("pageName");

JSONArray arr = obj.getJSONArray("posts");
for (int i = 0; i < arr.length(); i++)
{
    String post_id = arr.getJSONObject(i).getString("post_id");
    ......etc
}

Friday, April 1, 2016

bottle image upload

from bottle import route, request, run

    import os

    @route('/fileselect')
    def fileselect():
        return '''
    <form action="/upload" method="post" enctype="multipart/form-data">
      Category:      <input type="text" name="category" />
      Select a file: <input type="file" name="upload" multiple />
      <input type="submit" value="Start upload" />
    </form>
        '''

    @route('/upload', method='POST')
    def do_upload():
        category   = request.forms.get('category')
        upload     = request.files.get('upload')
        print dir(upload)
        name, ext = os.path.splitext(upload.filename)
        if ext not in ('.png','.jpg','.jpeg'):
            return 'File extension not allowed.'

        #save_path = get_save_path_for_category(category)
        save_path = "/home/user/bottlefiles"
        upload.save(save_path) # appends upload.filename automatically
        return 'OK'

    run(host='localhost', port=80)

Monday, March 7, 2016

android 常见 ui设计

Android Toolbar Example  安卓toolbar置底
http://www.ahotbrew.com/android-toolbar-example/



andoird 侧边栏弹出:
http://blog.teamtreehouse.com/add-navigation-drawer-android
Navigation drawer button


简单的天气app,注意有很多bug
http://code.tutsplus.com/tutorials/create-a-weather-app-on-android--cms-21587
Final product image

Saturday, February 27, 2016

opencv 3.1 + vs2015 + win8.1 之helloworld

1. 下载opencv,解压缩到 c:\ ,所有文件都在opencv目录下
添加 “OPENCV_DIR”  = “C:\OpenCV\build\x64\vc14"
在path添加 %OPENCV_DIR%\bin

2. 新建project ,c++的win32 的console
3.新建一个cpp文件,代码如下
#include "opencv2/opencv.hpp"
#include "opencv2/highgui.hpp"
#include <iostream>
using namespace cv;
using namespace std;

int main(int argc, char** argv) {

Mat image = imread("fruits.jpg", CV_LOAD_IMAGE_UNCHANGED);  // Read the image
if (image.empty()) { // invalid input
cerr << "Couldn't open or find the image " << "fruits.jpg" << endl;
return(-1);
}

namedWindow("Display window", CV_WINDOW_AUTOSIZE); // create window
imshow("Display window", image); // Show image inside it.

waitKey(0); // Wait for a keystroke
destroyWindow("Display window");
}

4. 在porperty manager下面debug x64下面 add an existing property sheet
OpenCV_Debug.props内容如下(注意,需要在debug|x64下添加)
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ImportGroup Label="PropertySheets" />
  <PropertyGroup Label="UserMacros" />
  <PropertyGroup />
  <ItemDefinitionGroup>
    <ClCompile>
      <AdditionalIncludeDirectories>$(OPENCV_DIR)\..\..\include</AdditionalIncludeDirectories>
    </ClCompile>
    <Link>
      <AdditionalLibraryDirectories>$(OPENCV_DIR)\lib</AdditionalLibraryDirectories>
      <AdditionalDependencies>opencv_world310d.lib;%(AdditionalDependencies)</AdditionalDependencies>
    </Link>
  </ItemDefinitionGroup>
  <ItemGroup />
</Project>

5. copy 一个fruits.jpg到该项目的目录下
6.先build再run

(注意,win10下可以出现找不到dll的情况,可以把dll copy 到运行目录下)
(还有注意property--linkder--advanced下面需要设置成64位,因为目前opencv 31只有64位的)