一次 Git Flow 的讨论
新项目启动,我建议使用 Fork + Pull Request 模式开发,保持提交的干净整洁,其他的同学则建议用一个 dev 分支中央式开发,方便多人协作和同步代码, 最终使用了一个 dev 分支的模式,后端觉得项目初始阶段相互依赖会比较多,方便协作,前端觉得这样方便同时与多人对接后端的接口。
对于 Git Flow 的思考
讨论结束后,我开始重新思考 Git Flow 的模式和使用,得到的答案是使用最基本的 Git 协作流程就可以适应各种 Git Flow。
Git 是为了管理 Linux 内核源代码而开发的版本控制系统,它与生俱来的特点就是 分布式 和 简单的分支管理
我的需求
- 协作方便。
- 提交历史要干净整洁。
- 方便 code review。
djkit 开发记录
原因
在公司的研发团队中,我除了要写代码,还要做一些项目架构和基础设施搭建的工作,每次搭建一个新项目都会有不少重复的工作,一两次还好,多了我真的有点受不了,于是思索着写这么一个东西来减少工作的痛苦。
技术架构
工作中主要使用 Python,API 主要使用 Django、Django RESTframework 来做,一些 Python 依赖和目录结构都是统一的,我就先减少这部分的重复工作吧。
requirements
Django==1.9.10 django-cors-headers==1.1.0 django-nose==1.4.4 django-rest-swagger==0.3.7 djangorestframework==3.4.6 djangorestframework-jwt==1.8.0 mysqlclient==1.3.7 raven==5.26.0
Django 使用 1.9.10 的原因是这个版本和 django-rest-swagger 的 0.3.7 版本配合比较好,比较适合我们团队, 可以很方便的在浏览器下调试,效果如下:
迁移 Octopress 到 Hexo
不折腾会死
前几天更新了 Python Mixin 学习笔记 这篇文章,文章中有一张图要显示,于是想添加 fancybox 插件来预览图片, 原本以为是非常简单的一件事,结果上 google 果然没有找到,同时发现博客的分页有 bug,再看看 Octopress 的 最后更新时间,想到 Octopress 这些茫茫多的问题于是便有了迁移 Octopress 到 Hexo 的念头。
迁移前的调研
google 找了一些迁移的文章,发现迁移没有想象中难,于是开始准备。
我的需求:
- 可以继续使用 Emacs 上的 org-octopress 插件写博客。
- 可以兼容我之前写的文章,代码高亮必须漂亮。
- 一定要有 fancybox。
- 没有分页等乱七八糟的明显 bug。
迁移后的优点
- generate 速度大幅提高。
- Hexo 更新快,插件多,没有很明显的 bug。
- 完美兼容我的写作习惯。
迁移过程
- 安装配置 Hexo。
- 迁移
source/_posts
里的博客源文件。 - 重新配置 org-octopress。
- 选择一个兼容 Octopress 文章的主题。
- 调整 Hexo 和主题的配置。
- 博客代码管理。
搭建私有 Docker Registry
为了团队的其他同学可以方便的更新开发环境到本地,我们决定搭建私有的 Docker Registry。
不使用公共服务的原因:
- Docker Hub 在国外,同步超级慢。
- DaoCloud 的私有 image 需要收费,对于我们小团队来说不太友好。
搭建步骤
- 环境准备。
- 配置 LetsEncrypt 证书。
- 安装 Docker Registry。
- 配置 Nginx。
- 后续工作。
Django项目如何使用Docker搭建环境
背景
公司的项目前后端是分离的,前端使用React作为技术栈,后端使用Django做完web开发框架, 前端的同学调试代码的时候需要启动后端的项目,后端的项目往往需要一堆环境依赖,例如数据库, Redis缓存,Python的库,每次搭建环境更新环境对于前端的同学来说都是一场噩梦,比如安装数据库, 编译Python的库等等都会遇到很多问题,为了提高效率,更好的统一开发环境,所以使用了Docker来做 这件事。
达到的效果
Ansible 使用入门
背景
由于经常有部署新服务器的需求,服务器部署过程中的配置和基础环境的安装很多都是重复性的 工作,所以使用配置管理工具将这些重复性的工作控制起来最合适不过了,之前有写过一篇用 SaltStack 做自动化运维的文章 saltstack 使用笔记,这次换用 Ansible ,顺便比较一下两种工具。
选用 Ansible 的原因
- 轻量级
- 通过 SSH 下发命令
uwsgi部署Django服务的坑
uwsgi部署Django服务
部署Django的方式
- gunicorn + Nginx
- uwsgi + Nginx
gunicorn + Nginx
http://gunicorn.org 最大的好处就是部署简单,如果没有特殊要求,推荐使用这种方式部署Python Web程序
uwsgi + Nginx
uwsgi 相对 gunicorn来说配置选项相对复杂,但是uwsgi是支持websocket的,出于这个原因选择uwsgi来部署django程序
- 问题1:uwsgi的默认监听进程的数量100
解决: 之前由于只知道启动uwsgi worker的数量,不管怎么调worker的数量,并发都很低,
配置listen参数解决,根据服务器配置和访问来决定,目前我开的是20000 - 问题2:supervisor没有办法优雅的重启uwsgi worker,导致服务器资源被重启的uwsgi进程占满
解决:配置no-orphans参数
- 配置范例
master = true processes = 8 thread = 100 thread-stacksize = 512 stats = 127.0.0.1:9194 buffer-size = 65535 enable-threads = true http-timeout = 60 socket-timeout = 60 chmod-socket = 666 harakiri = 60 max-requests = 20000 listen = 20000 no-orphans
Angular.js入门参考
Angular.js 入门参考
来源
很早听说这个前端MV*框架,最近公司的一个新项目未来可能需要支持手机App,所以决定
使用API驱动的设计,也就是网站后端只提供API,逻辑处理由前端实现,也就是Angular这些
前端框架要做的事情了,以后如果上手机App的话也可以使用同一套API,不用单独为手机App
开发新的API。
前端MV*是什么?
MV* 可以是MVC(Model, View, Controller)模式,也可以是MVVM(Model, View, ViewModel)
你可以把Angular用成你想要的模式,而MVVM正是我项目里面想要的东西,Model和UI的双向绑定
也就是Model变化UI相应的改变,UI改变Model也跟着改变。
Angular的使用场景和优缺点
使用场景
Single Page Application(单页面应用),即MVVM模式发挥优势的地方。
优点
- 使Javascript开发变得模块化。
- 保证开发人员的代码风格统一。
- 资源相对较多,上手相对简单。
缺点
- 调试很困难
Angular 基本概念
- Directive(指令)
- Scope(变量空间)
- Service(服务)
- Controller(控制器)
- Module(模块)
Directive
Angular指令,如ng-app, ng-controller, ng-model, ng-repeat等等。
Scope
Controller可以访问的变量空间。
Service
服务,如Http,window,element等服务,可以自定义公用的服务,达到模块化管理的目的。
Controller
View的控制模块。
Module
模块,要使用Angular必须先初始化模块
实例
index.html
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"/> <title>Document</title> </head> <!-- 使用directive初始化app --> <body ng-app="app"> <!-- 添加controller --> <div ng-controller="loginCtrl"> <!-- 使用ng-model绑定username和input, 两者只要有一个改变都会进行相应的改变 --> <input ng-model="username" name="username" type="text"/> <input ng-model="password" name="username" type="password"/> <!-- controller里面添加相应的函数 --> <a href ng-click="login()">登录</a> </div> </body> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.7/angular.min.js"></script> </html>
app.js
(function() { // 初始化App var app = angular.module('app', []); // 添加Controller app.controller( 'loginCtrl', // 依赖注入使用服务 $scope 和 $http ['$scope', '$http', function($scope, $http) { // 添加登录处理函数 $scope.login = function() { $http.post('/login/', {username: $scope.username, password: $scope.password}).success(function(data) { alert(data.msg); }); } }] ); })();
总结
本篇提供angular的入门,后续会有一些使用心得,总体来说angular上手还是很简单的。
Python使用支付宝接口
Python使用支付宝接口
来源
www.pinbot.me需要支持线上支付,先从支持支付宝开始。
使用支付宝什么接口
支付宝接口有很多,而线上支付使用的是即时到账接口,这里有官方介绍
如何使用该接口
实现代码参考了python-alipay
获取PID和32位KEY
只能是企业才能申请该接口的使用,实名认证通过后,你只要拿到PID和KEY
就行了。
使用该接口
- 使用该接口需要实现两个工具函数:
- 构造支付宝的支付链接,如果链接请求成功会生存支付宝的支付页面。
- 支付成功后需要验证支付宝返回的结果是否正确,如果正确就做相应的订单更新。
- 实现构造支付链接
注意事项:
- 传给支付宝的url参数需要字母顺序排序,且不能含有空格,空值。
- 验证,使用MD5验证,构造除了sign和sign_type的url参数和key做md5。
- 实现验证支付宝返回数据的验证
验证支付宝请求return_url和notify_url的数据
注意事项- 支付宝的notify_id在一分钟后会实效。
- 支付宝异步请求如果没有收到success的话会间隔发请求,直到获取到success,或者超过24小时。
代码: 一个配置类AlipayConfig
一个工具类AlipayUtils
AlipayConfig代码:
# coding: utf-8 class AlipayConfig(object): ALIPAY_PID = 'your apply pid' ALIPAY_KEY = 'your apply key' ALIPAY_SELLER_EMAIL = 'your apply email' ALIPAY_INPUT_CHARSET = 'utf-8' ALIPAY_SIGN_TYPE = 'MD5' ALIPAY_GATEWAY = 'https://mapi.alipay.com/gateway.do?' ALIPAY_NOTIFY_GATEWAY = 'https://mapi.alipay.com/gateway.do?service=notify_verify&' ALIPAY_RETURN_URL = 'your return url'
AlipayUtils代码:
# coding: utf-8 import urllib import requests from hashlib import md5 from collections import OrderedDict from alipay_config import AlipayConfig from Pinbot.settings import DEBUG class AlipayUtils(object): @classmethod def _convert_str_encode(cls, pay_option, encoding='utf-8'): ''' 将unicode编码转换成utf-8编码 ''' for key, value in pay_option.iteritems(): if not value: continue if isinstance(value, unicode): pay_option[key] = value.encode(encoding) return pay_option @classmethod def _get_url_params(cls, pay_option): ''' 使用OrderedDict将url参数按字母顺序排序 去除空的字段和sign, sign_type字段 ''' url_params = OrderedDict( sorted( [ item for item in pay_option.iteritems() if item[1] and item[0] not in ('sign', 'sign_type') ], key=lambda x: x[0] ) ) return url_params @classmethod def _get_sign(cls, url_params): ''' md5加密url参数和key ''' key = AlipayConfig.ALIPAY_KEY prestr = '&'.join('%s=%s' % item for item in url_params.iteritems()) sign = md5(prestr + key).hexdigest() return sign @classmethod def submit_order_url(cls, order): ''' 构造支付宝即时到账链接 ''' pay_option = dict([ ('service', 'create_direct_pay_by_user'), ('payment_type', '1'), ('_input_charset', AlipayConfig.ALIPAY_INPUT_CHARSET), ('partner', AlipayConfig.ALIPAY_PID), ('seller_email', AlipayConfig.ALIPAY_SELLER_EMAIL), ('return_url', AlipayConfig.ALIPAY_RETURN_URL), ('notify_url', ''), ('show_url', ''), ('out_trade_no', order.order_id), ('subject', order.subject_name()), ('body', order.order_detail()), ('total_fee', order.total_price if not DEBUG else 0.1), ]) # 将支付参数的编码统一成utf-8 pay_option = cls._convert_str_encode( pay_option, encoding=AlipayConfig.ALIPAY_INPUT_CHARSET, ) # 排序去除空值和sign,sign_type选项 url_params = cls._get_url_params(pay_option) # 加密和加密类型 url_params['sign'] = cls._get_sign(url_params) url_params['sign_type'] = AlipayConfig.ALIPAY_SIGN_TYPE # 生成支付url submit_url = AlipayConfig.ALIPAY_GATEWAY + urllib.urlencode(url_params) return submit_url @classmethod def verify_alipay_notify(cls, url_data): ''' 验证支付宝支付成功的返回信息 两个步骤: 1. 验证签名 2. 查询此notify是否在支付宝中有效 ''' # 验证签名 alipay_sign = url_data.get('sign') alipay_url_params = cls._get_url_params(url_data) sign = cls._get_sign(alipay_url_params) if sign != alipay_sign: return False # 查询信息是否在支付宝中有效 check_params = { 'partner': AlipayConfig.ALIPAY_PID, 'notify_id': url_data.get('notify_id') } result = requests.get( AlipayConfig.ALIPAY_NOTIFY_GATEWAY, params=check_params ) if result.text.lower().strip() == 'true': return True return False
一些运行结果:
# 生成支付的url https://mapi.alipay.com/gateway.do?_input_charset=utf-8&body=hehehehhe&out_trade_no=20140928110814-8cd3 &partner=xxxxx&payment_type=1&paymenthod=directPay&seller_email=xxx%40xxx.com& service=create_direct_pay_by_user&subject=hehehe&total_fee=2599&sign=603b228b7e6663d217dc44bdc12a5106& sign_type=MD5 # 支付宝返回数据的url 127.0.0.1:8000/payment/alipay_return/?body=套餐B&buyer_email=xxxx%40163.com&buyer_id=xxxxxxx& exterface=create_direct_pay_by_user&is_success=T& notify_id=RqPnCoPT3K9%252Fvwbh3InQ9JC%252Ft24wOkxbC3d3NsqzK9v6KWsnZ2vWqks9o41CFgeCkbai& notify_time=2014-09-30+14%3A21%3A22¬ify_type=trade_status_sync&out_trade_no=20140928110814-8cd4& payment_type=1&seller_email=xxx%40xxxx.com&seller_id=xxxx&subject=套餐B&total_fee=0.10& trade_no=2014093000715684&trade_status=TRADE_SUCCESS&sign=de81410e5fba18e8d97823a31c0724ac&sign_type=MD5
总结:
支付宝的文档确实有点看不懂,上面放了两个运行结果,希望大家做起来有点方向,知道构造什么,验证什么,
感谢github上这个repo, 给了我很大的帮助。
Python Mixin 学习笔记
2016-10-25 17:04 的邮件
正思考着如何给 djkit(Django starter)项目添加 admin 和 models 的支持, 此时收到一封邮件,来自 uhayate,内容如下:
看了他的 github 和博客,我确定我不认识这个同学,这下有意思了,我的博客居 然被人搜索到而且还发现了一个错误,赶紧回了邮件然后审查文章压压惊,之后便有了 这次更新,不得不说,两年前写的东西还是欠火候,至于评论功能,使用 Issue。
主要更新
- 新式类 MRO 的机制理解。
- 文章排版按照 中文文案排版指北 重排。
- Mixin 使用场景。
更新总结
- 多继承真的很复杂。
- 博客和代码一样,每当你过一段时间回头来看会问自己当时为什么要这样写,代码要经常重构,文章也需要经常更新,当别人看到你写的东西的时候就要拿出最好的给读者。
来由
对 Python 的面向对象编程研究的比较少,Django 是从 1.3 推荐使用 class base view, 免不了会用到 Python 的面向对象的特性,所以把研究的东西记录一下。
知识点
- Python 面向对象的基本使用和多继承 MRO(method resolution order)的机制。
- Mixin 和多继承有什么区别及应用场景。
- Python 中的静态方法、类方法、实例方法。