GithubHelp home page GithubHelp logo

blog's People

Contributors

miracledan avatar

Stargazers

 avatar  avatar

Watchers

 avatar

blog's Issues

Flask制作微信Html5滑屏宣传页

相对于其它方式,微信H5页面的传播力可谓惊人。为了方便大家把项目分享到朋友圈,我们新增了一个自动生成微信HTML5页面并分享的功能。
Raindrop创建自己的项目,填写信息,就可以把项目分享到朋友圈了。

效果

image

实现

滑屏开源库

出于对android上的微信X5阉割内核的畏惧(坑太多),一开始就放弃了自己实现滑屏效果的想法。所以首要任务是找到一个相对稳定的开源库。

github上搜索一下fullpage,有很多。一开始没有考虑清楚,用了star最多的项目,做完找人体验后发现不行:
1.库文件较大,加载等待时间长。
2.框架的回调事件在微信X5里面太慢,等的心碎。

微信官方也开源了推广页的开发库,并且提供一些特殊接口,使用起来也简单。
不过把上下滑屏和左右滑屏结合起来使用比较麻烦。

考虑到浏览器的兼容问题和体验流畅度,最后使用了powy1993/fullpage,适合移动端,在微信上也比较流畅。
这个库的接口很简单,引入js后,简单初始化一下就可以用了。

flask模板文件

使用上面的fullpage库制作好H5页面后,需要整合到flask里面。
添加一条路由返回渲染的Jinja模板,这个就不多说了。

@app.route('/project')
def project():
    ........
    return render_template('project/index.html', project=project)

这里要考虑模板中引入的js和css文件的打包发布问题:
1.js、css文件需要合并压缩,缩短加载时间。
2.当js、css内容改变后,需要强制客户端刷新缓存。
为此使用了Flask-Assets,这个库帮助开发者把webassets工具整合到Flask项目中。使用起来也很简单:

1.安装

$ pip install Flask-Assets
为了压缩css和js文件,还要安装如下两个库

$ pip install cssmin
$ pip install jsmin

2.初始化(proxy方式)

from flask.ext.assets import Bundle, Environment
assets = Environment()

def create_app(config_name):
    ......
    assets.init_app(app)

    bundles = {
        'fullpage_css': Bundle(
            'css/fullpage.css',
            'bower_components/animate.css/animate.min.css',
            output='gen/fullpage.css',
            filters='cssmin'),

        'fullpage_js': Bundle(
            'js/fullpage.js',
            output='gen/fullpage.js',
            filters='jsmin'),
    }

    assets.register(bundles)

3.在模板文件中引用

{% assets "fullpage_css" %}
    <link rel="stylesheet" href="{{ ASSET_URL }}" />
{% endassets %} 

{% assets "fullpage_js" %}
    <script type="text/javascript" src="{{ ASSET_URL }}"></script>
{% endassets %} 

当访问页面时,flask-assets会检查文件变化,将css和js合并压缩后供前端使用。
image
可以看到,flask-assets给文件加上了版本号,当文件内容变化时,会自动更新版本号,强制客户端刷新缓存。

二维码生成

现在需要把制作好的H5页面分享到微信中,用手去敲url显然是不行的。为此我们需要一个二维码生成工具,方便用户分享。
因为网站是用angular写的,我们找了一个angular的qrcode库angular-qrcode
使用很简单,安装后,在页面中加上如下标签:
<qrcode size="100" data={{vm.shareUrl}} download></qrcode>
{{vm.shareUrl}}是需要分享页面的url

效果如下,现在只需要扫一扫就可以把自己的项目分享到朋友圈了。*年,秀起来!
image

总结

使用一下组件完成该功能:
1.powy1993/fullpage:滑屏组件
2.Flask-Assets:静态文件打包工具
3.angular-qrcode:二维码生成工具

欢迎大家使用这个功能分享自己的项目,如果有需求请提到我们的项目议题中

利用scrapy集成社区爬虫功能

社区活跃度或者贡献越来越受到重视,往往会作为获得工作或者承接项目的加分项。为了方便用户展示自己的社区资料,雨点儿网中增加了一个社区爬虫功能。
当前只爬取了用户主页上一些简单的信息,如果有需求请提到我们的项目议题中

效果如下
spider

功能实现

如图所示,在之前的架构上,我增加了橙色虚线框内的部分,包括:
scrapyd:一个用于运行爬虫任务的webservice
spiders:使用scrapy框架实现的爬虫
mongo:存放爬取的数据

image

使用scrapy框架

scrapy是一个python爬虫框架,想要快速实现爬虫推荐使用这个。
可以参考如下资料自行学习:
1.官方文档官方例子
2.一个简单明了的入门博客注意:博客中scrapy的安装步骤可以简化,直接使用pip install scrapy,安装过程中可能会缺少几个lib,ubuntu使用 apt-get install libffi-dev libxml2-dev libxslt1-dev -y

使用mongodb存储数据

mongo非常适合存储爬虫数据,支持异构数据。这意味着你可以随时改变爬虫策略抓取不同的数据,而不用担心会和以前的数据冲突(使用sql就需要蛋疼的修改表结构了)。
通过scrapy的pipline来集成mongo,非常方便。
安装mongo
apt-get install mongodb
pip install pymongo

使用xpath提取页面数据

在编写爬虫的过程中需要使用xpath表达式来提取页面数据,在chrome中可以使用XPath Helper来定位元素,非常方便。使用方法:
1.打开XPath Helper插件
2.鼠标点击一下页面,按住shift键,把鼠标移动到需要选取的元素上,插件会将该元素标记为黄色,并给出对应的xpath表达式,如下图:

qq 20151021232428

3.在爬虫程序中使用这个表达式selector.xpath(..../text()").extract()

使用scrapyd把爬虫集成到系统中

编写好爬虫后,我门可以通过执行scrapy crawl spidername命令来运行爬虫程序,但这还不够。
通常我们通过手动或者定时任务(cron)来执行爬虫,而这里我们需要通过web应用来触发爬虫。即,当用户更新绑定的社交账号时,去执行一次爬虫。来分析一下:
1.爬虫执行过程中会阻塞当前进程,为了不阻塞用户请求,必须通过异步的方式来运行爬虫。
2.可能有多个用户同时更新资料,这就要求能够同时执行多个爬虫,并且要保证系统不会超载。
3.可以扩展成分布式的爬虫。

方案1:使用celery运行爬虫

鉴于项目当前的架构,准备使用celery来执行异步爬虫。但是碰到了两个问题:
1.scrapy框架下,需要在scrapy目录下执行爬虫,否则无法获取到settings,这个用起来有点别扭,不过还能解决。
2.celery中反复运行scrapy的爬虫会报错:raise error.ReactorNotRestartable()。原因是scrapy用的twisted调度框架,不可以在进程中重启。
stackoverflow上有讨论过这个问题,尝试了一下,搞不定。放弃这个方案。
如果你有解决这个问题的方法,期待分享:)

方案2:使用scrapyd

scrapy文档中提到了可以使用scrapyd来部署scrapyd是一个用于运行scrapy爬虫的webservice,使用者能够通过http请求来运行爬虫。
你只需要使用scrapyd-client将爬虫发布到scrapyd中,然后通过如下命令就可以运行爬虫程序。

$ curl http://localhost:6800/schedule.json -d project=myproject -d spider=spider2
{"status": "ok", "jobid": "26d1b1a6d6f111e0be5c001e648c57f8"}

这意味什么:
1.爬虫应用和自己的web应用完全解耦,只有一个http接口。
2.由于使用http接口,爬虫可以放在任何能够被访问的主机上运行。一个简易的分布式爬虫,不是吗?
3.scrapyd使用sqlite队列来保存爬虫任务,实现异步执行。
4.scrapyd可以同时执行多个爬虫,最大进程数可配,防止系统过载。

欢迎使用我们的爬虫功能来收集社交资料。

使用方法

1 成为雨点儿网用户,进入用户主页,点击编辑按钮
image
2 填写社交账号,点击更新按钮
image
3 爬虫会在几秒内完成工作,刷新个人主页就能看到你的社区资料了,你也可以把个人主页链接附在电子简历中哟:)

project

同城配送

微信号 好立刻 10w 广东-中山

项目描述

基于微信开发一个web app,解决最后一公里配送问题。项目开发包括一个微信前端和一个后台管理网站。

关键技术

angularjs,车载gps,百度地图api,nodejs

生鲜电商

微信号 快送菜 6w 广东-深圳

项目描述

基于微信开发一个web app,用户可以通过微信访问生鲜商城。项目开发包括一个微信前端和一个后台管理网站。

关键技术

angularjs, flask, 微信支付,mysql

智能硬件app

蓝牙灯 2个端 6w 广东-广州

项目描述

智能硬件app,通过手机控制携带蓝牙模块的灯。

关键技术

蓝牙通讯, android, ios

短租平台

网站 3w 广东-深圳

项目描述

为创业者提供联合办公室租赁资源的网站,适配移动端显示。

关键技术

angularjs,leancloud, nodejs

日记类社交app

2个端 8w 广东-广州

项目描述

记录日记,并能够分享给好友的app。

关键技术

短视频,语音,leancloud,android, ios

动漫社交app

ios 3w 北美

项目描述

垂直于动漫爱好者的社交网络。

关键技术

ios,php,mysql

angular框架下的网站运营分析方法

当前雨点儿网社区的活跃度的评估来源:

  1. 注册用户数。
  2. aliyun OSS上对静态资源的请求数,PV/UV分析;aliyun ECS的流量统计。

缺陷:

  1. 统计粒度太粗,无法对网站的运营进行有效。
  2. 分析数据需要等到第二天才能查看,延迟较大。

因此需要考虑增加google analytics等工具来强化网站运营分析。google analytics能够提供的功能:

1、实时访问统计。
ga

2、用户网站行为分析,即每个页面/路由的访问率。
ga2

3、区域分析,终端分析等等。
ga3

利用以上数据,可以很好的指导网站的开发运营。比如:

  1. 新功能上线后,可以分析这个功能所在路由的访问率。由此来验证该功能是否被用户接受。
  2. 通过分析访问设备中移动设备所占比例来决定是否投入更多的经历到移动端开发中。
  3. 分析用户来源,验证推广渠道是否有效。
  4. 分析用户的年龄、性别,验证当前网站内容是否能够吸引目标用户。
    ....

实现方法

类似下面这段代码大家一定在很多网站都有看到,这个就是用来向google服务器提网站浏览数据的脚本。

<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');

ga('create', 'UA-XXXX-Y', 'auto');
ga('send', 'pageview');

</script>

那么如何为自己的网站添加google的分析服务呢?

添加google analytics服务

1.翻墙&google账号

这个必须有。
有一点需要说明:

我朝局域网不影响google的网站统计功能,即可以正常统计墙内用户的访问记录。
但是,访问google analytics的admin,必须翻墙。

2.使用google analytics

2.1.注册

注册网址
image

2.2.选择website,填写必要的信息

image

2.3.完成后你将获得一个TrackID,以及对应的google analytics脚本,拷贝该脚本到你的需要跟踪的页面中即可。

image

3.测试验证

访问你的网站,查看google实时统计结果
注意:如果不生效,请清空浏览器缓存后再次访问

angular框架如何统计

雨点儿网使用了angularjs框架,是一个单页应用,因此我们需要通过路由来区分不同的页面。这里我们使用angulartics-google-analytics组件来实现这个功能。

