Статьи

Руководство для начинающих по Hazelcast, часть 7

Это продолжение серии, объясняющей, как использовать Hazelcast. Если один не прочитал остальные шесть постов, перейдите к оглавлению и прочитайте остальные посты.

Другая порода карты

MultiMap Hazelcast нарушает обычную форму использования интерфейсов java.util.Collection, которые использовались в предыдущих статьях. На самом деле, концепция MultiMap, на мой взгляд, полностью нарушает идею карты. В то время как карты нормалей связывают один ключ с одним значением, MultiMaps могут отображать несколько значений в один и тот же ключ . Это действительно важная концепция, множественные значения для одного и того же ключа. Значения могут храниться в двух разных коллекциях, набора или списка. Эти коллекции действуют как коллекции библиотеки java.util.Collections.

Это безопасно?

У MultiMaps есть способ их безумия. В карте нормалей можно сохранить несколько значений для каждого ключа, но это нужно сделать вручную. Это означает, что вынуть коллекцию из хранилища, внести любые изменения и затем вернуть коллекцию обратно в хранилище. Это может быть проблематично для безопасности потока, потому что предыдущие шаги должны быть сделаны атомарно, или есть возможность чтения устаревших или противоречивых данных другими потоками. MultiMaps помогает решить эту проблему, предлагая следующие услуги:

  • Можно добавить значение с помощью одной операции пут.
  • Можно заблокировать запись с помощью ключа. Это ключ (каламбур), потому что это означает, что разработчику не нужно отслеживать отдельную блокировку для каждой записи.

пример

Этот пример немного отличается, потому что я использовал отказоустойчивый плагин Maven в качестве основного движка при запуске примеров . Да, я написал два примера, потому что хотел показать два разных способа использования MultiMap. Одним из способов является то, что каждый поток получает свою собственную игровую площадку, ему присваивается уникальный ключ, или ему назначается общая игровая площадка, или все потоки используют один и тот же ключ. Это также пример того, как IdGenerator Hazelcast может использоваться как метод обеспечения безопасности потоков в приложении.

Файл Пом

Помните, этот пример кода использует преимущества плагина Apache Failsafe Maven . Отказоустойчивый плагин помогает в автоматизированных интеграционных тестах, не убивая сборку при первом сбое. Это вилка верного плагина. Я также экспериментировал с отчетами, доступными в Maven. Введите «mvn site» в командной строке, и он создаст веб-сайт.

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
<?xml version="1.0" encoding="UTF-8"?>
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    <modelVersion>4.0.0>
 
    <groupId>com.darylmathison>
    <artifactId>hazelcast-multimap-example>
    <version>1.0-SNAPSHOT>
    <description>Examples of using Hazelcast Multimap>
    <developers>
        <developer>
            <name>Daryl Mathison>
            <id>dmathison>
            <roles>
                <role>developer>
            >
        >
    >
 
    <scm>
    >
 
    <properties>
        <maven.compiler.source>1.8>
        <maven.compiler.target>1.8>
        <project.build.sourceEncoding>UTF-8>
    >
 
    <dependencies>
        <dependency>
            <groupId>com.hazelcast>
            <artifactId>hazelcast>
            <version>3.4.2>
        >
        <dependency>
            <groupId>junit>
            <artifactId>junit>
            <version>4.12>
            <scope>test>
        >
    >
 
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins>
                <artifactId>maven-failsafe-plugin>
                <version>2.18.1>
                <executions>
                    <execution>
                        <goals>
                            <goal>integration-test>
                            <goal>verify>
                        >
                    >
                >
            >
        >
    >
 
    <reporting>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins>
                <artifactId>maven-project-info-reports-plugin>
                <version>2.7>
                <reportSets>
                    <reportSet>
                        <reports>
                            <report>dependencies>
                            <report>index>
                            <report>project-team>
                            <report>scm>
                        >
                    >
                >
            >
            <plugin>
                <groupId>org.apache.maven.plugins>
                <artifactId>maven-javadoc-plugin>
                <version>2.10.3>
                <reportSets>
                    <reportSet>
                        <reports>
                            <report>javadoc>
                            <report>test-javadoc>
                        >
                    >
                >
            >
            <plugin>
                <groupId>org.apache.maven.plugins>
                <artifactId>maven-surefire-report-plugin>
                <version>2.18.1>
            >
            <plugin>
                <groupId>org.apache.maven.plugins>
                <artifactId>maven-jxr-plugin>
                <version>2.5>
                <configuration>
                    <linkJavadoc>true>
                >
                <reportSets>
                    <reportSet>
                        <reports>
                            <report>jxr>
                            <report>test-jxr>
                        >
                    >
                >
            >
        >
    >
