keepmoving
keepmoving

Reputation: 2043

How to improve performance using multithreading in spring Mvc

I am using spring MVC model in my project. In which controller get request from some some thired party application. Controller get 20 request per sec. Code look like this

@Controller
@RequestMapping("/action")
public class FrontController{

@AutoWired
private CommonService commonService;

(First Code)
@RequestMappint("/save")
public String saveData(@PathParam("id")String did){

    List<String, Object> map = commonService.getVmn(did);
    CallReporting callReporting = new CallReporting();
    callReporting.setName(map.get("name"));
    so---on (have 15 field)
    commonService.save(callReporting);
    }

    return "1";
}

This code is working fine but some time if mysql is busy then it takes time to return the value to calling application. So I droped the idea and start Async communication.

(Second Code)
@RequestMappint("/save")
public String saveData(@PathParam("id")String did){

    Thread thread = new Thread(new Runnable(){
        List<String, Object> map = commonService.getVmn(did);
        CallReporting callReporting = new CallReporting();
        callReporting.setName(map.get("name"));
        so---on (have 15 field)
        commonService.save(callReporting);
    });
    }

    return "1";
}

I started using the code something like this. So that calling party can get the response immediately(reduce the response time) and later on my application continue to work. But in First code i test the load with JMeter(20 req/sec ) and found it is working fine with cpu load(3%). But in second code with same load cpu load is going to more than 100%.Than i started using the ThreadPoolTaskExecutor with below configuration.

@AutoWired
private ThreadPoolTaskExecutor executor;

@RequestMappint("/save")
public String saveData(@PathParam("id")String did){

//Assume this code is in MyRunnableWorker Class
        List<String, Object> map = commonService.getVmn(did);
        CallReporting callReporting = new CallReporting();
        callReporting.setName(map.get("name"));
        so---on (have 15 field)
        commonService.save(callReporting);

    MyRunnableWorker worker = new MyRunnableWorker();
    executor.execute(worker)

    return "1";
}

<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
    <property name="corePoolSize" value="50" />
    <property name="maxPoolSize" value="100" />
    <property name="keep-alive" value="10" />
    <property name="queueCapacity" value="200" />
</bean>

but found the same result. Whats wrong with code can somebody suggest me. One important thing when i test with Jmeter in stage envorment then load is moving between 30% to 70%.. But in production server it is always around 100%. It is java consuming 100% other process is quite low. Production machine haveing linux OS. hexacore processer having 128 GB RAM. Can some one tell me what to do.

Upvotes: 1

Views: 10827

Answers (3)

Abderrazak BOUADMA
Abderrazak BOUADMA

Reputation: 1606

the bottle neck in your case are transactions (DB)

here's some proposals :

  1. if it's not necessary that data has to be instantly saved just use an asynchronous job to do it (JMS, In Memory Queue, etc)

  2. You may consider to load DB into RAM

Upvotes: 0

Jens Schauder
Jens Schauder

Reputation: 81862

It looks like you are turning the wrong knob.

In your first version you had up to N threads banging your database. With N being configured somewhere in your servlet container. And also doing the servlet thingy, accepting connections and stuff.

Now you created 200 additional threads, which do basically nothing but hitting the database. While the previous threads do their connection handling, request parsing thing. So you increased the load on the database and you added the load do to context switching (except when you have multiple hundred cores). And for some weird reason, your database didn't get any faster.

In order to improve, reduce the number of threads in your thread pool which hits the database. Measure how many threads the database can handle before performance degrades.

Make the queueCapacity large enough to cover the request spikes you need to handle.

Implement something that gets a meaningfull answer to the user when this isn't sufficient. Something like "Sorry ..."

And then take care of the real issue: How to make your database handle the requests fast enough.

It might be that some db tuning is in place, or you need to switch to a different database system, or maybe the implementation of the save method needs some tuning ...

But all this is for a different question.

Upvotes: 2

Koziołek
Koziołek

Reputation: 2874

Your ThreadPoolTaskExecutor has too many threads. CPU spend lot of time to switch context between threds. On test you run just your controller so there are no other threads (calls) that exist on production. Core problem is waiting for DB, because it is blocking thread

Solutions

  • set pool size smaller (make some experiment)
  • remove executor and use actor system like Akka for save operation.
  • Most important use batch to save objects in DB. There will be only one call per e.g. 1000 objects not 1k calls for 1k objects > JPA/Hibernate improve batch insert performance

Upvotes: 2

Related Questions