angulartics-google-analytics是一个基于angulartics的谷歌分析组件。
angulartics是专供angular应用使用的分析组件,支持多个第三方分析平台(包括百度,参考

使用angulartics-google-analytics

1.安装
bower install angulartics-google-analytics
2.加入到angular app中
angular.module('myApp', [
  'angulartics', 
  'angulartics.google.analytics'
]);
3.拷贝google analytics脚本到index.html中,并按照如下注释修改
<script>
      ...
      ga('create', 'UA-XXXXXXXX-X', 'auto'); // 如果在本地测试,把'auto'换成'none'
      //ga('send', 'pageview');  // 删除这一行
</script>
4.测试

访问你的网站,查看google实时统计结果
注意:如果不生效,请清空浏览器缓存后再次访问。

以上。
任何建议,欢迎提到我们的项目议题中

使用Git webhook来展示项目提交记录

images

当你默默撸代码的时候,是否想过让大家看到你卖力搬砖的过程
当你有一个团队的时候,是否想把团队的运作展示到项目主页上

Webhook

要实现这样的功能并不难,现在的主流Git平台都提供了webhook接口,可以把git仓库操作事件推送到指定的服务器上。

常用Git平台(无需翻墙)

平台 托管 git事件支持
github 公开项目免费,私有项目收费 所有
oschina 免费 PUSH
coding 基础版本免费 Push/MR/PR/Topic/Task/Document/Watch/Star
gitcafe 公开项目免费,私有项目收费 Push/Ticket/Pull Request/Ticket Comment/Pull Request Comment

raindrop网当前支持接收github 和 oschina的PUSH事件

如何配置webhook

Github配置

  1. 打开项目设置
    image
  2. 选择左侧 Webhooks & services
  3. Webhooks 一栏点击 Add webhook
  4. 填写关键信息
  5. 选择需要推送的事件类型(raindrop网当前支持手机PUSH事件)
  6. 保存设置

oschina配置

  1. 打开项目主页
  2. 打开项目管理页面
    image
  3. 在左侧菜单栏选择WebHook钩子
  4. 填写关键信息:
  5. 点击添加按钮。oschina可以把事件同时推送到多个服务器。

如果你将事件推送到了raindrop网,当你每次提交数据后,都可以在项目主页中看到你的提交次数统计。(可以装b咯)

image

项目介绍

简介

Raindrop是一个基于项目的互联网创业者社区,让志趣相投的创业者能够走到一起,共同推进和改善项目。

为什么取名raindrop?

raindrop,即雨滴,每一个雨滴都由无数的小水滴不断的碰撞聚合而成。
每个创业者都是一颗小水滴,需要互相碰撞,组成团队,完成项目。

为什么要做这个项目?

首先我们自己做为创业者,希望有一个能够交流讨论的圈子。每一个创业的码农都经历过初期的孤独,一个人设计,一个人编码,遇到问题的苦恼无从倾诉,完成项目的喜悦无人分享,渴望交流,却无从交流。

现在也有很多寻找合伙人、投资人的平台,主要通过聊天来组成团队。可是对码农来说,基于项目的讨论才是可靠有效的途径,经历学历都是可以吹的,可开发项目的过程是骗不了人的。

我们希望通过搭建一个raindrop社区,服务于每一个脚踏实地的开发者。我们不吹b,我们用认真态度、真实的数据来,为此提供以下功能:
1、收集并展示项目的git提交记录
2、项目议题和迭代管理
3、用户社交账号绑定和社交活动信息收集分析

团队

博客功能上线

blog864228174991855274

通过博客分享raindrop团队运作过程中的点点滴滴。

聊生活,谈技术,侃理想!

基于Flask-Angular的项目组网架构与部署

基于Raindrop网,分享项目的组网架构和部署。

项目组网架构

struct

架构说明

1.流

项目访问分为两个流,通过nginx分两个端口暴露给外部使用:
数据流:用户访问Raindrop网站。
控制流:管理人员通过supervisor监控、管理服务器进程。

图中除了将程序部署在ECS上外,还使用了OSS(对象存储服务,可以理解成一个nosql数据库),主要是为了存放一些静态文件,提高访问速度。阿里的OSS还可以与CDN一起使用,同样可以提高访问速度。

2.nginx

通过nginx对外暴露两个端口,如上所述,80端口供用户访问网站,另一个端口供管理人员使用。
80端口:根据请求的url配置了方向代理,分别导向client(angular)和server(flask).
其中server通过gunicorn部署在[socket]localhost:10000
配置如下:

server {
    listen 80 default_server;

    # set client body size to 4M (add by dh) #
    client_max_body_size 4M;

    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

    # product
    root /home/raindrop/www/client/dist;

    # Add index.php to the list if you are using PHP
    index index.html index.htm index.nginx-debian.html;

    server_name _;

    location / {
        # First attempt to serve request as file, then
        # as directory, then fall back to displaying a 404.
        try_files $uri $uri/ =404;
    }

    # access flask static folder
    location /static/ {
        # product
        root /home/raindrop/www/server/app;
    }

    location /api/ {
        proxy_pass http://localhost:10000/api/;
        proxy_redirect off;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;    
    }

    # Error pages
    error_page 413 @413_json;

    location @413_json {
        default_type application/json;      
        return 200 '{"msg": "Request Entity Too Large(max=4M)"}';
    }
}

3.gunicorn

gunicorn作为wsgi容器,用来执行flask server。
gunicorn可以使用异步socket:gevent,其本质是基于greenlet实现协程的第三方库,改善io阻塞问题,通过简单的配置就能使程序获得极高的并发处理能力。

注意:使用gunicorn != 使用gevent,如要开启gevent socket,启动gunicorn时需要增加--work-class参数,如下:
gunicorn --workers=4 --worker-class socketio.sgunicorn.GeventSocketIOWorker -b localhost:10000 wsgi:app

除了gunicorn,也可以选用uwsgi,但是有两点限制需要注意:
1.如果使用了Flask-socketio,请不要使用uwsgi,原因

Note regarding uWSGI: While this server has support for gevent and WebSocket, 
there is no way to use the custom event loop needed by gevent-socketio, 
so there is no directly available method for hosting Flask-SocketIO applications on it.
If you figure out how to do this please let me know!

2.如果使用异步WSGI Server,请勿使用uwsgi,原因:慎用异步 WSGI Server 运行 Flask 应用

 Flask(Werkzeug)的Local Thread无法与uwsgi基于uGreen的微线程兼容,引起Local Thread工作混乱。

4.celery

在程序运行过程中会有一些比较耗时但并非紧急的工作,这些任务可以使用异步任务来处理,提高server的响应速度。
celery - Distributed Task Queue是一个第三方库,提供异步队列和任务处理功能。
Raindrop使用celery配合实现feed功能。通过flask进行配置,使用redis作为异步队列来存储任务,并将处理结果(feed activity)存储在redis中。

5.supervisor

如上所述,我们需要在服务器上运行gunicorn和celery两个进程。
显然,我们需要一个monitor来帮我们管理这两个进程,避免进程崩溃不能及时拉起,阻塞业务。
supervisor:Supervisor process control system for UNIX是一个开源monitor程序,并且内置了web管理功能,可以远程监控、重启进程。配置如下:

[inet_http_server]
# web 管理端口,通过nginx暴露给远端用户
port=127.0.0.1:51000

[program:raindrop]
# 程序启动命令
command = gunicorn --workers=4 --worker-class socketio.sgunicorn.GeventSocketIOWorker -b localhost:10000 wsgi:app
directory = /home/raindrop/www/server
user = dh                                     
stopwaitsecs=60
stdout_logfile = /var/log/raindrop/supervisor-raindrop.log
redirect_stderr = true

[program:celery]
command = celery -P gevent -A wsgi.celery worker
directory = /home/raindrop/www/server
user = dh                                             
stopwaitsecs=60
stdout_logfile = /var/log/raindrop/supervisor-celery.log
redirect_stderr = true

6.数据库

mysql:网站主要数据存储在关系型数据库中
redis:缓存mysql数据 + celery异步队列

服务器

使用阿里云的服务器
ECS:部署服务端程序
OSS:存储前端静态文件(速度很快,对前端体验改善很大,采用angular框架强烈推荐使用)

组件

nginx: HTTP Server
angularjs: client框架
flask:server框架
gunicorn: web 容器
celery: 异步任务处理
supervisor: monitor
redis: 数据缓存 + 任务队列
mysql: 数据库

本地开发环境与工具

ubuntu12.04 64: 操作系统
virtualenv: python虚拟环境(隔离项目开发环境,不用担心包冲突了)
vagrant: 基于virtualbox的本地虚拟环境(环境可以导入导出,团队开发必备)
gulp: angular工程打包工具
pip: python包管理工具
fabric: 基于python的远程脚本(自动化部署神器)

打包部署

基于Ubuntu 12.0.4,其它系统安装命令(apt-get 等)请自行修改。

繁琐VS自动化

deploy

  1. 如果是第一次部署,需要初始化ECS服务器,安装基本工具:
    nginx, supervisor, redis, mysql, pip, virtualenv
  2. 打包项目代码
  3. 发布静态文件到OSS服务器
  4. 发布项目代码到ECS服务器,安装server依赖的包
  5. 修改mysql, nginx, supervisor配置文件
  6. 拉起所需进程

然后各种apt-get install, scp, tar, cp, mv,不得不说,这是一个烦人且毫无技术含量的工作,干过几次后基本就可以摔键盘了。
不过,有繁琐的地方,一定有自动化。其实完成上面这些工作,三条命令足以:

fab init_env
fab build
fab deploy

这要感谢Fabric:Simple, Pythonic remote execution and deployment项目,封装了非常简洁的远程操作命令。

Fabric

使用Fabric,只需要编写一个fabile.py脚本,在启动定义init_env, build, deploy三个任务:

# -*- coding: utf-8 -*-

import os, re, hashlib
from termcolor import colored
from datetime import datetime
from fabric.api import *
from fabric.contrib.files import exists

class FabricException(Exception):
    pass

env.abort_exception = FabricException

# 服务器地址,可以有多个,依次部署:
env.hosts = [
    '[email protected]'
]

env.passwords = {
    '[email protected]:22':'123456'
}

# sudo用户为root:
env.sudo_user = 'root'

# mysql
db_user = 'root'
db_password = '123456'

_TAR_FILE = 'raindrop.tar.gz'
_REMOTE_TMP_DIR = '/tmp'
_REMOTE_BASE_DIR = '/home/raindrop'
_ALIYUN_OSS = {
    'endpoint'       : 'oss-cn-qingdao.aliyuncs.com',
    'bucket'         : 'yourbucketname',
    'accessKeyId'    : 'youraccessKeyId' ,
    'accessKeySecret': 'youraccessKeySecret'
}

def build():
    '''
    必须先打包编译client,再打包整个项目
    '''
    with lcd(os.path.join(os.path.abspath('.'), 'client')):
        local('gulp build')

    # 上传静态文件到oss服务器,并修改index.html中对静态文件的引用
    with lcd(os.path.join(os.path.abspath('.'), 'client/dist')):
        with lcd('scripts'):
            for file in _list_dir('./'):
                if oss_put_object_from_file(file, local('pwd', capture=True) + '/' + file):
                    _cdnify('../index.html', file)

        with lcd('styles'):
            for file in _list_dir('./'):
                if oss_put_object_from_file(file, local('pwd', capture=True) + '/' + file):
                    _cdnify('../index.html', file)

        # 注意在oss上配置跨域规则,否则fonts文件无法加载
        # !!修改fonts文件夹请放开此段程序!!
        # with lcd('fonts'):
        #     for file in _list_dir('./'):
        #         oss_put_object_from_file('fonts/%s' % file, local('pwd', capture=True) + '/' + file)                

    with lcd(os.path.join(os.path.abspath('.'), 'server')):
        local('pip freeze > requirements/common.txt')
        excludes = ['oss', 'distribute']
        [local('sed -i -r -e \'/^.*' + exclude + '.*$/d\' "requirements/common.txt"') for exclude in excludes]

    local('python setup.py sdist')

# e.g command: fab deploy:'',Fasle
# 注意命令两个参数间不要加空格
def deploy(archive='', needPut='True'):
    if archive is '':
        filename = '%s.tar.gz' % local('python setup.py --fullname', capture=True).strip()
        archive = 'dist/%s' % filename
    else:
        filename = archive.split('/')[-1]

    tmp_tar = '%s/%s' % (_REMOTE_TMP_DIR, filename)

    if eval(needPut):
        # 删除已有的tar文件:
        run('rm -f %s' % tmp_tar)

        # 上传新的tar文件:
        put(archive, _REMOTE_TMP_DIR)

    # 创建新目录:
    newdir = 'raindrop-%s' % datetime.now().strftime('%y-%m-%d_%H.%M.%S')
    with cd(_REMOTE_BASE_DIR):
        sudo('mkdir %s' % newdir)

    # 重置项目软链接:
    with cd(_REMOTE_BASE_DIR):
        # 解压到新目录:
        with cd(newdir):
            sudo('tar -xzvf %s --strip-components=1' % tmp_tar)    

        # 保存上传文件
        if exists('www/server/app/static/upload/images/', use_sudo=True):
            sudo('cp www/server/app/static/upload/images/ %s/server/app/static/upload/ -r' % newdir)

        sudo('rm -f www')
        sudo('ln -s %s www' % newdir)

    with cd(_REMOTE_BASE_DIR):
        with prefix('source %s/env/local/bin/activate' % _REMOTE_BASE_DIR):
            sudo('pip install -r www/server/requirements/common.txt')

            # 启动服务
            with cd('www'):
                # mysql
                sudo('cp etc/my.cnf /etc/mysql/')
                sudo('restart mysql')

                # monitor
                sudo('cp etc/rd_super.conf /etc/supervisor/conf.d/')
                sudo('supervisorctl stop celery')
                sudo('supervisorctl stop raindrop')
                sudo('supervisorctl reload')
                sudo('supervisorctl start celery')
                sudo('supervisorctl start raindrop')

                # nginx
                sudo('cp etc/rd_nginx.conf /etc/nginx/sites-available/')
                # ln -f —-如果要建立的链接名已经存在,则删除之
                sudo('ln -sf /etc/nginx/sites-available/rd_nginx.conf /etc/nginx/sites-enabled/default')
                sudo('nginx -s reload')

def init_env():
    sudo('aptitude update')
    sudo('aptitude safe-upgrade')

    # sudo('apt-get install nginx')
    sudo('aptitude install python-software-properties')
    sudo('add-apt-repository ppa:nginx/stable')
    sudo('aptitude update')
    sudo('apt-get install nginx')

    sudo('apt-get install supervisor')
    sudo('apt-get install redis-server')
    sudo('apt-get install mysql-server')

    sudo('apt-get install python-pip python-dev build-essential')

    sudo('pip install virtualenv')

    run('mkdir /var/log/raindrop -p')

    with cd ('/home/raindrop'):
        sudo('virtualenv env')

def oss_put_object_from_file(key, file_path):
    from oss.oss_api import *
    oss = OssAPI(_ALIYUN_OSS['endpoint'], _ALIYUN_OSS['accessKeyId'], _ALIYUN_OSS['accessKeySecret'])
    res = oss.put_object_from_file(_ALIYUN_OSS['bucket'], key, file_path)
    return res.status == 200 and True or False


def _expand_path(path):
    print path
    return '"$(echo %s)"' % path

def sed(filename, before, after, limit='', backup='.bak', flags=''):
    # Characters to be escaped in both
    for char in "/'":
        before = before.replace(char, r'\%s' % char)
        after = after.replace(char, r'\%s' % char)
    # Characters to be escaped in replacement only (they're useful in regexen
    # in the 'before' part)
    for char in "()":
        after = after.replace(char, r'\%s' % char)
    if limit:
        limit = r'/%s/ ' % limit
    context = {
        'script': r"'%ss/%s/%s/%sg'" % (limit, before, after, flags),
        'filename': _expand_path(filename),
        'backup': backup
    }

    # Test the OS because of differences between sed versions

    with hide('running', 'stdout'):
        platform = local("uname")
    if platform in ('NetBSD', 'OpenBSD', 'QNX'):
        # Attempt to protect against failures/collisions
        hasher = hashlib.sha1()
        hasher.update(env.host_string)
        hasher.update(filename)
        context['tmp'] = "/tmp/%s" % hasher.hexdigest()
        # Use temp file to work around lack of -i
        expr = r"""cp -p %(filename)s %(tmp)s \
&& sed -r -e %(script)s %(filename)s > %(tmp)s \
&& cp -p %(filename)s %(filename)s%(backup)s \
&& mv %(tmp)s %(filename)s"""
    else:
        context['extended_regex'] = '-E' if platform == 'Darwin' else '-r'
        expr = r"sed -i%(backup)s %(extended_regex)s -e %(script)s %(filename)s"
    command = expr % context
    return local(command)

def _cdnify(index_file, cdn_file):
    sed(index_file, '([^<]*(src|href)=")[^<]*' + cdn_file + '"', '\\1http://' + _ALIYUN_OSS['bucket'] + '.' + _ALIYUN_OSS['endpoint'] + '/' + cdn_file + '"')

def _list_dir(dir=None, access=lcd, excute=local):
    """docstring for list_dir"""
    if dir is None:
        return []

    with access(dir):
        string = excute("for i in *; do echo $i; done", capture=True)
        files = string.replace("\r","").split("\n")
        return files        

通过fabric自动化部署有两点需要注意:
1.安装mysql时,设置的密码不能生效,需要登到服务器上手动设置一下:

mysql -u root
use mysql;
update user set password=PASSWORD('123456') where User='root';
flush privileges;
quit;

2.服务器上的用户需要自己手动创建。

gulp

在build任务中,使用gulp build打包client的angular代码。
这里先不多说,有需要请参考github项目generator-gulp-angular

setup

setup是python自己的打包工具。
build任务中,最后使用_python setup.py sdist_命令把整个工程打包成一个tar.gz文件,上传到服务器。
使用这个工具需要编写如下两个文件:
setup.py:打包脚本

#!/usr/bin/env python

from setuptools import setup, find_packages
import server

try:
    long_description = open('README.md').read()
except:
    long_description = server.__description__

REQUIREMENTS = []
exclude_lib = ['oss', 'distribute']
for lib in open("server/requirements/common.txt").readlines():
    for exclude in exclude_lib:
        if lib.lower() not in exclude:
            REQUIREMENTS.append(lib)

setup(
    name='raindrop',
    url='https://www.yudianer.com',
    version=server.__version__,
    author=server.__author__,
    author_email=server.__email__,
    description=server.__description__,
    long_description=long_description,
    license=server.__license__,
    packages=find_packages(),
    zip_safe=False,
    platforms='any',    
    install_requires=REQUIREMENTS
)

MANIFEST.in:指定打包哪些文件夹

recursive-include etc *
recursive-include client/dist *
recursive-include server/app *
recursive-include server/requirements *
recursive-exclude server *.pyc
prune server/app/static/upload/images
prune server/env 
prune server/tests

本文由raindrop网码农撰写。欢迎转载,但请注明出处。

text

发布项目

提交您的项目,我们会根据您的项目情况进行人工审核。

项目沟通

审核通过的项目,我们会安排专业人员和您进行一对一沟通。

需求分析

如您没有产品经理,我们可以提供专业的需求分析服务,解决您做需求分析的难题。

产品设计

平台有大量优秀的设计师合作,为你的项目进行产品原型和界面设计。

软件开发

在平台上聚集了各个技术方向的工程师,采用优选加备选的方式,您的项目不会因工程师个人情况变动而耽搁,项目交付有保障。

测试验收

作为第三方托管平台,我们会协助您进行项目的验收,使项目能达到预期。

后期维护

项目交付完成后,我们为您提供可靠的维护,维护阶段项目出现故障,可随时联系工程师修复。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.