Month: 五月 2017

Linux 命令行网络相关命令学习

iptables

使用 iptables -A 添加路由规则,使用 iptables -D 删除路由规则

fuser

查看哪个程序占用了给定端口

fuser XXX/tcp  # see which program is using tcp port XXX
fuser XXX/upd  # see which program is using udp port XXX
fuser -k XXX/tcp  # kill the program, if you have right permissions

curl

wget 功能很有限,httpie 虽然比较人性化但是有很多 bug,还是 curl 比较好用。

curl -L/--follow http://example.com           follow redirect
curl -I/--head http://example.com             only headers 
curl -o/--output file http://example.com      http -d
curl -v/--verbose http://example.com    
curl --data "key=value" http://example.com  
curl --data-urlencode "key=
curl -X/--request GET/POST http://example.com   
curl -H/--header "Accept: utf-8"    
curl --referer http://x.com http://example.com
curl --user-agent 

curl –cookie

使用代理

curl -x <protocol>://<user>:<password>@<host>:<port> --proxy-anyauth <url>

cookies

cookies 功能主要通过两个选项实现 -b/--cookie-c/--cookie-jar. --cookie 用于发送请求时
携带 Cookies, --cookie-jar 会把返回的 Cookie 按照 cookie.txt 规范存储到给定的文件中。

curl -v --cookie "USER_TOKEN=Yes" http://127.0.0.1:5000/
curl -v --cookie example.txt http://example.com

需要注意的是,如果 --cookie 的参数中包含了 =, 他就会被当做一个键值对来处理,而且可以
使用多个 Cookie, 用分号分开即可:”key1=val1;key2=val2;…”. 否则的话,这个参数会被当做文
件名,会读取对应文件的 Cookie, 这个文件同样要遵守 cookie.txt 的协议。

查看网络情况

需要注意的是,在 Linux 上 netstat 已经废弃了,应该使用新的 ss 命令。不过在 macOS 等 Unix 系统中,还是只有 netstat

netstat -a  list all ports
netstat -at list all tcp ports
netstat -l  all listening ports

netstat -in 查看提供网络接口的信息

netstat -rn 显示路由表

ifconfig eth0 显示接口的状态

ifconfig -a 所有接口的状态

ip addr eth0 全新的命令

Mac

和 Linux 基本相同,但是缺少 ip 指令,需要安装 iproute2mac 包

traceroute

nslookup

host

wget –noproxy

mtr

bt download

sudo add-apt-repository ppa:t-tujikawa/ppa
sudo apt-get update
sudo apt-get install aria2

使用 rsync 同步

rsync -azP localdir remotedir

  • -a archive,表示归档
  • -n dry-run,只显示要执行的操作而不具体执行
  • -z compress,压缩
  • -P –progress + –partial,显示进度同时断点续传
  • --exclude=<dir> exclude directory from being rsynced

默认情况下,rsync 使用增量同步,而不会删除文件。使用 --delete 删除文件

参考资料

  1. https://curl.haxx.se/docs/http-cookies.html

redis 实战总结

redis是做什么的

一个数据结构存储器,数据驻留在内存里,可以在程序的两次之间保存数据

一些实现细节和比较好的地方

redis 的 string 是 binary-safe 的,可以存储任意的二进制数据(bytes),甚至可以把图片存储在 redis 中

经常用到的场合

  1. 用作缓存

    1. 最基础的,最经典的应用场合,当查询数据库或者ES等存储代价比较高的时候,直接用查询的语句做 key,查询结果用作缓存
  2. 用做队列. Redis 5.0 之后最好用 Redis Stream,不要用 list。

  3. 用做集合,也就是存储一批数据的池子。用作有序集合

经常遇到的问题

过期之间只能指定到键级别,而不能指定到集合的键级别

pipeline

imporve performance by combining multi command into one and reduce TCP times

