[JAVA] Introduction to SpringBoot + In-Memory Data Grid (Event Handling)

What I did last time

Last time, I used SpringDataGeode to create a SpringBoot application that uses an in-memory data grid, and used SpringData to confirm that the persisted data is shared between the two applications.

Creating an application using SpringDataGeode

What to do this time

Event processing using in-memory data grid

This time I tried to implement event processing using the mechanism of in-memory data grid think.

Implement as shown in the figure below. Untitled Diagram.png

--First, implement a listener that detects changes in Region

UserRegionListener.java



package spring.geode.geodeCommon.listener;

import java.time.LocalDateTime;

import org.apache.geode.cache.CacheListener;
import org.apache.geode.cache.EntryEvent;
import org.apache.geode.cache.RegionEvent;

import spring.geode.geodeCommon.model.User;
import spring.geode.geodeCommon.region.UserRegion;

/**
 * {@link UserRegion}Listener that detects changes in
 *
 */
public class UserRegionListener implements CacheListener<Integer,User> {
	public void afterCreate(EntryEvent<Integer,User> event) {
		System.out.println(LocalDateTime.now());
		System.out.println("afterCreate!!!!!!!!!" + event.getNewValue());
	}

	public void afterDestroy(EntryEvent<Integer, User> event) {
		System.out.println("afterDestroy!!!!!!!!!" + event);
	}

	public void afterInvalidate(EntryEvent<Integer, User> event) {
		System.out.println("afterInvalidate!!!!!!!!!" + event);
	}

	public void afterRegionDestroy(RegionEvent<Integer, User> event) {
		System.out.println("afterRegionDestroy!!!!!!!!!" + event);
	}

	public void afterRegionCreate(RegionEvent<Integer, User> event) {
		System.out.println("afterRegionCreate!!!!!!!!!" + event);
	}

	public void afterRegionInvalidate(RegionEvent<Integer, User> event) {
		System.out.println("afterRegionInvalidate!!!!!!!!!" + event);
	}

	public void afterUpdate(EntryEvent<Integer, User> event) {
		System.out.println("afterUpdate!!!!!!!!!" + event);
	}

	public void afterRegionClear(RegionEvent<Integer, User> event) {
		System.out.println("afterRegionClear!!!!!!!!!" + event);
	}

	public void afterRegionLive(RegionEvent<Integer, User> event) {
		System.out.println("afterRegionLive!!!!!!!!!" + event);
	}

	public void close() {
		System.out.println("close!!!!!!!!!");
	}
}

To create a listener for Region, create a class that inheritsCacheListener <K, V>. Set Key, Value of the Region you want to register as a listener in Key, Value.

The following two were verified this time. -Region creation event processing: ʻafterRegionCreate method processing is executed -User new registration event processing: ʻafterCreate method processing is executed

--Next, the implementation that registers the listener in Region

UserRegion.java


package spring.geode.geodeCommon.region;

import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.Region;
import org.springframework.data.gemfire.ReplicatedRegionFactoryBean;

import spring.geode.geodeCommon.listener.UserRegionListener;
import spring.geode.geodeCommon.model.User;

/**
 *Manage users{@link Region}Create settings for
 *
 */
public class UserRegion {
	public Region<Integer, User> createUserRegion(final GemFireCache cache) {
        return cache.<Integer, User>getRegion("Users");
    }
	
    public ReplicatedRegionFactoryBean<Integer, User> createUserRegionFactory(GemFireCache cache) {
		ReplicatedRegionFactoryBean<Integer, User> replicatedRegionFactory = new ReplicatedRegionFactoryBean<>();
		UserRegionListener[] listeners = {new UserRegionListener()};
		listeners[0] = new UserRegionListener();
		replicatedRegionFactory.setCacheListeners(listeners);
		replicatedRegionFactory.setClose(false);
		replicatedRegionFactory.setCache(cache);
		replicatedRegionFactory.setRegionName("Users");
		replicatedRegionFactory.setPersistent(false);
        return replicatedRegionFactory;
    }

}

