aGVsbG8gd29ybGQ=
post @ 2017-11-20

曾有一段时间,一个叫”包你说”的小程序风靡微信平台,据产品大佬们的调查分析,日流水近百万,每日纯利润达十万~如此可观的收益,公司自然是想着要”截流”了~于是乎…这个重任就落到了本菜菜身上…如果你还不知道”包你说”的话…建议你先尝试着体验下,体验后你就发现,所谓的包你说,最大的难点无非就是在那个语音识别上…其它地方一如正常的CRUD…当然还有微信的小程序支付…让我郁闷了一阵

踩坑一:微信小程序支付

先说简单的小程序支付吧…如果你对支付类业务比较熟悉,你总是能够发现,无论是原生支付,还是第三方支付…无论哪个平台…其最核心的步骤无外乎两步:下单 and 通知

根据不同的支付文档,有时需要客户端访问商户系统,由商户后端下单,成功后返回客户端,有时这是由商户系统生成下单参数,由客户端去请求下单。而微信的小程序支付则是后一种…嗯,这到没什么…不过需要注意的是…微信的小程序支付文档上有个再签名过程,同时,一定要注意字段名字段示例值 !!!我就是被可恶的微信字段名坑得不浅…当然这也怨我自己粗心大意…其它诸如签名算法并没有什么难度的…贴一张图以示提醒了小程序支付流程 小程序支付参数

看到了吗…那个字段的示例值…更可恶的是…下单时的文档参数说明使用的是snakeCase,到了这里…全部变成camelCase…完全看心情有木有…没办法…也怨我看文档不仔细吧…

踩坑二:微信语音识别

在完成大概所有的CRUD和支付功能后…开始去玩…看起来最高大上的”语音识别”功能…我们最终选取的语音识别平台是百度…原因嘛…因为它免费…不过话又说起来…百度的语音识别接起来…还真挺简单的…直接提供了pip包,非常方便集成在flask框架中,我按照了示例…自己测试了文件上传…又调用百度语音识别api成功后…觉得自己大事已成…万万没想到的是…微信的语音文件格式并不可以被百度语音直接识别…WTF!怎么办…当时考虑使用讯飞…以为它可以转万能的语音格式…官方查阅之…原来不是…那就只有一种办法了…自行转码…于是又去搜索python的音频转码库…然而都没有对微信那种语音文件格式的支持…所以没办法了…到了这里…要么使用C语言…自己编写转码库和程序…让C调用…要么…嗯…使用第三方外部转码程序…好在Python和Unix/Linux对调用第三方应用支持比较好,这点没什么难度…那么话又说回来了…微信的语音文件到底是什么格式呢?百度之…大佬们众说纷坛…不过一个比较可信的主流的观点是SILK文件格式…嗯…于是各种搜索…终于让我找到了一款神器:ffmpeg万能解码器,支持丰富cli处理!在研究使用ffmpeg的时候…发现了 一个有趣的词…Hall of Shame有兴趣的朋友们可以自己去看看鹅厂的黑历史…然鹅…一番折腾后发现…ffmpeg并没有支持silk音频的解码库…需要自己额外下载…WTF!后来我又百度之…终于…发现了这位大佬,传送门,于是我按照文档…发现…居然告诉我这个转码错误

WTF!不是标准SILK格式,那是什么?!千翻查找…叫我发现了这个…传送门,原来是在文件头信息中多了一个无用的空字节…于是…我在自己的mac中搜索微信接收到的语音文件,发现是.aud.silk结尾…嗯,应该称之为silk变种文件比较合适。于是按照思路…先去掉第一个字符…然后转码…这次果然有效!!!然后再调用百度语音识别,居然…成功了!不过值得一提的是…我在转码时,没有使用那个Shell脚本,而是用到了这个,这份转码库的用法很简单:download下来后,进入ARM平台,然后make编译出可执行程序…编译过程可能会提示你安装一些编译的工具…(如果你没安装的话)之后就可以使用python去调用这个外部的第三方的程序了。这样就实现了微信的语音文件从上传到转码再到识别的过程,初步完成该核心功能,后续的识别率优化…可以考虑比对拼音…这样就可以避免同音不同字的识别尴尬,在一定程度上…提高识别率。

踩坑三:微信小程序的通知

用过摩拜单车小程序的童鞋们肯定都知道,摩拜单车在解锁和开锁后都会向用户发送一条通知。好吧,其实这个通知没什么难度,唯一觉得比较不舒服的是它的参数…嗯,尤其你要注意对于微信的access_token间断刷新…这个使用定时任务+redis就可以很好处理。接下来就是通知的核心参数form_id对于该参数…如果有支付过程的话,这个参数就会好拿很多…如果没有的话…那就会稍稍麻烦…当然我也只是研究使用了…支付情形…剩下的情形留着以后再去考虑吧

阅读此文