>>> p = r.pipeline()        # 创建一个管道
>>> p.set("hello","redis")
>>> p.sadd("faz","baz")
>>> p.incr("num")
>>> p.execute()
[True, 1, 1]
>>> r.get("hello")
"redis"

or 

>>> p.set("hello","redis").sadd("faz","baz").incr("num").execute()

默认的情况下,管道里执行的命令可以保证执行的原子性,执行pipe = r.pipeline(transaction=False)可以禁用这一特性。

key 的命名

colon sign : is a convention when naming keys. Try to stick with a schema. For instance “object-type:id:field” can be a nice idea, like in “user:1000:password”. I like to use dots for multi-words fields, like in “comment:1234:reply.to”.

使用方法

Redis 是个好东西,提供了很多好用的功能,而且大部分实现的都还既可靠又高效(主从复制除外)。所以一开始我们犯了一个天真的用法错误:把所有不同类型的数据都放在了一组 Redis 集群中。

  • 长生命周期的用户状态数据
  • 临时缓存数据
  • 后台统计用的流水数据

导致的问题就是当你想扩分片的时候,客户端 Hash 映射就变了,这是要迁移数据的。而所有数据放在一组 Redis 里,要把它们分开就麻烦了,每个 Redis 实例里面都是千万级的 key。

根据数据性质把 Redis 集群分类;我的经验是分三类:cache、buffer 和 db

  • cache:临时缓存数据,加分片扩容容易,一般无持久化需要。
  • buffer:用作缓冲区,平滑后端数据库的写操作,根据数据重要性可能有持久化需求。
  • db:替代数据库的用法,有持久化需求。

规避在单实例上放热点 key。

同一系统下的不同子应用或服务使用的 Redis 也要隔离开

EcmaScript 6

using es6 with babel

debugging mode

<script src="node_modules/babel-core/browser.js"></script> 
<script type="text/babel"> 
// Your ES6 code
</script>

production mode

babel script.js --out-file script-compiled.js

Yifei’s Notes

the main impovements of ES6 are loops and generators, let/const, arrow functions, class syntax
small pieces are function arguments, destructuring

Looping

there are 3 ways to loop a sequence in ES5, but there are problems

ES5 Loops

// not concise
for (var index = 0; index < myArray.length; index++) {
console.log(myArray[index]);
}

// no break or return
myArray.forEach(function (value) {
console.log(value);
});

// for objects, not arrays
for (var index in myArray) {
// don’t actually do this
console.log(myArray[index]);
}

Introducing ES6 Loops

// concise and correct
for (let value of myArray) {
console.log(value);
}

// also works on strings, sets and maps
for (let chr of “”) {
alert(chr);
}

// make a set from an array of words
var uniqueWords = new Set(words);
for (let word of uniqueWords) {
console.log(word);
}

for (var [key, value] of phoneBookMap) {
console.log(key + “‘s phone number is: ” + value);
}

// you can even make it work with objects
// dump an object’s own enumerable properties to the console
for (var key of Object.keys(someObject)) { console.log(key + “: ” + someObject[key]); }

Generator

Inside a generator-function, yield is a keyword, with syntax rather like return. The difference is that while a function (even a generator-function) can only return once, a generator-function can yield any number of times. The yield expression suspends execution of the generator so it can be resumed again later.

Generator functions are basically the pause-and-continue-able function. when you call a generator function, it returns an paused Generator object, which has a next() function, each time you call the next() function, a pair of yielded-value and status is returned.

In technical terms, each time a generator yields, its stack frame—the local variables, arguments, temporary values, and the current position of execution within the generator body—is removed from the stack. However, the Generator object keeps a reference to (or copy of) this stack frame, so that a later .next() call can reactivate it and continue execution.

function* fibs() {
var a = 0;
var b = 1;
while (true) {
yield a;
[a, b] = [b, a + b];
}
}

function* range(start, stop) { for (var i = start; i < stop; i++) yield i; }

