技術成長---Nginx實現廣播功能

  • Nginx
    • Nginx (engine x) 是一個高性能的HTTP和反向代理web服務器,同時也提供了IMAP/POP3/SMTP服務。
  • Openresty
    • OpenResty是一個基於 Nginx 與 Lua 的高性能 Web 平臺,其內部集成了大量精良的 Lua 庫、第三方模塊以及大多數的依賴項。用於方便地搭建能夠處理超高併發、擴展性極高的動態 Web 應用、Web 服務和動態網關。通過匯聚各種設計精良的 Nginx 模塊(主要由 OpenResty 團隊自主開發),從而將 Nginx 有效地變成一個強大的通用 Web 應用平臺。這樣,Web 開發人員和系統工程師可以使用 Lua 腳本語言調動 Nginx 支持的各種 C 以及 Lua 模塊,快速構造出足以勝任 10K 乃至 1000K 以上單機併發連接的高性能 Web 應用系統。其目標是讓你的Web服務直接跑在 Nginx 服務內部,充分利用 Nginx 的非阻塞 I/O 模型,不僅僅對 HTTP 客戶端請求,甚至於對遠程後端諸如 MySQL、PostgreSQL、Memcached 以及 Redis 等都進行一致的高性能響應。
  • Lua
    • Lua 是一種輕量小巧的腳本語言,用標準C語言編寫並以源代碼形式開放, 其設計目的是爲了嵌入應用程序中,從而爲應用程序提供靈活的擴展和定製功能。
    • 特性:
      • 輕量級: 它用標準C語言編寫並以源代碼形式開放,編譯後僅僅一百餘K,可以很方便的嵌入別的程序裏。
      • 可擴展: Lua提供了非常易於使用的擴展接口和機制:由宿主語言(通常是C或C++)提供這些功能,Lua可以使用它們,就像是本來就內置的功能一樣。
      • 支持面向過程(procedure-oriented)編程和函數式編程(functional programming);
      • 自動內存管理;只提供了一種通用類型的表(table),用它可以實現數組,哈希表,集合,對象;
      • 語言內置模式匹配;閉包(closure);函數也可以看做一個值;提供多線程(協同進程,並非操作系統所支持的線程)支持;
      • 通過閉包和table可以很方便地支持面向對象編程所需要的一些關鍵機制,比如數據抽象,虛函數,繼承和重載等。
    • 應用場景:
      • 遊戲開發
      • 獨立應用腳本
      • Web 應用腳本
      • 擴展和數據庫插件如:MySQL Proxy 和 MySQL WorkBench
      • 安全系統,如入侵檢測系統
  • 業務需求介紹:
    • 一個請求到達Nginx後將請求發到所有endpoint上,即配置文件中upstream中的每個server上。業務場景爲:每個用戶連接在同一個服務不同的節點上,如果需要向用戶發出命令傳遞信息,在只發一次請求的前提下,經過Nginx後只能到達服務的某一個節點上,這樣可能導致命令不能轉發到相對應用戶所在的節點上,所以需要將請求轉發到每一個節點上,保證命令傳遞的有效性.
  • 分析:
    • 正常一個請求發到Nginx後,請求只會轉發到某一個endpoint上(這裏有Nginx的負載均衡作用),要想把這個請求轉發到每個endpoint上,需要在請求到達Nginx後,使得Nginx將請求轉發到每一個endpoint上,而不是其中一個endpoint,經調研發現Openresty可以結合Lua腳本編程實現該功能.
  • 思路:
    • 具體方案爲:當請求到達Nginx後,Nginx沒有進行轉發前,獲取所有endpoint,然後對每個endpoint進行轉發請求,因此需要在Openresty中使用Lua腳本語言實現該功能.
  • 實施過程:
    • 創建三個web服務用於實驗
    • 使用Docker啓動Openresty
      • nginx.conf配置文件如下:
        • #user  nobody;
          worker_processes  1;
          
          error_log  logs/error.log;
          error_log  logs/error.log  notice;
          error_log  logs/error.log  info;
          
          pid        logs/nginx.pid;
          
          events {
              worker_connections  1024;
          }
          
          http {
              include       mime.types;
              default_type  application/octet-stream;
          
              log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                                '$status $body_bytes_sent "$http_referer" '
                                '"$http_user_agent" "$http_x_forwarded_for"';
          
              access_log  logs/access.log  main;
          
              # See Move default writable paths to a dedicated directory (#119)
              # https://github.com/openresty/docker-openresty/issues/119
              client_body_temp_path /var/run/openresty/nginx-client-body;
              proxy_temp_path       /var/run/openresty/nginx-proxy;
              fastcgi_temp_path     /var/run/openresty/nginx-fastcgi;
              uwsgi_temp_path       /var/run/openresty/nginx-uwsgi;
              scgi_temp_path        /var/run/openresty/nginx-scgi;
          
              sendfile        on;
              #tcp_nopush     on;
          
              #keepalive_timeout  0;
              keepalive_timeout  65;
          
              #gzip  on;
          
              upstream weblist {
                  server 10.xxx.xxx.xxx:9999;
                  server 10.xxx.xxx.xxx:7777;
                  server 10.xxx.xxx.xxx:8888;
              }
          
              server {
                  listen       80;
                  server_name  127.0.0.1;
                  #resolver 8.8.8.8;
                  location = /upstreams {
                      default_type text/plain;
                      content_by_lua_block {
                          local http = require "resty.http"
                          local httpc = http.new()
                          local upstream = require "ngx.upstream"
                          local get_servers = upstream.get_servers
                          local get_upstreams = upstream.get_upstreams
                          local us = get_upstreams()
                          for _, u in ipairs(us) do
                              ngx.say("upstream ", u, ":")
                              local srvs, err = get_servers(u)
                              if not srvs then
                                  ngx.say("failed to get servers in upstream ", u)
                              else
                                  for _, srv in ipairs(srvs) do
                                      local res, err = httpc:request_uri("http://"..srv["addr"], {
                                          method = ngx.var.request_method,--set request method
                                          body = ngx.var.request_body,--set request body
                                          headers = ngx.req.get_headers()--set request headers
                                      })
                                      if res.status == ngx.HTTP_OK then
                                          ngx.print(res.body)
                                      else
                                          ngx.print(res.status)
                                      end
                                      ngx.print("\n")
                                  end
                              end
                          end
                      }
                  }
          
                  location / {
                      proxy_pass http://weblist;
                  }
                  error_page   500 502 503 504  /50x.html;
                  location = /50x.html {
                  root   /usr/local/openresty/nginx/html;
                  }
              }
          }
      • Dokcer啓動命令
        • docker run -itd -p 80:80 -v ~/dockerapp/openresty/conf/nginx.conf:/usr/local/openresty/nginx/conf/nginx.conf -v ~/dockerapp/openresty/lua/lualib/:/usr/local/openresty/lualib/ openresty/openresty
    • 添加引用庫

      • lua-resty-http

        • 將OpenResty 引用第三方 resty 庫非常簡單,只要將 lua-resty-http/lib/resty/ 目錄下的 http.lua 和http_headers.lua 兩個文件拷貝到 /usr/local/openresty/lualib/resty 目錄下即可(假設你的 OpenResty 安裝目錄爲 /usr/local/openresty)。

      • lua-upstream-nginx-module

        • 同lua-resty-http的操作,在此不再贅述。

  • 注意⚠️

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章