>

MultimapAccessThread

Это базовый класс для каждого из потоков типа доступа.

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
package com.darylmathison.multimap;
 
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.core.HazelcastInstanceAware;
import com.hazelcast.core.MultiMap;
 
import java.io.Serializable;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
 
/**
 * Abstract class to access MultiMap.
 */
public abstract class MultiMapAccessThread implements Serializable, Runnable, HazelcastInstanceAware {
    protected com.hazelcast.core.HazelcastInstance instance;
    protected MultiMap<Long, Long> map;
    protected String mapName;
    protected Lock l = new ReentrantLock();
 
    public void setHazelcastInstance(HazelcastInstance instance) {
        l.lock();
        try {
            this.instance = instance;
            if (mapName != null && !mapName.isEmpty()) {
                map = instance.getMultiMap(mapName);
            }
        } finally {
            l.unlock();
        }
    }
 
    public String getMapName() {
        return mapName;
    }
 
    public void setMapName(String mapName) {
        l.lock();
        try {
            this.mapName = mapName;
        } finally {
            l.unlock();
        }
    }
}

IdMultiMapAccessThread

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
package com.darylmathison.multimap;
 
/**
 * This thread accesses only one "slot" in a multimap.
 */
public class IdMultiMapAccessThread extends MultiMapAccessThread {
    private Long id;
 
    @Override
    public void run() {
        l.lock();
        boolean shouldRun = (map != null && id != null);
        l.unlock();
        if(shouldRun) {
            for (long i = 0; i < 10; i++) {
                map.put(id, i);
            }
        }
    }
 
    public Long getId() {
        return id;
    }
 
    public void setId(Long id) {
        this.id = id;
    }
}

GroupMultiMapAccessThread

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
package com.darylmathison.multimap;
 
/**
 * Thread designed to share the same "slot" on a MultiMap.
 */
public class GroupMultiMapAccessThread extends MultiMapAccessThread {
 
    private static final long MAX = 10;
 
    private Long groupId;
 
    /**
     * When an object implementing interface Runnable is used
     * to create a thread, starting the thread causes the object's
     * run method to be called in that separately executing
     * thread.
     *
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
* The general contract of the method run is that it may
     * take any action whatsoever.
     *
     * @see Thread#run()
     */
    @Override
    public void run() {
        l.lock();
        boolean shouldRun = (groupId != null && map != null);
        l.unlock();
        if(shouldRun) {
            map.lock(groupId);
            try {
                if (map.get(groupId).isEmpty()) {
                    System.out.println("adding to list");
                    for (long i = 0; i < MAX; i++) {
                        map.put(groupId, i);
                    }
                } else {
                    System.out.println("nothing to add");
                }
            } finally {
                map.unlock(groupId);
            }
        }
    }
 
    public void setGroupId(Long groupId) {
        l.lock();
        this.groupId = groupId;
        l.unlock();
    }
}

HazelcastInstanceResource

Это правило запускается и закрывает экземпляр Hazelcast, необходимый для запуска потоков.

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
package com.darylmathison.multimap.test.rule;
 
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.core.IExecutorService;
import org.junit.rules.ExternalResource;
 
/**
 * Created by Daryl on 4/27/2015.
 */
public class HazelcastInstanceResource extends ExternalResource {
    public static final String SERVICE_NAME = "Charlotte";
    HazelcastInstance instance;
    IExecutorService service;
 
    @Override
    protected void before() throws Throwable {
        super.before();
        instance = Hazelcast.newHazelcastInstance();
        service = instance.getExecutorService(SERVICE_NAME);
    }
 