// This should “ding” three times
for (var value of range(0, 3)) {
alert(“Ding! at floor #” + value);
}

This is possible because generators are iterators. All generators have a built-in implementation of .next() and Symbol.iterator.

Template Strings

Hello ${user.name}, welcome to our server for the ${times} times

Rest Parameters and Defaults

ES5 version

function containsAll(haystack) {
  for (var i = 1; i < arguments.length; i++) {
    var needle = arguments[i];
    if (haystack.indexOf(needle) === -1) {
      return false;
  }
}
return true;
}

ES6 version

function containsAll(haystack, ...needles) {
  for (var needle of needles) {
    if (haystack.indexOf(needle) === -1) {
      return false;
  }
}
return true;
}

ES6 supports default parameters, The default argument gets evaluated at call time, so unlike e.g. in Python, a new object is created each time the function is called.

Class

ES6 support static method, supuer, getter/setter
you can even subclass builtin types

class Circle {
constructor(radius) {
this.radius = radius;
Circle.circlesMade++;
};

static draw(circle, canvas) {
    // Canvas drawing code
};

static get circlesMade() {
    return !this._count ? 0 : this._count;
};
static set circlesMade(val) {
    this._count = val;
};

area() {
    return Math.pow(this.radius, 2) * Math.PI;
};

get radius() {
    return this._radius;
};
set radius(radius) {
    if (!Number.isInteger(radius))
        throw new Error("Circle radius must be an integer.");
    this._radius = radius;
};

}

var [,,third] = [“foo”, “bar”, “baz”];
var [head, …tail] = [1, 2, 3, 4];
console.log(tail);
// [2, 3, 4]

var robotA = { name: “Bender” };
var robotB = { name: “Flexo” };

var { name: nameA } = robotA;
var { name: nameB } = robotB;

console.log(nameA);
// “Bender”
console.log(nameB);
// “Flexo”

// this is a syntax sugar for variable and key share the same name
var { foo, bar } = { foo: “lorem”, bar: “ipsum” };
console.log(foo);
// “lorem”
console.log(bar);
// “ipsum”

var [missing = true] = [];
console.log(missing);
// true

var { message: msg = “Something went wrong” } = {};
console.log(msg);
// “Something went wrong”

var { x = 3 } = {};
console.log(x);
// 3

// parameters
function removeBreakpoint({ url, line, column }) {
// …
}

// super works as expected, calling super constructor and access base properties

// you can even subclass builtin types

CommonJS

There is a special object called module.exports, when requireing, the value of module.exports is returned.

something like that…
var require = function(path) {
// …
return module.exports;
};

ES6 export

use the export keyword

// lib.js
export function foo() {}
export class bar {}
// or
export {baz, foz};
export {foo as fart};

// use.js
import {foo, bar} from “lib.js”;
import {foo as fart} from “lib.js”; // renaming
import {* as lib} from “lib.js”; // import everything and put in a object

ES6 import commonJS

most packages are written in commonJS, for using as ES6 modules:
import _ from “lodash” // which is
import {default as _} from “lodash” // which is
let _ = require(“lodash”);

you can also do module.exports in ES6
export default value;

Python 程序员的 Java 快速教程

和 Python 一样,Java 同样是一个面向对象的语言,甚至更进一步的说,Java 是一个只有面向对象范式的语言。Java 是一个比较正常的语言,甚至可以说正常到无聊。

变量和类型

像其他语言一样,Java 中也有 int/double/char 这些类型。但是 Java 有一个神坑:没有 uint64 类型,只有 signed int。不过相比 JavaScript 没有 long int 类型来说还是不算太坑吧。这个甚至影响到了 Thrift,为了兼容 Java,也不提供 uint64 类型。

  • 除了基础类型之外,Java 还提供了一些封装类型 Integer/Double 等。
  • Java 中不允许变量隐藏
  • Java 中不允许将其他类型用做 bool 值,也就是 if (1) 是不合法的。
  • 类型转换:Integer.parseInt, Interger.toString
  • instanceof 运算符相当于 Python 的 isinstance 函数。
  • Java 中没有 const 关键字,统一使用 final。
  • Java 的修饰符有 public/protected/default/private 几种类型,一般来说,只用 public 和 private,另外两个不要用。sychonized 修饰符实际上是一个锁。
  • Java 中的 true 和 false 是小写。
  • Java 的循环和 C++ 完全一样。除此之外,还增加了 foreach 循环,类似于 C++ 11 中增加的。if 语句也和 C++ 完全一样。switch 语句也完全一样。
  • 常用的数学函数都位于 java.lang.Math 包中。

相等比较

Java 一个特别坑爹的地方:大多数对象应该使用 .equals 比较,而不能使用 == 比较,因为 == 比较的是他们是否是同一个对象,而不是比较值。

就连内置的 String 对象都需要使用 .equals 比较。

另外,使用 .equals 的话还会遇到对象是否是 null 的问题,这时候可以使用 Object.equals(a, b) 来比较。

变量的作用域

public class Variable {
    static int allClicks=0;    // 类变量
    String str="hello world";  // 实例变量
    public void method(){
        int i =0;  // 局部变量
    }
}

类型转换

java 需要显式转换类型
Assume that we have the following declarations of function f and variables h1 and h2:

public static void f( RaceHorse r ) { ... } 
Horse h1 = new RaceHorse(); 
Horse h2 = new Horse(); 

Now consider the following three calls to f:

  1. f(h1); // compile-time error (missing cast)
  2. f((RaceHorse)h1); // fine! h1 really does point to a RaceHorse
  3. f((RaceHorse)h2); // runtime error (bad cast) h2 points to a Horse

静态方法和成员变量并没有 dynamic dispatching
可以使用 super.xxx() 调用父类已经被重载的方法

传值还是传引用

Java 中所有的对象都是传递的值,但是这个值是对象的地址,所以实际上是「传引用」的语义。包括原生数组在内,在函数中改变这个对象的属性都会影响到实参。

需要注意的是:在不少其他语言中,原生数组都是值传递的。

public static void changeContent(int[] arr) {
   // If we change the content of arr.
   arr[0] = 10;  // Will change the content of array in main()
}

public static void changeRef(int[] arr) {
   // If we change the reference
   arr = new int[2];  // Will not change the array in main()
   arr[0] = 15;
}

public static void main(String[] args) {
    int [] arr = new int[2];
    arr[0] = 4;
    arr[1] = 5;

    changeContent(arr);
    System.out.println(arr[0]);  // Will print 10.. 

    changeRef(arr);
    System.out.println(arr[0]);  // Will still print 10.. 
                                 // Change the reference doesn't reflect change here..
}

enum

enum Color {
    GREEN,
    RED,
    BLUE
}

字符串

Java 的字符串和 Python 的字符串也类似,附带的方法也基本都有对应的,只是 Java 的名字更长一些。

在需要组合字符串的情形下,建议使用 StringBuilder 和 StringBuffer。其中 StringBuilder 不是线程安全的,但是速度要快一些。

StringBuffer sBuffer = new StringBuffer("菜鸟教程官网:");
sBuffer.append("www");
sBuffer.append(".runoob");
sBuffer.append(".com");
System.out.println(sBuffer);  

容器类型

Java 的数组和 C++ 中的类似,但是语法上更加明晰,适应人的思维一些。需要注意的是数组也是「传引用」的。

double[] myList;         // 首选的方法
double myList[];         //  效果相同,但不是首选方法,兼容 C++
dataType[] arrayRefVar = new dataType[arraySize];
dataType[] arrayRefVar = {value0, value1, ..., valuek};
array.length  // 返回数组的长度

java.util.Arrays 中包含了一些 Array 的实用方法:fill, sort, binarySearch, equals 等等。

除了原生数组以外,Java 中常用的容器有:ArrayList, LinkedList, HashSet, TreeSet, HashMap, TreeMap, Stack。他们都在 java.util 中。至于他们的实现和时间复杂度显而易见都可以通过名字推测出来了,这里不再赘述。

容器类型使用了泛型,和 C++ 中的泛型基本没啥区别,后面再讲。

List<String> list = new ArrayList<String>();
list.add("Hello");
list.add("World");
list.add("HAHAHAHA");
Iterator<String> ite=list.iterator();
while (ite.hasNext()) {
    System.out.println(ite.next());
}

遍历一个字典

Map<String, String> map = new HashMap<String, String>();
map.put("1", "value1");
map.put("2", "value2");
map.put("3", "value3");

System.out.println("通过 Map.entrySet 遍历 key 和 value");
for (Map.Entry<String, String> entry : map.entrySet()) {
    System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
}

函数

Java 中没有独立的函数,函数必须依附于类存在。

Java 中没有 **kwargs 这种字典参数,但是可以使用 type... vals 这种方式。一般的语言中也都只支持这一种方式。

OO

  • Java 中所有类都是 Object 的子类
  • Java 会为你提供默认构造器(无参构造器),和 C++ 一样,不写构造函数也没啥问题。
  • 只有类成员才会默认初始化,普通变量不会
  • 匿名内部类非常有用。 new ClassName() {} 创建一个继承自 ClassName 的匿名类的对象
  • 嵌套类是指使用了 static class 的内部类
  • Java 中的类只能继承一个父类,也就是单根继承。使用 extends 关键字。但是一个类可以实现多个接口,接口中只能有函数签名,而不能有具体实现。
  • 可以使用 super 和 this 分别调用父类和子类的方法。使用 super(args) 来调用父类的构造函数。
  • 使用 super 调用基类的构造器,使用 this 调用自身其他的构造器,如果没有调用基类的构造器,会隐式调用基类的默认构造器
  • 内部类:OuterClassName.InnerClassName. 内部类可以直接访问其外围对象的成员,而不需要任何特殊条件:OuterClass.this OuterClass.new
  • override 是子类和父类之间的同一个方法的关系。overload 是类中同名函数的不同参数签名对应的不同版本。但是无法通过返回值来重载函数
int x = a.f()

其中,消息是 f(), 对象是 a, 面向对象就是「给对象发消息」

当定义自己的类型的时候,最好事先下面这几个方法:

  1. toString
  2. equals
  3. hashCode
  4. clone

public String toString(): Returns a String representation of the object. It is used, for example, by System.out.print to print an object. The default version of toString is not very useful, so you should override this method whenever you want to provide a String representation of your class objects.
public boolean equals(Object ob): Returns true iff the object (pointed to by “this”) and ob are the same. The default version uses pointer equality; i.e., it returns true only if “this” and “ob” contain the same address. You may want to override this method to provide a more liberal notion of equality. For example, the String class overrides equals so that it returns true for two Strings that contain the same sequence of characters.
public int hashCode(): Returns an integer for this object suitable for use as a hash code (e.g., for use with the Hashtable class defined in javil.util). This method should be overridden whenever the equals method is, so that hashCode returns the same value for two “equal” objects.
protected Object clone(): Returns a copy of this object (note that no constructor is called for the new object). The default version just copies the values of all fields (i.e., a “shallow” copy). That is probably not what you want when your class has fields that contain pointers (i.e., arrays or classes). So in that case you should override the clone method to do a deep copy — clone all pointer fields.
Cloning

To permit your object to be cloned you must declare that your object implements the Cloneable interface. (See the notes on INTERFACES.) For example:

public class List implements Cloneable { 
    private Object items[]; // a pointer field! 
    ... 
} 

If you forget to do this, an attempt to clone will cause the exception CloneNotSupportedException to be thrown.

static block

Java has no implementation of static constructors, but has a static block that can be used to initialize class variables (static variables).
This block will be called when the class is loaded.

static {
    className = "Bicycle";
}

泛型

Java 支持泛型,就像 C++ 里一样,用 <T> 来表示。Java 泛型是使用类型擦除来实现的,所以算是不很完备吧,不过比没有强多了。

TODO: <?> 是什么意思呢?

包和 import

Java 中没有 from x import y 的语法,而只有 import xxx。import 之后会默认使用最后一个名字作为导入的名字,而 Python 中必须依然写完整的路径名。

使用 import 时,默认去 $CLASSPATH 查找对应的包

项目结构

可以参考 maven 的标准结构。

编译

javac MyClass.java # 生成 MyClass.class 等其他一些文件,每个类对应一个 class 文件
java MyClass # 这里没有 class 后缀

.java 文件编译生成 .class 文件,jar 文件是。class 文件的打包

使用 package name; 来声明包,所有的源文件要放在 name 这个文件夹下
可以使用 import package.class 或者 import package.* 来导入包
CLASSPATH 存放 package 的根目录

异常

try{
  // 程序代码
}catch异常类型 1 异常的变量名 1){
  // 程序代码
}catch异常类型 2 异常的变量名 2){
  // 程序代码
}finally{
  // 程序代码
}

