Cache实战之牛刀小试

上个星期,公司一个项目中出现一个性能问题,问题是在并发50个用户的情况下有些API调用会很慢,其实最终的原因是由于后台的C库不是线程安全的,所以我们在Java中用JNI调用的时候使用了同步方法,这些同步方法导致性能在并发情况下急剧下降。

周末回到家,想了一下,如果后台的库不能解决线程安全问题,那就只能在前台解决了。分析了一下开发,发现大部分API调用都是查询方法,所以我想起来可以使用Cache类减缓这种压力。

下面是在使用Cache之前,大体代码结构:

Job.java一个作业类,描述基本作业性息。

package org.garbagecan.cachestudy.job; import java.io.Serializable; public class Job implements Serializable { private String id; private String name; public Job() { } public Job(String id, String name) { this.id = id; this.name = name; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }  

 

JobService.java一个作用服务接口,描述提供给前台使用的一些方法,这里为了方便只提供了三个方法,两个更新方法,一个查询方法。

package org.garbagecan.cachestudy.job; import java.util.List; public interface JobService { boolean submitJob(Job job); boolean killJob(Job job); List<Job> getJobs(); }  

 

JobServiceImplWithoutCache.java一个没用使用Cache的JobService接口实现类。

package org.garbagecan.cachestudy.job; import java.util.List; public class JobServiceImplWithoutCache implements JobService { public boolean submitJob(Job job) { return BackendJobManager.submitJob(job); } public boolean killJob(Job job) { return BackendJobManager.killJob(job); } public List<Job> getJobs() { return BackendJobManager.getJobs(); } }  

 

JobServiceImplWithCache.java一个使用ehcache作为缓冲机制的JobService接口实现类。这里只做测试用,所以没有负责最终CacheManager的清理工作,生产环境下应该提供一个CacheManager的封装来管理所有的缓存。

这里需要注意的是Cache对象的创建,其中最后两个参数5和2分别表示

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

另外注意在submit和kill方法里,都对cache做了同步更新的操作,从而保证每次getJobs取的数据都是最新的。

package org.garbagecan.cachestudy.job; import net.sf.ehcache.Cache; import net.sf.ehcache.CacheManager; import net.sf.ehcache.Element; import java.util.List; public class JobServiceImplWithCache implements JobService { private static CacheManager cacheManager; private static final Cache cache; private static final String JOBS_CACHE_NAME = "jobsCache"; private static final String JOBS_CACHE_KEY = "jobs"; static { cacheManager = new CacheManager(); cache = new Cache(JOBS_CACHE_NAME, 1000, false, false, 5, 2); cacheManager.addCache(cache); //cacheManager.shutdown(); } public boolean submitJob(Job job) { synchronized (cache) { cache.remove(JOBS_CACHE_KEY); } return BackendJobManager.submitJob(job); } public boolean killJob(Job job) { synchronized (cache) { cache.remove(JOBS_CACHE_KEY); } return BackendJobManager.killJob(job); } public List<Job> getJobs() { List<Job> jobs; synchronized (cache) { Element ele = cache.get(JOBS_CACHE_KEY); if (ele != null) { jobs = (List<Job>)ele.getValue(); } else { System.out.println("Call backend API."); jobs = BackendJobManager.getJobs(); cache.put(new Element(JOBS_CACHE_KEY, jobs)); } } return jobs; } }  

 

BackendJobManager.java是用来模拟后台JNI代码的,其中三个方法都是同步方法,并且在getJobs()方法里故意sleep(1000)来模拟后台API调用开销。

package org.garbagecan.cachestudy.job; import java.util.ArrayList; import java.util.List; public class BackendJobManager { private static int id = 1; public static synchronized boolean submitJob(Job job) { return true; } public static synchronized boolean killJob(Job job) { return true; } public static synchronized List<Job> getJobs() { try { // Simulate time to spend Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } List<Job> jobs = new ArrayList<Job>(); for (int i = 0; i < 100000; i++) { Job job = new Job("job_id_" + id, "job_name_" + id); jobs.add(job); id++; } return jobs; } }  

 

Test.java测试类,分不使用Cache和使用Cache两种情况进行测试。其中测试中模拟了50-100个线程来进行并发测试,从结果看效果还是一幕了然的。对于使用Cache的情况,如果并发的更新操作越少,并发查询的效果越好,因此使用Cache在大量查询的应用中还是有比较大的用处的。

package org.garbagecan.cachestudy.job; public class Test { public static void main(String[] args) throws Exception { //testWithoutCache(); testWithCache(); } private static void testWithoutCache() { for (int idx = 1; idx <= 100; idx++) { final String thread_name = "thread_" + idx; new Thread(new Runnable() { public void run() { long begin = System.currentTimeMillis(); JobService jobService = new JobServiceImplWithoutCache(); jobService.getJobs(); long end = System.currentTimeMillis(); System.out.println("Time for " + thread_name + " : " + (end - begin)); } }).start(); } } private static void testWithCache() throws Exception { for (int idx = 1; idx <= 100; idx++) { final String thread_name = "thread_" + idx; new Thread(new Runnable() { public void run() { long begin = System.currentTimeMillis(); JobService jobService = new JobServiceImplWithCache(); jobService.getJobs(); long end = System.currentTimeMillis(); System.out.println("Time for " + thread_name + " : " + (end - begin)); } }).start(); } for (int idx = 1; idx < 10; idx++) { final String thread_name = "submit_job_thread_" + idx; final String jobId = "job_" + idx; final String jobName = "job_" + idx; new Thread(new Runnable() { public void run() { long begin = System.currentTimeMillis(); JobService jobService = new JobServiceImplWithCache(); jobService.submitJob(new Job(jobId, jobName)); long end = System.currentTimeMillis(); System.out.println("Time for " + thread_name + " : " + (end - begin)); } }).start(); } } }  

 

这里只是一个简单的Cache使用的列子,其实如果项目中已经使用了spring的话,spring已经对各种主流的Cache框架做了集成,并且用起来也都是通过配置的方式来做的,这样对调用方来说也更透明,更值得推荐。

 

 

 

 

 

 

转载于:https://www.cnblogs.com/wdpp/archive/2011/01/09/2386280.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值