python find pid by listen port

Codes

import sys
import subprocess
import shlex
import re

def find_pid_by_listen_port(port):
    if sys.platform == 'win32':
        output = subprocess.check_output('netstat -a -n -o', universal_newlines=True)
        match = re.search('.*:{0} +.* +.+ +[0-9]+'.format(port),output)
        if match:
            return shlex.split(match.group(0))[-1]
        else:
            return None
    else:
        raise Exception('not support platform ' + sys.platform)
    
pid = find_pid_by_listen_port(9160)
print(pid)

python download Cassandra and start up in Windows

Description

This program will kill existing Cassandra before deploy new one.

Codes

import os
import shutil
import sys
import math
import urllib.request
import subprocess
import shlex
import signal
import time
import re
from datetime import date

def http_download(download_url, download_file):
    with urllib.request.urlopen(download_url) as f:
        with open(download_file,'wb') as target:
            filesize = int(f.getheader('Content-Length'))
            wrotesize = 0
            while True:
                if wrotesize == int(filesize):
                    break
                wrotesize += target.write(f.read(1024))
                download_percent = math.ceil((wrotesize/filesize)*100)
                print('\rDownload {0} to {1} ... {2}%'.format(download_url,download_file,download_percent),end='')
    print()

def find_pid_by_listen_port(port):
    if sys.platform == 'win32':
        output = subprocess.check_output('netstat -a -n -o', universal_newlines=True)
        match = re.search('.*:{0} +.* +.+ +[0-9]+'.format(port),output)
        if match:
            return shlex.split(match.group(0))[-1]
        else:
            return None
    else:
        raise Exception('not support platform ' + sys.platform)
    
    
download_url = 'http://ftp.tc.edu.tw/pub/Apache/cassandra/2.0.4/apache-cassandra-2.0.4-bin.tar.gz'
download_file = 'd:/apache-cassandra-2.0.4-bin.tar.gz'
unzip_folder = 'd:/deploy/work/{0}/apache-cassandra-2.0.4-bin'.format(date.today().isoformat())
execute_folder = unzip_folder + '/apache-cassandra-2.0.4'
execute_path = execute_folder + '/bin/cassandra.bat'
executable = 'start cmd /c ' + execute_path
executable_argv = shlex.split(executable)
pid = find_pid_by_listen_port(9160)
if pid:
    print('kill process id',pid)
    os.kill(int(pid),signal.SIGTERM)
if os.path.exists('d:/deploy'):
    print('Delete d:/deploy')
    shutil.rmtree('d:/deploy')
http_download(download_url, download_file)
os.makedirs(unzip_folder)
print('unzip {0} to {1}'.format(download_file, unzip_folder))
shutil.unpack_archive(download_file, unzip_folder)
print('Execute',execute_path)
subprocess.Popen(executable_argv,shell=True)
    

python download text percent progress

Description

想學一下 Maven download 時候的 text percent progress

Codes

import sys
import math
import urllib.request

def http_download(download_url, download_file):
    with urllib.request.urlopen(download_url) as f:
        with open(download_file,'wb') as target:
            filesize = int(f.getheader('Content-Length'))
            wrotesize = 0
            while True:
                if wrotesize == int(filesize):
                    break
                wrotesize += target.write(f.read(1024))
                download_percent = math.ceil((wrotesize/filesize)*100)
                print('\rDownload {0} to {1} ... {2}%'.format(download_url,download_file,download_percent),end='')

if len(sys.argv) < 3:
    print('Usage: {0} download_url download_file'.format(sys.argv[0]))
else:    
    download_url = sys.argv[1]
    download_file = sys.argv[2]
    http_download(download_url, download_file)

使用方式

c:\workspace_python>python test.py http://ftp.tc.edu.tw/pub/Apache/cassandra/2.0.4/apache-cassandra-2.0.4-bin.tar.gz d:/apache-cassandra-2.0.4-bin.tar.gz

python http download cassandra

Reference

http://www.apache.org/dyn/closer.cgi?path=/cassandra/2.0.4/apache-cassandra-2.0.4-bin.tar.gz
http://docs.python.org/3/library/urllib.request.html#module-urllib.request

Description

用 urllib 下載 cassandra 到本地

Codes

簡單的方式

import urllib.request

with urllib.request.urlopen('http://ftp.tc.edu.tw/pub/Apache/cassandra/2.0.4/apache-cassandra-2.0.4-bin.tar.gz') as f:
    with open('d:/apache-cassandra-2.0.4-bin.tar.gz','wb') as target:
        target.write(f.read())

如果想要看進度可以這樣

import urllib.request

with urllib.request.urlopen('http://ftp.tc.edu.tw/pub/Apache/cassandra/2.0.4/apache-cassandra-2.0.4-bin.tar.gz') as f:
    with open('d:/apache-cassandra-2.0.4-bin.tar.gz','wb') as target:
        filesize = f.getheader('Content-Length')
        wrotesize = 0
        while True:
            if wrotesize == int(filesize):
                break
            wrotesize += target.write(f.read(1024))
            print('download...',wrotesize,'of',filesize)
            

想從公司內的 Maven download 還需要認證

import urllib.request

pwdmgr = urllib.request.HTTPPasswordMgrWithDefaultRealm()
pwdmgr.add_password(None,'http://mvn.company.site','myid','mypw')
auth_handler = urllib.request.HTTPBasicAuthHandler(pwdmgr)
opener = urllib.request.build_opener(auth_handler)
urllib.request.install_opener(opener)
with urllib.request.urlopen('http://mvn.company.site/downloadfile.zip') as f:
    with open('d:/downloadfile.zip','wb') as target:
        target.write(f.read())

後記

還不太清楚 HTTPBasicAuthHandler 的 realm 要怎麼指定正確, 
使用 Maven response 的 realm 也不行.
只好先用 HTTPPasswordMgrWithDefaultRealm 了...

python compress/decompress file

Reference

http://docs.python.org/3/library/shutil.html#module-shutil

Description

把 d:/nginx-1.4.3 壓縮到 d:/ziptarget/test.zip
再把 d:/ziptarget/test.zip 解壓縮到 d:/unziptarget/

Codes (適合 python 3.2 以上)

import shutil

unzipfile_folder = 'd:/unziptarget'
archive_name = 'd:/ziptarget/test'
root_dir = 'd:/nginx-1.4.3'
archive_path = shutil.make_archive(archive_name, 'zip', root_dir)
shutil.unpack_archive(archive_path, unzipfile_folder)

python 的 iterator 與 (神奇的) yield (iterator generator)

Reference

http://docs.python.org/3/tutorial/classes.html#iterators
http://docs.python.org/3/tutorial/classes.html#generators

Iterator

這段程式

for i in range(5):
    print(i)

執行結果為

0
1
2
3
4

可以執行的原因是 range 回傳了一個可以 iterate 的物件,
讓 python 可以呼叫 "next" 取得下一個值.
如果想要自己實作一個能用 for iterate 的物件時,
只需要實作 __iter__ 與 __next__ 這兩個 function,
for 迴圈執行時 python 會自動執行這兩個 function.
我們只要在 __next__ 呼教時維持好物建的狀態即可.
通知 for 迴圈停止的方式是丟出一個 StopIteration 的 error


class NumberIterator:
    def __init__(self, max):
        self.max = max
        self.current = 0
    def __iter__(self):
        print('iter is called. max=',self.max)
        return self
    def __next__(self):
        print('next is called, current=', self.current, ', max=', self.max)
        if ( self.current == self.max ):
            raise StopIteration
        self.current += 1
        return self.current
        
it = NumberIterator(5)
# "for" statement will call iter() of NumberIterator
# so 'iter is called. max=' will be printed
for i in it:
    print(i) #print 1 ~ 5

# there is no "for" statement, so 'iter is called. max=' won't be printed
it = NumberIterator(4)
print(next(it))
print(next(it))
print(next(it))
print(next(it))
print(next(it)) #StopIteration

執行結果

d:\workspace_python>python test.py
iter is called. max= 5
next is called, current= 0 , max= 5
1
next is called, current= 1 , max= 5
2
next is called, current= 2 , max= 5
3
next is called, current= 3 , max= 5
4
next is called, current= 4 , max= 5
5
next is called, current= 5 , max= 5
next is called, current= 0 , max= 4
1
next is called, current= 1 , max= 4
2
next is called, current= 2 , max= 4
3
next is called, current= 3 , max= 4
4
next is called, current= 4 , max= 4
Traceback (most recent call last):
  File "test.py", line 27, in 
    print(next(it)) #StopIteration
  File "test.py", line 11, in __next__
    raise StopIteration
StopIteration

yield (iterator generator)

神奇的 yield, 這個 statement 是出現在 function 裡面, 
讓程式先回傳, 但 function 內的狀態保持不變, 
等下次程式的 __next__ 被呼叫時, 程式會從 yield 之後的碼繼續執行.
def test_yield():
    i = 0
    print('before the first yield',i)
    yield i
    print('after the first yield',i)
    i += 1
    print('before the second yield',i)
    yield i
    print('after the second yield',i)
    i += 1
    for i in range(3):
        print('before yield in for stmt')
        yield i**2
        print('after yield in for stmt')
    
