简体   繁体   中英

What is the best way to make bundles run parallel, threaded, in OSGI

I'm working with the OSGI framework to make an application that heavily relies on processing of packages of data.

Every bundle processes a package and then sends it to the next. What I want is that every bundle works parallel. So I want to make every bundle run in it's own Thread or multiple Threads. The 'problem' is that OSGI doesn't really support multithreading. Every bundle running on the same JVM just runs in 1 Thread, so it follows a synchronous model.

My thoughts: So the nature of the application is kinda producer consumer like. Bundle A offers a service with a method that is used to send a package to A, let's call it method ain. Bundle B has a likewise setup, and so does C. They all have aa/b/cout method, in this method they use the service of the next bundle so in A.aout you would call bin like this: bservice.bin(package).

So every bundle is a consumer and a producer of data packages, this leads me to think that using ExecutorService and BlockingQueues might work, but I'm not quite sure how to implement this 'correctly' between bundles, and with all of them being both Consumer and Producer I'm not quite sure if this is the best way to go around this problem.

I hope you guys can help and/or have any suggestions.

-- EDIT --

Bundle A AServiceImplementation

public class AServiceImplementation implements AService {

    private BService bservice;

    public void bindBService(BService service) {
        bservice = service;
        System.out.println("bundle gateway bound to b service");
    }

    public void unbindBService(BService service) {
        this.bservice = null;
    }

    public void process(int id) {
        bservice.transmitIn(id);        
    }
}

Bundle B BServiceImplementation

public class BServiceImplementation implements BService {

    private CService cservice;

    public void bindCService(CService service) {
        cservice = service;
        System.out.println("bundle gateway bound to b service");;
    }

    public void unbindCService(CService service) {
        this.cservice = null;
    }

    public void transmitIn(int id){
        // So if I would implement it THIS is where I would assign the data to 
        // a thread to get it processed in the process method.
        // but doesn't that make THIS method, transmitIn a bottleneck since all packages
        // need to pass through here?
        process(id);
    }

    public void process(int id) {
        // Lots of processing happens here  
    }
}

I don't really understand how to make it that for example bundle A transmits the data to bundle B through the transmitIn method without transmitIn being a bottleneck, since I'd make my 'workdistribution' to different threads in that method (as seen in the code above)

Ahum, a full misunderstanding. OSGi could not care less what you do with concurrency, one of its great advantages is that it does not change the computing model like so many app servers do. From a bundle or package point of view, threads are utterly irrelevant. So any Java solution works here.

In your example all processing would take place on the initial callers thread, the one that calls AServiceImplementation. If you want to have processing in parallel you can use an executor for every task you submit, this is the pattern where Executors come in handy. This makes the processing async so you have no return value. Watch out that processing will be limited by the # of available threads in the Executor you provide. You also have to write your processing code very careful to handle proper synchronization for non-local variables.

Another method is to queue. Each service call creates an object with the task's information and this object is then posted into a blocking queue. In an OSGi activator you create a Thread that reads the queue and processes it. In this model you can process without worrying (too much) about concurrency since all the processing always happens in the same thread. For performance reasons you could start multiple threads, but processing is than a bit harder.

However, my advice is to skip this optimization and built your system as simple as possible first. If it runs, and you find you have performance problems, it is early enough to start worrying about these issues. With your current level of understanding I am afraid that you stand the chance to create a highly complex solution to a 'possible' problem.

The 'problem' is that OSGI doesn't really support multithreading. Every bundle running on the same JVM just runs in 1 Thread, so it follows a synchronous model.

Sorry, but this is a total misunderstanding. Let me quote from OSGi Core specification, section 4.2.7:

The OSGi Framework is running in a multi-threaded environment. After the framework is started, it will start bundles and these bundles will be activated. Activated bundles normally start background threads or react on events from other bundles. That is, after the start method returns, the framework has moved to the ACTIVE state and many bundles can be busy on different threads.

In other words, you are free to create any threading solution that fits your needs. It is not an OSGi issue.

UPDATE:

Your service implementations could share an ExecutorService like this:

public class BServiceImplementation implements BService {

private ExecutorService executorService;

private CService cservice;

public void bindCService(CService service) {
    cservice = service;
    System.out.println("bundle gateway bound to b service");
}

public void bindExecutorService(ExecutorService executorService) {
    this.executorService = executorService;
}

public void unbindCService(CService service) {
    this.cservice = null;
}

public void transmitIn(final int id) {
    executorService.submit(new Runnable() {
        @Override
        public void run() {
            process(id);
        }
    });
}

public void process(int id) {
    // Lots of processing happens here
}
}

Then just have another bundle expose a ThreadPoolExecutor as an ExecutorService.

I was wondering if you just can't use the following line of code in your activate method:

public void activate(){
new Thread(this).start();
}

now you just have to make you bundles Runnable, and implement the run() method. In this run method you could let your consumer-end of the bundle wait for new tasks.

Then you let every consumer (wich is the service-end of the communicating bundle) define a BlockingQueue. When a bundle (producer-end) binds to a certain service, you also make sure a reference to this queue is held. This way each bundle should have just one queue.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM