最后更新于 .

发现自己经常会一篇文章写了(一)之后,很久都不写(二),搞得最后自己都快要忘记了,所以这次赶紧把统一支付的文章给补上。

上次的文章中将统一支付的v1版本已经讲解ok了,但是还剩下两个问题:

  • 服务器端没有办法做分布式
  • 客户端对支付sdk进行插件式管理十分困难

 

我们一个个来说

一. 解决服务器端分布式的问题

解决这个问题的核心思路比较简单:

之前我们是把event的通知放在进程内存中,现在我们做成网络通信

由于支付的请求量本身不属于高并发,所以就放弃了打算直接写通知server的想法,转而看一下有没有什么简单的解决方案。

而由于自己之前redis的使用经历,恰好知道redis有一个pubsub模式,很适合做这种监听和通知的工作。

python的实现示例代码如下:

import time
import config
from share.vals import rds
from share.utils import safe_str
from gevent.timeout import Timeout
from urllib import quote, unquote

class RedisPubSub(object):
    """
    用redis订阅/发布消息
    """
    # 订阅频道
    sub_key ...

最后更新于 .

其实想跟大家分享这套支付系统的架构已经很久了,今天总算有时间写出来了。

先说说这套系统的需求由来吧:

  1. 笔者公司的游戏产品已经有几款了,每次上各种渠道都是要搭配不同的计费方式,并且每开发游戏都要重复一遍痛苦的接入sdk流程
  2. 游戏的支付需要出各种报表以及统计,每个游戏单独去做对人力的消耗巨大

基于以上几点,我这边设计了统一支付系统。

这个系列一共会分两篇文章,分别对应系统的v1版和v2版,我们这一篇先从v1起介绍。

在仔细分析了国内的大多数支付sdk之后,我们梳理出游戏的支付流程大体可以实现为两类:

  • 第三方sdk服务器进行支付结果通知
  • 第三方sdk客户端直接返回支付结果通知,没有服务器支付结果通知。

对于调用方而言,这两种方式各有好处。

  • 第一种方式更加安全,但是支付调用的时间相对较长
  • 第二种方式速度更快,但是很容易被不怀好意的人破解。参见之前的文章:google支付接口被刷以及解决方案

接下来,我们来看一下我这边设计的统一支付流程。

客户端:

1

服务器端:

2

简单解释一下:

  • 每次支付开始,都要让服务器生成一个订单作为此次支付的记录,订单的id即为 bill_id。订单有4中状态:订单生成,支付失败,支付成功,发货成功。
  • pay_server即为统一支付系统的服务器端,考虑到调用量和方便调试,使用了简单的http协议+json+sign的方式

对于服务器内部,唯一麻烦的一点是,《等待pay_server支付结果通知》这个接口。因为这个http请求需要支持挂起,在第三方支付服务器通知了pay_server之后,pay_server 根据通知里面透传的bill_id 将订单状态修改后,再给客户端结果 ...

最后更新于 .

接着上一篇继续,前面两篇算是把从创业开始发展到今天的整个过程大体说了一遍,但是接下来的文字才是真正我想要和大家分享和探讨的。

这个话题就是:作为一个技术合伙人,职责到底应该是什么?

其实随着公司的发展,我就一直在思考这个问题:一方面是来自于担心公司技术进度失控的不安全感,一方面是来自于自己不再有太多时间编码,而带来的对自身技术能力落伍的担心。

还是按照创业的阶段来说吧。

一、创业第二阶段-队长

这个时候,公司可能就3、4个人,一个产品、一个前端、一个后端。什么技术合伙人、技术总监之类的,没必要在意这个名字,这个时候,唯一的要求就是做出产品。

而这个能力,又分两块:

1. 自己编码的能力

2. 设计架构,主导技术开发的能力

这个时候,更像一个队长。你要带领几个队员,但你自己也要冲锋。

你要能够将产品解构为代码实现,你要熟悉整个产品的研发,无论前端还是后端。

这个阶段,是非常关键的为团队打基础的时间。

在产品的实现过程中,要逐渐规划出自己的技术框架,将底层、公共逻辑拆分出来,为下一个阶段做准备。

而队长,就是要将这些东西,在这个阶段尽快沉淀、积累下来。

举个例子,服务器端语言用什么?python。web框架用什么?django ...

最后更新于 .