for i in test_yield():
    print(i)

執行結果
d:\workspace_python>python test.py
before the first yield 0
0
after the first yield 0
before the second yield 1
1
after the second yield 1
before yield in for stmt
0
after yield in for stmt
before yield in for stmt
1
after yield in for stmt
before yield in for stmt
4
after yield in for stmt

JDK6 to JDK7 => Basic IO

Reference

讀寫小檔案

如果檔案比較小可以使用 Files 提供的 read/write.
package test;

import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Arrays;
import java.util.List;

public class TestIO {

    public static void main(String[] params) throws IOException {
        Charset cs = Charset.forName("utf-8");
        Path path = Paths.get("d:/", "test.txt");
        List lines = Arrays.asList("line1", "line2", "line3");
        Files.write(path, lines, cs, StandardOpenOption.TRUNCATE_EXISTING);
        List readLines = Files.readAllLines(path, cs);
        System.out.println(readLines);
    }

}

讀寫大檔案

如果是要讀寫大的檔案, 可以用 Files 的 newBufferedReader/newBufferedWriter/newByteChannel
package test;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.UUID;

public class TestIO {

    public static void main(String[] params) throws IOException {
        Charset cs = Charset.forName("utf-8");
        Path path = Paths.get("d:/", "test.txt");
        int wLineCnt = 25000000;
        try (
            BufferedWriter writer = Files.newBufferedWriter(
                      path, cs, StandardOpenOption.TRUNCATE_EXISTING)) {
            for ( int i = 0; i < wLineCnt; i++ ) {
                writer.write(i + ":" + UUID.randomUUID().toString() + "\n");
            }
            writer.flush();            
        }
        try (BufferedReader reader = Files.newBufferedReader(path, cs)) {
            int rLineCnt = 0;
            String line = null;
            while ((line = reader.readLine()) != null) {
                int num = Integer.valueOf(line.substring(0, line.indexOf(":")));
                if ( rLineCnt++ != num ) {
                    throw new RuntimeException(
                                "not equal! " + num + "," + rLineCnt);
                }
            }
            if ( rLineCnt != wLineCnt ) {
                throw new RuntimeException(
                           "not equal! " + wLineCnt + "," + rLineCnt);
            }
        }
        System.out.println("done");
    }

}

取根目錄

package test;

import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;

public class TestIO {

    public static void main(String[] params) throws IOException {
        for (Path path: FileSystems.getDefault().getRootDirectories()) {
            System.out.println(path); //列出 C:/ & D:/ ..etc
        }
    }
    
}


建資料夾

package test;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;

public class TestIO {

    public static void main(String[] params) throws IOException {
        Files.createDirectories(Paths.get("d:/test1/test2/test3/test4"));
    }
    
}

Iterate資料夾

JDK 7提供新的 interface 讓你 iterate 資料夾: DirectoryStream.
在 API 上說這個 interface 比較 scalable, 可以用來 iterate 很大的 folder.
上網查有人說就的 File#listFiles 會把所有的 folder 載入會佔用比較多的記憶體.
要省記憶體應該要讓資料夾下有上百萬或上千萬個資料夾或檔案才有感覺吧.
我測一百萬個資料夾也沒甚麼差.
後來覺得關鍵是 API 這句話:
The iterator is weakly consistent. It is thread safe but does not freeze the directory while iterating, so it may (or may not) reflect updates to the directory that occur after the DirectoryStream is created.
在資料夾下檔案或資料夾數量很大時, 原本的 File 仍須要給 API client 精準的資訊.
scale 愈大, "精準"的成本就愈大.
DirectoryStream 選擇一開始就告訴你它不準, 所以贏在起跑點, 可以用較少的資源取資料.
等你真的要處理某個檔案再動作就行了.
我覺得這種 interface 更有利於硬碟在遠端的 case. 硬碟不在本機,
要求 File 物件保證整個資料夾資訊要對太難, 這時候 DirectoryStream 就可以處理很好.
要注意 DirectoryStream 用完要關掉, 可以在 try () 裡面宣告.

package test;

import java.io.File;
import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class TestIO {

    public static void main(String[] params) throws IOException {
        long start = System.currentTimeMillis();
        createFolders();
        System.out.println("spend " + (System.currentTimeMillis() - start) 
                    + " millis to create folders");
        
        start = System.currentTimeMillis();
        System.out.println(listFoldersNIO(Paths.get("d:/test")));
        System.out.println("spend " + (System.currentTimeMillis() - start) 
                    + " millis to list folders (NIO)");

        start = System.currentTimeMillis();
        System.out.println(listFoldersIO(Paths.get("d:/test").toFile()));
        System.out.println("spend " + (System.currentTimeMillis() - start) 
                    + " millis to list folders (IO)");
    }

    private static void createFolders() throws IOException {
        for ( int i = 0; i < 1000000; i++ ) {
            Files.createDirectories(Paths.get("d:/test/test" + i));
            System.out.println(i);
        }
        System.out.println("done");
    }

    private static int listFoldersIO(File folder) {
        int cnt = 0;
        for ( String child: folder.list() ) {
            new File(child);
            cnt++;
        }
        return cnt;
    }
    
    private static int listFoldersNIO(Path path) throws IOException {
        int cnt = 0;
        try ( DirectoryStream dirStream = Files.newDirectoryStream(path) ) {
            for ( Path child: dirStream ) {
                child.toFile();
                cnt++;
            }
        }
        return cnt;
    }
    
}

指定 pattern 找資料夾下的檔名

JDK 7提供新的概念叫 Glob, 蠻好理解的.
不過其中有個 ** 的語法說明 "works like * but crosses directory boundaries" 讓我有錯誤的期望以為只要 Files.newDirectoryStream(p, "**/*.{java,jar}") 就可以列舉全部的檔案, 測試結果不行..
可能要用 Files#walkFileTree. 我還沒看 walkFileTree 有甚麼特殊的地方,
但 API 沒特別好用的話我自己作就好了為什麼要提供 walkFileTree 呢...?
(在下面的段落練習了 walkFileTree, 比起自己寫用 walkFileTree 簡潔多了 XD)

package test;

import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class TestIO {

    public static void main(String[] params) throws IOException {
        listAll(Paths.get("d:\\Isaac"), "*.{java,jar}");
    }

    private static void listAll(Path p, String globPattern) throws IOException {
        try (
            DirectoryStream<Path> s = Files.newDirectoryStream(p, globPattern)
            ) {
            for ( Path c: s ) {
                System.out.println(c);
            }
        }
        try (DirectoryStream<Path> s = Files.newDirectoryStream(p)) {
            for ( Path c: s ) {
                if ( Files.isDirectory(c, LinkOption.NOFOLLOW_LINKS) ) {
                    listAll(c, globPattern);
                }
            }
        }
    }
    
}

搜尋 activemq source code 中有關鍵字 "Queue" 的檔案

自己實作 DirectoryStream.Filter 的時候不能方便的使用 glob 有點可惜..

package test;

import java.io.BufferedReader;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.Paths;

public class TestIO {

    public static void main(String[] params) throws IOException {
        listAll(Paths.get("d:\\activemq-parent-5.8.0-source-release"), "Queue");
    }

    private static void listAll(Path p, final String containTxt) 
    throws IOException {
        DirectoryStream.Filter<Path> f = new DirectoryStream.Filter<Path>() {
            @Override
            public boolean accept(Path entry) throws IOException {
                if ( Files.isDirectory(entry, LinkOption.NOFOLLOW_LINKS) 
                               || !entry.toString().endsWith(".java") ) {
                    return false;
                }
                try (BufferedReader reader = Files.newBufferedReader(
                                      entry, Charset.forName("UTF8"))) {
                    String line = null;
                    while ( (line = reader.readLine()) != null ) {
                        if ( line.contains(containTxt) ) {
                            return true;
                        }
                    }
                }
                return false;
            }
        };
        try (DirectoryStream<Path> s = Files.newDirectoryStream(p, f)) {
            for ( Path c: s ) {
                System.out.println("match " + c);
            }
        }
        try (DirectoryStream<Path> s = Files.newDirectoryStream(p)) {
            for ( Path c: s ) {
                if ( Files.isDirectory(c, LinkOption.NOFOLLOW_LINKS) ) {
                    listAll(c, containTxt);
                }
            }
        }
    }
    
}

用 Files#walkFileTree + FileVisitor 列出所有資料夾下的 java 與 jar 檔

package test;

import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;

public class TestIO {

    public static void main(String[] params) throws IOException {
        Path p = Paths.get("d:/Isaac");
        FileVisitor<Path> f = new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult visitFile(Path file,BasicFileAttributes attrs)                throws IOException {
                String filepath = file.toString();
                if ( filepath.endsWith(".java") || filepath.endsWith(".jar") ) {
                    System.out.println(filepath);
                }
                return super.visitFile(file, attrs);
            }
        };
        Files.walkFileTree(p, f);
    }

}