Java 中比较坑爹的一点是要求异常 (Checked Exception) 也是函数签名的一部分。如果想要避免在函数定义中加入 throws XXXException 的话,必须把所有抛出的 checked exception
全都捕获,偷懒的话,可以 catch(Exception e)。

从语义上来说,checked exception 是可以恢复的错误,而其他则是运行时错误。

尽量避免在 try…catch…finally 中使用控制转移语句

                +--------+
                | Object |
                +--------+
                    |
              +-----------+
              | Throwable |
              +-----------+
                /         \
               /           \
          +-------+      +-----------+
          | Error |      | Exception |
          +-------+      +-----------+
            /  |  \        / |        \
            \________/    \______/      \
            +------------------+
unchecked    checked    | RuntimeException |
            +------------------+
            /   |    |      \
            \_________________/

                       unchecked

checked 和 unchecked exception, RuntimeException 通常是指 unchecked exception

实际上,对于自己编写的异常类来讲,推荐默认的是继承 RuntimeException,除非有特殊理由才继承 Exception。 C#中没有 Checked Exception 的概念,这种推荐的做法等于是采用了 C#的设计理念:把是否捕获和何时捕获这个问题交给使用者决定,不强制使用者。当然,如果某些情况 下明确提醒捕获更加重要还是可以采用 Checked Exception 的。对于编写一个方法来讲,“是否在方法上声明一个异常”这个问题比“是否采用 Checked Exception”更加重要。

