事务嵌套解决方案

mysql官方并不支持事务嵌套,当第二个事务开始时候,会隐式的先调用commit提交之前的事务,而后在开启第二次的事务。

在正常开发中难免会出现失误嵌套的情况。如何解决呢?

laravel/lumen框架使用了mysqlSAVEPOINT方法来解决这一问题。

mysql savepoint

首先来看下mysql原生案例。

2次开启事务案例

savepoint 案例

上面例子中username=gg被回滚掉了,最终数据库中插入了username=ee,ff,hh。如果最后commit换成rollback所有数据将全部回滚。

laravel/lumen 实现

开启事务时检测当前transactions等级,如果等于1开启事务,如果大于1则创建相应levelsavepoint

commit操作时候,检测当前transactions等级,如果不等于1跳过操作,并且transactions等级减1。如果等于1,事务提交。

rollback操作,检测当前transactions等级,如果不等于1回滚到相应等级的savepoint,等于1这全部回滚。

Functional Programming

  • 函数式编程是什么
  • 函数式编程的特点
  • 函数式编程的意义
  • 函数式编程的几个技术

一、函数式编程是什么?

函数式编程就是编写纯函数,尽可能移除隐含的输入与输出,这样我们的代码就是仅描述输入与输出之间关系的代码。

1、 纯函数

Q: 什么是纯函数?
A: 该函数所有的输入都是显示的(无隐含输入),同样它的所有输出都是显示输出。

让我们来看2个例子:

示例1我们可以清晰的知道输入的是一个int类型,输出的也是一个int类型。对于这种我们称之为纯函数
示例2这个函数既没有输入也没有输出,然而很明显它依赖着什么,并且很显然也在做着什么。但是我们不知道。对于这种我们称之为不纯函数

为什么我们要写纯函数呢?因为不纯函数有很多副作用。详情可参考:
【译】什么是函数式编程?

二、 函数式编程的特点

1、 函数是”第一等公民”

所谓”第一等公民”(first class),指的是函数与其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数,传入另一个函数,或者作为别的函数的返回值。

举例来说,下面代码中的$print变量就是一个函数,可以作为另一个函数的参数。

2、 没有”副作用”

所谓”副作用”(side effect),指的是函数内部与外部互动(最典型的情况,就是修改全局变量的值),产生运算以外的其他结果。
函数式编程强调没有”副作用”,意味着函数要保持独立,所有功能就是返回一个新的值,没有其他行为,尤其是不得修改外部变量的值。

3、 不修改状态

上一点已经提到,函数式编程只是返回新的值,不修改系统变量。因此,不修改变量,也是它的一个重要特点。
在其他类型的语言中,变量往往用来保存”状态”(state)。不修改变量,意味着状态不能保存在变量中。函数式编程使用参数保存状态,最好的例子就是递归。下面的代码是一个将字符串逆序排列的函数,它演示了不同的参数如何决定了运算所处的”状态”。

4、 引用透明

引用透明(Referential transparency),指的是函数的运行不依赖于外部变量或”状态”,只依赖于输入的参数,任何时候只要参数相同,引用函数所得到的返回值总是相同的。
有了前面的第三点和第四点,这点是很显然的。其他类型的语言,函数的返回值往往与系统状态有关,不同的状态之下,返回值是不一样的。这就叫”引用不透明”,很不利于观察和理解程序的行为。

三、 函数式编程的意义

简单的说函数式编程有什么好处,我们为什么要去掌握它。

1、 代码简洁,开发快速

函数式编程大量使用函数,减少了代码的重复,因此程序比较短,开发速度较快。

2、 接近自然语言,易于理解

函数式编程的自由度很高,可以写出很接近自然语言的代码。
用户函数式语言表达(1 + 2) * 3 - 4

对它进行变形,不难得到另一种写法:

3、 更方便的代码管理

函数式编程不依赖、也不会改变外界的状态,只要给定输入参数,返回的结果必定相同。因此,每一个函数都可以被看做独立单元,很有利于进行单元测试(unit testing)和除错(debugging),以及模块化组合。