搜尋 activemq source code 中有關鍵字 "Queue" 的檔案 (用 glob)

前面才提到 "自己實作 FileVisitor 的話不方便用 glob 很可惜" 而已, 就發現其實有提供. 那就是 PathMatcher.
package test;

import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;

public class TestIO {

    public static void main(String[] params) throws Throwable {
        final PathMatcher matcher = 
                   FileSystems.getDefault().getPathMatcher("glob:*.{java,jar}");
        Files.walkFileTree(Paths.get("d:/Isaac"), new SimpleFileVisitor<Path>(){
            @Override
            public FileVisitResult visitFile(Path file,
                    BasicFileAttributes attrs) throws IOException {
                if ( matcher.matches(file.getFileName()) ) {
                    System.out.println("match " + file);
                }
                return super.visitFile(file, attrs);
            }
        });
    }

}

監聽一個 path 是否備新增/刪除/修改

package test;

import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class TestIO {

    public static void main(String[] params) throws Throwable {
        ExecutorService e = Executors.newSingleThreadExecutor();
        Path path = Paths.get("d:/Isaac");
        try (final WatchService w = FileSystems.getDefault().newWatchService()) {
            path.register(w, StandardWatchEventKinds.ENTRY_CREATE, 
                             StandardWatchEventKinds.ENTRY_DELETE, 
                             StandardWatchEventKinds.ENTRY_MODIFY);
            e.execute(new Runnable(){
                @Override
                public void run() {
                    while (true) {
                        try {
                            WatchKey k = w.take();
                            for ( WatchEvent<?> e: k.pollEvents() ) {
                                @SuppressWarnings("unchecked")
                                WatchEvent<Path> pathEvent = (WatchEvent<Path>)e;
                                Path path = pathEvent.context();
                                System.out.println(pathEvent.kind() + ":"  path);
                                if (!k.reset()) {
                                    break;
                                }
                            }
                        } catch (Throwable ex) {
                            ex.printStackTrace();
                        }                    
                    }
                }});
            TimeUnit.DAYS.sleep(1);
        }   
    }
}


JDK6 to JDK7 => API Enhancements

Description

打算把系統的執行環境從 JDK6 升級到 JDK7, 整理一下有哪些可用.
比較期待的部分是語法, NIO2, G1. 這頁是整理語法的部分.

Reference