Create a Region that manages the User with the createUserRegion method and register the bean in the ʻApplicationContext`.

Set to Region with the createUserRegionFactory method. The process of registering the listener created above in Region is also performed here.

--Implementation to set the Region and settings implemented above in the app

GeodeClientApplication.java


package spring.geode.client.geodeClient;

import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.Region;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.gemfire.ReplicatedRegionFactoryBean;
import org.springframework.data.gemfire.config.annotation.PeerCacheApplication;
import org.springframework.data.gemfire.repository.config.EnableGemfireRepositories;

import spring.geode.client.geodeClient.repository.UserRepository;
import spring.geode.geodeCommon.model.User;
import spring.geode.geodeCommon.region.UserRegion;

@SpringBootApplication
@PeerCacheApplication(name = "SpringGeodeClientApplication",locators = "localhost[40404]")
@EnableGemfireRepositories(basePackageClasses = UserRepository.class)
public class GeodeClientApplication {

	public static void main(String[] args) {
		SpringApplication.run(GeodeClientApplication.class, args);
	}

	@Configuration
	static class CacheInitializer {
		
		@Bean
	    Region<Integer, User> userRegion(final GemFireCache cache) {
			return new UserRegion().createUserRegion(cache);
	    }
		
		@Bean
	    public ReplicatedRegionFactoryBean<Integer, User> replicatedRegion(GemFireCache cache) {
			return new UserRegion().createUserRegionFactory(cache);
	    }
	}
}

GeodeServerApplication.java


package spring.geode.server.geodeServer;

import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.Region;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.gemfire.ReplicatedRegionFactoryBean;
import org.springframework.data.gemfire.config.annotation.EnableEntityDefinedRegions;
import org.springframework.data.gemfire.config.annotation.EnableLocator;
import org.springframework.data.gemfire.config.annotation.EnableManager;
import org.springframework.data.gemfire.config.annotation.PeerCacheApplication;
import org.springframework.data.gemfire.repository.config.EnableGemfireRepositories;

import spring.geode.geodeCommon.model.User;
import spring.geode.geodeCommon.region.UserRegion;
import spring.geode.server.geodeServer.repository.UserRepository;

@SpringBootApplication
@PeerCacheApplication(name = "SpringGeodeServerApplication", locators = "localhost[40404]")
@EnableGemfireRepositories(basePackageClasses = UserRepository.class)
public class GeodeServerApplication {

	public static void main(String[] args) {
		SpringApplication.run(GeodeServerApplication.class, args);
	}

	@Configuration
	@EnableLocator(port = 40404)
	@EnableManager(start = true)
	static class LocatorManagerConfiguration {
	}

	@Configuration
	static class CacheInitializer {
		
		@Bean
	    Region<Integer, User> userRegion(final GemFireCache cache) {
			return new UserRegion().createUserRegion(cache);
	    }
		
		@Bean
	    public ReplicatedRegionFactoryBean<Integer, User> replicatedRegion(GemFireCache cache) {
			return new UserRegion().createUserRegionFactory(cache);
	    }
	}
}

The settings in Region and Region are reflected in the processing in the CacheInitializer class. Implement this in an app that starts as a cache built-in server. I named the class Client and Server, but this time it starts in P2P format, so I added the @ PeerCacheApplication annotation to the class. Don't worry about the class name. ..

This completes the implementation of event processing.

Since the process to start locator is implemented in GeodeServerApplication.java, start it from GeodeServerApplication.java and check the operation.

GeodeServerApplication.Log excerpt at java startup



[info 2019/02/03 02:56:32.334 JST <main> tid=0x1] Initializing region Users

[info 2019/02/03 02:56:32.334 JST <main> tid=0x1] Initialization of region Users completed

afterRegionCreate!!!!!!!!!RegionEventImpl[region=org.apache.geode.internal.cache.DistributedRegion[path='/Users';scope=DISTRIBUTED_NO_ACK';dataPolicy=REPLICATE; concurrencyChecksEnabled];op=REGION_CREATE;isReinitializing=false;callbackArg=null;originRemote=false;originMember=192.168.11.3(SpringGeodeServerApplication:5899)<ec><v0>:1024;tag=null]
[info 2019/02/03 02:56:32.705 JST <main> tid=0x1] Initializing ExecutorService 'applicationTaskExecutor'

org.apache.coyote.AbstractProtocol start
information: Starting ProtocolHandler ["http-nio-9090"]
[info 2019/02/03 02:56:32.877 JST <main> tid=0x1] Tomcat started on port(s): 9090 (http) with context path ''

[info 2019/02/03 02:56:32.880 JST <main> tid=0x1] Started GeodeServerApplication in 3.66 seconds (JVM running for 5.179)

GeodeClientApplication.Log excerpt at java startup



[info 2019/02/03 02:56:49.107 JST <main> tid=0x1] Initializing region Users

[info 2019/02/03 02:56:49.113 JST <main> tid=0x1] Region Users requesting initial image from 192.168.11.3(SpringGeodeServerApplication:5899)<ec><v0>:1024

[info 2019/02/03 02:56:49.116 JST <main> tid=0x1] Users is done getting image from 192.168.11.3(SpringGeodeServerApplication:5899)<ec><v0>:1024. isDeltaGII is false

[info 2019/02/03 02:56:49.116 JST <main> tid=0x1] Initialization of region Users completed

afterRegionCreate!!!!!!!!!RegionEventImpl[region=org.apache.geode.internal.cache.DistributedRegion[path='/Users';scope=DISTRIBUTED_NO_ACK';dataPolicy=REPLICATE; concurrencyChecksEnabled];op=REGION_CREATE;isReinitializing=false;callbackArg=null;originRemote=false;originMember=192.168.11.3(SpringGeodeClientApplication:5901)<v1>:1025;tag=null]
[info 2019/02/03 02:56:49.510 JST <main> tid=0x1] Initializing ExecutorService 'applicationTaskExecutor'

org.apache.coyote.AbstractProtocol start
information: Starting ProtocolHandler ["http-nio-9000"]
[info 2019/02/03 02:56:49.919 JST <main> tid=0x1] Tomcat started on port(s): 9000 (http) with context path ''
[info 2019/02/03 02:56:49.922 JST <main> tid=0x1] Started GeodeClientApplication in 4.245 seconds (JVM running for 5.513)

If you can see the above log in the startup logs of the two apps, the region creation event processing is OK.

Region Users requesting initial image from 192.168.11.3(SpringGeodeServerApplication:5899)<ec><v0>:1024

By the way, in the above log, the Region named ʻUsers of GeodeClientApplicationseems to initializeRegion using the image of Regioncreated byGeodeServerApplication`.