4、易于”并发编程”

函数式编程不需要考虑”死锁”(deadlock),因为它不修改变量,所以根本不存在”锁”线程的问题。不必担心一个线程的数据,被另一个线程修改,所以可以很放心地把工作分摊到多个线程,部署”并发编程”(concurrency)。

四、 函数式编程的几个技术

1、 map、 reduce & filte

函数式编程中,我们不应该用循环迭代的方式,我们应该用更为高级的方法。

这样的代码很易读,因为,这样的代码是在描述要干什么,而不是怎么干。

2、 pipeline

详情参考我的另外一篇博客:pipeline

3、 recursing 递归

递归最大的好处就简化代码,他可以把一个复杂的问题用很简单的代码描述出来。注意:递归的精髓是描述问题,而这正是函数式编程的精髓。

通常递归的效率低下,内存占用量比较大,这是它长期不能在业界推广的主要原因。

所以这里推荐使用尾递归。

递归和尾递归有何区别?

以斐波那契数列为例:

初看一下没有看出什么太大区别。但是执行效率却是天差地别。

递归函数在return的时候进行了计算f1($a - 1) + f1($a - 2)。这个时候计算机底层就会创建stack累积而后计算

这个曲线就代表内存占用大小的峰值,从左到右,达到顶峰,再从右到左收缩。

尾递归不会如此耗内存。

他的内存比较平稳。

下面是一个f1f2以及循环测试,展示他们各自的使用时间,可以看出他们之间的巨大差别。

4、 currying 柯里化

写一个数组连接成字符串的函数

现在我要其中所有数字加1, 然后在连接

然后结果所有数字乘以2, 再进行连接

是不是觉得很烦了?
如果需要再添加一个功能,是不是又要添加一个参数了?
是不是违背了函数式编程简洁、接近人类语言的好处了?

这个使用就需要用到柯里化了。

什么是柯里化?

柯里化是这样的一个转换过程,把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,如果其他的参数是必要的,返回接受余下的参数且返回结果的新函数。

这里有人就会说了,柯里化不是到最后变换成接受一个单一参数的函数吗?这里为什么还有2个参数?

其实如果将其转换为没想对象的来看他就是接收了一个参数:

当然还有其他非柯里化方式可以来实现简化如下:

5、 higher order function 高阶函数

所谓高阶函数就是函数当参数,把传入的函数做一个封装,然后返回这个封装函数。现象上就是函数传进传出,就像面向对象对象满天飞一样。

6、 lazy evaluation 惰性求值

柯里化中我们的案例就又到了惰性求值。

7、 determinism 确定性

确定性就是只要输入参数一致,输出结果就是一致的。就像数学那样 f(x) = y ,这个函数无论在什么场景下,都会得到同样的结果。

参考以及转载自:
【译】什么是函数式编程?
阮一峰:函数式编程初探
左耳朵耗子:函数式编程
为什么要柯里化

swoole入门 —— C/S架构

一、什么是swoole

官方定义:特别霸气。
Swoole:重新定义PHP

官方介绍:

PHP语言的异步、并行、高性能网络通信框架,使用纯C语言编写,提供了PHP语言的异步多线程服务器,异步TCP/UDP网络客户端,异步MySQL,数据库连接池,AsyncTask,消息队列,毫秒定时器,异步文件读写,异步DNS查询。

它让PHP能够做出了WEB以外的更多事。

二、安装

swoole是一个php扩展。安装方式与其他扩展一样。

也可以用pecl进行安装:

三、Client-Server 架构

3.1、 同步模型

同步模型类似nginx+php-fpm组合。

编写服务端代码:
主要作用:常驻运行服务,接收信息,然后添加上Swoole: 进行返回。

然后运行它,因为我们启用了'daemonize' => true选项,所以他将会变成一个守护进程。

再编写一个同步客户端:

运行客户端:
发送hello world!!!信息,并且接收到了Swoole: hello world!!!返回信息。

3.2、 异步模型

