Nginx as an API agent
There are a number of reasons why you use Nginx as an API agent. First of all, because he is open source, and secondly, Nginx has a lot of installation base, he has a strong community support behind, and performance is very good. It's obvious to us that if open source software has the same solution, why should we use proprietary software?
Another great advantage is Nginx's support for Lua, Nginx+lua is a great combination that allows you to extend Nginx with a high-performance scripting language. Nginx has many methods that are self-contained, but there is no limit to using LUA.
The principle is simple. Is there a situation in which you prefer to use a Nginx API instead of its own method? Oh, you can be very simple to add.
Extended Target: Sentiment API (can be any API)
To showcase the strengths of Nginx and LUA, we will use a simple rest API to invoke sentiment without using any one line of API source code (which can be directly used on GitHub).
The sentiment API is a very basic API that returns a word or sentence with an emotional value analysis. For example, the following request (you can try it yourself)
Copy Code code as follows:
Curl Http://api-sentiment.3scale.net/v1/word/fantastic.json
The above request returns a JSON string containing the fantastic affective parsing word.
Copy Code code as follows:
{"Sentiment": 4, "word": "Fantastic"}
We have the extension object, let's go ahead ...
Analysis section: Extending the sentiment API
There are many ways you can extend the sentiment API (or your own API). To fit the theme of this article, we have defined three scenarios to demonstrate the Nginx+lua and scalability of the story.
1 want to do data conversion?
Want to convert output data from JSON to XML format? Or better yet, convert the XML to JSON.
2 want to change the signature of your API method?
You want to replace the beautiful rest-form URL Path/v1/word/word.json with the seemingly more "beautiful" signature method parameters/sentiment?action=word&word=word& Version=v1.
We can't tolerate the way the path:-) this should be a negative example, since the sentiment API changed to restful, this example should be reversed.
3 want to create a new API method?
No problem, you can create a new API to get what you want, or it's possible that you can extend your API methods without contacting any API source code.
We'll show you a new way to create a sentiment API: to find words that have the most emotional value in a sentence. This method is not available in the sentiment API, but we can create it through Nginx and Lua.
This case has a lot of potential for both users and API developers. You can basically allow yourself to customize the API without modifying the source code, or, this is a cool part that allows you to customize APIs that you can't control. Want to create your own method on a Twitter API that contains a series of methods? Of course, the result may be that your application code is more concise.
This is just three simple examples of using Nginx+lua extensions. There are other examples of how simple and powerful your API is just to highlight the use of Nginx+lua.
Let's start by doing something practical ...
Using LUA to extend Nginx
We assume that you should have an understanding of Nginx basic concepts (servers, locations, etc.)
Extension Nginx We must first provide LUA support, which is not part of the Ngnix. We don't have to worry because many components have been compiled into Lua, like this:
- Openresty (in 3scale)
- Tengine
If you insist on installing:-) yourself You can install the following components yourself:
- Lua Nginx Module
- Httpproxy Module
In fact, if you don't want to use Lua but prefer Perl, check out this page at the CPAN page, which provides all the documentation.
Basic section
The entire process is the agent request to the real API, mainly through the following process: 1 Capture request passed to API 2 response request, then 3 processing response.
The following shows the related configuration in the Nginx configuration file:
Copy Code code as follows:
Upstream Backend {
# service Name:api;
Server api-sentiment.3scale.net:80 max_fails=5 fail_timeout=30;
}
server {
Listen 8181;
Location ~/v1/word/(. *) \.json$ {
Proxy_pass Http://backend/v1/word/$1.json;
}
}
Here we have only configured a routing address:/v1/word/your-word-goes-here.json. This route returns a result on the sentiment API. Nginx is only responsible for making a simple pass.
You can start your nginx (listening to local port 8181) and send a request in the following way
Copy Code code as follows:
Curl Http://localhost:8181/v1/word/fantastic.json
It will return one of the same JSON
Copy Code code as follows:
{"Sentiment": 4, "word": "Fantastic"}
We just made a relay to the real sentiment API. Let's continue with our interest ...
1) Data Conversion
JSON to XML
Add a new route to the Nginx configuration file as follows:
Copy Code code as follows:
Upstream Backend {
# service Name:api;
Server api-sentiment.3scale.net:80 max_fails=5 fail_timeout=30;
}
server {
Listen 8181;
Location ~/v1/word/(. *) \.json$ {
Proxy_pass Http://backend/v1/word/$1.json;
}
Location ~/v1/word/(. *) \.xml$ {
Content_by_lua_file/path_to/json_to_xml.lua;
}
}
We have only added a new route:/v1/word/your-word-goes-here.xml. This route converts JSON from the sentiment API output to XML format. Instead of making a pass, we implement logic by calling a LUA file (don't worry, it's simple).
Now you can do the following work,
Curl Http://localhost:8181/v1/word/fantastic.xml
You will get the following information:
Copy Code code as follows:
<?xml version= "1.0" encoding= "UTF-8"?>
<response>
<sentiment>4</sentiment>
<word>fantastic</word>
</response>
What's going on here? Well, we basically converted the JSON data from the sentiment API output into XML format!
the magic of Lua
Converting JSON to XML requires a series of Lua Libs:
- Cjson: Install via luarocks or download manual installation on the project home page.
- Luaxml: We'll use a patch version to get him to work under Nginx, where you can download the patch version here
If you have problems installing Luaxml, you can install Luarocks as an alternative, put luaxml files in the Lua Lib directory within Openresty, and find the LUA Libs default directory is openresty.
When we access XML routing, Nginx will invoke the Lua file
Copy Code code as follows:
Local XML = require ("Luaxml")
Require ("OS")
Local Cjson = require "Cjson"
Local Path = Ngx.var.request:split ("") [2]
Local m = Ngx.re.match (path,[=[/([^/]+) \. ( Json|xml) $]=]--match last word
Local res = ngx.location.capture ("/v1/word/" ... m[1] ... ". JSON")
Local value=cjson.new (). Decode (Res.body)
Local response = xml.new ("response")
response.word= xml.new ("word")
Response.sentiment = Xml.new ("sentiment")
Response.timestamp = xml.new ("timestamp")
Table.insert (Response.word, Value.word)
Table.insert (Response.sentiment, value.sentiment)
Table.insert (Response.timestamp, Os.date ())
Ngx.say (' <?xml version= "1.0" encoding= "UTF-8"?> ", Xml.str (response,0))
This LUA file makes a local JSON request, using the following configuration
Copy Code code as follows:
Local res = ngx.location.capture ("/v1/word/" ... m[1] ... ". JSON")
It directly requests the real sentiment API, and once you have the JSON object, we can follow the rules into XML format, from
Copy Code code as follows:
{"Sentiment": 4, "word": "Fantastic"}
To
Copy Code code as follows:
<?xml version= "1.0" encoding= "UTF-8"?>
<response>
<sentiment>4</sentiment>
<word>fantastic</word>
</response>
Note that the split function does not exist in Lua, but you can refer to the But here for you.
Now that the conversion is a manual process, we need to know the field name of the JSON, but we can also assign the JSON object name to the specified XML tag in an automatic way.
Now that we have converted to XML, we want to add extra fields to the output XML, such as how to handle the timestamp?
Add a time stamp
In the LUA code block, you have the entire LUA environment variable free to use, so we use the OS module to get the current time.
We only need to add the following lines before the Ngx.say line.
Copy Code code as follows:
Require ("OS")
Response.timestamp = xml.new ("timestamp")
Table.insert (Bar.timestamp, Os.date ())
When we call/xml, we will output the following results from the API
Copy Code code as follows:
<?xml version= "1.0" encoding= "UTF-8"?>
<response>
<sentiment>3</sentiment>
<word>hello</word>
<timestamp>wed 9 15:34:56 2013</timestamp>
</response>
Cool, huh? It's not hard,:)
XML to JSON
To demonstrate the example we do a transformation from XML to JSON. Let's add a new configuration to the Nginx configuration file:
Copy Code code as follows:
Location ~ ^/round-trip/v1/word/(. *). json$ {
Content_by_lua_file/path_to/xml_to_json.lua;
}
The Xml_to_json.lua is as follows:
Copy Code code as follows:
Local XML = require ("Luaxml")
Local Cjson = require "Cjson"
Local Path = Ngx.var.request:split ("") [2]
Local m = Ngx.re.match (path,[=[/([^/]+) \.json]=])
Local res = ngx.location.capture ("/v1/word/" ... m[1] ... ". xml")
Local my_xml = Xml.eval (res.body)
Local sent_val = My_xml:find ("sentiment") [1]
Local word_val = My_xml:find ("word") [1]
Local T = {sentiment = sent_val, Word = word_val}
Local Value=cjson.encode (t)
Ngx.say (value)
As you can see, when we click on the XML endpoint route we just created, we will parse the XML using Luaxml and generate a reasonable JSON using Cjson.
Note that we do not follow any specification here, the conversion of XML to JSON is generally problematic because XML is better readable than JSON. In general, you need to follow certain specifications for conversion, such as Badgerfish or Parker, or the specifications you create yourself.
2) Overriding API methods
Using Nginx to rewrite your API methods is trivial, so it's easier to use their APIs for developers to employed. The classic example is the old twisted API, which we want to beautify to make it more friendly to rest.
One way to solve this problem is to modify the routing of API source code. However, many times you do not want to change the source code, although changes in the source code can be achieved, but is a backward way. To overcome the concerns of source code, you can add a layer to the nginx so that you don't have to touch and redeploy the odd code:-)
To illustrate with the example, we will transform a rest-like API approach
Copy Code code as follows:
For some of the more "beautiful" ways to use query parameters, the following are:
Copy Code code as follows:
/sentiment?action=word&version=v1&word=word
This "Upgrade" can be implemented in a variety of ways. For those familiar with Nginx, the problem can be solved simply by rewriting the rules. If you prefer to use the "sysadmin" approach, you can do the following:
Copy Code code as follows:
Location ~/sentiment$ {
Content_by_lua '
Local params = Ngx.req.get_query_args ()
if (params.action = = "word" and params.version ~= nil) Then
Local res= ngx.location.capture ("/" ... params.version.)
"/word/". Params.word.. ". JSON")
Ngx.say (Res.body)
End
';
}
Just like the above. The sentiment API now also accepts the following old API methods:
Copy Code code as follows:
Curl HTTP://LOCALHOST:8181/SENTIMENT?ACTION=WORD&WORD=FANTASTIC&VERSION=V1
This returns the expected JSON object.
3) Data Aggregation
Nginx and LUA can help us accomplish more complex things, like forming a new API approach based on different methods.
In the example, we extend the sentiment API by creating a new method that returns the most emotive words in a sentence.
Perhaps using this method is not worth talking about:-D but every time you want to do 4 method calls by calling an API, or you can invoke the other 3 different methods through a single task. You can assemble your methods into an API method for the application to call!
Let's continue to look at this example, first we need to add a new configuration,
Copy Code code as follows:
Location ~ ^/v1/max/(. *). json$ {
Content_by_lua_file/path_to/max.lua;
}
Next, we just need to write the aggregation method in the Lua script:
Copy Code code as follows:
Local Path = Ngx.var.request:split ("") [2]--Path
Local t={}
Local Cjson = require "Cjson"
Ngx.log (0, path[2])
Local m = Ngx.re.match (path,[=[^/v1/max/(. +). json]=])
Local words = M[1]:split ("+")--words in the sentence
Local Max = Nil
For i,k in pairs (words) do
Local Res_word = Ngx.location.capture ("/v1/word/" ... K.. ". JSON")
Local value=cjson.new (). Decode (Res_word.body)
If max = = Nil or max.sentiment < value.sentiment Then
max = value
End
End
Ngx.say (Cjson.new (). Encode (max))
As you can see, he can't be any simpler. First, we get the sentences, split the words, and then invoke the API request/v1/word for each word. We store the objects with higher value for emotional analysis.
The end result is simple, like the following request:
Copy Code code as follows:
Curl-g Http://localhost:8181/v1/max/nginx+and+lua+are+amazing.json
We get the words with the highest positive emotions,
Copy Code code as follows:
{"Sentiment": 4, "word": "Amazing"}
The logic of the Max.lua aggregate function can be as complex as you want, or you can get any API method, whether it's an API you can control.
Can I plug it in? It's perfectly OK. You can create any complex plug-ins and keep them invisible in the application.
Conclusion
The three examples we mentioned are just a toy-nature experiment using NGINX and LUA.
On 3scale, we've applied similar architectures to production environments, like some high load environments, and nothing makes us happier than this result.
We are constantly discovering that more and more places can use this feature, like a Netflix post post that recently reminded us that reducing the number of calls to APIs can achieve significant performance gains on some large traffic terminals or defective devices.
Nginx + LUA is a technology that changes the conventional, although it is not very common, but believe us, once you try you will be attracted by his strong, flexible and simple.
Extending an API has never been simpler. Have loved!