多线程

Java 中使用一个 Runnable 来表示一个可以独立调度的单元。Runnable 需要实现 void run() 函数

class MyRunnable implements Runnable {}
Thread t = new Thread(new Runnable())

AtomicInteger AtomicLong AtomicReference

日期处理

使用 java.util.Date 类来表示一个日期,需要注意的是 Java 的时间戳是毫秒级的,Python 是秒级别的。

如果要格式化打印时间,需要使用:java.text.SimpleDateFormat。需要注意的是,这里使用的格式化方式和 Python 略微不同,不过也很好理解

import java.text.SimpleDateFormat
Date now = new Date();
SimpleDateFormat ft = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("当前时间为:" + ft.format(now));
ft.Parse("2020-05-30 10:38:00") // 解析时间

尤其注意其中,M 表示的是月份,而 m 表示的是分钟,这点和 Python 是反过来的。s 表示秒,S 表示毫秒。

上面的 java.util.Date 有一些历史问题,所以在 java8 中又提供了 java.time 这个 API。

lambda 表达式

(args) -> {statements;}

正则

特别坑的一点是:Java 中没有 raw string,也就是正则表达式中的 \ 都必须写成 \\

Pattern pattern = Pattern.compile(Pattern.quote("\r\n?|\n"));

注解

Java 的注解和 Python 的装饰器长得非常像,但是确实完全不一样的东西。注解有三种类型:

  1. 给编译器的提示。比如说忽略某些错误。
  2. 编译时或者部署时的处理。一些软件可以通过解析注解来生成文档等。
  3. 运行时处理。