在mysql中,有个函数叫“GROUP_CONCAT”,平常使用可能发现不了问题,然而一旦数据量增大时,会发现内容被截取了!!!是的,你没看错,就是被截取了!!真的是非常令人蛋疼的问题呀,尤其是在类似聚合分组的业务,将数据反馈给客户端时,就更为严重了!多番查找折腾后发现,原来…
MYSQL内部对这个是有设置的,默认不设置的长度是1024,可以通过使用show variables like "group_concat_max_len命令来查看该变量值group_concat_max_len

想要指定GROUP_CONCAT的长度有两种方法:

  1. 修改变量 group_concat_max_len的值

    • 修改session变量,只对该连接客户有效

      1
      SET [SESSION] group_concat_max_len=102400;
    • 修改global变量,对所有连接都生效

      1
      SET GLOBAL group_concat_max_len=102400;
  2. 修改配置文件(全局生效)

在mysql启动的配置文件下,进行设置,各平台下配置文件存放的目录不同,配置文件的具体存放位置,不是本文要讨论的内容

以我的mac os系统为例,我已经将mysql的配置文件写在/etc目录下,只需要在mysqld节点下group_concat_max_len = [size] (size是你定义的大小,上限为4294967295)group_concat_len

曾经看到过一些文章的说法是将group_concat_max_len设置为-1,然后就可以达到上限4294967295,我在自己的机器下试了试,发现并不可以…我的系统是mac os 10.12 ,mysql的版本是5.7.19

本文参考自CSDN,原文地址

阅读此文
post @ 2017-09-12

这几天在使用mysql过程中发现了这样一个有趣问题:如果在执行delete操作时,where子句不是直接指定条件,mysql就会报错,内容如下:

Error Code: 1175. You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column To disable safe mode, toggle the option in Preferences -> SQL Editor and reconnect.

查阅官方的手册后,官方时如此介绍的:

For beginners, a useful startup option is --safe-updates (or --i-am-a-dummy, which has the same effect). It is helpful for cases when you might have issued a DELETE FROM *tbl_name* statement but forgotten the WHERE clause. Normally, such a statement deletes all rows from the table. With --safe-updates, you can delete rows only by specifying the key values that identify them. This helps prevent accidents.

原来mysql为了避免出现初学者在执行delete操作时未制定where条件导致整个表被删除,因此在系统变量中开启了SAVE-UPDATES模式,这样就能有效减少错误的产生了。

SAVE-UPDATES下有3个作用:这里就直接po出文档了

  • You are not permitted to execute an UPDATE or DELETE statement unless you specify a key constraint in the WHERE clause or provide a LIMIT clause (or both)
  • The server limits all large SELECT results to 1,000 rows unless the statement includes a LIMIT clause
  • The server aborts multiple-table SELECT statements that probably need to examine more than 1,000,000 row combinations

综上,可以使用如下办法解决

1
SET SQL_SAVE_UPDATES = 0;

并不建议全局设置该变量,小心驶得万年船,尤其对经验不足童鞋来说~

更多内容请看这里,传送门

阅读此文
post @ 2017-09-05

在《python多线程技术(一)》篇当中,曾粗略介绍了多线程技术及运行状态,及python对于多线程的支持和简单实用,还有印象的朋友一定还记得python中实现多线程的两种方式吧,如果不熟悉的话,建议你先去读一读我上一篇的博文哦🤓

本篇是python多线程技术的精华所在,在真实项目开发中所使用到的多线程编程模型大部分都可以在我将要给出的代码示例中找到,如果本篇的内容过于繁多的话,按照我一如既往,短小精悍的写作特点来看的话~我也许还会写上第三篇哦😛

常见线程运行模型

在开始撸码之前,我们还是看看的常见的线程模型有哪些~一起看看这些言简意赅,图文并茂的示意图(感谢麦子学院丁敬香老师提供的教学资料)

1.井水不犯河水井水不犯河水
2.独木桥上相遇-线程等待独木桥上相遇-线程等待
3.甘当幕后英雄-后台线程甘当幕后英雄-后台线程
4. 我先用你后用-线程同步我先用你后用-线程同步
5.操作也有先后-线程同步操作也有先后-线程同步
6共享要悠着用-线程同步共享要悠着用-线程同步
7.我用完后叫你-线程通信我用完后叫你-线程通信

具体代码实例

#####1. 线程等待实例

在python的多线程编程中实现线程的等待非常容易,只需要调用线程对象的join(self, timeout=None)方法即可

被调用join()方法的线程会一直阻塞调用者的线程,直到自己结束(正常结束,或引发未处理异常),或超出timeout的时间。

1.1.1 一般情形

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import threading
import time

class MyThread(threading.Thread):
def run(self):
for i in range(20):
print('threading: ', i)
time.sleep(0.1)

if __name__ == '__main__':
t = MyThread()
t.start()
#t.join() # 取消注释即转变为线程等待情形
for i in range(10):
print('Main:', i)
time.sleep(0.1)

运行结果如下
非等待

可以看到是两个线程交叉运行,如果我们需要在子线程运行完成后再运行主进程,调用join方法即可

1.1.2 等待情形

代码如上,取消join方法所在行的注释即可

运行结果如下
等待

2. 守护线程(后台线程)

在python中实现后台线程的情景相当容易,其步骤如下:

