I'm trying to setup a small springboot application using ehcache with jgroups replication in kubernetes, but somehow not able to discover the other members to form a cluster. The bootstrap request to find other nodes is not working, as the message is sent synchronously but not received by the other pod residing on another node.
maven pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.test.k8s</groupId>
<artifactId>ehcache-jgroups-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>ehcache-jgroups-demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>${ehcache.version}</version>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-jgroupsreplication</artifactId>
<version>1.7</version>
<exclusions>
<exclusion>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
</exclusion>
<exclusion>
<groupId>org.jgroups</groupId>
<artifactId>jgroups</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.jgroups</groupId>
<artifactId>jgroups</artifactId>
<version>3.6.17.Final</version>
</dependency>
<dependency>
<groupId>org.jgroups.kubernetes</groupId>
<artifactId>kubernetes</artifactId>
<version>0.9.2</version>
<exclusions>
<exclusion>
<groupId>io.undertow</groupId>
<artifactId>undertow-core</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Springboot application
@SpringBootApplication
public class EhcacheJgroupsDemoApplication {
public static void main(String[] args) {
SpringApplication.run(EhcacheJgroupsDemoApplication.class, args);
}
}
@EnableCaching
@Configuration
class EHCacheConfig {
@Bean
public CacheManager cacheManager(){
return new EhCacheCacheManager(ehCacheManagerFactory().getObject());
}
@Bean
public EhCacheManagerFactoryBean ehCacheManagerFactory(){
EhCacheManagerFactoryBean ehCacheBean = new EhCacheManagerFactoryBean();
ehCacheBean.setConfigLocation(new ClassPathResource("ehcache.xml"));
ehCacheBean.setShared(true);
return ehCacheBean;
}
}
@Component
@Slf4j
class CacheManagerCheck implements CommandLineRunner {
private final CacheManager cacheManager;
public CacheManagerCheck(CacheManager cacheManager) {
this.cacheManager = cacheManager;
}
@Override
public void run(String... strings) {
log.info("\n\n" + "=========================================================\n"
+ "Using cache manager: " + this.cacheManager.getClass().getName() + "\n"
+ "=========================================================\n\n");
}
}
@RestController
class CountryController {
@Autowired
CountryRepository countryRepository;
@GetMapping(value = "/country/{code}")
public ResponseEntity<Country> getCountryByCode(@PathVariable("code") String code) {
Country country = countryRepository.findByCode(code);
return ResponseEntity.ok(country);
}
@DeleteMapping(value = "/country/{code}")
public ResponseEntity deleteCountryByCode(@PathVariable("code") String code) {
countryRepository.deleteCode(code);
return ResponseEntity.ok().build();
}
}
@Slf4j
@Component
class CountryRepository {
@Cacheable(value="countries", key="#code")
public Country findByCode(String code) {
log.info("---> Loading country with code={}", code);
return new Country(code);
}
@CacheEvict(value="countries",key = "#code")
public int deleteCode(String code){
log.info("---> Deleting country with code={}", code);
return 0;
}
}
@Data
class Country implements Serializable {
private final String code;
}
<?xml version="1.0" encoding="UTF-8"?>
<ehcache
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<cacheManagerPeerProviderFactory
class="x.x.x.x.JGroupsCacheManagerPeerProviderFactory"
properties="file=jgroups/tcp.xml" />
<defaultCache maxElementsInMemory="50" eternal="false"
overflowToDisk="false" memoryStoreEvictionPolicy="LFU">
<cacheEventListenerFactory
class="net.sf.ehcache.distribution.jgroups.JGroupsCacheReplicatorFactory"
properties="replicateAsynchronously=false, replicatePuts=true,
replicateUpdates=true, replicateUpdatesViaCopy=false,
replicateRemovals=true"/>
<bootstrapCacheLoaderFactory
class="net.sf.ehcache.distribution.jgroups.JGroupsBootstrapCacheLoaderFactory"
properties="bootstrapAsynchronously=false"/>
</defaultCache>
<cache name="countries" eternal="false" maxElementsInMemory="100"
overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="0"
timeToLiveSeconds="60" memoryStoreEvictionPolicy="LRU">
<cacheEventListenerFactory
class="net.sf.ehcache.distribution.jgroups.JGroupsCacheReplicatorFactory"
properties="replicateAsynchronously=false, replicatePuts=true,
replicateUpdates=true, replicateUpdatesViaCopy=false,
replicateRemovals=true"/>
<bootstrapCacheLoaderFactory
class="net.sf.ehcache.distribution.jgroups.JGroupsBootstrapCacheLoaderFactory"
properties="bootstrapAsynchronously=false"/>
</cache>
</ehcache>
tcp.xml
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="urn:org:jgroups"
xsi:schemaLocation="urn:org:jgroups http://www.jgroups.org/schema/jgroups.xsd">
<TCP
bind_addr="${jgroups.tcp.address:match-interface:en.*}"
bind_port="7800"
recv_buf_size="5M"
send_buf_size="1M"
max_bundle_size="64K"
enable_diagnostics="true"
thread_naming_pattern="cl"
thread_pool.min_threads="0"
thread_pool.max_threads="500"
thread_pool.keep_alive_time="30000" />
<org.jgroups.protocols.kubernetes.KUBE_PING
namespace="${KUBE_NAMESPACE:ehcache-demo}"/>
<MERGE3 max_interval="30000"
min_interval="10000"/>
<VERIFY_SUSPECT timeout="1500"/>
<BARRIER />
<pbcast.NAKACK2 xmit_interval="500"
xmit_table_num_rows="100"
xmit_table_msgs_per_row="2000"
xmit_table_max_compaction_time="30000"
use_mcast_xmit="false"
discard_delivered_msgs="true" />
<UNICAST3
xmit_table_num_rows="100"
xmit_table_msgs_per_row="1000"
xmit_table_max_compaction_time="30000"/>
<pbcast.STABLE stability_delay="1000" desired_avg_gossip="50000"
max_bytes="8m"/>
<pbcast.GMS print_local_addr="true" join_timeout="3000"
view_bundling="true"/>
<MFC max_credits="2M"
min_threshold="0.4"/>
<FRAG2 frag_size="60K" />
<pbcast.STATE_TRANSFER />
<CENTRAL_LOCK />
<COUNTER/>
</config>
Solved it exposing container port for undertow server and forcing to use ipv4 stack - https://github.com/kunal-bhatia/ehcache-jgroups-demo