Last week, a performance problem occurred in a company project. The problem was that some API calls would be slow when 50 concurrent users were involved, in fact, the final reason is that the C library in the background is not thread-safe, so we used the synchronization method when calling with JNI in Java, these Synchronization Methods cause a sharp reduction in performance in concurrency.
When I got home over the weekend, I thought about it. If the back-end database cannot solve the thread security problem, it can only be solved at the front-end. After analyzing the development, I found that most API calls are query methods, so I can use the cache class to relieve this pressure.
The following is a general exampleCodeStructure:
Job. Java is a job class that describes the basic job attributes.
Package Org. garbagecan. cachestudy. job; <br/> Import Java. io. serializable; <br/> public class job implements serializable {<br/> private string ID; <br/> private string name; <br/> public job () {<br/>}< br/> public job (string ID, string name) {<br/> This. id = ID; <br/> This. name = Name; <br/>}< br/> Public String GETID () {<br/> return ID; <br/>}< br/> Public void setid (string ID) {<br/> This. id = ID; <br/>}< br/> Public String getname () {<br/> return name; <br/>}< br/> Public void setname (string name) {<br/> This. name = Name; <br/>}< br/>
Jobservice. Java is a service interface used to describe some methods provided to the foreground. Here, only three methods are provided for convenience, two update Methods and one query method.
Package Org. garbagecan. cachestudy. job; <br/> Import Java. util. list; <br/> Public interface jobservice {<br/> Boolean submitjob (job); <br/> Boolean killjob (job ); <br/> List <job> getjobs (); <br/>}< br/>
Jobserviceimplwithoutcache. Java is a jobservice interface implementation class that does not use cache.
Package Org. garbagecan. cachestudy. job; <br/> Import Java. util. list; <br/> public class jobserviceimplwithoutcache implements jobservice {<br/> Public Boolean submitjob (job) {<br/> return backendjobmanager. submitjob (job); <br/>}< br/> Public Boolean killjob (job) {<br/> return backendjobmanager. killjob (job); <br/>}< br/> public list <job> getjobs () {<br/> return backendjobmanager. getjobs (); <br/>}< br/>
Jobserviceimplwithcache. Java is a jobservice interface implementation class that uses ehcache as the buffer mechanism. This is only used for testing, so it is not responsible for the final cachemanager cleaning. in the production environment, a cachemanager encapsulation should be provided to manage all the caches.
Note that the cache object is created. The last two parameters 5 and 2 represent
Timetoliveseconds
-The default amount of time to live for an element from its creation date
Timetoidleseconds
-The default amount of time to live for an element from its last accessed or modified Date
In addition, note that cache is synchronously updated in the submit and kill methods to ensure that the data retrieved by getjobs is updated every time.
Package Org. garbagecan. cachestudy. job; <br/> Import net. SF. ehcache. cache; <br/> Import net. SF. ehcache. cachemanager; <br/> Import net. SF. ehcache. element; <br/> Import Java. util. list; <br/> public class jobserviceimplwithcache implements jobservice {<br/> Private Static cachemanager; <br/> Private Static final cache; <br/> Private Static final string jobs_cache_name = "jobscache"; <br/> PR Ivate static final string jobs_cache_key = "Jobs"; </P> <p> static {<br/> cachemanager = new cachemanager (); <br/> cache = new cache (jobs_cache_name, 1000, false, false, 5, 2); <br/> cachemanager. addcache (cache); <br/> // cachemanager. shutdown (); <br/>}< br/> Public Boolean submitjob (job) {<br/> synchronized (cache) {<br/> cache. remove (jobs_cache_key); <br/>}< br/> return backendjobmanager. submitjob (Jo B); <br/>}< br/> Public Boolean killjob (job) {<br/> synchronized (cache) {<br/> cache. remove (jobs_cache_key); <br/>}< br/> return backendjobmanager. killjob (job); <br/>}< br/> public list <job> getjobs () {<br/> List <job> jobs; <br/> synchronized (cache) {<br/> element ele = cache. get (jobs_cache_key); <br/> If (Ele! = NULL) {<br/> jobs = (list <job>) ELE. getvalue (); <br/>}else {<br/> system. out. println ("Call backend API. "); <br/> jobs = backendjobmanager. getjobs (); <br/> cache. put (new element (jobs_cache_key, jobs); <br/>}< br/> return jobs; <br/>}< br/>
Backendjobmanager. Java is used to simulate the background JNI code. Three of the methods are synchronous methods, and the getjobs () method deliberately sleep (1000) to simulate the background API call overhead.
Package Org. garbagecan. cachestudy. job; <br/> Import Java. util. arraylist; <br/> Import Java. util. list; <br/> public class backendjobmanager {<br/> Private Static int id = 1; </P> <p> Public static synchronized Boolean submitjob (job) {<br/> return true; <br/>}< br/> Public static synchronized Boolean killjob (job) {<br/> return true; <br/>}< br/> Public static synchronized list <job> getjobs () {<br/> try {<br/> // simulate time to spend <br/> thread. sleep (1000); <br/>} catch (interruptedexception e) {<br/> E. printstacktrace (); <br/>}< br/> List <job> jobs = new arraylist <job> (); <br/> for (INT I = 0; I <100000; I ++) {<br/> job = new job ("job_id _" + id, "job_name _" + id); <br/> jobs. add (job); <br/> ID ++; <br/>}< br/> return jobs; <br/>}< br/>
test. Java test class. The test is performed in two cases: no cache or no cache. In the test, 50-threads are simulated for concurrent testing. The result shows that the result is still a crash. When cache is used, the less concurrent update operations, the better the performance of concurrent queries. Therefore, cache is of great use in a large number of queries.
Package Org. garbagecan. cachestudy. job; <br/> public class test {<br/> Public static void main (string [] ARGs) throws exception {<br/> // testwithoutcache (); <br/> testwithcache (); <br/>}< br/> Private Static void testwithoutcache () {<br/> for (INT idx = 1; idx <= 100; idx ++) {<br/> final string thread_name = "thread _" + idx; <br/> New thread (New runnable () {<br/> Public void run () {<br/> long begin = system. currenttimemillis (); <br/> jobservice = new jobserviceimplwithoutcache (); <br/> jobservice. getjobs (); <br/> long end = system. currenttimemillis (); <br/> system. out. println ("time for" + thread_name + ":" + (end-begin); <br/>}< br/> }). start (); <br/>}< br/> Private Static void testwithcache () throws exception {<br/> for (INT idx = 1; idx <= 100; idx ++) {<br/> final string thread_name = "thread _" + idx; <br/> New thread (New runnable () {<br/> Public void run () {<br/> long begin = system. currenttimemillis (); <br/> jobservice = new jobserviceimplwithcache (); <br/> jobservice. getjobs (); <br/> long end = system. currenttimemillis (); <br/> system. out. println ("time for" + thread_name + ":" + (end-begin); <br/>}< br/> }). start (); <br/>}< br/> for (INT idx = 1; idx <10; idx ++) {<br/> final string thread_name = "submit_job_thread _" + idx; <br/> final string jobid = "job _" + idx; <br/> final string jobname = "job _" + idx; <br/> New thread (New runnable () {<br/> Public void run () {<br/> long begin = system. currenttimemillis (); <br/> jobservice = new jobserviceimplwithcache (); <br/> jobservice. submitjob (new job (jobid, jobname); <br/> long end = system. currenttimemillis (); <br/> system. out. println ("time for" + thread_name + ":" + (end-begin); <br/>}< br/> }). start (); <br/>}< br/>
Here is just a simple column used by cache. In fact, if spring is already used in the project, spring has integrated various mainstream cache frameworks, in addition, it is also implemented through configuration, which is more transparent to the caller and is recommended.