0%

背景

最近工作中有个项目,用 Phalcon 框架开发,涉及 RabbitMQ,所以自己写了个类来处理消息。
部署时用 Supervisor 监管,但发现一段时间没有业务触发就会出现 SQLSTATE[HY000]: General error: 2006 MySQL server has gone away 的错误。

分析

为什么会出现这个错误?官方给出了解释 https://dev.mysql.com/doc/refman/5.7/en/gone-away.html

The most common reason for the MySQL server has gone away error is that the server timed out and closed the connection.
In this case, you normally get one of the following error codes (which one you get is operating system-dependent).

也就是说,出现 gone away,大部分是因为服务超时或断连了,而且错误码不一定是 2006(CR_SERVER_GONE_ERROR),也可能是 2013(CR_SERVER_LOST)。
常见的原因大致有:

  • 运行线程被杀掉了。
  • 关闭连接后又发出了SQL查询。
  • 运行环境没有授权连接MySQL。
  • 客户端的TCP/IP连接超时。
    比如做了这样的设置 mysql_options(..., MYSQL_OPT_READ_TIMEOUT,...)mysql_options(..., MYSQL_OPT_WRITE_TIMEOUT,...)
  • wait_timeout,即,服务端主动关闭超时未交互的连接。
  • 异常查询请求导致服务端主动关闭连接。
    比如 超出了 max_allowed_packet 变量设定的查询限制。

所以,大概率是 wait_timeout 导致的。

解决

show variables like '%timeout%'; 查看 MySQL 的 wait_timeoutinteractive_timeout
如果 my.ini 中没有配置,则默认 wait_timeout 为 28800,即,8小时。
为了测试,修改本地 MySQL 的 my.ini 配置:

1
2
3
4
[mysqld]
...
wait_timeout=5
interactive_timeout=5

直接上代码(一段实现异常重连的示例代码):

阅读全文 »

基础概念

通过一段C++示例代码来理清几个概念。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
using namespace std;
int main () {
int num = 20; // 实际变量的声明
int *p; // 指针变量的声明
p = &num; // 在指针变量中存储 num 的地址,即,指针的值为 num 的地址

// 输出实际变量的值
cout << "Value of `num` variable: ";
cout << num << endl;

// 输出在指针变量中存储的地址
cout << "Address stored in `p` variable: ";
cout << p << endl;

// 访问指针中地址的值
cout << "Value of `*p` variable: ";
cout << *p << endl;

return 0;
}

运行结果:

1
2
3
Value of `num` variable: 20
Address stored in `p` variable: 0x7ffe4705e3ac
Value of `*p` variable: 20

alt

阅读全文 »

可变对象与不可变对象

在面向对象中,对象是类的一个实例;而在Python中,对象就是变量。
Python中一切皆对象,分为 不可变对象 与 可变对象。

存储方式

在理解可变与不可变之前,先来看看Python对象在内存中是如何存储的:
对象有三个数据:id(标识。在CPython中,对应内存地址)、type(类型)、value(值)。

1
2
3
4
5
>>> message = 'hello'
>>> id(message)
2836204005360
>>> type(message)
<class 'str'>

其中,id([object]) 获取对象的内存地址,type([object]) 获取对象的类型。
alt
变量 message 并不是真正的对象,它是对象的引用,相当于记录了对象在堆空间的地址,通过这个地址可以访问到对应的对象。

不可变(immutable)与可变(mutable)

上例中,存储在堆中的字符串对象是独立的(没有引用外部),所以它本身是不可变的。
不可变对象:对象在内存中的值不能被改变。
当改变某个变量时,由于其所引用的对象不可变,所以创建了新对象,变量再指向这个新对象的地址。

阅读全文 »

什么是分布式锁

锁的作用是解决在并发情况下共享资源的互斥问题。
(1)单机场景下(多线程):部分语言已原生实现了多线程加锁控制,eg. Java 的 Synchronized。
(2)分布式场景下:基于CAP定理,满足AP,通过 最终一致性 来保障C,所以,一般采用 分布式事务分布式锁 等来保障数据的最终一致性。

锁的分类

  • 线程是否需要对资源加锁
    • 乐观锁:乐观地认为数据不会被修改,持有数据不会上锁,写入时使用 CAS 判断,适用于读多写少的场景。
    • 悲观锁:悲观地认为数据会被修改,持有数据就会上锁。
  • 多线程并发访问资源加锁实现细节,eg. Java 的 Synchronized
    • 无锁:不加锁,用 CAS 控制资源读写。
    • 偏向锁:适用于同一线程多次访问的情况,通过 ThreadID 直接返回已获取的锁。
    • 轻量级锁:在偏向锁阶段,若多个线程同时访问,会撤销偏向锁,升级为轻量级锁,未获取到资源的线程会使用自旋获取锁。
    • 重量级锁:自旋到达一定次数还未获取锁,膨胀为重量级锁,管理多个线程。
  • 资源已被锁定,线程是否阻塞
    • 自旋锁:不阻塞,循环判断(此循环并不一定是程序中的循环语法,主要指持续占用CPU时间片)是否获取锁。
      适应性自旋锁:自旋的时间/次数不再固定,而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定。
      eg. 如果在同一个锁对象上,自旋等待刚刚成功获得过锁,并且持有锁的线程正在运行中,那么虚拟机就会认为这次自旋也是很有可能再次成功,进而它将允许自旋等待持续相对更长的时间;
      如果对于某个锁,自旋很少成功获得过,那在以后尝试获取这个锁时将可能省略掉自旋过程,直接阻塞线程,避免浪费处理器资源。
    • 互斥锁:阻塞,等待重新调度获取锁。
  • 多个线程是否能够共享一把锁:共享锁(eg. 信号量)、独占锁(又称 排他锁)。
  • 是否重复获取:
    • 不可重入锁:若当前线程执行某个方法已经获取了锁,那么此线程中不允许再次获取同一个锁。
    • 可重入锁:线程可以重复获取同一个锁,eg. Java 的 ReentrantLock。
阅读全文 »

函数式编程

函数式编程是一种编程范式,它将电脑运算视为函数的计算。

编程范式(programming paradigm),指的是计算机编程的基本风格或典范模式。

常见的编程范式有:函数式编程、程序编程、面向对象编程、指令式编程等,其他的,如,声明式编程(eg. SQL)、泛型编程。
其中,程序编程 也称 命令式编程 或 过程化编程(所以,面向过程属于程序编程范式?)。
函数式编程语言一般有如下几个特性:

函数是“第一等公民”

即函数可以出现在任何地方,比如可以把函数作为参数传递给另一个函数,不仅如此还可以将函数作为返回值。

闭包(Closure) 和 高阶函数

闭包可以理解成“定义在一个函数内部的函数”。
本质上,闭包是将函数内部和函数外部连接起来的桥梁,就是能在一个函数外部执行这个函数内部定义的方法,并访问这个函数内部定义的变量。
举个栗子:

1
2
3
4
5
6
7
8
function func1() {
var num = 100;
function func2() {
return num;
}
return func2;
}
console.log(func1()());// 100

上例中,func2 即为闭包。一般这种情况下,可以用匿名函数的写法:

1
2
3
4
5
6
7
function func1() {
var num = 100;
return function() {
return num;
};
}
console.log(func1()());// 100

高阶函数 指 接受函数作为参数 或 将函数作为返回值 的 函数。上例中,func1 的返回值是一个函数,func1 即为高阶函数。

阅读全文 »