Enterprise Java

Apache Ignite and Spring on your Kubernetes Cluster Part 1: Spring Boot application

On a previous series of blogs we spun up an Ignite cluster on a Kubernetes cluster.
In this tutorial we shall use the Ignite cluster created previously on with a Spring Boot Application.

Let’s create our project using Spring Boot. The Spring Boot application will connect to the Ignite cluster.

Let’s add our dependencies.

<?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 https://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.2.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.gkatzioura</groupId>
    <artifactId>job-api-ignite</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>job-api-ignite</name>
    <description>Demo project for Spring Boot</description>
 
    <properties>
        <java.version>1.8</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.apache.ignite</groupId>
            <artifactId>ignite-kubernetes</artifactId>
            <version>2.7.6</version>
        </dependency>
        <dependency>
            <groupId>org.apache.ignite</groupId>
            <artifactId>ignite-spring</artifactId>
            <version>2.7.6</version>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.ignite</groupId>
                    <artifactId>ignite-indexing</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
 
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
 
</project>

As in previous tutorials we shall use GitHub’s Job api.

The first step would be to add the Job Model that deserializes.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
package com.gkatzioura.jobapi.model;
 
import java.io.Serializable;
 
import lombok.Data;
 
@Data
public class Job implements Serializable {
 
    private String id;
    private String type;
    private String url;
    private String createdAt;
    private String company;
    private String companyUrl;
    private String location;
    private String title;
    private String description;
 
}

The we need a repository for the Jobs. Beware the class needs to be serializable. Ignite caches data off-heap.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package com.gkatzioura.jobapi.repository;
 
import java.util.ArrayList;
import java.util.List;
 
import com.gkatzioura.jobapi.model.Job;
import lombok.Data;
import org.apache.ignite.Ignite;
 
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Repository;
import org.springframework.web.client.RestTemplate;
 
@Repository
public class GitHubJobRepository {
 
    private static final String JOB_API_CONSTANST = "https://jobs.github.com/positions.json?page={page}";
    public static final String GITHUBJOB_CACHE = "githubjob";
 
    private final RestTemplate restTemplate;
    private final Ignite ignite;
 
    GitHubJobRepository(Ignite ignite) {
        this.restTemplate = new RestTemplate();
        this.ignite = ignite;
    }
 
    @Cacheable(value = GITHUBJOB_CACHE)
    public List<Job> getJob(int page) {
        return restTemplate.getForObject(JOB_API_CONSTANST,JobList.class,page);
    }
 
    public List<Job> fetchFromIgnite(int page) {
        for(String cache: ignite.cacheNames()) {
            if(cache.equals(GITHUBJOB_CACHE)) {
                return (List<Job>) ignite.getOrCreateCache(cache).get(1);
            }
        }
 
        return new ArrayList<>();
    }
 
    @Data
    private static class JobList  extends ArrayList<Job> {
    }
}

The main reason the JobList class exists is for convenience for unmarshalling.
As you can see the repository has the annotation @Cacheable. This mean that our requests will be cached. The fetchFromIgnite method is a test method for the sake of this example. We shall use it to access the data cached by ignite directly.

We shall also add the controller.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.gkatzioura.jobapi.controller;
 
import java.util.List;
 
import com.gkatzioura.jobapi.model.Job;
import com.gkatzioura.jobapi.repository.GitHubJobRepository;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 
@RestController
@RequestMapping("/jobs")
public class JobsController {
 
    private final GitHubJobRepository gitHubJobRepository;
 
    JobsController(GitHubJobRepository gitHubJobRepository) {
        this.gitHubJobRepository = gitHubJobRepository;
    }
 
    @GetMapping("/github/{page}")
    public List<Job> gitHub(@PathVariable("page") int page) {
        return this.gitHubJobRepository.getJob(page);
    }
 
    @GetMapping("/github/ignite/{page}")
    public List<Job> gitHubIgnite(@PathVariable("page") int page) {
        return this.gitHubJobRepository.fetchFromIgnite(page);
    }
 
}

Two methods on the controller, the one to fetch the data as usual and caches them behind the scenes and the other on that we shall use for testing.

It’s time for us to configure the Ignite client that uses the nodes on our Kubernetes cluster.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package com.gkatzioura.jobapi.config;
 
 
import lombok.extern.slf4j.Slf4j;
import org.apache.ignite.Ignite;
import org.apache.ignite.Ignition;
import org.apache.ignite.cache.spring.SpringCacheManager;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
import org.apache.ignite.spi.discovery.tcp.ipfinder.kubernetes.TcpDiscoveryKubernetesIpFinder;
 
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
 
@Configuration
@EnableCaching
@Slf4j
public class SpringCacheConfiguration {
 
    @Bean
    public Ignite igniteInstance() {
        log.info("Creating ignite instance");
        TcpDiscoveryKubernetesIpFinder tcpDiscoveryKubernetesIpFinder = new TcpDiscoveryKubernetesIpFinder();
        tcpDiscoveryKubernetesIpFinder.setNamespace("default");
        tcpDiscoveryKubernetesIpFinder.setServiceName("job-cache");
 
        TcpDiscoverySpi tcpDiscoverySpi = new TcpDiscoverySpi();
        tcpDiscoverySpi.setIpFinder(tcpDiscoveryKubernetesIpFinder);
 
        IgniteConfiguration igniteConfiguration = new IgniteConfiguration();
 
        igniteConfiguration.setDiscoverySpi(tcpDiscoverySpi);
        igniteConfiguration.setClientMode(false);
 
        return Ignition.start(igniteConfiguration);
    }
 
    @Bean
    public SpringCacheManager cacheManager(Ignite ignite) {
        SpringCacheManager springCacheManager =new SpringCacheManager();
        springCacheManager.setIgniteInstanceName(ignite.name());
        return springCacheManager;
    }
 
}

We created our cache. It shall use the Kubernetes TCP discovery mode.

The next step is to add our Main class.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
package com.gkatzioura.jobapi;
 
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
 
@SpringBootApplication
@EnableCaching
public class IgniteKubeClusterApplication {
 
    public static void main(String[] args) {
        SpringApplication.run(IgniteKubeClusterApplication.class, args);
    }
 
}

The next blog will be focused on shipping the solution to kubernetes.

Published on Java Code Geeks with permission by Emmanouil Gkatziouras, partner at our JCG program. See the original article here: Apache Ignite and Spring on your Kubernetes Cluster Part 1: Spring Boot application

Opinions expressed by Java Code Geeks contributors are their own.

Emmanouil Gkatziouras

He is a versatile software engineer with experience in a wide variety of applications/services.He is enthusiastic about new projects, embracing new technologies, and getting to know people in the field of software.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button