Annotation 更像是一个规范化的注释。而不会像 Python 装饰器那样改变程序的行为。

内置注解

@Deprecated,已废弃的方法
@Override,覆盖方法
@SuppressWranings,忽略错误

JDK 选择

Oracle Java is more stable than OpenJDK, they are baisclly the same, you can think oracle java as a bugfix version of OpenJDK

参考

  1. https://softwareengineering.stackexchange.com/questions/162643/why-is-clean-code-suggesting-avoiding-protected-variables
  2. https://www.baeldung.com/java-checked-unchecked-exceptions
  3. https://stackoverflow.com/questions/1256667/raw-strings-in-java-for-regex-in-particular
  4. http://maven.apache.org/guides/introduction/introduction-to-the-standard-directory-layout.html
  5. https://docs.oracle.com/javase/tutorial/java/annotations/
  6. https://stackoverflow.com/questions/513832/how-do-i-compare-strings-in-java
  7. https://learnxinyminutes.com
  8. Java for c++ programmers http://pages.cs.wisc.edu/~hasti/cs368/JavaTutorial/
  9. Effecitve Java
  10. https://stackoverflow.com/questions/40480/is-java-pass-by-reference-or-pass-by-value
  11. https://stackoverflow.com/questions/12757841/are-arrays-passed-by-value-or-passed-by-reference-in-java

