1. In a distributed environment, deploying a user-defined data structure as cluster wide service can provide access to all its operations, from anywhere in the grid. For example, you can have your own implementation of a distributed SecureMap, which auto-encrypts its values and is deployed as a service on all the cluster nodes.

    In GridGain, you can implement your own custom data structures and deploy them as distributed services on the grid. You can also access them from clients via service proxies which connect to remotely deployed distributed services. This allows to invoke your own services from anywhere, regardless of the deployment topology, be that a cluster-singleton, a node singleton, or any other deployment.

    As an example, let's define a simple counter service as MyCounterService interface.

    public class MyCounterService {
        /**
         * Increment counter value and return the new value.
         */
        int increment() throws GridException;
         
        /**
         * Get current counter value.
         */
        int get() throws GridException;
    }
    

    An implementation of a distributed service has to implement both, GridService and MyCounterService interfaces.

    The easiest way to implement our counter service is to store the counter value in a GridGain distributed cache. The key for this counter value is the name of the service. This allows us to create multiple instances of this counter service with different names, hence having different counters within the grid.

    public class MyCounterServiceImpl implements GridService, MyCounterService {
        private static final long serialVersionUID = 0L;
        
        // Auto-inject grid instance. 
        @GridInstanceResource
        private Grid grid;
        
        /** Instance of distributed cache. */
        private GridCache<String, Integer> cache;
     
        /** Service name. */
        private String name;
     
        /**
         * Service initialization.
         */
        @Override public void init(GridServiceContext ctx) throws Exception {      
            cache = grid.cache("myCounterCache");
     
            name = ctx.name();
             
            System.out.println("Service was initialized: " + ctx.name());
        }
     
        /**
         * Cancel this service.
         */
        @Override public void cancel(GridServiceContext ctx) {
            System.out.println("Service was cancelled: " + ctx.name());
        }
        
        /**
         * Start service execution.
         */
        @Override public void execute(GridServiceContext ctx) throws Exception {
            // Our counter service does not need to have any execution logic
            // of its own, as it is only accessed via MyCounterService public API.
            System.out.println("Executing distributed service: " + ctx.name());
        }
      
        /**
         * Get current counter value.
         */
        @Override public int get() throws GridException {
            Integer i = cache.get(name);
    
            // If there is no counter, then we return 0.  
            return i == null ? 0 : i;
        }
     
        /**
         * Increment our counter. 
         */
        @Override public int increment() throws GridException {
            // Since the cache is partitioned, 'transformAndCompute(...)' method
            // ensures that the closure will execute on the cluster member where 
            // GridGain caches our counter value.
            return cache.transformAndCompute(name, new MyTransformClosure());
        }
     
        /**
         * GridGain transform closure which increments the value
         * currently stored in cache.
         */
        private static class MyTransformClosure implements GridClosure<Integer, GridBiTuple<Integer, Integer>> {
            @Override public GridBiTuple<Integer, Integer> apply(Integer i) {
                int newVal = i == null ? 1 : i + 1;
     
                // First value in the tuple is the new value to store in cache,
                // and the 2nd value is to be returned from 'transformAndCompute(...)' call.
                // In our case, both values are the same.
                return new GridBiTuple<>(1, 1);
            }      
        }
    }
    

    We can now create a service proxy and invoke our distributed service.

    try (Grid g = GridGain.start("examples/config/example-cache.xml")) {
        //Get an instance of GridServices for remote nodes.
        GridServices svcs = grid.forRemotes().services();
        try {
             // Deploy node singleton. An instance of the service
             // will be deployed on every remote cluster node.
             svcs.deployNodeSingleton("myCounterService", new MyCounterServiceImpl()).get();
    
             // Get service proxy for remotely deployed service.
             // Since service was deployed on all remote nodes, our 
             // proxy is *not sticky* and will load-balance service 
             // invocations across all remote nodes in the cluster.
             MyCounterService cntrSvc = grid.services().
                serviceProxy("myCounterService", MyCounterService.class, /*not-sticky*/false);
    
             // Invoke a remote distributed counter service.
             cntrSvc.increment();
    
             // Print latest counter value from a remote counter service.
             System.out.println("Incremented value : " + cntrSvc.get());
        }
        finally {
            // Undeploy all services.
            grid.services().cancelAll();
        }
    }
    

    In the above example, cntrSvc is a proxy for the remotely deployed service, myCounterService. You can find more information about GridGain distributed services here.

Blog Archive
Loading
Dynamic Views theme. Powered by Blogger.