d1f476a538c9e8340945ec5651764f5f9b9a9fe4
[msb/apigateway.git] / msb-core / openresty-ext / src / assembly / resources / openresty / nginx / luaext / customrouter.lua
1 --[[
2
3     Copyright 2016 2015-2016 ZTE, Inc. and others. All rights reserved.
4
5     Licensed under the Apache License, Version 2.0 (the "License");
6     you may not use this file except in compliance with the License.
7     You may obtain a copy of the License at
8
9         http://www.apache.org/licenses/LICENSE-2.0
10
11     Unless required by applicable law or agreed to in writing, software
12     distributed under the License is distributed on an "AS IS" BASIS,
13     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14     See the License for the specific language governing permissions and
15     limitations under the License.
16
17         Author: Zhaoxing Meng
18         email: meng.zhaoxing1@zte.com.cn
19
20 ]]
21 -- put red into the connection pool of size 100,
22 -- with 10 seconds max idle time
23 local function close_redis(red)
24         if not red then
25                 return
26         end
27         --release connection to the pool
28         local pool_max_idle_time = 10000
29         local pool_size = 100
30         local ok, err = red:set_keepalive(pool_max_idle_time, pool_size)
31         if not ok then
32                 ngx.log(ngx.ERR, "set keepalive error:", err)
33         end
34         return
35 end
36
37 local function query_ipurl_updatecache(red,key)
38         local keyprefix = "msb:routing:custom:"..key
39
40
41         local infokey=keyprefix..":info"
42         -- first of all check whether the status is 1(enabled)
43         local status = red:hget(infokey,"status")
44         if not (status=="1")  then
45                 ngx.log(ngx.WARN, key.."is disabled.status=", status)
46                 return nil
47         end
48
49         -- Try to get url for key
50         local url, err = red:hget(infokey,"url")
51         ngx.log(ngx.WARN, "==url:", url)
52         if not url or url == ngx.null then
53                 return nil
54         end
55
56         -- Try to get ip:port for key
57         local serverkey=keyprefix..":lb:server1"
58         local server, err = red:hget(serverkey,"ip")..":"..red:hget(serverkey,"port")
59         ngx.log(ngx.WARN, "==server:", server)
60         if not server or server == ngx.null then
61                 return nil
62         end
63
64         -- get the local cache
65         local cache = ngx.shared.ceryx
66         local uri = ngx.var.uri
67         -- Save found key to local cache for 5 seconds
68         cache:set("custom:key:"..uri,key,5)   
69         cache:set("custom:server:"..uri,server,5)
70         cache:set("custom:url:"..uri,url,5)
71
72         ngx.var.key = key
73         ngx.var.server = server
74         ngx.var.url = url
75         return true
76 end
77
78 local function query_allkeys_updatecache(red)
79         -- Try to get all keys start with "msb:routing:custom:"
80         local allkeys, err = red:keys("msb:routing:custom:*")
81         if not allkeys or allkeys == ngx.null then
82                 ngx.log(ngx.ERR,err)
83                 return ""
84         end
85         --把所有键值处理后放到集合中,去除重复
86         local key_set={}
87         for key, value in ipairs(allkeys) do
88                 name = string.gsub(string.gsub(string.gsub(value,"msb:routing:custom:",""),":info",""),":lb:server1","")
89                 key_set[name]=true                                                 
90         end
91         --取出所有的�?放到table中准备排�?
92         local key_table = {}
93         local index = 1
94         for key,_ in pairs(key_set) do
95                 --为了避免效率问题,暂时不用table.insert()
96                 --table.insert(key_table,key)
97                 key_table[index] = key
98                 index = index + 1
99         end
100         --对所有键进行倒序排序,用于实现最长前缀匹配
101         table.sort(key_table, function (a, b)
102                         return a > b
103                 end)
104
105         local servicenames = ""
106         local delimiter = "<>"
107         for i=1,#key_table do
108                 servicenames=servicenames..key_table[i]..delimiter
109         end
110
111         -- get the local cache
112         local cache = ngx.shared.ceryx
113         -- Save all keys to local cache for 30 seconds(0.5 minutes)
114         cache:set("customrouter:allkeys", servicenames, 30)
115         return servicenames;
116 end
117
118 local function query_router_info()
119         local uri = ngx.var.uri
120         ngx.log(ngx.WARN, "==uri:", uri)
121
122         -- Check if key exists in local cache
123         local cache = ngx.shared.ceryx
124         local key, flags = cache:get("custom:key:"..uri)   
125         local server, flags = cache:get("custom:server:"..uri)
126         local url, flags = cache:get("custom:url:"..uri)   
127         if key and server and url then
128                 ngx.var.key = key
129                 ngx.var.server = server
130                 ngx.var.url = url
131                 ngx.log(ngx.WARN, "==using custom cache:", key.."&&"..server.."&&"..url)
132                 return
133         end
134
135         local redis = require "resty.redis"
136         local red = redis:new()
137         red:set_timeout(1000) -- 1000 ms
138         local redis_host = "127.0.0.1" 
139         local redis_port = 6379 
140         local res, err = red:connect(redis_host, redis_port)
141
142         -- Return if could not connect to Redis
143         if not res then
144                 ngx.log(ngx.ERR, "connect to redis error:", err)
145                 return
146         end
147
148         -- Check if all servicenames exists in local cache
149         local servicenames, flags = cache:get("customrouter:allkeys")
150         if servicenames then 
151                 ngx.log(ngx.WARN,"==get all keys from cache:",servicenames)
152         else
153                 servicenames = query_allkeys_updatecache(red)
154         end
155
156         local delimiter = "<>"
157         -- '.-' 表示最短匹�?
158         for key in string.gmatch(servicenames,"(.-)"..delimiter) do
159                 ngx.log(ngx.WARN, "==key_table key:", key)
160                 local from, to, err = ngx.re.find(uri, "^"..key.."(/(.*))?$", "jo")
161                 --判断key是否为输入uri�?前缀"
162                 if from then
163                         ngx.log(ngx.WARN,"Matched! start-end:",from,"-",to)
164                         local result = query_ipurl_updatecache(red,key)
165                         if result then
166                                 break
167                         end
168                 else
169                         ngx.log(ngx.WARN,"not Matched")
170                         if err then
171                                 ngx.log(ngx.WARN,"ngx.re.find throw error: ",err)
172                                 return
173                         end
174                 end
175         end
176
177         return close_redis(red)
178 end
179
180 local function rewrite_router_url()
181         local server=ngx.var.server
182         if server=="fallback" then
183                 ngx.status = ngx.HTTP_NOT_FOUND
184                 ngx.exit(ngx.status)
185         end
186         local url=ngx.var.url
187         local key=ngx.var.key
188         local rewriteduri = ngx.re.sub(ngx.var.uri, "^"..key.."(.*)", url.."$1", "o")
189         ngx.log(ngx.WARN, "==rewrited uri:", rewriteduri)
190         ngx.req.set_uri(rewriteduri)
191 end
192
193 query_router_info()
194 rewrite_router_url()