Resin Proxy Cache Support and REST support explained

From Resin 4.0 Wiki

(Difference between revisions)
Jump to: navigation, search
(Created page with " ==Resin provides real DevOps support== Resin is an end to end solution from load balancer, to http proxy cache, to cloud deployment. This is not to say that you could not u...")
 
 
Line 11: Line 11:
  
 
==JMX REST support==
 
==JMX REST support==
Resin has a nice API for managing Resin that is exposed via JMX.
+
Resin has a nice API for managing Resin that is exposed via JMX (Java Management Extensions).
 
But in this day of interoperability, cloud, REST and DevOps, it is not good enough to have a JMX interface, one must have a server that can  
 
But in this day of interoperability, cloud, REST and DevOps, it is not good enough to have a JMX interface, one must have a server that can  
 
be managed as a service via a REST interface.
 
be managed as a service via a REST interface.
  
All JMX operations and quite a few other operations are available via our [http://www.caucho.com/resin-4.0/admin/resin-admin-rest.xtp
+
All JMX operations and quite a few other operations are available via our [http://www.caucho.com/resin-4.0/admin/resin-admin-rest.xtp REST admin interface] as well as JMX and CLI.
REST admin interface] as well as JMX and CLI.
+
 
 +
==Let's create a simple page to demonstrate how the cache works==
 +
Let's create a really simple JSP file so we can focus more on the REST, JMX, etc. setup.
 +
Using a standard Resin 4.0.32 install, I place this file under '''/var/resin/webapps/ROOT/cache.jsp'''.
 +
 
 +
<pre>
 +
<%@ page session="false" %>
 +
<%! int counter; %>
 +
<%
 +
response.addHeader("Cache-Control", "max-age=150");
 +
%>
 +
Count: <%= counter++ %>
 +
</pre>
 +
 
 +
The above says cache the page for 150 seconds. If you enable Resin http proxy caching, Resin will cache this page for every client for 150 seconds.
 +
Let's say, we changed the data that backs this page, and we want a faster update, how would we tell Resin to evict this page out of cache.
 +
 
 +
(Side note: Since Resin http proxy cache is built into Resin, it knows about every Java web resource and can cache them all without the need to use a cache providers special server side includes. Your cache includes can just be jsp:include and <%@include, etc.)
 +
 
 +
When you load this page, it gets put into the Resin cache. With just the above, it does not matter if the end users hits shift refresh from their browser or whatever, this page is going to stay in the cache. (There are ways to enable some cache validation checking to be triggered from client, but that is for another lesson as it involves more cache controls like ETag, etc.)
 +
 
 +
==But first lets setup the http proxy cache==
 +
 
 +
 
 +
To enable the cache, you just need to modify /etc/resin/resin.properties and make sure the following two properties get set:
 +
 
 +
<pre>
 +
 
 +
# Enable the proxy-cache - for caching static content in memory
 +
proxy_cache_enable : true
 +
 
 +
# Sets the proxy cache memory size
 +
proxy_cache_size : 256m cache
 +
</pre>
 +
 
 +
Now the cache is setup, but like we mentioned earlier, there is no way to invalidate it. What if we know some process just happened and a certain page is invalid. What if you just did a data import or ran some batch job or received a new product xml file or whatever. Perhaps you have some backend script that stages this data, and now you need to kill this page out of the cache. Now in the Java world we have [http://wiki4.caucho.com/Resin_4_Application_Server_JMX_Tutorial JMX], and it is a nice way to interface with such things like caching, but what if these scripts were written in Ruby, Perl, Bash, or whatever. This is where the REST Admin service comes into play.
 +
 
 +
==Turning on REST support==
 +
To turn on the REST admin support, you guessed it, modify /etc/resin/resin.properties file as follows:
 +
 
 +
<pre>
 +
# Enable Resin REST Admin
 +
rest_admin_enable : true
 +
 
 +
# Require SSL for REST Admin
 +
# rest_admin_ssl : true
 +
</pre>
 +
 
 +
If you are not on an internal safe network, you should install open ssl support from the get go, and set '''rest_admin_ssl''' to true (it is commented out now). For now, I am going to assume that you are just messing around or you really trust your network's security and we will leave SSL support off. (We should come back and add it and change the examples accordingly.)
 +
 
 +
==Setting up a user for the rest calls==
 +
 
 +
Run the generate-password command to create a new password.
 +
 
 +
<pre>
 +
$ resinctl generate-password --user foobar --password foobar
 +
</pre>
 +
 
 +
'''Output'''
 +
<pre>
 +
admin_user : foobar
 +
admin_password : {SSHA}MW8h/2zwAk4Oqa3yfObDEKMcht3OKvil
 +
</pre>
 +
 
 +
Modify /etc/resin/resin.properties and put those two  entries in there. Now you have an admin user called foobar with the password foobar.
 +
You can add more users just search /etc/resin/*.xml and see where $admin_user is getting used.
 +
 
 +
==Invalidating the page from the command line with Curl==
 +
 
 +
If you don't see a Curl call to a REST example, then you should be suspicious.
 +
Thus without further ado, let's show using Curl to invalidate our proxy cache via a REST call to the Resin REST admin.
 +
 
 +
The easier way to invalidate a page is to you use the curl command as follows:
 +
 
 +
<pre>
 +
curl --user foobar:foobar
 +
      --data "values=.* .*cache.jsp"
 +
        "http://localhost:8080/resin-rest/jmx-call?pattern=resin:type=ProxyCache&operation=clearCacheByPattern"
 +
</pre>
 +
 
 +
(Newlines added for clarity).
 +
This is the return, which is just JSON.
 +
 
 +
<pre>
 +
{"bean":"resin:type=ProxyCache","operation":"clearCacheByPattern(java.lang.String, java.lang.String)","return-value":"null"}
 +
</pre>
 +
 
 +
All Resin jmx beans are exposed. Thus all of Resin jmx support is open to REST calls.
 +
 
 +
The '''--data "values=*. *cache.jsp"''' passes two arguments to the jmx method '''ProxyCache::clearCacheByPattern'''.
 +
The URL starts with the URI: '''/resin-rest/''' where you will find all of the Resin admin support.
 +
The URI under this /resin-rest/'''jmx-call''' (jmx-call) means we want to invoke a JMX method.
 +
The query param '''pattern=resin:type=ProxyCache''' means we want to call a method on the JMX bean '''resin:ProxyCache'''
 +
The query param '''operation=clearCacheByPattern''' means we want to call the '''clearCacheByPattern''' method.
 +
 
 +
You can also use curl to submit SSL/TLS requests. After you run this from a bash script or Perl you can go the test cache.jsp and it will reload as expected.
 +
 
 +
(see http://javadoc4.caucho.com/com/caucho/management/server/ProxyCacheMXBean.html)
 +
 
 +
 
 +
==REST calls from other languages==
 +
Here are some other example from other languages making REST calls.
 +
 
 +
 
 +
===Ruby Rest call to Resin JMX===
 +
<pre>
 +
require "net/http"
 +
require "uri"
 +
 
 +
$values = ".* .*cache.jsp"
 +
$clearCacheByPatternURL = "http://localhost:8080/resin-rest/jmx-call?pattern=resin:type=ProxyCache&operation=clearCacheByPattern"
 +
$connected=false
 +
 
 +
def main
 +
 
 +
    begin
 +
response = Net::HTTP.get_response(URI.parse("http://localhost:8080/"))
 +
        puts response.body
 +
        connected=true
 +
    rescue Net::HTTPExceptions => ex
 +
        connected=false
 +
        puts ("The error was " +  ex)
 +
    ensure
 +
        #conn.close() unless conn.nil?
 +
    end
 +
 
 +
    if connected
 +
        invalidateCache()
 +
    else
 +
        puts ("Resin does not appear to be up")
 +
    end
 +
end
 +
 
 +
 
 +
def invalidateCache()
 +
    begin
 +
uri = URI.parse($clearCacheByPatternURL)
 +
    http = Net::HTTP.new(uri.host, uri.port)
 +
    request = Net::HTTP::Post.new(uri.request_uri)
 +
    request.basic_auth("foobar", "foobar")
 +
        request.set_form_data({"values" => $values})
 +
    response = http.request(request)
 +
puts response.body
 +
puts "No error " + response.code
 +
    rescue Net::HTTPExceptions => ex
 +
        connected=false
 +
        puts ("The error was " +  ex)
 +
    ensure
 +
        #http.close() unless http.nil?
 +
    end
 +
 +
end
 +
 
 +
if __FILE__ == $0
 +
  main
 +
end
 +
</pre>
 +
 
 +
 
 +
===Python Rest call to Resin REST Admin===
 +
<pre>
 +
import httplib
 +
import base64
 +
 
 +
requestBody = "values=.* .*cache.jsp"
 +
host = "localhost"
 +
clearCacheByPatternURI = "/resin-rest/jmx-call?pattern=resin:type=ProxyCache&operation=clearCacheByPattern"
 +
connected=False
 +
 
 +
def main():
 +
    global connected
 +
    try:
 +
        conn = httplib.HTTPConnection(host, port=8080)
 +
        conn.request("GET", "/")
 +
        response = conn.getresponse()
 +
        data = response.read()
 +
        print (data)
 +
        connected=True
 +
    except httplib.HTTPException, ex:
 +
        connected=False
 +
        print ("The error was %s" % ex)
 +
    finally:
 +
        if conn:
 +
            conn.close()
 +
 
 +
    if connected:
 +
        invalidateCache()
 +
    else:
 +
        print ("Resin does not appear to be up")
 +
 
 +
def invalidateCache():
 +
    auth = base64.encodestring("foobar:foobar")
 +
    headers = {"Authorization" : "Basic %s" % auth, 
 +
              "Content-Type": "application/x-www-form-urlencoded"}
 +
 
 +
    try:
 +
        conn = httplib.HTTPConnection(host, port=8080)
 +
        conn.request("POST", clearCacheByPatternURI, requestBody, headers)
 +
        response = conn.getresponse()
 +
        data = response.read()
 +
        print (data)
 +
        print ("No error %s " % response.status)
 +
    except httplib.HTTPException, ex:
 +
        print ("err was %s" % ex)
 +
    finally:
 +
        if conn:
 +
            conn.close()
 +
   
 +
if  __name__ =='__main__':
 +
    main()
 +
 
 +
 
 +
</pre>
 +
 
 +
 
 +
===PERL Rest call to Resin REST Admin===
 +
<pre>
 +
coming soon
 +
</pre>
 +
 
 +
 
 +
===Java Rest call to Resin REST Admin===
 +
<pre>
 +
coming soon
 +
</pre>
 +
 
 +
 
 +
===Go programming Rest call to Resin REST Admin===
 +
<pre>
 +
package main
 +
 
 +
import (
 +
    "fmt"
 +
    "net/http"
 +
    "io/ioutil"
 +
    "bytes"
 +
)
 +
 
 +
var (
 +
    connected bool
 +
    client *http.Client = &http.Client{}
 +
    requestBody *bytes.Buffer = bytes.NewBufferString("values=.* .*cache.jsp")
 +
    clearCacheByPatternURL string = "http://localhost:8080/resin-rest/jmx-call" +
 +
                                "?pattern=resin:type=ProxyCache&operation=clearCacheByPattern"
 +
)
 +
 
 +
func main() {
 +
 
 +
if resp, err := http.Get("http://localhost:8080/"); err==nil {
 +
 
 +
        if body, err := ioutil.ReadAll(resp.Body); err==nil {
 +
                    fmt.Println(string(body))
 +
    connected = true
 +
                }
 +
 
 +
} else {
 +
      fmt.Println("The error was", err)
 +
}
 +
 
 +
if connected {
 +
  invalidateCache();
 +
} else {
 +
      fmt.Println("Resin does not appear to be up");
 +
}
 +
}
 +
 
 +
func invalidateCache() {
 +
    if request, err := http.NewRequest("POST", clearCacheByPatternURL, requestBody); err==nil {
 +
    fmt.Println(request.Method)
 +
request.SetBasicAuth("foobar", "foobar")
 +
        request.Header.Add("Content-Type", "application/x-www-form-urlencoded")
 +
response, err := client.Do(request)
 +
 
 +
        if body, err := ioutil.ReadAll(response.Body); err==nil {
 +
                    fmt.Println(string(body))
 +
    connected = true
 +
        }
 +
if err==nil{
 +
  fmt.Println("No error " + response.Status)
 +
} else {
 +
  fmt.Println("err was", err)
 +
}
 +
    }
 +
}
 +
</pre>
 +
 
 +
==Further reading==
 +
** [http://blog.caucho.com/2009/11/17/resin-rest-adminstration-interface/ Resin REST support]
 +
** [http://www.caucho.com/resin-4.0/admin/resin-admin-rest.xtp Resin REST Admin page]

Latest revision as of 00:00, 12 January 2013

Contents

Resin provides real DevOps support

Resin is an end to end solution from load balancer, to http proxy cache, to cloud deployment. This is not to say that you could not use Resin with NginX or Varnish or XYZ, because you can. This is to say that Resin's built in support is typically the fastest most scalable, most supportable option. To demonstrate this, let's show how we can use Resin built-in proxy cache (similar to Varnish or Squid) with Resin's REST admin support.

This article will not only help you with using our Proxy Cache Support, but also with learning about our REST administrations, the envy of DevOps who have to support Java everywhere.


JMX REST support

Resin has a nice API for managing Resin that is exposed via JMX (Java Management Extensions). But in this day of interoperability, cloud, REST and DevOps, it is not good enough to have a JMX interface, one must have a server that can be managed as a service via a REST interface.

All JMX operations and quite a few other operations are available via our REST admin interface as well as JMX and CLI.

Let's create a simple page to demonstrate how the cache works

Let's create a really simple JSP file so we can focus more on the REST, JMX, etc. setup. Using a standard Resin 4.0.32 install, I place this file under /var/resin/webapps/ROOT/cache.jsp.

<%@ page session="false" %>
<%! int counter; %>
<%
response.addHeader("Cache-Control", "max-age=150");
%>
Count: <%= counter++ %>

The above says cache the page for 150 seconds. If you enable Resin http proxy caching, Resin will cache this page for every client for 150 seconds. Let's say, we changed the data that backs this page, and we want a faster update, how would we tell Resin to evict this page out of cache.

(Side note: Since Resin http proxy cache is built into Resin, it knows about every Java web resource and can cache them all without the need to use a cache providers special server side includes. Your cache includes can just be jsp:include and <%@include, etc.)

When you load this page, it gets put into the Resin cache. With just the above, it does not matter if the end users hits shift refresh from their browser or whatever, this page is going to stay in the cache. (There are ways to enable some cache validation checking to be triggered from client, but that is for another lesson as it involves more cache controls like ETag, etc.)

But first lets setup the http proxy cache

To enable the cache, you just need to modify /etc/resin/resin.properties and make sure the following two properties get set:


# Enable the proxy-cache - for caching static content in memory
proxy_cache_enable : true

# Sets the proxy cache memory size
proxy_cache_size : 256m cache

Now the cache is setup, but like we mentioned earlier, there is no way to invalidate it. What if we know some process just happened and a certain page is invalid. What if you just did a data import or ran some batch job or received a new product xml file or whatever. Perhaps you have some backend script that stages this data, and now you need to kill this page out of the cache. Now in the Java world we have JMX, and it is a nice way to interface with such things like caching, but what if these scripts were written in Ruby, Perl, Bash, or whatever. This is where the REST Admin service comes into play.

Turning on REST support

To turn on the REST admin support, you guessed it, modify /etc/resin/resin.properties file as follows:
# Enable Resin REST Admin
rest_admin_enable : true

# Require SSL for REST Admin
# rest_admin_ssl : true

If you are not on an internal safe network, you should install open ssl support from the get go, and set rest_admin_ssl to true (it is commented out now). For now, I am going to assume that you are just messing around or you really trust your network's security and we will leave SSL support off. (We should come back and add it and change the examples accordingly.)

Setting up a user for the rest calls

Run the generate-password command to create a new password.

$ resinctl generate-password --user foobar --password foobar

Output

admin_user : foobar
admin_password : {SSHA}MW8h/2zwAk4Oqa3yfObDEKMcht3OKvil

Modify /etc/resin/resin.properties and put those two entries in there. Now you have an admin user called foobar with the password foobar. You can add more users just search /etc/resin/*.xml and see where $admin_user is getting used.

Invalidating the page from the command line with Curl

If you don't see a Curl call to a REST example, then you should be suspicious. Thus without further ado, let's show using Curl to invalidate our proxy cache via a REST call to the Resin REST admin.

The easier way to invalidate a page is to you use the curl command as follows:

curl --user foobar:foobar 
       --data "values=.* .*cache.jsp" 
         "http://localhost:8080/resin-rest/jmx-call?pattern=resin:type=ProxyCache&operation=clearCacheByPattern"

(Newlines added for clarity). This is the return, which is just JSON.

{"bean":"resin:type=ProxyCache","operation":"clearCacheByPattern(java.lang.String, java.lang.String)","return-value":"null"}

All Resin jmx beans are exposed. Thus all of Resin jmx support is open to REST calls.

The --data "values=*. *cache.jsp" passes two arguments to the jmx method ProxyCache::clearCacheByPattern. The URL starts with the URI: /resin-rest/ where you will find all of the Resin admin support. The URI under this /resin-rest/jmx-call (jmx-call) means we want to invoke a JMX method. The query param pattern=resin:type=ProxyCache means we want to call a method on the JMX bean resin:ProxyCache The query param operation=clearCacheByPattern means we want to call the clearCacheByPattern method.

You can also use curl to submit SSL/TLS requests. After you run this from a bash script or Perl you can go the test cache.jsp and it will reload as expected.

(see http://javadoc4.caucho.com/com/caucho/management/server/ProxyCacheMXBean.html)


REST calls from other languages

Here are some other example from other languages making REST calls.


Ruby Rest call to Resin JMX

require "net/http"
require "uri"

$values = ".* .*cache.jsp"
$clearCacheByPatternURL = "http://localhost:8080/resin-rest/jmx-call?pattern=resin:type=ProxyCache&operation=clearCacheByPattern"
$connected=false

def main

    begin
	response = Net::HTTP.get_response(URI.parse("http://localhost:8080/"))
        puts response.body
        connected=true
    rescue Net::HTTPExceptions => ex
        connected=false
        puts ("The error was " +  ex)
    ensure
        #conn.close() unless conn.nil?
    end

    if connected
        invalidateCache()
    else
        puts ("Resin does not appear to be up")
    end
end


def invalidateCache()
    begin
	uri = URI.parse($clearCacheByPatternURL)
    	http = Net::HTTP.new(uri.host, uri.port)
    	request = Net::HTTP::Post.new(uri.request_uri)
    	request.basic_auth("foobar", "foobar")
        request.set_form_data({"values" => $values})
    	response = http.request(request)
	puts response.body
	puts "No error " + response.code
    rescue Net::HTTPExceptions => ex
        connected=false
        puts ("The error was " +  ex)
    ensure
        #http.close() unless http.nil?
    end
 
end

if __FILE__ == $0
   main
end


Python Rest call to Resin REST Admin

import httplib
import base64 

requestBody = "values=.* .*cache.jsp"
host = "localhost"
clearCacheByPatternURI = "/resin-rest/jmx-call?pattern=resin:type=ProxyCache&operation=clearCacheByPattern"
connected=False

def main():
    global connected
    try:
        conn = httplib.HTTPConnection(host, port=8080) 
        conn.request("GET", "/")
        response = conn.getresponse()
        data = response.read() 
        print (data)
        connected=True
    except httplib.HTTPException, ex:
        connected=False
        print ("The error was %s" % ex)
    finally:
        if conn:
            conn.close()

    if connected:
        invalidateCache()
    else:
        print ("Resin does not appear to be up")

def invalidateCache():
    auth = base64.encodestring("foobar:foobar") 
    headers = {"Authorization" : "Basic %s" % auth,  
               "Content-Type": "application/x-www-form-urlencoded"} 

    try:
        conn = httplib.HTTPConnection(host, port=8080) 
        conn.request("POST", clearCacheByPatternURI, requestBody, headers) 
        response = conn.getresponse() 
        data = response.read() 
        print (data)
        print ("No error %s " % response.status)
    except httplib.HTTPException, ex:
        print ("err was %s" % ex)
    finally:
        if conn:
            conn.close()
    
if  __name__ =='__main__':
    main()



PERL Rest call to Resin REST Admin

coming soon


Java Rest call to Resin REST Admin

coming soon


Go programming Rest call to Resin REST Admin

package main

import (
    "fmt"
    "net/http"
    "io/ioutil"
    "bytes"
)

var (
    connected bool
    client *http.Client = &http.Client{}
    requestBody *bytes.Buffer = bytes.NewBufferString("values=.* .*cache.jsp") 
    clearCacheByPatternURL string = "http://localhost:8080/resin-rest/jmx-call" +
                                 "?pattern=resin:type=ProxyCache&operation=clearCacheByPattern"
)

func main() {

	if resp, err := http.Get("http://localhost:8080/"); err==nil {

        	if body, err := ioutil.ReadAll(resp.Body); err==nil {
                    fmt.Println(string(body))
		    connected = true
                }

	} else {
	       fmt.Println("The error was", err)
	}

	if connected {
	   invalidateCache();
	} else {
	       fmt.Println("Resin does not appear to be up");
	}
}

func invalidateCache() {
     if request, err := http.NewRequest("POST", clearCacheByPatternURL, requestBody); err==nil {
     	fmt.Println(request.Method)
	request.SetBasicAuth("foobar", "foobar")
        request.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	response, err := client.Do(request)

        if body, err := ioutil.ReadAll(response.Body); err==nil {
                    fmt.Println(string(body))
		    connected = true
        }
	if err==nil{
	   fmt.Println("No error " + response.Status)
	} else {
	   fmt.Println("err was", err)
	}
     }
}

Further reading

Personal tools
TOOLBOX
LANGUAGES