接着上一篇文章说吧。
在这之后,差不多快到年底吧,经过朋友的介绍认识了现在的合伙人,做产品策划和运营。
之前也只是打电话聊了一下,不能说一见如故吧,但是聊下来之后也是仔细考虑了好几天,才决定是不是要进行这样的合作。
后来当面聊了一次,决定先以比较浅的合作方式试一下,就开发现有产品的某个模块,互相也都彼此磨合一下,如果感觉不错的话就再往下走。
这样差不多搞到年后,终于决定了以技术合伙人的身份进入团队,基本形成了目前创始人团队铁三角的关系。
之后开始招人,当时还在小黑屋内,而且还在烧自己的钱,所以招人的成本和质量控制的都很严格。
不过现在想起来,当时的严格对现在的我们还是有很大的好处,当时招的同事承担了当时基本所有客户端的开发,他后来又介绍了一位朋友,也是客户端开发,也很不错,现在两人都是公司的骨干。
当然,说是以技术合伙人身份加入,其实说白了还是写代码,然而现在的自己还是很怀念那时候可以酣畅淋漓写代码的日子的。
当时工作极其的辛苦,每天都在12点之后下班,当时公司一共3个人,后来又找了俩兼职,我基本每天都是12点半开车把大家送到家,然后自己才到家。
其实现在想起来那段日子也蛮难为自己媳妇的,基本都见不到几次面。(当然现在晚上也还是很晚,只是比当时12点这种量级还是好一点了)
后来,因为政策上的一些问题,公司产品出了点问题。
我们三个合伙人决定引入投资。
因为人脉上的关系,这次的投资引入的还是蛮快的。
我们三个一块见了次投资人,其他事情基本就另外两个合伙人在跑了。
这里的事情就不细说了。
投资进来之后,公司几个月内的生存暂时不是问题,但是压力也相应而来,毕竟拿了别人的钱 ...

最后更新于 .

算起来,从离开腾讯自己创业已经过去一年半了,其中经历了太多事情,也有太多的东西想要记录和分享给大家,所以开了这个系列,希望能记录下来。
先说一下目前自己处于的状态,因为目前的状态会严重影响我对事情的判断,而很可能一年后的自己看到自己今天写的话会觉得全是扯淡。
目前公司已经拿到投资。
公司总共17个人,其中创始人包括我共3个,分别负责渠道、产品、技术。
研发团队分3个组,分别是android、cocos2dx、后端,人数分别是4、3、3;产品 2人;美术1人;渠道1人。
挤在一个130平的房子内,周一至周六都上班,周一~周五都是最早10点下班,周六让大家早点走,6点吧。
研发的进度的话,android来开发一个完整的棋牌游戏在1.5月左右。
cocos2dx也是这样一个时间表。
我们后端是用的python,所以开发会很快,一般在一周内就能写出一个可玩的牌桌内逻辑。
现状大体就是这样,先在这临时存个档,一会再读回来。
先从1年半前说起吧。
2013年3月份,自己从腾讯正式离职,当时还处于国内互联网“开放”吵得比较火的时候,而自己手里是有几款能盈利的pc应用的,所以相当于自己给自己发工资,日子过的还不错,也没怎么觉得多有压力。
到4月份,自己其中的一个产品被腾讯下线;再到8月份,自己另一款产品也被腾讯下线。
话说被下线的当天 ...

最后更新于 .

在很早的时候,就听网上的文章说:

python有GIL,所以在单进程内,即使使用多线程也无法利用到多核的优势,同一时刻,python的字节码只会运行在一个cpu上。

以前也是奉为真理,直到今天在对自己的python server做性能测试的时候,发现一个python进程的cpu居然达到了120%。

当用c++编程的时候,如果使用多线程,那么确实进程cpu超过100%非常正常,但是对python来说,似乎这样就和网上的文章冲突了。

所以还是决定自己亲身试验一下,编写代码如下:

from thread import start_new_thread

def worker():
    while 1:
        #print 1
        pass

for it in range(0, 15):
    start_new_thread(worker, ())


raw_input()

 

运行环境为: centos6.4 64位, python 2.7.

得到的结果如下:

E588C2D7 1608 42CC B800 AD5338C87F47

可以清楚的看到,pid为31199的python进程cpu达到了787.9%,接近理论能达到的最大值 800%。

而上方的8个cpu也分别达到了近100 ...

最后更新于 .