服务端不去动它。

编写异步客户端:

与同步相比写法上主要区别是:

1、 创建swoole_client对象时,需要指明SWOOLE_SOCK_ASYNC异步。
2、 异步操作,connect连接时,并不会立即连接上。所以需要将发送和接收请求写在回掉函数内。

运行客户端:

3.3、 将同步异步化

swoole的另外一个特性Eventloop。可以将各种fd添加到swoole的事件循环中,将同步的IO异步化。

另外结合promisefifo,进行数据传输。可以达到同时请求N个数据源,同时获取数据的作用。

3.4、 将异步同步化

与同步异步化相同,因为本身是异步IO,省略了将同步IO异步话的操作。

3.33.4使用到了有名管道,当传输出局超过64K时,处理起来比较麻烦,需要注意。

四、 HttpServer & WebSocketServer

4.1、 HttpServer

启动服务之后浏览器直接访问http://ip:9502/就可以访问了。

4.2、 WebSocketServer

运行后得到:

服务端:

浏览器

协程与异步 IO(转载)

本文转载自: http://dongliu84.appspot.com/post/5906310176440320

这篇文章解释了我心中很多关于协程的疑问。

以下为正文。

协程(coroutine)的概念已经广为人知,这里就不多说了。作为用户态主动调度的执行单位,协程非常的轻量,并且调度是协作式的,协程可以避免传统多线程程序的上下文切换、调度和锁竞争等开销。

很多人都有这个误解,认为有了协程,就可以用同步程序的方式,写出异步的程序,原先同步的程序和第三方库,也会自动变成异步的。为什么说这是个误解呢,因为要写出有异步效果的程序,只有协程是不够的,还需要有底层IO的支持。协程的调度原本是编程者手工来做的,但是和异步IO结合的话,在发生IO时,自动将IO操作交给异步实现去执行,并让渡出协程的执行权,由调度器去调度执行其他协程。

除去天生支持协程或变种协程的lua, stackless-python, golang 之外,一个本不支持协程的语言也可以通过各种变通方法,来提供对协程的支持。事实上,基本上所有的的语言都有对协程支持的第三方实现,如 Java 的 Kilim, c++ 的boost.coroutine, Python 的 eventlet 和 gevent 等。

问题是,这些语言的底层IO实现,并未对协程调用做处理,其结果是仍然会阻塞这个协程,并没有实现异步的效果。而常用的http /数据库/缓存等lib 都是基于这些语言的底层IO库实现的(如httpclient/libcurl/mysql-connector等),所以使用了这些库的话,在IO 操作时不会让渡执行权,在当前协程阻塞在IO操作上的时候,其他协程也完全无法执行,这比多线程的实现还要糟糕。

除了 IO 之外,第三方lib 如果使用了线程,或是使用了锁,信号量等线程下的同步机制,或是使用了同步的Queue等,在协程环境下使用的时候也会出现各种问题。

所以,java, c++这样的语言,虽然有协程的实现,但是使用范围并不广,因为协程不能利用原先已经存在的大量lib ,需要按照协程的方式重新实现一遍。

一个例外是Python。 Python这样的动态语言,可以使用monkey patch的方式替换系统的IO / thread / queue / lock 等实现, 将其替换成对应的协程。Gevent就是这种做法。

而lua,stackless, golang 这样天生支持协程的语言,所有的IO lib 都是协程实现的,自然可以放心使用。

虽然非常简单,使用协程依然有需要注意的地方。比如:

协程不能有同步IO, 但并非所有的IO操作都一定是异步的。例如文件IO、Pipe、终端输入输出、DNS 解析等,经常没有异步的实现,通常的实现会用线程池进行一个封装,但依然需要在意。
协程只有在发生IO的时候才会让渡执行权,因此存在调度公平性的问题。在大量cpu操作、没有IO的情况下,当前协程会一直占用执行,其他协程得不到执行的机会。所以协程要注意不要有大量密集CPU操作。
协程的开销。协程依然需要占用一定的内存,如goroutine 目前是最小4k 的空间占用。协程切换的代价比较小,但不等于没有,比如stackfull 的协程实现在协程切换是需要做context copy,也有一定的开销。相比较纯异步API 编写的程序,协程的效率通常会差一些。
关于goroutine