Next, check if the listener processing is executed when the new user data is registered in the Region on the GeodeClientApplcaition side and the data is synchronized with the GeodeServerApplcaition.

User registration request to GeodeClientApplication


curl -X POST -H "Content-Type: application/json" -d '{"name":"Michel", "age":"100"}' localhost:9000/register/user

Log in GeodeServerApplcaition after sending a request

GeodeServerApplication.java log excerpt


[info 2019/02/03 04:48:30.587 JST <pool-3-thread-1> tid=0x5b] Initialization of region _monitoringRegion_192.168.11.3<v1>1025 completed

[info 2019/02/03 04:48:30.593 JST <pool-3-thread-1> tid=0x5b] Initializing region _notificationRegion_192.168.11.3<v1>1025

[info 2019/02/03 04:48:30.595 JST <pool-3-thread-1> tid=0x5b] Initialization of region _notificationRegion_192.168.11.3<v1>1025 completed

2019-02-03T04:48:46.176823
afterCreate!!!!!!!!!User(id=-1816523715, name=Michel, age=100)

As expected, the event handling method seems to be firing in GeodeServerApplication.

bonus

The difference between "when data is persisted in GeodeClientApplication "and" when event processing is executed in GeodeServerApplication" is taken, and after data is persisted in one cache server, it is persisted in another cache server. I measured the latency until the data synchronization event was executed.

When I repeated the above operation check 10 times with the for statement and took the average latency, the result was 0.002ms, which was very fast. It may not be very helpful because the number of trials is small, but I am personally satisfied.

Next, let's implement persistence.

Recommended Posts

Introduction to SpringBoot + In-Memory Data Grid (Event Handling)
Introduction to SpringBoot + In-Memory Data Grid (Data Persistence)
An introduction to Spring Boot + in-memory data grid
Introduction to Machine Learning with Spark "Price Estimate" # 2 Data Preprocessing (Handling of Category Variables)
Introduction to SWING
Introduction to web3j
Introduction to Micronaut 1 ~ Introduction ~
[Java] Introduction to Java
Introduction to migration
Introduction to java
Introduction to Doma
Introduction to RSpec 4. Create test data with Factory Bot