lua

lua_pcall 是使用 c 中的 setjmp 实现的, 对应在lua 中的函数就是 pcall

pcall/error 大概就相当于其他语言中的 try-catch /throw了

local ok, errorobject = pcall(function() 
    --here goes the protected code 
    ... 
end) 

if not ok then 
    --here goes the error handling code 
    --(errorobject has more information about the error) 
    ... 
end 

协程

对称协程只有一个关键字: transfer, 类似于 goto 语句, 把控制权移交给其他的任意一个协程; 而非对称协程一般有两个关键字:resume 和 yield, 使用 resume打开一个协程, 然后在这个协程中使用 yield 返回.

学习lua可以获得

  • 怎样实现一门语言,编译原理,离散数学
  • lua本身
  • 虚拟机,jit
  • C语言能力的增强

lua 的标准库补充 Penlight

Programming in Lua

Lua编程手册

Lua Unofficial FAQ

知乎上 lua 相关的问题

openresty

  1. The *bylua modules that tweak the nginx behaviour (for ex the rewriteby_lua that is the lua equivalent of nginx http rewrite) module are always run after the standard nginx modules.
  2. The choice of *bylua module to use largely depends upon the problem that you are trying to solve. For example the initbylua module is used for initialization operations where as accessbylua may be used to implement access policies for a location block. Personally among the various directives I find most use for contentby_lua.

From http://www.staticshin.com/programming/definitely-an-open-resty-guide/

http://www.londonlua.org/scriptingnginxwith_lua/slides.html

Lua can access nginx at different phase, the most important directives are:

Rewritebylua
Accessbylua
Contentbylua
Initbylua
Setbylua

Rememeber to set luacodecache when developing

Use ngx.location.capture to issue a sub-request to other locations in nginx

ngx.ctx is a lua table to store data with a lifetime

lua coroutine

Lua 的协程是非对称的协程也就是 resume 和 yeild 相当于调用和返回

Python 的协程是对称的协程, 相当于 goto.

Lua中携程相关的函数都放在coroutine包中
coroutine.create(function) 返回一个thread类型的值表示一个协程,并且处于suspend状态。
resume(co, params…) 执行一个协程,并且能够传递参数,返回运行的状态的函数yield返回的结果