goroutine的实现并不完全是传统意义上的协程。在协程阻塞的时候(cpu计算或者文件IO等),多个goroutine会变成多线程的方式执行。golang1.2之后还有类似erlang reduction的机制,来改善goroutine调度的公平性。这个机制只有在函数调用等场合下才会生效,所以效果还比较有限。

Pipeline

  • 什么是pipeline
  • pipeline的模型
  • pipeline的实现

什么是pipeline

Pipeline模型最早被使用在Unix操作系统中。据称,如果说Unix是计算机文明中最伟大的发明,那么,Unix下的Pipe管道就是跟随Unix所带来的另一个伟大的发明。我认为管道的出现,所要解决的问题,还是软件设计中老生常谈的设计目标——高内聚,低耦合。它以一种“链式模型”来串接不同的程序或者不同的组件,让它们组成一条直线的工作流。这样给定一个完整的输入,经过各个组件的先后协同处理,得到唯一的最终输出。

pipeline的模型

输入流经过各个pipe进行处理加工,到达最终目标,然后反转变成输出流,进行输出。

有很多web框架都使用这个思想。比如说:asp.net使用iis处理http请求、javaTomcatStruts还有mina网络通信框架等。

当然phplaravel/lumen框架也使用这个模型。

这个模式的优点是流程式(有序)+可拆卸(配置)。在设计模式上实现了高内聚,低耦合。

缺点:每次它对于一个输入(或者一次请求)都必须从链头开始遍历,这确实存在一定的性能损耗。

pipeline的实现

laravel/lumen框架将pipeline的实现,单独封装成了一个组建illuminate/pipeline。它对外只开放了4个方法:sendthroughthenvia

lumen框架的示例来讲解:

send方法:设置pipeline中传输的参数。
through方法:确定输入流输出流需要经过的pipe管道
then方法:最终需要执行的目标方法

源码解读:

这段为Illuminate\Pipeline\Pipeline类的构造方法,示例中将$this传入,即将App对象设置为Pipeline的容器。

send方法很简单,设置passable,即设置pipeline中传输的参数。

through就是设置所有的需要通过的管道。

then方法看似了了几行代码,实则难以理解。想要理解他,就必须先理解array_reduce函数。

array_reduce解析

php.net官网中的示例,实现array_reduce的方式:

循环数组$array,使用$callback回调方法去处理数组中的每一个元素,并将本次处理得到的结果,作为参数,传入下一次执行。而$initial是作为一个初始化的值,传入第一次处理函数。

官网中另外一个例子,可以简单说明array_reduce是一个变相的递归调用,就是一个递归调用。

以上6句代码的效果是一样的。

一个array_reduce简单的示例:

可以将其变种为:

再次变种

感觉是不是很熟悉了?

在源代码中是这个样子的:

getSlice源码

这个方法可以分为2个部分查看。

第一部分:

这段为执行匿名函数$pipe,并将$passable$stack作为参数传入其中。

那么我们应该知道$pipe的写法,就是一个接收2个参数的匿名函数。并且他的返回值,将作为参数传给下一个匿名方法。

此时我们将其组合起来执行:

得到一个$b$b是一个递归的匿名函数,将其交由call_user_func执行:

我们发现我们输出的顺序是反过来的,因为他是一个匿名递归,将其换种方式来看是:

他的顺序是先进后出的一个栈(Stack)模型。

如果我们将echo输出内容理解为实际的逻辑操作的话,那么就可以实现对pipeline的输入流和输出流的控制。

结果:

到这里,已经基本上将pipeline的实现方式理清。

getSlice的第一部分是对匿名函数的实现,而第二部分这是对类的实现。这里就不再多做叙述。

via方法,这是控制默认调用类的哪个方法。默认的是handle方法。