Skip to content

Commit 1eaecfc

Browse files
committed
First Commit with Elasticache support
1 parent 317537e commit 1eaecfc

File tree

11 files changed

+519
-2
lines changed

11 files changed

+519
-2
lines changed
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
#Thu May 05 16:54:23 IST 2016
1+
#Wed Apr 26 15:17:28 IST 2017
22
distributionBase=GRADLE_USER_HOME
33
distributionPath=wrapper/dists
44
zipStoreBase=GRADLE_USER_HOME
55
zipStorePath=wrapper/dists
6-
distributionUrl=https\://services.gradle.org/distributions/gradle-2.3-bin.zip
6+
distributionUrl=https\://services.gradle.org/distributions/gradle-2.3-all.zip

micro-elasticache/build.gradle

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
apply plugin: 'groovy'
2+
apply plugin: 'java'
3+
repositories {
4+
5+
maven { url "https://jitpack.io" }
6+
}
7+
8+
description = 'micro-elasticache'
9+
dependencies {
10+
compile group: 'com.amazonaws', name: 'aws-java-sdk-elasticache', version: '1.10.42'
11+
compile project(':micro-core')
12+
compile project(':micro-guava')
13+
testCompile group: 'org.codehaus.groovy', name: 'groovy-all', version:'2.3.3'
14+
testCompile(group: 'org.spockframework', name: 'spock-core', version:'0.7-groovy-2.0') { exclude(module: 'groovy-all') }
15+
testCompile group: 'com.cyrusinnovation', name: 'mockito-groovy-support', version:'1.3'
16+
testCompile 'cglib:cglib-nodep:2.2'
17+
testCompile group: 'org.hamcrest', name: 'hamcrest-all', version:hamcrestVersion
18+
testCompile project(':micro-grizzly-with-jersey')
19+
}
20+
21+
sourceSets {
22+
main {
23+
java { srcDirs = ['src/main/java']}
24+
resources { srcDir 'src/main/resources' }
25+
}
26+
27+
test {
28+
java { srcDirs = []}
29+
groovy { srcDirs = ['src/test/java'] }
30+
resources { srcDir 'src/test/resources' }
31+
}
32+
33+
}
34+
35+
modifyPom {
36+
project {
37+
name 'Microserver elasticache'
38+
description 'Opinionated rest microservices'
39+
url 'https://github.com/aol/micro-server'
40+
inceptionYear '2015'
41+
42+
groupId 'com.aol.microservices'
43+
artifactId 'micro-elasticache'
44+
version "$version"
45+
46+
47+
scm {
48+
url 'scm:git@github.com:aol/micro-server.git'
49+
connection 'scm:git@github.com:aol/micro-server.git'
50+
developerConnection 'scm:git@github.com:aol/micro-server.git'
51+
}
52+
53+
licenses {
54+
license {
55+
name 'The Apache Software License, Version 2.0'
56+
url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
57+
distribution 'repo'
58+
}
59+
}
60+
61+
developers {
62+
developer {
63+
id 'johnmcclean-aol'
64+
name 'John McClean'
65+
email 'john.mcclean@teamaol.com'
66+
}
67+
developer {
68+
id 'morrowgi'
69+
name 'Gordon Morrow'
70+
email 'gordon.morrow@teamaol.com'
71+
}
72+
}
73+
74+
}
75+
}
76+
77+
extraArchive {
78+
sources = true
79+
tests = true
80+
javadoc = true
81+
}
82+
83+
nexus {
84+
sign = true
85+
repositoryUrl = 'https://oss.sonatype.org/service/local/staging/deploy/maven2'
86+
snapshotRepositoryUrl = 'https://oss.sonatype.org/content/repositories/snapshots'
87+
}
88+