    @Override
    protected void after() {
        super.after();
        service.shutdown();
        instance.shutdown();
    }
 
    public HazelcastInstance getInstance() {
        return instance;
    }
 
    public IExecutorService getService() {
        return service;
    }
}

IdMultiMapAccessIT

Вот пример использования IdGenerator для создания новых «игровых площадок» или ключей для потоков для размещения данных.

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
47
48
49
50
51
52
53
54
55
56
57
package com.darylmathison.multimap;
 
import com.darylmathison.multimap.test.rule.HazelcastInstanceResource;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.core.IExecutorService;
import com.hazelcast.core.IdGenerator;
import org.junit.ClassRule;
import org.junit.Test;
 
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
 
/**
 * Integration test for IdMultiMapAccessThread
 */
public class IdMultiMapAccessThreadIT {
 
    public static final String MAP_NAME = "idAccessMap";
    public static final String GEN_NAME = "singleAccess";
    public static final int NUM_THREADS = 10;
 
    @ClassRule
    public static HazelcastInstanceResource hazelcastInstanceResource = new HazelcastInstanceResource();
 
    @Test
    public void testIdThreads() {
        List threads = generateThreads(hazelcastInstanceResource.getInstance());
        List<Future<?>> futures = new ArrayList<>(NUM_THREADS);
        IExecutorService spinner = hazelcastInstanceResource.getService();
        for(IdMultiMapAccessThread thread: threads) {
            futures.add(spinner.submit(thread));
        }
 
        for(Future<?> future: futures) {
            try {
                future.get();
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        }
    }
 
    private List generateThreads(HazelcastInstance instance) {
        IdGenerator gen = instance.getIdGenerator(GEN_NAME);
        List threads = new ArrayList<>(NUM_THREADS);
        for(int i = 0; i < NUM_THREADS; i++) {
            IdMultiMapAccessThread thread = new IdMultiMapAccessThread();
            thread.setMapName(MAP_NAME);
            thread.setId(gen.newId());
            threads.add(thread);
        }
 
        return threads;
    }
}

GroupMultiMapAccessThreadIT

Это пример использования IdGenerator для создания общей игровой площадки или слота.

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
47
48
49
50
51
52
53
54
55
56
package com.darylmathison.multimap;
 
import com.darylmathison.multimap.test.rule.HazelcastInstanceResource;
import com.hazelcast.core.IExecutorService;
import com.hazelcast.core.IdGenerator;
import org.junit.ClassRule;
import org.junit.Test;
 
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
 
/**
 * GroupMultiMapAccessThread Integration Test
 */
public class GroupMultiMapAccessThreadIT {
    public static final int NUM_THREADS = 10;
    public static final String GEN_NAME = "groupIdGenerator";
    public static final String MAP_NAME = "groupMap";
 
    @ClassRule
    public static HazelcastInstanceResource hazelcastInstanceResource = new HazelcastInstanceResource();
 
    @Test
    public void testGroupMultiMapAccessThread() {
        List threads = createThreads();
        IExecutorService service = hazelcastInstanceResource.getService();
        List<Future<?>> futures = new ArrayList<>(NUM_THREADS);
        for(GroupMultiMapAccessThread thread: threads) {
            futures.add(service.submit(thread));
        }
 
        for(Future<?> future: futures) {
            try {
                future.get();
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        }
    }
 
    private List createThreads() {
        List ret = new ArrayList<>(NUM_THREADS);
        IdGenerator gen = hazelcastInstanceResource.getInstance().getIdGenerator(GEN_NAME);
        Long groupId = gen.newId();
        for(int i = 0; i < NUM_THREADS; i++) {
            GroupMultiMapAccessThread thread = new GroupMultiMapAccessThread();
            thread.setMapName(MAP_NAME);
            thread.setGroupId(groupId);
            ret.add(thread);
        }
 
        return ret;
    }
}

Вывод

В этом посте была составлена ​​таблица MultiMap от Hazelcast. Было показано, что MultiMaps может хранить несколько значений для данного ключа. Также было показано, как поток может совместно использовать данные в MultiMap или может хранить данные для себя, используя IdGenerator в качестве возможного генератора ключей. Код можно найти в GitHub здесь .

использованная литература