PageRenderTime 54ms CodeModel.GetById 6ms RepoModel.GetById 0ms app.codeStats 0ms

/README.md

https://github.com/ztoh/nginx-clojure
Markdown | 707 lines | 535 code | 172 blank | 0 comment | 0 complexity | 51113b9072392949151c13343e76a6a0 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. Nginx-Clojure
  2. =============
  3. ![Alt text](logo.png)Nginx-Clojure is a [Nginx](http://nginx.org/) module for embedding Clojure or Java or Groovy programs, typically those [Ring](https://github.com/ring-clojure/ring/blob/master/SPEC) based handlers.
  4. There are some core features :
  5. 1. Compatible with [Ring](https://github.com/ring-clojure/ring/blob/master/SPEC) and obviously supports those Ring based frameworks, such as Compojure etc.
  6. 1. Use Clojure / Java / Groovy(**_NEW_** ) to write simple ring handlers for http services.
  7. 1. Use Clojure / Java / Groovy(**_NEW_** ) to write a simple nginx rewrite handler to set var or return errors before proxy pass or content ring handler
  8. 1. Non-blocking coroutine based socket which is Compatible with Java Socket API and works well with largely existing java library such as apache http client, mysql jdbc drivers.
  9. With this feature one java main thread can handle thousands of connections.
  10. 1. Handle multiple sockets parallel in sub coroutines, e.g. we can invoke two remote services at the same time feature
  11. 1. Asynchronous callback API of socket for some advanced usage
  12. 1. Run initialization clojure code when nginx worker starting
  13. 1. Support user defined http request method
  14. 1. Compatible with the Nginx lastest stable version 1.6.0. (Nginx 1.4.x is also ok, older version is not tested and maybe works.)
  15. 1. One of benifits of [Nginx](http://nginx.org/) is worker processes are automatically restarted by a master process if they crash
  16. 1. Utilizes lazy headers and direct memory operation between [Nginx](http://nginx.org/) and JVM to fast handle dynamic contents from Clojure or Java code.
  17. 1. Utilizes [Nginx](http://nginx.org/) zero copy file sending mechanism to fast handle static contents controlled by Clojure or Java code.
  18. 1. Supports Linux x64, Linux x86 32bit, Win32 and Mac OS X. Win64 users can also run it with a 32bit JRE/JDK.
  19. By the way it is very fast, the benchmarks can be found [HERE](https://github.com/ptaoussanis/clojure-web-server-benchmarks) .
  20. 1. Installation
  21. =============
  22. The lastest release is 0.2.4. Please check the [Update History](HISTORY.md) for more details.
  23. 1.1 Installation by Binary
  24. -------------
  25. 1. First you can download Release 0.2.4 from [here](https://sourceforge.net/projects/nginx-clojure/files/).
  26. The zip file includes Nginx-Clojure binaries about Linux x64, Linux i586, Win32 and Mac OS X.
  27. 1. Unzip the zip file downloaded then rename the file `nginx-${os-arc}` to `nginx`, eg. for linux is `nginx-linux-x64`
  28. 1.2 Installation by Source
  29. -------------
  30. Nginx-Clojure may be compiled successfully on Linux x64, Win32 and Mac OS X x64.
  31. 1. First download from [nginx site](http://nginx.org/en/download.html) or check out nginx source by hg from http://hg.nginx.org/nginx.
  32. For Win32 users MUST check out nginx source by hg because the zipped source doesn't contain Win32 related code.
  33. 1. Check out Nginx-Clojure source from github OR download the zipped source code from https://github.com/xfeep/nginx-clojure/releases
  34. 1. If you want to use Http SSL module, you should install openssl and openssl dev first.
  35. 1. Setting Java header include path in nginx-clojure/src/c/config
  36. ```nginx
  37. #eg. on ubuntu
  38. JNI_HEADER_1="/usr/lib/jvm/java-7-oracle/include"
  39. JNI_HEADER_2="${JNI_HEADER_1}/linux"
  40. ````
  41. 1. Add Nginx-Clojure module to Nginx configure command, here is a simplest example without more details about [InstallOptions](http://wiki.nginx.org/InstallOptions)
  42. ```bash
  43. #If nginx source is checked out from hg, please replace ./configure with auto/configure
  44. $./configure \
  45. --add-module=nginx-clojure/src/c
  46. $ make
  47. $ make install
  48. ```
  49. 1. Create the jar file about Nginx-Clojure
  50. Please check the lein version `lein version`, it should be at least 2.0.0.
  51. ```bash
  52. $ cd nginx-clojure
  53. $ lein jar
  54. ```
  55. Then you'll find nginx-clojure-${version}.jar (eg. nginx-clojure-0.2.4.jar) in the target folder.
  56. The jar file is self contained. If your project use clojure it naturally depends on the clojure core jar, e.g clojure-1.5.1.jar.
  57. If your project use groovy it naturally depends on the groovy runtime jar, e.g. groovy-2.3.4.jar.
  58. 2. Configurations
  59. =================
  60. 2.1 JVM Path , Class Path & Other JVM Options
  61. -----------------
  62. Setting JVM path and class path within `http {` block in nginx.conf
  63. ```nginx
  64. #for win32, jvm_path maybe is "C:/Program Files/Java/jdk1.7.0_25/jre/bin/server/jvm.dll";
  65. #for macosx, jvm_path maybe is "/Library/Java/JavaVirtualMachines/1.6.0_65-b14-462.jdk/Contents/Libraries/libserver.dylib";
  66. #for ubuntu, jvm_path maybe is "/usr/lib/jvm/java-7-oracle/jre/lib/amd64/server/libjvm.so";
  67. #for centos, jvm_path maybe is "/usr/java/jdk1.6.0_45/jre/lib/amd64/server/libjvm.so";
  68. #for centos 32bit, jvm_path maybe is "/usr/java/jdk1.7.0_51/jre/lib/i386/server/libjvm.so";
  69. jvm_path "/usr/lib/jvm/java-7-oracle/jre/lib/amd64/server/libjvm.so";
  70. #jvm_options can be repeated once per option.
  71. #for clojure, you should append clojure core jar, e.g -Djava.class.path=jars/nginx-clojure-0.2.4.jar:mypath-xxx/clojure-1.5.1.jar,please replace ':' with ';' on windows
  72. #for groovy, you should append groovy runtime jar, e.g. -Djava.class.path=jars/nginx-clojure-0.2.4.jar:mypath-xxx/groovy-2.3.4.jar, please replace ':' with ';' on windows
  73. jvm_options "-Djava.class.path=jars/nginx-clojure-0.2.4.jar";
  74. #jvm heap memory
  75. jvm_options "-Xms1024m";
  76. jvm_options "-Xmx1024m";
  77. #for enable java remote debug uncomment next two lines, make sure "master_process = off;" or "worker_processes = 1;"
  78. #jvm_options "-Xdebug";
  79. #jvm_options "-Xrunjdwp:server=y,transport=dt_socket,address=8400,suspend=n";
  80. ````
  81. ###Some Useful Tips
  82. These tips are really useful. Most of them are from real users. Thanks [Rickr Nook](https://github.com/rickr-nook) who give us some useful tips.
  83. 1. When importing Swing We Must specifiy `jvm_options "-Djava.awt.headless=true"` , otherwise the nginx will hang.
  84. 1. By adding the location of your clojure source files to the classpath,then just issue "nginx -s reload" and changes to the sources get picked up!
  85. 1. You can remove clojure-1.5.1.jar from class path and point at your "lein uberjar" to pick up a different version of clojure.
  86. 1. To use Java 7 on OSX, in nginx.conf your may set `jvm_path "/Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/server/libjvm.dylib";`
  87. 2.2 Initialization Handler for nginx worker
  88. -----------------
  89. You can embed clojure/groovy code in the `http { ` block to do initialization when nginx worker starting. e.g
  90. ```nginx
  91. http {
  92. ......
  93. handler_type 'clojure'; # or handler_type 'groovy'
  94. handler_code '....'; #the same with Ring Handler for Location (details in next section)
  95. ....
  96. }
  97. ```
  98. Or you can reference an exteranl clojure/java/groovy ring handler for initialization when nginx worker starting.
  99. ```nginx
  100. http {
  101. ......
  102. handler_type 'clojure'; # or handler_type 'java' / handler_type 'groovy'
  103. handler_name 'my.test/InitHandler'; # or for java it maybe 'my.test.MyJavaInitHandler'
  104. ....
  105. }
  106. ```
  107. The ring handler can use status 500 and body to report some errors or just return nothing.
  108. For more detail example of ring handler please see the next secion.
  109. Please Keep these in your mind:
  110. * By default if the initialization failed the nginx won't start successfully and the worker will exit after reporting an error message in error log file but the master keep running and take the port.
  111. * Because the maybe more than one nginx worker processes, so this code will run everytime per worker starting.
  112. * If you use [SharedHashMap](https://github.com/OpenHFT/HugeCollections/wiki/Getting-Started) to share data
  113. among nginx worker processes, Java file lock can be used to let only one nginx worker process do the initialization.
  114. * If you enabled [coroutine support](#), nginx maybe will start successfully even if your initialization failed after some socket operations. If you case it, you can
  115. use `nginx.clojure.core/without-coroutine` to wrap your handler, e.g.
  116. For clojure
  117. ```nginx
  118. handler_code '
  119. (do
  120. (use \'nginx.clojure.core)
  121. (without-coroutine
  122. (fn[ctx]
  123. ....
  124. )
  125. ))
  126. ';
  127. ```
  128. 2.3 Ring Handler for Location
  129. -----------------
  130. Within `location` block,
  131. * Directive `handler_type` is used to setting a type of handler.
  132. * Directive `handler_code` is used to setting an inline Ring handler.
  133. * Directive `handler_name` is used to setting an external Ring handler which is in a certain jar file included by your classpath.
  134. ###2.3.1 Inline Ring Handler
  135. For Clojure :
  136. ```nginx
  137. location /clojure {
  138. handler_type 'clojure';
  139. handler_code '
  140. (fn[req]
  141. {
  142. :status 200,
  143. :headers {"content-type" "text/plain"},
  144. :body "Hello Clojure & Nginx!" ;response body can be string, File or Array/Collection/Seq of them
  145. })
  146. ';
  147. }
  148. ```
  149. Now you can start nginx and access http://localhost:8080/clojure, if some error happens please check error.log file.
  150. For Groovy :
  151. ```nginx
  152. location /groovy {
  153. handler_type 'groovy';
  154. handler_code '
  155. import nginx.clojure.java.NginxJavaRingHandler;
  156. import java.util.Map;
  157. public class HelloGroovy implements NginxJavaRingHandler {
  158. public Object[] invoke(Map<String, Object> request){
  159. return [200, //http status 200
  160. ["Content-Type":"text/html"], //headers map
  161. "Hello, Groovy & Nginx!"]; //response body can be string, File or Array/Collection of them
  162. }
  163. }
  164. ';
  165. }
  166. ```
  167. Now you can start nginx and access http://localhost:8080/groovy, if some error happens please check error.log file.
  168. ###2.3.2 Reference of External Ring Handlers
  169. Please make sure the external Ring handler is in a certain jar file or a directory included by your classpath.
  170. It is also OK if you do not compile the Clojure/Groovy to java class file and just put the source of them in a certain jar file or a directory included by your classpath.
  171. For Clojure the exteranl Ring handler example is here
  172. ```clojure
  173. (ns my.hello)
  174. (defn hello-world [request]
  175. {:status 200
  176. :headers {"Content-Type" "text/plain"}
  177. ;response body can be string, File or Array/Collection/Seq of them
  178. :body "Hello World"})
  179. ```
  180. Then we can reference it in nginx.conf
  181. ```nginx
  182. location /myClojure {
  183. handler_type 'clojure';
  184. handler_name 'my.hello/hello-world';
  185. }
  186. ```
  187. For more details and more useful examples for [Compojure](https://github.com/weavejester/compojure) which is a small routing library for Ring that allows web applications to be composed of small, independent parts. Please refer to https://github.com/weavejester/compojure
  188. For Java
  189. ```java
  190. package mytest;
  191. import static nginx.clojure.MiniConstants.*;
  192. import java.util.HashMap;
  193. import java.util.Map;
  194. public class Hello implements NginxJavaRingHandler {
  195. @Override
  196. public Object[] invoke(Map<String, Object> request) {
  197. return new Object[] {
  198. NGX_HTTP_OK, //http status 200
  199. ArrayMap.create(CONTENT_TYPE, "text/plain"), //headers map
  200. "Hello, Java & Nginx!" //response body can be string, File or Array/Collection of them
  201. };
  202. }
  203. }
  204. ```
  205. ```nginx
  206. location /myJava {
  207. handler_type 'java';
  208. handler_name 'mytest.Hello';
  209. }
  210. ```
  211. For Groovy
  212. ```groovy
  213. package mytest;
  214. import nginx.clojure.java.NginxJavaRingHandler;
  215. import java.util.Map;
  216. public class HelloGroovy implements NginxJavaRingHandler {
  217. public Object[] invoke(Map<String, Object> request){
  218. return
  219. [200, //http status 200
  220. ["Content-Type":"text/html"],//headers map
  221. "Hello, Groovy & Nginx!" //response body can be string, File or Array/Collection of them
  222. ];
  223. }
  224. }
  225. ```
  226. ###2.3.3 Pure Java Handler
  227. The section is **__deprecated__**. Please check the above section and use new derective `handler_type` and `handler_name` for easier work.
  228. ```java
  229. package my;
  230. import nginx.clojure.Constants;
  231. import clojure.lang.AFn;
  232. import clojure.lang.IPersistentMap;
  233. import clojure.lang.PersistentArrayMap;
  234. public class HelloHandler extends AFn {
  235. @Override
  236. public Object invoke(Object r) {
  237. IPersistentMap req = (IPersistentMap)r;
  238. //get some info from req. eg. req.valAt(Constants.QUERY_STRING)
  239. //....
  240. //prepare resps, more details about Ring handler on this site https://github.com/ring-clojure/ring/blob/master/SPEC
  241. Object[] resps = new Object[] {Constants.STATUS, 200,
  242. Constants.HEADERS, new PersistentArrayMap(new Object[]{Constants.CONTENT_TYPE.getName(),"text/plain"}),
  243. Constants.BODY, "Hello Java & Nginx!"};
  244. return new PersistentArrayMap(resps);
  245. }
  246. }
  247. ```
  248. In nginx.conf, eg.
  249. ```nginx
  250. location /java {
  251. clojure;
  252. clojure_code '
  253. (do (import \'[my HelloHandler]) (HelloHandler.) )
  254. ';
  255. }
  256. ```
  257. You should set your JAR files to class path, see [2.1 JVM Path , Class Path & Other JVM Options](#21-jvm-path--class-path--other-jvm-options) .
  258. 2.4 Chose Coroutine based Socket Or Asynchronous Socket Or Thread Pool for slow I/O operations
  259. -----------------
  260. If the http service should do some slow I/O operations such as access external http service, database, etc. nginx worker will be blocked by those operations
  261. and the new user request even static file request will be blocked. It really sucks Before v0.2.0 the only choice is using thread pool but now we have
  262. three choice :
  263. 1. Coroutine based Socket
  264. * :smiley:It's Java Socket API Compatible and work well with largely existing java library such as apache http client, mysql jdbc drivers etc.
  265. * :smiley:It's non-blocking, cheap, fast and let one java main thread be able to handle thousands of connections.
  266. * :smiley:Your old code **_need not be changed_** and those plain and old java socket based code such as Apache Http Client, MySQL mysql jdbc drivers etc. will be on the fly with epoll/kqueue on Linux/BSD!
  267. * :worried:You must do some steps to get the right class waving configuration file and set it in the nginx conf file.
  268. 1. Asynchronous Socket
  269. * :smiley:It's the fastest among those three choice and you can controll it finely.
  270. * :smiley:It can work with default mode or Coroutine based Socket enabled mode but can't work with Thread Pool mode.
  271. * :worried:Your old code **_must be changed_** to use the event driven pattern.
  272. 1. Thread Pool
  273. * :smiley:It's a trade off choice and almost all Java server such as Jetty, Tomcat , Glassfish etc. use thread pool to handle http requests.
  274. * :smiley:Your old code **_need not be changed_**.
  275. * :worried:The nginx worker will be blocked after all threads are exhuasted by slow I/O operations.
  276. * :worried:Becase the max number of threads is alwarys more smaller than the total number of socket connections supported by Operation Systems and
  277. thread in java is costlier than coroutine, facing large amount of connections this choice isn't as good as Coroutine based choice.
  278. ### 2.4.1 Enable Coroutine based Socket
  279. #### 1. Get a User Defined Class Waving Configuration File for Your Web App
  280. * Turn on Run Tool Mode
  281. ```nginx
  282. http {
  283. ...
  284. #To make sure generated Class Waving Configuration File won't be mixed by many workers.
  285. worker_processes 1;
  286. #turn on run tool mode, t means Tool
  287. jvm_options "-javaagent:jars/nginx-clojure-0.2.4.jar=tmb";
  288. #for clojure, you should append clojure core jar, e.g -Djava.class.path=jars/nginx-clojure-0.2.4.jar:mypath-xxx/clojure-1.5.1.jar,please replace ':' with ';' on windows
  289. jvm_options "-Xbootclasspath/a:jars/nginx-clojure-0.2.4.jar";
  290. ...
  291. }
  292. ```
  293. * Setting Output Path of Waving Configuration File
  294. ```nginx
  295. #Optional The default value is $nginx-workdir/nginx.clojure.wave.CfgToolOutFile
  296. #Setting Output Path of Waving Configuration File,
  297. jvm_options "-Dnginx.clojure.wave.CfgToolOutFile=/tmp/my-wave-cfg.txt";
  298. ```
  299. * Setting Dump Configuration Service
  300. ```nginx
  301. location /dump {
  302. handler_type 'java';
  303. handler_name 'nginx.clojure.java.WaveConfigurationDumpHandler';
  304. }
  305. ```
  306. * Start Nginx which Compiled with Nginx Clojure Module
  307. * Run curl or httpclient based junit tests to access your http services which directly or indirectly use Java Socket API, e.g Apache Http Client, MySQL JDBC Driver etc.
  308. * After All responses completed We'll get a generated class waving configuration file e.g `my-wave-cfg.txt` by access Dump Configuration Service.
  309. ```bash
  310. /*use curl or just put the Dump Service url to browser and click GO!
  311. *Dump Service will generate Waving Configuration File to the path defined by
  312. *java system property `nginx.clojure.wave.CfgToolOutFile`
  313. */
  314. curl -v http://localhost:8080/dump
  315. ```
  316. Don't foget reset `worker_processes` and turn off run tool mode for product enviroument after get class waving configuration
  317. #### 2. Enable Coroutine Support
  318. * Turn on Coroutine Support
  319. ```nginx
  320. http {
  321. ...
  322. #make sure it is reset to a normal number after above step, e.g. 8 worker processes.
  323. worker_processes 8;
  324. #turn on coroutine mode
  325. jvm_options "-javaagent:jars/nginx-clojure-0.2.4.jar=mb";
  326. #append nginx-clojure & clojure runtime jars to jvm bootclasspath
  327. #for win32, class path seperator is ";", e.g "-Xbootclasspath/a:jars/nginx-clojure-0.2.4.jar;jars/clojure-1.5.1.jar"
  328. jvm_options "-Xbootclasspath/a:jars/nginx-clojure-0.2.4.jar:jars/clojure-1.5.1.jar";
  329. #coroutine-udfs is a directory to put your User Defined Class Waving Configuration File
  330. #for win32, class path seperator is ";", e.g "-Djava.class.path=coroutine-udfs;YOUR_CLASSPATH_HERE"
  331. #Note: DON NOT put nginx-clojure & clojure runtime jars here, because they have been appened to the jvm bootclasspath
  332. jvm_options "-Djava.class.path=coroutine-udfs:YOUR_CLASSPATH_HERE";
  333. #copy the waving configuration file generated from previous step to you any classpath dir e.g. coroutine-udfs
  334. #setting user defined class waving configuration files which are in the above boot classpath, the seperator is ","
  335. jvm_options "-Dnginx.clojure.wave.udfs=my-wave-cfg.txt";
  336. ...
  337. }
  338. ```
  339. * restart nginx or reload nginx
  340. Now every nginx worker can handle thousands of connections easily!
  341. Those plain and old java socket based code such as Apache Http Client, MySQL mysql jdbc drivers etc. will be on the fly with epoll/kqueue on Linux/BSD!
  342. Nginx won't blocked until nginx connections exhuasted or jvm OutOfMemory!
  343. ### 2.4.2 Use Asynchronous Socket
  344. Asynchronous Socket Can be used with default mode or coroutined enabled mode without any additional settings. It just a set of API.
  345. It uses event driven pattern and works with a java callback handler or clojure function for callback.
  346. Please check the source code and examples for more details.
  347. * source: [nginx.clojure.net.NginxClojureAsynSocket](src/java/nginx/clojure/net/NginxClojureAsynSocket.java)
  348. * example:
  349. * Java [nginx.clojure.net.SimpleHandler4TestNginxClojureAsynSocket](test/java/nginx/clojure/net/SimpleHandler4TestNginxClojureAsynSocket.java)
  350. * Clojure [nginx.clojure.asyn-socket-handlers-for-test](test/clojure/nginx/clojure/asyn_socket_handlers_for_test.clj)
  351. In future we'll give more clojure style wrapper and examples. **_Pull requests are also welcome!_**
  352. ### 2.4.3 Use Thread Pool
  353. If your tasks are often blocked by slow I/O operations, the thread pool method can make the nginx worker not blocked until
  354. all threads are exhuasted. When facing large amount of connections this choice isn't as good as above coroutine based choice or asynchronous socket.
  355. eg.
  356. ```nginx
  357. #turn off coroutine mode, n means do nothing. You can also comment this line to turn off coroutine mode
  358. jvm_options "-javaagent:jars/nginx-clojure-0.2.4.jar=nmb";
  359. jvm_workers 40;
  360. ```
  361. Now Nginx-Clojure will create a thread pool with fixed 40 threads per JVM instance/Nginx worker to handle requests. If you get more memory, you can set
  362. a bigger number.
  363. 2.5 Nginx rewrite handler
  364. -----------------
  365. A nginx rewrite handler can be used to set var or return errors before proxy pass or content ring handler.
  366. If the rewrite handler returns `phrase-done` (Clojure) or `PHRASE_DONE` (Groovy/Java), nginx will continue to invoke proxy_pass or
  367. content ring handler.
  368. If the rewrite handler returns a general response, nginx will send this response to the client and stop to continue to invoke proxy_pass or
  369. content ring handler.
  370. ### 2.5.1 Simple Example about Nginx rewrite handler
  371. Here's a simple clojure example for Nginx rewrite handler :
  372. ```nginx
  373. set $myvar "";
  374. location /rewritesimple {
  375. handler_type 'clojure';
  376. handler_rewrite_code '
  377. (do (use \'[nginx.clojure.core])
  378. (fn[req]
  379. (set-ngx-var! req "myvar" "Hello")
  380. phrase-done))
  381. ';
  382. handler_code '
  383. (do (use \'[nginx.clojure.core])
  384. (fn[req]
  385. (set-ngx-var! req "myvar"
  386. (str (get-ngx-var req "myvar") "," "Xfeep!"))
  387. {
  388. :status 200,
  389. :headers {"content-type" "text/plain"},
  390. :body (get-ngx-var req "myvar")
  391. }))
  392. ';
  393. }
  394. ```
  395. ### 2.5.2 Simple Dynamic Balancer By Nginx rewrite handler
  396. We can alos use this feature to complete a simple dynamic balancer , e.g.
  397. ```nginx
  398. set $myhost "";
  399. location /myproxy {
  400. handler_type 'clojure';
  401. handler_rewrite_code '
  402. (do (use \'[nginx.clojure.core])
  403. (fn[req]
  404. ;compute myhost (upstream name or real host name) based req & remote service, e.g.
  405. (let [myhost (compute-myhost req)])
  406. (set-ngx-var! req "myhost" myhost)
  407. phrase-done))
  408. ';
  409. proxy_pass $myhost
  410. }
  411. ```
  412. The equivalent java code is here
  413. ```java
  414. package my.test;
  415. import static nginx.clojure.java.Constants.*;
  416. public static class MyRewriteProxyPassHandler implements NginxJavaRingHandler {
  417. @Override
  418. public Object[] invoke(Map<String, Object> req) {
  419. String myhost = computeMyHost(req);
  420. NginxClojureRT.setNGXVariable(req.nativeRequest(), "myhost", myhost);
  421. return PHRASE_DONE;
  422. }
  423. private String computeMyHost(Map<String, Object> req) {
  424. //compute a upstream name or host name;
  425. }
  426. }
  427. ```
  428. Then we set the java rewrtite handler in nginx.conf
  429. ```nginx
  430. set $myhost "";
  431. location /myproxy {
  432. handler_type 'java';
  433. handler_rewrite_name 'my.test.MyRewriteProxyPassHandler';
  434. proxy_pass $myhost
  435. }
  436. ```
  437. ### 2.5.3 Simple Access Controller By Nginx rewrite handler
  438. For clojure
  439. ```nginx
  440. handler_type 'clojure';
  441. handler_rewrite_code '
  442. (do (use \'[nginx.clojure.core])
  443. (import \'[com\.test AuthenticationHandler])
  444. (fn[req]
  445. (if ((AuthenticationHandler.) req)
  446. ;AuthenticationHandler returns true so we go to proxy_pass
  447. phrase-done
  448. ;else return 403
  449. {:status 403}
  450. )))
  451. ';
  452. proxy_pass http://localhost:8084;
  453. ```
  454. For Java
  455. * nginx.conf
  456. ```nginx
  457. handler_type 'java';
  458. handler_rewrite_name 'com.test.MyHandler';
  459. proxy_pass http://localhost:8084;
  460. ```
  461. * MyHandler.java
  462. ```java
  463. package com.test;
  464. import static nginx.clojure.java.Constants.*;
  465. public class MyHandler implements NginxJavaRingHandler {
  466. public Object[] invoke(Map<String,Object> req) {
  467. /*do some computing here*/
  468. if (goto-proxy-pass) {
  469. return PHRASE_DONE;
  470. }else { //return 403
  471. return Object[] resps = new Object[] {
  472. Constants.STATUS, 403,
  473. //add some headers -- optional for no-20X response
  474. //Constants.HEADERS, nginx.clojure.java.ArrayMap.create(new Object[]{CONTENT_TYPE,"text/plain"}),
  475. //body text -- optional for no-20X response
  476. // Constants.BODY, "xxxxxxxxxxxxxx!"
  477. };
  478. }
  479. }
  480. ```
  481. 3. More about Nginx-Clojure
  482. =================
  483. 3.1 Handle Multiple Coroutine Based Sockets Parallel
  484. -----------------
  485. Sometimes we need invoke serveral remote services before completing the ring response. For better performance we need a way to handle multiple sockets parallel in sub coroutines.
  486. e.g. fetch two page parallel by clj-http
  487. ```clojure
  488. (let [[r1, r2]
  489. (co-pvalues (client/get "http://service1-url")
  490. (client/get "http://service2-url"))]
  491. ;println bodies of two remote response
  492. (println (str (:body r1) "====\n" (:body r2) ))
  493. ```
  494. Here `co-pvalues` is also non-blocking and coroutine based. In fact it will create two sub coroutines to handle two sockets.
  495. For Java/Groovy, we can use `NginxClojureRT.coBatchCall` to do the same thing. Here 's a simple example for Groovy.
  496. ```groovy
  497. def (r1, r2) = NginxClojureRT.coBatchCall(
  498. {"http://mirror.bit.edu.cn/apache/httpcomponents/httpclient/".toURL().text},
  499. {"http://mirror.bit.edu.cn/apache/httpcomponents/httpcore/".toURL().text})
  500. return [200, ["Content-Type":"text/html"], r1 + r2];
  501. ```
  502. 3.2 Shared Map among Nginx Workers
  503. -----------------
  504. Generally use redis or memorycached is the better choice to implement a shared map among Nginx workers. We can do some initialization of the
  505. shared map by following the guide of [2.2 Initialization Handler for nginx worker](#22-initialization-handler-for-nginx-worker).
  506. If you like shared map managed in nginx process better than redis or memcached, you can choose [SharedHashMap](https://github.com/OpenHFT/HugeCollections/wiki)
  507. which is fast and based on Memory Mapped File so that it can store large amout of records and won't need too much java heap memory.
  508. 3.3 User Defined Http Method
  509. -----------------
  510. Some web services need user defined http request method to define special operations beyond standard http request methods.
  511. e.g. We use `MYUPLOAD` to upload a file and overwrite the one if it exists. The `curl` command maybe is
  512. ```bash
  513. curl -v -X MYUPLOAD --upload-file post-test-data \
  514. "http://localhost:8080/myservice"
  515. ```
  516. In the nginx.conf, we can use `always_read_body on;` to force nginx to read http body.
  517. ```nginx
  518. location /myservice {
  519. handler_type 'clojure';
  520. always_read_body on;
  521. handler_code '....';
  522. }
  523. ```
  524. 4. Useful Links
  525. =================
  526. * [Ring Documents](https://github.com/ring-clojure/ring/wiki)
  527. * [Comojure Documents](https://github.com/weavejester/compojure/wiki)
  528. * [Simple Examples](test/nginx-working-dir/conf/nginx.conf) in Nginx Clojure Testing Configuration & [Testing Client Code](test/clojure/nginx/clojure/test_all.clj)
  529. * [Nginx Clojure Ring Handlers Examples for Testing](test/clojure/nginx/clojure/ring_handlers_for_test.clj) (Testing with Ring Core 1.2.1)
  530. 5. License
  531. =================
  532. Copyright © 2013-2014 Zhang, Yuexiang (xfeep) and released under the BSD 3-Clause license.
  533. This program uses:
  534. * Re-rooted ASM bytecode engineering library which is distributed under the BSD 3-Clause license
  535. * Modified Continuations Library Written by Matthias Mann is distributed under the BSD 3-Clause license