最近在做游戏服务分层的时候,一直想把mysql的访问独立成一个单独的服务DBGate,原因如下:

  1. 请求收拢到DBGate,可以使DBGate变为无状态的,方便横向扩展
  2. 当请求量或者存储量变大时,mysql需要做分库分表,DBGate可以内部直接处理,外界无感知
  3. 通过restful限制对数据请求的形式,仅支持简单的get/post/patch/put 进行增删改查,并不支持复杂查询。这个也是和游戏业务的特性有关,如果网站等需要复杂查询的业务,对此并不适合
  4. DBGate使用多进程模式,方便控制与mysql之间的链接数,进行mysql访问量阀值保护
  5. 方便在DBGate上进行访问量统计,慢查询统计、权限控制等等一系列逻辑
  6. 目前是使用python,以后要使用其他语言进行mysql操作时,只要进行标准的http请求即可,不会出现不兼容的情况

当然坏处也是有的:

  1. 首当其冲就是单次请求的响应时间变长,毕竟中间加了一层服务,并且还是http格式
  2. 部署上比原来复杂了一些,很多对mysql直接操作的思维需要进行转变,一开始可能会有些不适

不过总的来说,还是利大于弊,所以最终还是决定搭建DBGate

当然,我们不可能去手工挨个写每个库表对应的restful服务,值得庆幸的是django和flask都提供了对应的解决方案,我们一个个介绍.

Flask

参考链接: flask-restless

flask-restless使用方法比较简单,我直接贴一下代码即可:

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

import datetime
from flask ...

最后更新于 .

最近在调整游戏的后台架构,之前因为需要快速出产品,所以整个代码都揉成一团,也基本没有做任何分层处理。现在服务器端的开发也开始逐渐招进来,所以打算打算换一套统一的架构,以后做新游戏只要做其中的业务逻辑即可。

其实之前在腾讯的时候,基本不会用到message queue这种,所有的分布式处理都是由自己写c++ server来互相通信的。这样的处理虽然开发量稍微大一点,但是性能和灵活性确实很高。

现在自己在外面做,虽然自己已经封装了一套server的框架出来,但是毕竟还有太多的轮子需要自己制造,所以就想到了之前一直有了解过celery,来看一下这种基于message queue的任务系统能达到什么性能。

RabbitMQ

celery首推的mq是rabbitmq,所以需要先安装一下:

在mac下用brew 安装:

brew install rabbitmq

安装成功之后,即可启动server了。

不过在这之前,我们先把后台管理的插件打开:

rabbitmq-plugins enable rabbitmq_management

之后执行如下命令,启动server:

rabbitmq-server

这个时候就可以通过 http://127.0.0.1:15672/ 来访问后台管理端了,默认的用户名和密码是guest guest,可以自己在页面上修改。截图如下:

NewImage

 

Redis

celery也支持redis作为broker和backend,所以redis也需要安装一下 ...

最后更新于 .

测了一下django、flask、bottle、tornado 框架本身最简单的性能。对django的性能完全无语了。

django、flask、bottle 均使用gunicorn+gevent启动,单进程,并且关闭DEBUG,请求均只返回一个字符串ok。

tornado直接自己启动,其他内容一致。

测试软件为 siege,测试os为cenos6 64位,测试命令为:

siege -c 100 -r 100 -b http://127.0.0.1:5000/

django测试结果为:

Transactions:		       10000 hits
Availability:		      100.00 %
Elapsed time:		       18.51 secs
Data transferred:	        0.02 MB
Response time:		        0.18 secs ...

最后更新于 .

上一篇文章聊了下数据存储和常用的传输协议,不过对于自定义传输协议这里留了个坑,正好有点时间,就抓紧填上:)

既然选择原生socket,那么有个基本的选择就是tcp/udp的问题.

这个其实还是看业务自己的选择,只是如果选择了udp的话,那么很多问题都可以不用考虑,比如粘包问题。但是udp有个限制是每次传输的数据大小不能超过64K,这个要注意。

为了考虑复杂的情况,我们还是主要说tcp的实现,这篇文章先说下socket使用相关的一些库和代码吧

Android端开发

对于android端,我们有两个主要选择:阻塞socket和非阻塞socket。

阻塞socket就是正常的socket,当调用recv的时候,会阻塞住直到返回数据。

非阻塞socket在android上可以直接使用nio,因为自己之前一直是做c++和python,所以其实一开始nio的时候真心有些不太使用,把几个要注意的点列一下:

1. 当网络断线的时候,有些手机如S4,会出现这个问题,channel.finishConnect()会一直等超时 60秒。之后会抛出一个 TimeoutException.

解决方法是:

channel.socket().setSoTimeout(5)

来设置成只等5秒。

比较诡异的是,明明是异步io了,为什么还是会有等待超时的情况。还要在研究下。

2. 当网络不在线的时候,启动connect,依然可以进入isConnectable的判断中,并且可以调用finishconnect。

解决方案:finishConnect之后 ...