1.建立线程

2.设置线程的daemon属性为True

3.启动线程

需要注意的是被设定为后台运行的线程,会在主程序退出时主动自杀。

2.1.1 一般情形

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import threading
import time

def dmn():
"""模拟后台线程的运行"""
print('dmn start ...')
time.sleep(2) # 后台线程来不及结束,将会随着主线程的结束而退出
print('dmn end.')


def ndmn():
"""非后台线程运行"""
print('ndmn start ...')
time.sleep(1)
print('ndmn end.')

if __name__ == '__main__':
d = threading.Thread(target=dmn)
#d.daemon= True # 模拟后台线程时,请取消次注释
n = threading.Thread(target=ndmn)
print('Main statr')
d.start()
n.start()
print('Main end.'

运行结果如下
非等待线程

可以看到主线程结束后,两个线程也相继结束

2.2.2 守护线程情形

代码如上,取消注释后,运行结果如下
守护线程

可以看到主线程结束后,守护线程就自动退出,还来不及执行print(‘dmn end.’)

3. 线程同步(重点,难点)

python中线程同步的方式纷繁复杂,多种多样,为处理不同业务情形提供相应的工具和概念。python的theading模块主要提供了以下3种线程同步的方式:

1.指令锁(Lock)

2.条件变量(Condition)

3.信号量(Semaphore)

3.1 指令锁
3.2 条件变量
3.3 信号量
阅读此文
post @ 2017-08-28

最近一直在学习python的当中多线程技术,感觉使用起来的确比java要方便多~正好乘这个机会,当作是一个记录和总结了

多线程技术概述

1.进程与线程

  • 进程:正在运行中的程序
  • 线程:进程中负责程序执行的控制单元(执行路径)一个进程可以有多个执行路径,称之为多线程

2.多线程技术的意义

开启多个线程是为了同时运行多部分代码,每一个线程都有自己运行的内容。这个内容就是多线程要执行的任务

3.多线程的好处与弊端

  • 好处:解决了多部分程序同时运行的问题,由主机的操作系统给每个进程/线程安排一个小的时间片,

    在所有进程/线程间快速循环,使得每个执行单位都得到CPU的执行时间。

  • 弊端:线程太多反而会导致cpu处理效率的降低,这是因为应用程序的执行都是cpu做着快速的切换完成的,而这个切换是随机的。

4.python中多线程局限性(这里的python如不加特殊说明都是指CPython)

Python解释器内部使用了全局解释器锁(GIL),限制了一个Python程序只能在一个CPU核心上运行。

所以…某种以上上…python语言的多线程特性…只是一个美丽的谎言…但这并不足以作为拒绝learning的理由,君不见node.js的异步单线程模型在大部分应用场合下~速度不都是杠杠滴嘛

创建多线程的方式

python当中封装了有专门针对多线程解决方案的threading模块,我们对于多线程技术的各种功能需求,都可以在此模块当中找到,挑几个重要的介绍下吧~threadingmodule

类名 功能描述
Thread 表示控制线程的类,封装线程对象一般属性和开启以及等待方法
Lock 指令锁,封装由同步所需的获取🔒和释放锁的方法
RLock 可重入锁,用于同一线程多次获取同一资源,相应地也要release匹配
Condition 条件变量,常用于生产者,消费者模型中的共享变量控制及线程间的通信
Semaphore 信号量,内部维护着一个计数器,可用于实现对稀缺资源的控制,如数据库连接池等
Event 事件,用于

在python中有两种方式创建线程,以下来自Thread类doc说明

There are two ways to specify the activity: by passing a callable object to the constructor, or by overriding the run() method in a subclass

下面先介绍下Thread对象相关属性和方法,以及线程的创建方式Thread对象

  • 第一种方式:继承Thread类并重写run()方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    class MyThread(threading.Thread):



    def __init__(self):
    super().__init__()

    def run(self):
    s = 0
    for i in range(30):
    s += i;
    time.sleep(0.1)
    print(s)

    th = MyThread()
  • 第二种方式:在实例化Thread对象时传入线程的运行目标任务(一个函数)

    1
    2
    3
    4
    5
    6
    7
    8
    def thfun():
    s = 0
    for i in range(30):
    s += i;
    time.sleep(0.1)
    print(s)

    th = threading.Thread(target=thfun)

创建线程后即可调用 start()方法调用开启线程

1
th.start()

多线程的运行状态

引用某大佬的一幅图来解释吧,在此先膜拜下大佬~threadstatus

其它

受篇幅限制,本次关于python下多线程技术就暂且聊到这里了,太长的文章想必大佬们也没耐心看下去…相信这里的内容对任何有一定语言基础的朋友来说都是再容易不过了🤓~接下来的博主要更新的文章将是多线程技术中的核心与精华(不限语言),暂且剧透下吧😈:指令锁实现同步、条件变量模拟生产者消费者模型、信号量实现对稀缺资源的控制、使用事件实现线程间的通信…敬请期待,预计1~2篇,下次我会po上全部完整代码😎

阅读此文
⬆︎TOP