micro-elasticache/readme.md

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
# Elasticache plugin for BASE microservices
2+
3+
[micro-elasticache example apps](https://github.com/aol/micro-server/tree/master/micro-elasticache/src/test/java/app)
4+
5+
Basically Available Soft statE
6+
7+
* Simple Couchbase Client (Couchbase bucket as distributed / persistent map)
8+
* Manifest comparator : Versioned key for loading refreshed state
9+
10+
# Manifest comparison
11+
12+
Manifest comparison stores a manifest along with each value. The manifest contains the version for the value, if the version has changed, the latest verson of the value will be loaded.
13+
14+
15+
key : manifest [contains version info]
16+
versionedKey : key with version
17+
18+
This allows large immutable datastructures to be stored in as a key/value pair (with separate key/value pairing for version info), and reloaded automatically on change.
19+
20+
## Injecting the manifest comparator
21+
22+
Inject the Spring bean created by micro-couchbase ManifestComparator into your own beans. Customise the key used (allows multiple ManifestComparators to be used)
23+
24+
```java
25+
public class ManifestComparatorResource {
26+
27+
28+
private final ManifestComparator<String> comparator;
29+
@Autowired
30+
public ManifestComparatorResource(ManifestComparator comparator) {
31+
this.comparator = comparator.<String>withKey("test-key");
32+
}
33+
```
34+
35+
## Create a scheduled job
36+
37+
See micro-events, for Microserver help in managing scheduled jobs.
38+
39+
Create a scheduled job to check the manifest for changes & automatically reload data when stale.
40+
41+
In the example below we run the versioned key cleaner once per day, and check for changes every minute.
42+
43+
```java
44+
@Component
45+
public class DataLoader implements ScheduledJob<Job>{
46+
47+
private final ManifestComparator<String> comparator;
48+
@Autowired
49+
public DataLoader(ManifestComparator comparator) {
50+
this.comparator = comparator.<String>withKey("test-key");
51+
}
52+
@Override
53+
public SystemData<String,String> scheduleAndLog() {
54+
try{
55+
boolean changed = comparator.isOutOfDate();
56+
comparator.load();
57+
return SystemData.<String,String>builder().errors(0).processed(isOutOfDate?1:0).build();
58+
}catch(Exception e){
59+
return SystemData.<String,String>builder().errors(1).processed(0).build();
60+
}
61+
}
62+
63+
}
64+
65+
@Component
66+
public class DataCleaner implements ScheduledJob<Job>{
67+
68+
private final ManifestComparator<String> comparator;
69+
@Autowired
70+
public DataCleaner(ManifestComparator comparator) {
71+
this.comparator = comparator.<String>withKey("test-key");
72+
}
73+
@Override
74+
public SystemData<String,String> scheduleAndLog() {
75+
try{
76+
comparator.cleanAll();
77+
return SystemData.<String,String>builder().errors(0).processed(1).build();
78+
}catch(Exception e){
79+
return SystemData.<String,String>builder().errors(1).processed(0).build();
80+
}
81+
82+
}
83+
84+
}
85+
86+
@Component
87+
public class Schedular{
88+
89+
private final DataCleaner cleaner;
90+
private final DataLoader loader;
91+
92+
public Schedular(DataCleaner cleaner,DataLoader loader){
93+
this.cleaner = cleaner;
94+
this.loader = loader;
95+
}
96+
97+
98+
@Scheduled(cron = "0 1 1 * * ?")
99+
public synchronized void scheduleCleaner(){
100+
cleaner.scheduleAndLog();
101+
}
102+
@Scheduled(cron = "0 * * * * *")
103+
public synchronized void scheduleLoader(){
104+
loader.scheduleAndLog();
105+
}
106+
107+
}
108+
109+
```
110+
111+
Elsewhere a single writer service can write data to the store for all services of a type to load. See micro-mysql or micro-curator for a DistributedLock implementation
112+
113+
e.g.
114+
115+
```java
116+
117+
@Component
118+
public class DataWriter {
119+
120+
private final DistributedLockService lockService;
121+
122+
private final ManifestComparator<String> comparator;
123+
@Autowired
124+
public DataWriter(DistributedLockService lockService,ManifestComparator comparator) {
125+
this.lockService = lockService;
126+
this.comparator = comparator.<String>withKey("test-key");
127+
}
128+
129+
public void write(Supplier<String> data){
130+
if(lockService.tryLock("single-writer-lock-a") {
131+
comparator.saveAndIncrement(data.get());
132+
}
133+
}
134+
135+
136+
137+
}
138+
139+
```
140+
141+
## Configurable properties
142+
143+
Key used to store data used by the configured ManfiestComparator in Couchbase (default is default-key)
144+
145+
couchbase.manifest.comparison.key=default-key
146+
147+
Comma separate list of Couchbase servers
148+
149+
couchbaseServers=
150+
151+
Couchbase bucket (default is couchbase_bucket) to store / read data in
152+
153+
couchbaseBucket=couchbase_bucket
154+
155+
Couchbase password
156+
157+
couchbasePassword=
158+
159+
Switch off Couchbase enabled / disabled
160+
161+
couchbaseClientEnabled:true
162+
163+
Couchbase client operation
164+
165+
couchbaseClientOperationTimeout:120000
166+
167+
## Getting The Microserver Couchbase Plugin
168+
169+
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.aol.microservices/micro-couchbase/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.aol.microservices/micro-couchbase)
170+
171+
### Maven
172+
```xml
173+
<dependency>
174+
<groupId>com.aol.microservices</groupId>
175+
<artifactId>micro-couchbase</artifactId>
176+
<version>x.yz</version>
177+
</dependency>
178+
```
179+
### Gradle
180+
```groovy
181+
compile 'com.aol.microservices:micro-couchbase:x.yz'
182+
```
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.aol.micro.server.elasticache;
2+
3+
import org.springframework.scheduling.annotation.Async;
4+
5+
import java.util.Optional;
6+
import java.util.concurrent.Future;
7+
8+
/**
9+
* Created by gordonmorrow on 5/3/17.
10+
*/
11+
public interface CacheService {
12+
@Async("distributedCacheTaskExecutor")
13+
public abstract Future<Boolean> put(String key, Object o);
14+
15+
public abstract Optional<Object> get(String key);
16+
17+
public abstract boolean isAvailable();
18+
19+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package com.aol.micro.server.elasticache;
2+
3+
/**
4+
* Created by gordonmorrow on 4/24/17.
5+
*/
6+
7+
import lombok.extern.slf4j.Slf4j;
8+
import net.spy.memcached.*;
9+
10+
import java.io.IOException;
11+
import java.net.URISyntaxException;
12+
import java.util.ArrayList;
13+
14+
15+
import net.spy.memcached.auth.AuthDescriptor;
16+
import net.spy.memcached.auth.PlainCallbackHandler;
17+
import net.spy.memcached.MemcachedClient;
18+
import org.springframework.beans.factory.annotation.Value;
19+
import org.springframework.context.annotation.Bean;
20+
import org.springframework.context.annotation.Configuration;
21+
import org.springframework.util.StringUtils;
22+
23+
import java.net.InetSocketAddress;
24+
25+
import java.util.List;
26+
import java.util.Optional;
27+
28+
@Slf4j
29+
@Configuration
30+
public class ConfigureElasticache {
31+
32+
private ClientMode mode = ClientMode.Dynamic;
33+
34+
@Value("${elasticache.hostname:lana-staging-ec.6o3auf.cfg.use1.cache.amazonaws.com")
35+
private String hostname = "lana-staging-ec.6o3auf.cfg.use1.cache.amazonaws.com";
36+
37+
@Value("${elasticache.port:6379")
38+
private int port = 6379;
39+
40+
@Value("${elasticache.retries:3")
41+
private int retries = 3;
42+
43+
@Value("${elastiache.retry.after.seconds:1")
44+
private int retryAfterSeconds = 1;
45+
46+
private MemcachedClient cache;
47+
48+
InetSocketAddress socketAddress = new InetSocketAddress(hostname, port);
49+
50+
@Bean(name = "elasticacheClient")
51+
public TransientElasticacheDataConnection transientCache() throws IOException, URISyntaxException {
52+
log.info("Creating MemcacheClient for servers: {}", hostname);
53+
return new TransientElasticacheDataConnection(createMemcachedClient(socketAddress), retries, retryAfterSeconds);
54+
}
55+
56+
private MemcachedClient createMemcachedClient(InetSocketAddress socketAddress) throws IOException {
57+
58+
try {
59+
return new MemcachedClient(socketAddress);
60+
} catch (IOException e) {
61+
e.printStackTrace();
62+
return null;
63+
}
64+
65+
}
66+
}
67+
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.aol.micro.server.elasticache;
2+
import java.util.Optional;
3+
/**
4+
* Created by gordonmorrow on 5/3/17.
5+
*/
6+
public interface DistributedCacheManager<V> {
7+
void setConnectionTested(boolean result);
8+
boolean isAvailable();
9+
boolean put(String key, int exp, V value);
10+
Optional<V> get(String key);
11+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.aol.micro.server.elasticache;
2+
3+
import com.aol.cyclops.data.collections.extensions.persistent.PSetX;
4+
import com.aol.micro.server.Plugin;
5+
6+
/**
7+
* Created by gordonmorrow on 5/3/17.
8+
*/
9+
public class ElasticachePlugin implements Plugin {
10+
11+
public PSetX<Class> springClasses() {
12+
return PSetX.of(ConfigureElasticache.class);
13+
}
14+
}

0 commit comments

Comments
 (0)