Points

  1. Strings in switch Statements
    package test;
    
    public class TestStringSwitch {
    
     public static void main(String[] params) {
      inJDK7("abc");
      inJDK6("abc");
     }
     
     private static void inJDK7(String txt) {
      switch (txt) {
      case "a":
       System.out.println("a");
       break;
      case "b":
       System.out.println("b");
       break;
      case "abc":
       System.out.println("abc");
       break;
      default:
        System.out.println("not match");
      }
     }
     
     private static void inJDK6(String txt) {
      if ( "a".equals(txt) ) {
       System.out.println("a");
      } else if ( "b".equals(txt) ) {
       System.out.println("b");
      } else if ( "abc".equals(txt) ) {
       System.out.println("abc");
      } else {
       System.out.println("not match");
      }
     }
     
     
    }
    
  2. Type Inference for Generic Instance Creation
    package test;
    
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    public class TestGenericDeclare {
    
      public static void main(String[] params) {
        // JDK6
        Map> map0 = new HashMap>();
        
        // JDK7
        Map> map1 = new HashMap<>();
      }
      
    }
    
  3. The try-with-resources Statement
    試用後可以看到這個語法可以讓程式簡化很多.
    package test;
    
    import java.io.BufferedReader;
    import java.io.File;
    import java.io.FileReader;
    import java.io.IOException;
    import java.io.Reader;
    
    public class TestAutoClosable {
    
        public static void main(String[] params) throws IOException {
            File file1 = new File("d:/test.txt");
            File file2 = new File("d:/test2.txt");
            inJDK6(file1,file2);
            inJDK7(file1,file2);
        }
    
        private static void inJDK7(File file1, File file2) throws IOException {
            try (
                FileReader fileReader1 = new FileReader(file1);
                FileReader fileReader2 = new FileReader(file2);
                BufferedReader reader1 = new MyBufferedReader("7 reader1", fileReader1);
                BufferedReader reader2 = new MyBufferedReader("7 reader2", fileReader2);
            ) {
                System.out.println(reader1.readLine());
                System.out.println(reader2.readLine());
            }
        }
        
        private static void inJDK6(File file1, File file2) {
            BufferedReader reader1 = null;
            BufferedReader reader2 = null;
            try {
                reader1 = new MyBufferedReader("JDK6 style reader1", new FileReader(file1));
                reader2 = new MyBufferedReader("JDK6 style reader2", new FileReader(file2));
                System.out.println(reader1.readLine());
                System.out.println(reader2.readLine());
            } catch(Throwable ex) {
                ex.printStackTrace();
            } finally {
                if ( reader1 != null ) {
                    try {
                        reader1.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if ( reader2 != null ) {
                    try {
                        reader2.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        
        private static class MyBufferedReader extends BufferedReader {
            private final String name;
            public MyBufferedReader(String name, Reader reader) {
                super(reader);
                this.name = name;
            }
            @Override
            public void close() throws IOException {
                super.close();
                System.out.println("close " + name);
            }
        }
        
    }
    
  4. Catching Multiple Exception Types and Rethrowing Exceptions with Improved Type Checking
    有時對於不特別處理的 checked exception 動作也只是往外丟, 在 JDK7 之前需要在 method 上特別註明這個 checked exception, 但 JDK7 之後, 沒有特別需要的, 直接往外丟的 exception 就算沒有在 method 上宣告, compiler 也不會判你錯.
    package test;
    
    public class TestHandleMoreThanOneException {
    
        public static void main(String[] params) throws Exception {
            rethrowEx("3");
        }
    
        private static void rethrowEx(String exNum) throws Ex1,Ex2 {
            try {
                throwEx(exNum);
            } catch (Ex1 | Ex2 e) {
                throw e;
            } catch (Exception e) {
                throw e;
            }
        }
        
        private static void throwEx(String exNum) throws Ex1, Ex2 {
            switch (exNum) {
            case "1":
                throw new Ex1();
            case "2":
                throw new Ex2();
            default:
                System.out.println("not match");
            }
        }
        
        public static class Ex1 extends Exception {}
        public static class Ex2 extends Exception {}
        
    }
    


python 處理 json

Description

現在很常用 json 當成儲存資料的格式, 用 java 的話需要導入 library.
python 則是內建 json 的 module, 只要操作原生的資料結構就行了. 很方便.

Reference

Codes

>>> import json
>>> m = {'a':1,'b':[1,2,3,4,5,6]}
>>> with open('d:/test.txt','w') as f:
        json.dump(m,f)

        
>>> with open('d:/test.txt') as f:
        f.readline()

        
'{"a": 1, "b": [1, 2, 3, 4, 5, 6]}'
>>> with open('d:/test.txt') as f:
        x = json.load(f)

        
>>> x
{'a': 1, 'b': [1, 2, 3, 4, 5, 6]}
>>> 

File content

{"a": 1, "b": [1, 2, 3, 4, 5, 6]}

python 的 main method

之前看 python 文章好像都沒提到怎麼寫 main method, 似乎這件事情很自然, 不過我卻不知道.
看了 6.1.1. Executing modules as scripts 裡面有寫
"the code in the module will be executed, just as if you imported it, but with the __name__ set to "__main__". That means that by adding this code at the end of your module:"
if __name__ == "__main__":
    import sys
    fib(int(sys.argv[1]))
試著作一次

  1. C:\Python33\say.py
    def say(words):
            print(words)
    
    if __name__ == '__main__':
            import sys
            say(sys.argv[1])
            print(__name__,sys.argv[0])
    
    
  2. Output
    C:\Python33>python.exe say.py hello
    hello
    __main__ say.py

這樣就可以了, 很簡單..

處理正在 iterate 的 dictionary


今天練習 Python 的時候發現 5.6. Looping Techniques 有個提示
"To change a sequence you are iterating over while inside the loop (for example to duplicate certain items), it is recommended that you first make a copy. Looping over a sequence does not implicitly make a copy."

照著文件上的程式輸入
>>> words = ['a','b','cc']
>>> for w in words:
    if len(w)>1:
        print('insert',w)
        words.insert(0,w)

執行之後就出現
insert cc
insert cc
insert cc
insert cc
insert cc
insert cc
insert cc
insertTraceback (most recent call last):
  File "<pyshell#208>", line 3, in <module>
    print('insert',w)
  File "C:\Python33\lib\idlelib\PyShell.py", line 1318, in write
    return self.shell.write(s, self.tags)
KeyboardInterrupt

要 Ctrl+C 才能停下來.
這時候檢查一下哪打錯了, 原來我打
for w in words:

但文件上是
for w in words[:]:

修正之後果然就正常了.
原來 words[:] 是 words 的 copy.
試一下
>>> words is words
True
>>> words is words[:]
False
果然沒錯.

在 Java 也是一樣, 如果要改變正在 iterate 的 list 也會有問題
package test;

import java.util.Arrays;
import java.util.List;

public class TestIter {

    public static void main(String[] params) {
        List words = Arrays.asList("a","b","cc");
        for (String w: words) {
            if (w.length() > 1) {
                System.out.println("insert " + w);
                words.add(w);
            }
        }
    }
    
}
會報錯
insert cc
Exception in thread "main" java.lang.UnsupportedOperationException
    at java.util.AbstractList.add(Unknown Source)
    at java.util.AbstractList.add(Unknown Source)
    at test.TestIter.main(TestIter.java:13)

要改成
package test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class TestIter {

    public static void main(String[] params) {
        List words = Arrays.asList("a","b","cc");
        List copyWords = new ArrayList(words);
        for (String w: words) {
            if (w.length() > 1) {
                System.out.println("insert " + w);
                copyWords.add(0,w);
            }
        }
        System.out.println(words);
        System.out.println(copyWords);
    }
    
}

寫了 Python 跟 Java 這兩段程式, 感覺有微妙的差異...

巴斯卡三角形 in Python

Source Code

def print_pascal(row_cnt):
    pascal = []
    for row in range(row_cnt):
        cols = []
        for col in range(row+1):
            if row == 0:
                cols.append(1)
            elif col == 0 or col == row:
                cols.append(1)
            else:
                val = pascal[row-1][col-1]+pascal[row-1][col];
                cols.append(val)
        pascal.append(cols)
    for row in pascal:
        for i in range(row_cnt-len(row)):
            print('%5s' %'',end='')
        for col in row:
            print('%5d' %col,end='%5s' %'')
        print()

Usage


>>> print_pascal(6)
                             1     
                        1         1     
                   1         2         1     
              1         3         3         1     
         1         4         6         4         1     
    1         5        10        10         5         1  

Nginx Practice in Windows - 6 simple proxy server

Description

使用 nginx 的一個主因是要支援 web application 放在別台機器上, 將相關的 request 導過去.

Reference

Practice

  1. prepare files
    D:\nginx-1.4.3\nginx.exe
    D:\nginx-1.4.3\conf\nginx.conf
    D:\nginx-1.4.3\testhtml\testhtml.html
    D:\nginx-1.4.3\internal\internal.html
    D:\nginx-1.4.3\internal\service\service.html
    D:\nginx-1.4.3\internal\service\serviceA\serviceA.html
    
  2. setup nginx.conf
    (注意 proxy_pass 的 127.0.0.1 要對應到 port 12345 的 server_name 127.0.0.1, 在練習的時候發現如果設定成 proxy_pass http://localhost:12345 那 nginx 就很容易遇到錯誤: 68 upstream timed out (10060: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond) while connecting to upstream, client: 127.0.0.1, server: 127.0.0.1, request: "GET /service/serviceA/serviceA7.html HTTP/1.1)
        server {
            listen       8080;
            server_name  127.0.0.1;
            root         testhtml;
            location / {
                index testhtml.html;
            }
            location /service/ {
                proxy_pass http://127.0.0.1:12345;
            }
        } 
        server {
            listen       12345;
            server_name  127.0.0.1;
            root         internal;
            location / {
                index  internal.html;
            }        
            location /service/ {
                index  service.html;
            }
        } 
    
  3. run "nginx -s reload"
  4. run "telnet 127.0.0.1 8080"
  5. send request
    GET /service/ HTTP/1.1
    Host: 127.0.0.1:8080
    Connection: close
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
    Accept: */*
    Accept-Encoding: gzip,deflate,sdch
    Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4
    
  6. get response
    HTTP/1.1 200 OK
    Server: nginx/1.4.3
    Date: Wed, 30 Oct 2013 23:25:39 GMT
    Content-Type: text/html
    Content-Length: 20
    Connection: close
    Last-Modified: Wed, 30 Oct 2013 23:25:34 GMT
    ETag: "5271956e-14"
    Accept-Ranges: bytes
    
    This is service.html
  7. 如果把 debug log 打開, 就是把 nginx.conf 上的"#error_log  logs/error.log  debug;" 注解拿掉, 就可以看到 error.log 裡面出現下面這些 log, 看似 nginx 在 proxy_pass 的時候改寫了 http request 然後重新發一次. (不過說起來 proxy 本來就是在作這種事情就是了)

    2013/10/31 07:25:39 [debug] 4348#6716: *139 http script copy: "Host: "
    2013/10/31 07:25:39 [debug] 4348#6716: *139 http script var: "127.0.0.1:12345"
    2013/10/31 07:25:39 [debug] 4348#6716: *139 http script copy: "
    "
    2013/10/31 07:25:39 [debug] 4348#6716: *139 http script copy: "Connection: close
    "
    2013/10/31 07:25:39 [debug] 4348#6716: *139 http script copy: ""
    2013/10/31 07:25:39 [debug] 4348#6716: *139 http script copy: ""
    2013/10/31 07:25:39 [debug] 4348#6716: *139 http proxy header: "User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36"
    2013/10/31 07:25:39 [debug] 4348#6716: *139 http proxy header: "Accept: */*"
    2013/10/31 07:25:39 [debug] 4348#6716: *139 http proxy header: "Accept-Encoding: gzip,deflate,sdch"
    2013/10/31 07:25:39 [debug] 4348#6716: *139 http proxy header: "Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4"
    2013/10/31 07:25:39 [debug] 4348#6716: *139 http proxy header:
    "GET /service/ HTTP/1.0
    Host: 127.0.0.1:12345
    Connection: close
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
    Accept: */*
    Accept-Encoding: gzip,deflate,sdch
    Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4
    "
  8. send request
    GET /service/serviceA/serviceA.html HTTP/1.1
    Host: 127.0.0.1:8080
    Connection: close
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
    Accept: */*
    Accept-Encoding: gzip,deflate,sdch
    Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4
    
  9. get response
    HTTP/1.1 200 OK
    Server: nginx/1.4.3
    Date: Wed, 30 Oct 2013 23:33:12 GMT
    Content-Type: text/html
    Content-Length: 21
    Connection: close
    Last-Modified: Wed, 30 Oct 2013 23:33:08 GMT
    ETag: "52719734-15"
    Accept-Ranges: bytes
    
    This is serviceA.html

Nginx Practice in Windows - 5 static content

Description

使用 nginx 主要目的之一就是要存放靜態檔如 html 或圖片

Reference

Practice

設定讓 "GET /" 的 request 首頁導向 testhtml/data/www/index.html

  1. prepare folders
    D:\nginx-1.4.3\nginx.exe
    D:\nginx-1.4.3\conf\nginx.conf
    D:\nginx-1.4.3\testhtml\data\www\index.html
    D:\nginx-1.4.3\testhtml\data\images
    
  2. edit index.html
    Hello!, This is testhtml\data\www\index.html
  3. setup nginx.conf
        server {
            listen       8080;
            server_name  127.0.0.1;
            location / {
                root   testhtml/data/www;
                index  index.html;
            }
        } 
    
  4. run "nginx -s reload"
  5. run "telnet 127.0.0.1 8080"
  6. send request
    GET / HTTP/1.1
    Host: server1:8080
    Connection: close
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
    Accept: */*
    Accept-Encoding: gzip,deflate,sdch
    Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4
    
  7. get response
    HTTP/1.1 200 OK
    Server: nginx/1.4.3
    Date: Sun, 27 Oct 2013 23:22:23 GMT
    Content-Type: text/html
    Content-Length: 44
    Last-Modified: Sun, 27 Oct 2013 23:22:19 GMT
    Connection: close
    ETag: "526da02b-2c"
    Accept-Ranges: bytes
    
    Hello!, This is testhtml\data\www\index.html
    

設定去取 "/" 以外的檔案

  1. prepare files
    D:\nginx-1.4.3\testhtml\data\www\www.html
    D:\nginx-1.4.3\testhtml\data\data.html
    D:\nginx-1.4.3\conf\nginx.conf
    
  2. edit data.html
    Hello!, This is data.html 
  3. edit www.html
    Hello!, This is www.html
    
  4. setup nginx.conf
        server {
            listen       8080;
            server_name  127.0.0.1;
            location / {
                root   testhtml/data/www;
                index  www.html;
            }
            location /data/ {
                root   testhtml;
                index  data.html;
            }
        } 
    
  5. run "nginx -s reload"
  6. run "telnet 127.0.0.1 8080"
  7. send request
    GET / HTTP/1.1
    Host: server1:8080
    Connection: close
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
    Accept: */*
    Accept-Encoding: gzip,deflate,sdch
    Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4
    
  8. get response
    HTTP/1.1 200 OK
    Server: nginx/1.4.3
    Date: Mon, 28 Oct 2013 15:33:05 GMT
    Content-Type: text/html
    Content-Length: 24
    Last-Modified: Sun, 27 Oct 2013 23:32:37 GMT
    Connection: close
    ETag: "526da295-18"
    Accept-Ranges: bytes
    
    Hello!, This is www.html
  9. send request (這個 request 是去要 /data/ 的 resource, 其實也可以對上 "/" 的 location, 不過 nginx 會選擇 prefix 對應最長的 location)
    GET /data/ HTTP/1.1
    Host: server1:8080
    Connection: close
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
    Accept: */*
    Accept-Encoding: gzip,deflate,sdch
    Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4
    
  10. get response
    HTTP/1.1 200 OK
    Server: nginx/1.4.3
    Date: Mon, 28 Oct 2013 15:49:13 GMT
    Content-Type: text/html
    Content-Length: 25
    Last-Modified: Sun, 27 Oct 2013 23:32:15 GMT
    Connection: close
    ETag: "526da27f-19"
    Accept-Ranges: bytes
    
    Hello!, This is data.html
    
  11. send request (這個 request 是去找 "/" 的 index.html)
    GET /data/www/www.html HTTP/1.1
    Host: server1:8080
    Connection: close
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
    Accept: */*
    Accept-Encoding: gzip,deflate,sdch
    Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4
    
  12. get response
    HTTP/1.1 200 OK
    Server: nginx/1.4.3
    Date: Mon, 28 Oct 2013 15:50:23 GMT
    Content-Type: text/html
    Content-Length: 24
    Last-Modified: Sun, 27 Oct 2013 23:32:37 GMT
    Connection: close
    ETag: "526da295-18"
    Accept-Ranges: bytes
    
    Hello!, This is www.html

如果多個 location 使用同樣的 root, 只要在 server 區塊中設定 root 即可

  1. prepare files
    D:\nginx-1.4.3\testhtml\testhtml.html
    D:\nginx-1.4.3\testhtml\data\data.html
    D:\nginx-1.4.3\conf\nginx.conf
    
  2. edit data.html
    Hello!, This is data.html 
  3. edit testhtml.html
    Hello!, This is testhtml.html
    
  4. setup nginx.conf
        server {
            listen       8080;
            server_name  127.0.0.1;
            root   testhtml;
            location / {
                index  testhtml.html;
            }
            location /data/ {
                index  data.html;
            }
        } 
    
  5. run "nginx -s reload"
  6. run "telnet 127.0.0.1 8080"
  7. send request
    GET / HTTP/1.1
    Host: server1:8080
    Connection: close
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
    Accept: */*
    Accept-Encoding: gzip,deflate,sdch
    Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4
    
  8. get response
    HTTP/1.1 200 OK
    Server: nginx/1.4.3
    Date: Mon, 28 Oct 2013 23:51:46 GMT
    Content-Type: text/html
    Content-Length: 29
    Last-Modified: Mon, 28 Oct 2013 23:45:17 GMT
    Connection: close
    ETag: "526ef70d-1d"
    Accept-Ranges: bytes
    
    Hello!, This is testhtml.html
  9. send request
    GET /data/ HTTP/1.1
    Host: server1:8080
    Connection: close
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
    Accept: */*
    Accept-Encoding: gzip,deflate,sdch
    Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4
    
  10. get response
    HTTP/1.1 200 OK
    Server: nginx/1.4.3
    Date: Mon, 28 Oct 2013 23:52:29 GMT
    Content-Type: text/html
    Content-Length: 25
    Last-Modified: Sun, 27 Oct 2013 23:32:15 GMT
    Connection: close
    ETag: "526da27f-19"
    Accept-Ranges: bytes
    
    Hello!, This is data.html

Nginx Practice in Windows - 4 server names

Description

    server {
        listen       127.0.0.1:8080;
        server_name  server1;
        location /test {
            return 200 'server1';
        }
    } 
在這個 server 設定中, server_name 是拿來比對 request header "HOST" 然後決定交由哪個 server 提供服務.
這個 server_name 除了能服務完全相同的 HOST 以外還支援 * (wildcard) 跟 regular expression

Reference

Practice

如果與 HOST 完全一樣, 就取 HOST 完全一樣的 server.

  1. setup nginx.conf (這裡 ~開頭就表示要用 regular expression)
        server {
            listen       127.0.0.1:8080 default;
            server_name  *.server1;
            location /test {
                return 200 '*.server1';
            }
        } 
        server {
            listen       127.0.0.1:8080;
            server_name  server1;
            location /test {
                return 200 'server1';
            }
        } 
        server {
            listen       127.0.0.1:8080;
            server_name  www.*;
            location /test {
                return 200 'www.*';
            }
        } 
        server {
            listen       127.0.0.1:8080;
            server_name  ~.*server1.*;
            location /test {
                return 200 '~.*server1.*';
            }
        } 
    
  2. run "nginx -s reload"
  3. run "telnet 127.0.0.1 8080"
  4. send request
    GET /test HTTP/1.1
    Host: server1:8080
    Connection: close
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
    Accept: */*
    Accept-Encoding: gzip,deflate,sdch
    Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4
    
  5. get response
    HTTP/1.1 200 OK
    Server: nginx/1.4.3
    Date: Wed, 23 Oct 2013 22:36:16 GMT
    Content-Type: application/octet-stream
    Content-Length: 7
    Connection: close
    
    server1
    

一個 server_name 不能指定兩個 *

  1. setup nginx.conf
        server {
            listen       127.0.0.1:8080;
            server_name  *.server1.*;
            location /test {
                return 200 '*.server1';
            }
        } 
    
  2. run "nginx -s reload"
  3. got error message
    nginx: [emerg] invalid server name or wildcard "*.server1.*" on 127.0.0.1:8080
    

當 * 開頭跟 * 結尾的兩個 server_name 都 mapping 到一個 request 時, 用 * 開頭的 server 服務

  1. setup nginx.conf
        server {
            listen       127.0.0.1:8080;
            server_name  *.server1;
            location /test {
                return 200 '*.server1';
            }
        } 
        server {
            listen       127.0.0.1:8080;
            server_name  www.*;
            location /test {
                return 200 'server1.*';
            }
        } 
        server {
            listen       127.0.0.1:8080;
            server_name  server1;
            location /test {
                return 200 'server1';
            }
        } 
        server {
            listen       127.0.0.1:8080 default;
            server_name  ~.*server1.*;
            location /test {
                return 200 '~.*server1.*';
            }
        } 
    
  2. run "nginx -s reload"
  3. run "telnet 127.0.0.1 8080"
  4. send request
    GET /test HTTP/1.1
    Host: www.server1
    Connection: close
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
    Accept: */*
    Accept-Encoding: gzip,deflate,sdch
    Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4
    
  5. get response
    HTTP/1.1 200 OK
    Server: nginx/1.4.3
    Date: Wed, 23 Oct 2013 22:58:48 GMT
    Content-Type: application/octet-stream
    Content-Length: 9
    Connection: close
    
    *.server1
    
  6. run "telnet 127.0.0.1 8080"
  7. send request (這個 request 的 Host 比較長, 但 nginx 仍可以判斷)
    GET /test HTTP/1.1
    Host: www.abc.qqq.hahaha.server1
    Connection: close
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
    Accept: */*
    Accept-Encoding: gzip,deflate,sdch
    Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4
    
  8. get response
    HTTP/1.1 200 OK
    Server: nginx/1.4.3
    Date: Wed, 23 Oct 2013 22:58:48 GMT
    Content-Type: application/octet-stream
    Content-Length: 9
    Connection: close
    
    *.server1

如果 mapping 到的 * 開頭的 server 不止一個, 就取 mapping 到最長的 server

  1. setup nginx.conf
        server {
            listen       127.0.0.1:8080;
            server_name  *.server1;
            location /test {
                return 200 '*.server1';
            }
        } 
        server {
            listen       127.0.0.1:8080;
            server_name  *.hahaha.server1;
            location /test {
                return 200 '*.hahaha.server1';
            }
        } 
        server {
            listen       127.0.0.1:8080;
            server_name  www.*;
            location /test {
                return 200 'www.*';
            }
        } 
        server {
            listen       127.0.0.1:8080;
            server_name  server1;
            location /test {
                return 200 'server1';
            }
        } 
        server {
            listen       127.0.0.1:8080 default;
            server_name  ~.*server1.*;
            location /test {
                return 200 '~.*server1.*';
            }
        } 
    
  2. run "nginx -s reload"
  3. run "telnet 127.0.0.1 8080"
  4. send request
    GET /test HTTP/1.1
    Host: www.abc.qqq.hahaha.server1
    Connection: close
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
    Accept: */*
    Accept-Encoding: gzip,deflate,sdch
    Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4
    
    
  5. get response
    HTTP/1.1 200 OK
    Server: nginx/1.4.3
    Date: Wed, 23 Oct 2013 23:07:06 GMT
    Content-Type: application/octet-stream
    Content-Length: 16
    Connection: close
    
    *.hahaha.server1

如果對應不到 * 開頭的 server_name, 就找 mapping 到最長的以 * 結尾的 server_name

  1. setup nginx.conf
        server {
            listen       127.0.0.1:8080;
            server_name  *.server1;
            location /test {
                return 200 '*.server1';
            }
        } 
        server {
            listen       127.0.0.1:8080;
            server_name  *.hahaha.server1;
            location /test {
                return 200 '*.hahaha.server1';
            }
        } 
        server {
            listen       127.0.0.1:8080;
            server_name  www.*;
            location /test {
                return 200 'www.*';
            }
        } 
        server {
            listen       127.0.0.1:8080;
            server_name  www.abc.qqq.*;
            location /test {
                return 200 'www.abc.qqq.*';
            }
        } 
        server {
            listen       127.0.0.1:8080;
            server_name  server1;
            location /test {
                return 200 'server1';
            }
        } 
        server {
            listen       127.0.0.1:8080 default;
            server_name  ~.*server1.*;
            location /test {
                return 200 '~.*server1.*';
            }
        } 
    
  2. run "nginx -s reload"
  3. run "telnet 127.0.0.1 8080"
  4. send request
    GET /test HTTP/1.1
    Host: www.abc.qqq.hahaha.server1.net
    Connection: close
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
    Accept: */*
    Accept-Encoding: gzip,deflate,sdch
    Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4
    
    
  5. get response
    HTTP/1.1 200 OK
    Server: nginx/1.4.3
    Date: Wed, 23 Oct 2013 23:13:59 GMT
    Content-Type: application/octet-stream
    Content-Length: 13
    Connection: close
    
    www.abc.qqq.*

"*" 只能出現在 server_name 的開頭或結尾, 不能放中間

  1. setup nginx.conf
        server {
            listen       127.0.0.1:8080;
            server_name  *.server1.*;
            location /test {
                return 200 '*.server1.*';
            }
        } 
    
  2. run "nginx -s reload"
  3. got error message
    nginx: [emerg] invalid server name or wildcard "*.server1.*" on 127.0.0.1:8080

如果 * 也都對應不到 server_name, 就用 regular expression (~ 開頭) 找第一個對應到的 server_name

  1. setup nginx.conf
        server {
            listen       127.0.0.1:8080;
            server_name  *.server1;
            location /test {
                return 200 '*.server1';
            }
        } 
        server {
            listen       127.0.0.1:8080;
            server_name  *.hahaha.server1;
            location /test {
                return 200 '*.hahaha.server1';
            }
        } 
        server {
            listen       127.0.0.1:8080;
            server_name  www.*;
            location /test {
                return 200 'www.*';
            }
        } 
        server {
            listen       127.0.0.1:8080;
            server_name  www.abc.qqq.*;
            location /test {
                return 200 'www.abc.qqq.*';
            }
        } 
        server {
            listen       127.0.0.1:8080;
            server_name  server1;
            location /test {
                return 200 'server1';
            }
        } 
        server {
            listen       127.0.0.1:8080;
            server_name  ~.*server1.*;
            location /test {
                return 200 '~.*server1.*';
            }
        } 
        server {
            listen       127.0.0.1:8080 default;
            server_name  ~.*abc\.qqq\.hahaha\.server1.*;
            location /test {
                return 200 '~.*abc\.qqq\.hahaha\.server1.*';
            }
        } 
    
  2. run "nginx -s reload"
  3. run "telnet 127.0.0.1 8080"
  4. send request
    GET /test HTTP/1.1
    Host: xyz.abc.qqq.hahaha.server1.net
    Connection: close
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
    Accept: */*
    Accept-Encoding: gzip,deflate,sdch
    Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4
    
    
  5. get response
    HTTP/1.1 200 OK
    Server: nginx/1.4.3
    Date: Wed, 23 Oct 2013 23:37:15 GMT
    Content-Type: application/octet-stream
    Content-Length: 13
    Connection: close
    
    ~.*server1.*
  6. setup nginx.conf
        server {
            listen       127.0.0.1:8080;
            server_name  *.server1;
            location /test {
                return 200 '*.server1';
            }
        } 
        server {
            listen       127.0.0.1:8080;
            server_name  *.hahaha.server1;
            location /test {
                return 200 '*.hahaha.server1';
            }
        } 
        server {
            listen       127.0.0.1:8080;
            server_name  www.*;
            location /test {
                return 200 'www.*';
            }
        } 
        server {
            listen       127.0.0.1:8080;
            server_name  www.abc.qqq.*;
            location /test {
                return 200 'www.abc.qqq.*';
            }
        } 
        server {
            listen       127.0.0.1:8080;
            server_name  server1;
            location /test {
                return 200 'server1';
            }
        } 
        server {
            listen       127.0.0.1:8080 default;
            server_name  ~.*abc\.qqq\.hahaha\.server1.*;
            location /test {
                return 200 '~.*abc\.qqq\.hahaha\.server1.*';
            }
        } 
    
        server {
            listen       127.0.0.1:8080;
            server_name  ~.*server1.*;
            location /test {
                return 200 '~.*server1.*';
            }
        } 
  7. run "nginx -s reload"
  8. run "telnet 127.0.0.1 8080"
  9. send request
    GET /test HTTP/1.1
    Host: xyz.abc.qqq.hahaha.server1.net
    Connection: close
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
    Accept: */*
    Accept-Encoding: gzip,deflate,sdch
    Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4
    
    
  10. get response
    HTTP/1.1 200 OK
    Server: nginx/1.4.3
    Date: Wed, 23 Oct 2013 23:39:05 GMT
    Content-Type: application/octet-stream
    Content-Length: 31
    Connection: close
    
    ~.*abc\.qqq\.hahaha\.server1.*

如果 server_name 的 regular expression 包含 "{" 跟 "}", 就必須用 "" 把 regular expression 包起來, 否則 nginx 會報錯

  1. setup nginx.conf
        server {
            listen       127.0.0.1:8080 default;
            server_name  ~.*abc\.q{3}\.hahaha\.server1.*;
            location /test {
                return 200 '~.*abc\.q{3}\.hahaha\.server1.*';
            }
        }
     
  2. run "nginx -s reload"
  3. get error message
    nginx: [emerg] directive "server_name" is not terminated by ";" in C:\Users\isaac\Downloads\nginx-1.4.3\nginx-1.4.3/conf/nginx.conf:72
    
  4. setup nginx.conf
        server {
            listen       127.0.0.1:8080;
            server_name  *.server1;
            location /test {
                return 200 '*.server1';
            }
        } 
        server {
            listen       127.0.0.1:8080;
            server_name  *.hahaha.server1;
            location /test {
                return 200 '*.hahaha.server1';
            }
        } 
        server {
            listen       127.0.0.1:8080;
            server_name  www.*;
            location /test {
                return 200 'www.*';
            }
        } 
        server {
            listen       127.0.0.1:8080;
            server_name  www.abc.qqq.*;
            location /test {
                return 200 'www.abc.qqq.*';
            }
        } 
        server {
            listen       127.0.0.1:8080;
            server_name  server1;
            location /test {
                return 200 'server1';
            }
        } 
        server {
            listen       127.0.0.1:8080 default;
            server_name  "~.*abc\.q{3}\.hahaha\.server1.*";
            location /test {
                return 200 '~.*abc\.q{3}\.hahaha\.server1.*';
            }
        } 
        server {
            listen       127.0.0.1:8080;
            server_name  ~.*server1.*;
            location /test {
                return 200 '~.*server1.*';
            }
        }
     
  5. run "nginx -s reload"
  6. run "telnet 127.0.0.1 8080"
  7. send request
    GET /test HTTP/1.1
    Host: xyz.abc.qqq.hahaha.server1.net
    Connection: close
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
    Accept: */*
    Accept-Encoding: gzip,deflate,sdch
    Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4
    
    
  8. get response
    HTTP/1.1 200 OK
    Server: nginx/1.4.3
    Date: Wed, 23 Oct 2013 23:51:10 GMT
    Content-Type: application/octet-stream
    Content-Length: 31
    Connection: close
    
    ~.*abc\.q{3}\.hahaha\.server1.*

在 server_name 可用 regular expression 替 mapping 到的值命名成變數以便在之後使用

  1. setup nginx.conf
        server {
            listen       127.0.0.1:8080;
            server_name  ~(?P.*)\.server1.*;
            location /test {
                return 200 'you request ${serviceName} service, service_name pattern ~(?P.*)\.server1.* is Python compatible syntax, supported since PCRE-4.0';
            }
        } 
        server {
            listen       127.0.0.1:8081;
            server_name  ~(?.*)\.server1.*;
            location /test {
                return 200 'you request ${serviceName} service, service_name pattern ~(?.*)\.server1.* is Perl 5.10 compatible syntax, supported since PCRE-7.0';
            }
        } 
    
  2. run "nginx -s reload"
  3. run "telnet 127.0.0.1 8080"
  4. send request
    GET /test HTTP/1.1
    Host: tw.mail.server1.net
    Connection: close
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
    Accept: */*
    Accept-Encoding: gzip,deflate,sdch
    Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4
    
  5. get response
    HTTP/1.1 200 OK
    Server: nginx/1.4.3
    Date: Sun, 27 Oct 2013 05:14:53 GMT
    Content-Type: application/octet-stream
    Content-Length: 135
    Connection: close
    
    you request tw.mail service, service_name pattern ~(?P.*)\.server1.* is Python compatible syntax, supported since PCRE-4.0
    
  6. run "telnet 127.0.0.1 8081"
  7. send request
    GET /test HTTP/1.1
    Host: tw.mail.server1.net
    Connection: close
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
    Accept: */*
    Accept-Encoding: gzip,deflate,sdch
    Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4
    
  8. get response
    HTTP/1.1 200 OK
    Server: nginx/1.4.3
    Date: Sun, 27 Oct 2013 05:16:06 GMT
    Content-Type: application/octet-stream
    Content-Length: 137
    Connection: close
    
    you request tw.mail service, service_name pattern ~(?.*)\.server1.* is Perl 5.10 compatible syntax, supported since PCRE-7.0
    
    

在 server_name 透過 regular expression 將 mapping 到的值可在之後用數字編號當變數使用

  1. setup nginx.conf
        server {
            listen       127.0.0.1:8080;
            server_name  ~(.*)\.(.*);
            location /test {
                return 200 'you request service "$1" in domain "$2"';
            }
        } 
    
  2. run "nginx -s reload"
  3. run "telnet 127.0.0.1 8080"
  4. send request
    GET /test HTTP/1.1
    Host: mail.server1
    Connection: close
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
    Accept: */*
    Accept-Encoding: gzip,deflate,sdch
    Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4
    
  5. get response
    HTTP/1.1 200 OK
    Server: nginx/1.4.3
    Date: Sun, 27 Oct 2013 05:22:37 GMT
    Content-Type: application/octet-stream
    Content-Length: 46
    Connection: close
    
    you request service "mail" in domain "server1"
    

如果沒指定 server_name, nginx 就會用 hostname 作為 server_name

  1. setup nginx.conf
        server {
            listen       127.0.0.1:8080;
            location /test {
                return 200 'you request ${hostname}';
            }
        }  
    
  2. run "nginx -s reload"
  3. run "telnet 127.0.0.1 8080"
  4. send request
    GET /test HTTP/1.1
    Host: 127.0.0.1
    Connection: close
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
    Accept: */*
    Accept-Encoding: gzip,deflate,sdch
    Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4
    
  5. get response
    HTTP/1.1 200 OK
    Server: nginx/1.4.3
    Date: Sun, 27 Oct 2013 05:41:41 GMT
    Content-Type: application/octet-stream
    Content-Length: 20
    Connection: close
    
    you request isaac-pc

設定一個"全部"的 server_name

  1. setup nginx.conf
        server {
            listen       8080;
            server_name  _;
            location / {
                return 200 'you request ${http_Host}';
            }
        }
    
  2. run "nginx -s reload"
  3. run "telnet 127.0.0.1 8080"
  4. send request
    GET / HTTP/1.1
    Host: abc:8080
    Connection: close
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
    Accept: */*
    Accept-Encoding: gzip,deflate,sdch
    Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4
    
  5. get response
    HTTP/1.1 200 OK
    Server: nginx/1.4.3
    Date: Wed, 30 Oct 2013 23:39:30 GMT
    Content-Type: application/octet-stream
    Content-Length: 20
    Connection: close
    
    you request abc:8080
  6. setup nginx.conf
        server {
            listen       8080;
            server_name  --;
            location / {
                return 200 'you request ${http_Host}';
            }
        }  
  7. run "nginx -s reload"
  8. run "telnet 127.0.0.1 8080"
  9. send request
    GET / HTTP/1.1
    Host: abc:8080
    Connection: close
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
    Accept: */*
    Accept-Encoding: gzip,deflate,sdch
    Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4
    
  10. get response
    HTTP/1.1 200 OK
    Server: nginx/1.4.3
    Date: Wed, 30 Oct 2013 23:39:30 GMT
    Content-Type: application/octet-stream
    Content-Length: 20
    Connection: close
    
    you request abc:8080
  11. setup nginx.conf
  12.     server {
            listen       8080;
            server_name  !@#;
            location / {
                return 200 'you request ${http_Host}';
            }
        }  
  13. run "nginx -s reload"
  14. run "telnet 127.0.0.1 8080"
  15. send request
    GET / HTTP/1.1
    Host: abc:8080
    Connection: close
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
    Accept: */*
    Accept-Encoding: gzip,deflate,sdch
    Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4
    
  16. get response
    HTTP/1.1 200 OK
    Server: nginx/1.4.3
    Date: Wed, 30 Oct 2013 23:39:30 GMT
    Content-Type: application/octet-stream
    Content-Length: 20
    Connection: close
    
    you request abc:8080

Nginx Practice in Windows - 3

Reference

Description

    server {
        listen       8080;
        server_name  example.com;
        location /test {
           return 200 'example.com';
        }
    }
nginx 這個 server 區塊的設定中, server_name 是用來判斷 request 的 Host header 是打算導給哪個 server. 比方說這個例子就是如果 header 指定了 example.com 會導向這個 server.

Practice

如果有找到與 request header 的 Host 相對應的 server, 就會導過去

  1. setup nginx.conf
        server {
            listen       8080;
            server_name  server1;
            location /test {
                return 200 'server1';
            }
        } 
        server {
            listen       8080;
            server_name  server2;
            location /test {
                return 200 'server2';
            }
        } 
    
  2. run "nginx -s reload"
  3. run "telnet 127.0.0.1 8080"
  4. send request
    GET /test HTTP/1.1
    Host: server2:8080
    Connection: close
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
    Accept: */*
    Accept-Encoding: gzip,deflate,sdch
    Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4
    
  5. get response
    HTTP/1.1 200 OK
    Server: nginx/1.4.3
    Date: Mon, 21 Oct 2013 23:04:24 GMT
    Content-Type: application/octet-stream
    Content-Length: 7
    Connection: close
    
    server2
    

沒有指定誰是預設 server 而且透過 request header 的 Host 又找不到相對應的 server_name, 就會回傳第一個.

  1. setup nginx.conf
        server {
            listen       8080;
            server_name  server1;
            location /test {
                return 200 'server1';
            }
        } 
        server {
            listen       8080;
            server_name  server2;
            location /test {
                return 200 'server2';
            }
        } 
    
  2. run "nginx -s reload"
  3. run "telnet 127.0.0.1 8080"
  4. send request
    GET /test HTTP/1.1
    Host: 127.0.0.1:8080
    Connection: close
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
    Accept: */*
    Accept-Encoding: gzip,deflate,sdch
    Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4
    
  5. get response
    HTTP/1.1 200 OK
    Server: nginx/1.4.3
    Date: Mon, 21 Oct 2013 22:55:05 GMT
    Content-Type: application/octet-stream
    Content-Length: 7
    Connection: close
    
    server1
    

有指定 default_server, 當 request header Host 找不到相對應的 server_name 時, 就會導向 default server. (文件上說0.8.21之後使用 default_server, 之前使用 default. 不過我在 nginx-1.4.3 for Windows 要使用 default 才可以. 是 Linux 版的才可改 default_server?)

  1. setup nginx.conf
        server {
            listen       8080;
            server_name  server1;
            location /test {
                return 200 'server1';
            }
        } 
        server {
            listen       8080 default;
            server_name  server2;
            location /test {
                return 200 'server2';
            }
        } 
    
  2. run "nginx -s reload"
  3. run "telnet 127.0.0.1 8080"
  4. send request
    GET /test HTTP/1.1
    Host: 127.0.0.1:8080
    Connection: close
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
    Accept: */*
    Accept-Encoding: gzip,deflate,sdch
    Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4
    
  5. get response
    HTTP/1.1 200 OK
    Server: nginx/1.4.3
    Date: Mon, 21 Oct 2013 22:59:23 GMT
    Content-Type: application/octet-stream
    Content-Length: 7
    Connection: close
    
    server2
    

如果指定的 server 包含不同 ip, 那每個 ip 就會有自己的 default server.

  1. setup nginx.conf
        server {
            listen       127.0.0.1:8080;
            server_name  server1;
            location /test {
                return 200 'server1';
            }
        } 
        server {
            listen       127.0.0.1:8080;
            server_name  server2;
            location /test {
                return 200 'server2';
            }
        } 
     server {
            listen       127.0.0.2:8081;
            server_name  server3;
            location /test {
                return 200 'server3';
            }
        } 
        server {
            listen       127.0.0.2:8081;
            server_name  server4;
            location /test {
                return 200 'server4';
            }
        }
    
     
  2. run "nginx -s reload"
  3. run "telnet 127.0.0.1 8080"
  4. send request
    GET /test HTTP/1.1
    Host: localhost
    Connection: close
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
    Accept: */*
    Accept-Encoding: gzip,deflate,sdch
    Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4 
  5. get responseget response
    HTTP/1.1 200 OK
    Server: nginx/1.4.3
    Date: Mon, 21 Oct 2013 23:20:33 GMT
    Content-Type: application/octet-stream
    Content-Length: 7
    Connection: close
    
    server1
  6. run "telnet 127.0.0.2 8081"
  7. send request
    GET /test HTTP/1.1
    Host: localhost
    Connection: close
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
    Accept: */*
    Accept-Encoding: gzip,deflate,sdch
    Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4 
  8. get responseget response
    HTTP/1.1 200 OK
    Server: nginx/1.4.3
    Date: Mon, 21 Oct 2013 23:21:52 GMT
    Content-Type: application/octet-stream
    Content-Length: 7
    Connection: close
    
    server3
  9. setup nginx.conf
        server {
            listen       127.0.0.1:8080;
            server_name  server1;
            location /test {
                return 200 'server1';
            }
        } 
        server {
            listen       127.0.0.1:8080 default;
            server_name  server2;
            location /test {
                return 200 'server2';
            }
        } 
     server {
            listen       127.0.0.2:8081;
            server_name  server3;
            location /test {
                return 200 'server3';
            }
        } 
        server {
            listen       127.0.0.2:8081 default;
            server_name  server4;
            location /test {
                return 200 'server4';
            }
        } 
    
     
  10. run "nginx -s reload"
  11. run "telnet 127.0.0.1 8080"
  12. send request
    GET /test HTTP/1.1
    Host: localhost
    Connection: close
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
    Accept: */*
    Accept-Encoding: gzip,deflate,sdch
    Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4 
  13. get responseget response
    HTTP/1.1 200 OK
    Server: nginx/1.4.3
    Date: Mon, 21 Oct 2013 23:20:33 GMT
    Content-Type: application/octet-stream
    Content-Length: 7
    Connection: close
    
    server2
  14. run "telnet 127.0.0.2 8081"
  15. send request
    GET /test HTTP/1.1
    Host: localhost
    Connection: close
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
    Accept: */*
    Accept-Encoding: gzip,deflate,sdch
    Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4 
  16. get responseget response
    HTTP/1.1 200 OK
    Server: nginx/1.4.3
    Date: Mon, 21 Oct 2013 23:21:52 GMT
    Content-Type: application/octet-stream
    Content-Length: 7
    Connection: close
    
    server4


Nginx Practice in Windows - 2

Reference

Practice

變數的有效範圍是 request, 所以就算不同 request 也可以使用變數

  1. config nginx.conf
        server {
            listen       8080;
            server_name  localhost;
            location / {
                root   html;
                index  index.html index.htm;
            }
            location /test {
                set $foo "hello";
                rewrite ^ /test2;
            }
            location /test2 {
                return 200 "${foo} world";
            }
            error_page   500 502 503 504  /50x.html;
            location = /50x.html {
                root   html;
            }
        } 
    
  2. run "nginx -s reload"
  3. request "http://127.0.0.1:8080/test"
  4. get response: hello world

使用 Nginx 內建變數

  1. config nginx.conf
        server {
            listen       8080;
            server_name  localhost;
            location / {
                root   html;
                index  index.html index.htm;
            }
            location /test {
                return 200 "Your address:${remote_addr}:${remote_port}. nginx version:${nginx_version}. nginx pid:${pid}";
            }
            error_page   500 502 503 504  /50x.html;
            location = /50x.html {
                root   html;
            }
        } 
    
  2. run "nginx -s reload"
  3. request "http://127.0.0.1:8080/test"
  4. get response: Your address:127.0.0.1:53102. nginx version:1.4.3. nginx pid:8132
  5. run tasklist /fi "imagename eq nginx.exe"
    C:\nginx-1.4.3>tasklist /fi "imagename eq nginx.exe"
    
    映像名稱                       PID 工作階段名稱      工作階段 #    RAM使用量
    ========================= ======== ================ =========== ============
    nginx.exe                     4044 Console                    1      6,044 K
    nginx.exe                     8132 Console                    1      6,400 K
    

取得 request parameter value: ${arg_XXX}

  1. Note: nginx 取 parameter 不分大小寫
  2. config nginx.conf
        server {
            listen       8080;
            server_name  localhost;
            location / {
                root   html;
                index  index.html index.htm;
            }
            location /test {
         return 200 "The parameter abc value: ${arg_abc}";
            }
            error_page   500 502 503 504  /50x.html;
            location = /50x.html {
                root   html;
            }
        } 
    
  3. run "nginx -s reload"
  4. request "http://127.0.0.1:8080/test?abc=qq"
  5. get response: The parameter abc value: qq
  6. request "http://127.0.0.1:8080/test?Abc=qq"
  7. get response: The parameter abc value: qq 

取得 header value: ${http_XXX}

  1. config nginx.conf
        server {
            listen       8080;
            server_name  localhost;
            location / {
                root   html;
                index  index.html index.htm;
            }
            location /test {
                return 200 "The header 'abc' value: ${http_abc}";
            }
            error_page   500 502 503 504  /50x.html;
            location = /50x.html {
                root   html;
            }
        } 
    
  2. run "nginx -s reload"
  3. run "telnet 127.0.0.1 8080"
  4. send request
    GET /test HTTP/1.1
    Host: 127.0.0.1:8080
    Connection: close
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
    Accept: */*
    Accept-Encoding: gzip,deflate,sdch
    Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4
    ABC: 123
    
  5. get response
    HTTP/1.1 200 OK
    Server: nginx/1.4.3
    Date: Sat, 19 Oct 2013 14:03:40 GMT
    Content-Type: application/octet-stream
    Content-Length: 27
    Connection: close
    
    The header 'abc' value: 123
    

取得 cookie value: ${cookie_XXX}

  1. config nginx.conf
        server {
            listen       8080;
            server_name  localhost;
            location / {
                root   html;
                index  index.html index.htm;
            }
            location /test {
                return 200 "The cookie 'abc' value: ${cookie_abc}";
            }
            error_page   500 502 503 504  /50x.html;
            location = /50x.html {
                root   html;
            }
        } 
    
  2. run "nginx -s reload"
  3. run "telnet 127.0.0.1 8080"
  4. send request
    GET /test HTTP/1.1
    Host: 127.0.0.1:8080
    Connection: close
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
    Accept: */*
    Accept-Encoding: gzip,deflate,sdch
    Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4
    Cookie: ABC=123
    
  5. get response
    HTTP/1.1 200 OK
    Server: nginx/1.4.3
    Date: Sat, 19 Oct 2013 14:09:30 GMT
    Content-Type: application/octet-stream
    Content-Length: 27
    Connection: close
    
    The cookie 'abc' value: 123
    

Nginx Practice in Windows - 1

Description

最近在練習 Nginx, 不過電腦的環境只有 Windows 也沒打算裝 VM,
所以跟網路上文章的環境不太一樣, 在這紀錄怎麼在Windows上練習.
不過最終應該還是要裝個 Linux 環境來練習比較好..

Reference

Preparation

  1. Download nginx for Windows
    1. 下載後解壓縮
    2. 在 console mode 到解壓縮後的資料夾
    3. 開 nginx => start nginx
    4. 關 nginx => nginx -s stop
    5. 看 nginx process => tasklist /fi "imagename eq nginx.exe"
    6. reload config => nginx -s reload
  2. Download Simple REST Client for Chrome

Practice

用 set 定義變數

  1. 設定 nginx.conf, 加上 /test 這個 location, 用 set $foo 設定變數 response
    1. 如果要回傳的字串可以直接使用變數, 直接指定 $foo
          
          server {
              listen       8080;
              server_name  localhost;
              location / {
                  root   html;
                  index  index.html index.htm;
              }
              location /test {
                  set $foo "hello world";
                  return 200 "$foo";
              }
              error_page   500 502 503 504  /50x.html;
              location = /50x.html {
                  root   html;
              }
          } 
      
    2. 如果要回傳的字串需要在變數外再加東西,使 nginx 無法判斷變數, 就必須變成 ${foo}.
      (注意不是每次都用 ${foo}, 而是 nginx 會無法判斷的時候才要 ${foo})
          server {
              listen       8080;
              server_name  localhost;
              location / {
                  root   html;
                  index  index.html index.htm;
              }
              location /test {
                  set $foo "hello";
                  return 200 "${foo} world";
              }
              error_page   500 502 503 504  /50x.html;
              location = /50x.html {
                  root   html;
              }
          } 
      
  2. run "nginx -s reload"
  3. 用 Simple REST Client 送出 GET 到 http://127.0.0.1:8080/test
    GET /test HTTP/1.1
    Host: 127.0.0.1:8080
    Connection: keep-alive
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
    Accept: */*
    Accept-Encoding: gzip,deflate,sdch
    Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4
    
  4. 得到 response
    HTTP/1.1 200 OK
    Server: nginx/1.4.3
    Date: Sat, 19 Oct 2013 11:12:59 GMT
    Content-Type: application/octet-stream
    Content-Length: 11
    Connection: keep-alive
    
    hello world

Lessons Learned While Benchmarking vLLM with GPU

Recently, I benchmarked vLLM on a GPU to better understand how much throughput can realistically be expected in an LLM serving setup. One ...