This commit is contained in:
crusader 2017-09-27 15:58:21 +09:00
parent 162fa61d3c
commit afcca18ba0
15 changed files with 386 additions and 27 deletions

View File

@ -0,0 +1,3 @@
# Caffeine
cache.cache-names:memberListByDomain,memberListByProbeKey
cache.caffeine.spec: initialCapacity=100,maximumSize=500,expireAfterAccess=5m,recordStats

View File

@ -0,0 +1,4 @@
# Redis
redis.host=192.168.1.50
redis.port=6379
redis.channels=/web,/probe,/auth

27
pom.xml
View File

@ -18,9 +18,12 @@
<properties> <properties>
<grpc.version>1.2.0</grpc.version> <grpc.version>1.2.0</grpc.version>
<jedis.version>2.9.0</jedis.version>
<caffeine.version>2.5.6</caffeine.version>
<protoc.version>3.2.0</protoc.version> <protoc.version>3.2.0</protoc.version>
<spring.version>4.3.9.RELEASE</spring.version> <spring.version>4.3.9.RELEASE</spring.version>
<spring.data.jpa.version>1.11.4.RELEASE</spring.data.jpa.version> <spring.data.jpa.version>1.11.4.RELEASE</spring.data.jpa.version>
<spring.data.redis.version>1.8.7.RELEASE</spring.data.redis.version>
<spring.crypto.version>4.2.3.RELEASE</spring.crypto.version> <spring.crypto.version>4.2.3.RELEASE</spring.crypto.version>
<hibernate.version>5.2.10.Final</hibernate.version> <hibernate.version>5.2.10.Final</hibernate.version>
<javax.mail.version>1.4.7</javax.mail.version> <javax.mail.version>1.4.7</javax.mail.version>
@ -59,6 +62,14 @@
<version>${spring.data.jpa.version}</version> <version>${spring.data.jpa.version}</version>
</dependency> </dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-redis -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>${spring.data.redis.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-test --> <!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
@ -124,6 +135,22 @@
<version>1.5</version> <version>1.5</version>
</dependency> </dependency>
<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>${jedis.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.github.ben-manes.caffeine/caffeine -->
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>${caffeine.version}</version>
</dependency>
<dependency> <dependency>
<groupId>com.loafle</groupId> <groupId>com.loafle</groupId>
<artifactId>overflow_api_server</artifactId> <artifactId>overflow_api_server</artifactId>

View File

@ -0,0 +1,71 @@
package com.loafle.overflow.commons.model;
import java.util.ArrayList;
import java.util.List;
public class PublishMessage {
private List<String> targets;
private PublishMessageBody message;
public void setTargets(List<String> targets) {
this.targets = targets;
}
public void addTarget(String target) {
if (null == targets) {
targets = new ArrayList<>();
}
targets.add(target);
}
public List<String> getTargets() {
return targets;
}
public void setMessage(PublishMessageBody message) {
this.message = message;
}
public PublishMessageBody getMessage() {
return message;
}
public static class PublishMessageBody {
private String method;
private List<String> params;
public PublishMessageBody() {
}
public PublishMessageBody(String method) {
this.method = method;
}
public PublishMessageBody(String method, List<String> params) {
this(method);
this.params = params;
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
public List<String> getParams() {
return params;
}
public void setParams(List<String> params) {
this.params = params;
}
public void addParams(String param) {
if (null == this.params) {
this.params = new ArrayList<>();
}
this.params.add(param);
}
}
}

View File

@ -0,0 +1,9 @@
package com.loafle.overflow.commons.service;
import com.loafle.overflow.module.domain.model.Domain;
public interface MessagePublisher {
void publishToMember(final String channel, final String memberID, final String method, final Object... params);
void publishToDomain(final String channel, final Domain domain, final String method, final Object... params);
void publishToDomainByProbeKey(final String channel, final String probeKey, final String method, final Object... params);
}

View File

@ -10,6 +10,7 @@ import com.loafle.overflow.module.noauthprobe.dao.NoAuthProbeDAO;
import com.loafle.overflow.module.noauthprobe.model.NoAuthProbe; import com.loafle.overflow.module.noauthprobe.model.NoAuthProbe;
import com.loafle.overflow.module.probe.model.Probe; import com.loafle.overflow.module.probe.model.Probe;
import com.loafle.overflow.module.probe.service.ProbeService; import com.loafle.overflow.module.probe.service.ProbeService;
import com.loafle.overflow.commons.service.MessagePublisher;
import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.type.TypeReference; import org.codehaus.jackson.type.TypeReference;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -40,6 +41,9 @@ public class NoAuthProbeService {
@Autowired @Autowired
private ObjectMapper objectMapper; private ObjectMapper objectMapper;
@Autowired
private MessagePublisher messagePublisher;
public NoAuthProbe regist(NoAuthProbe noAuthProbe) { public NoAuthProbe regist(NoAuthProbe noAuthProbe) {
noAuthProbe.setTempProbeKey(UUID.randomUUID().toString()); noAuthProbe.setTempProbeKey(UUID.randomUUID().toString());
@ -48,6 +52,8 @@ public class NoAuthProbeService {
ApiKey apiKey = apiKeyService.readByApiKey(noAuthProbe.getApiKey()); ApiKey apiKey = apiKeyService.readByApiKey(noAuthProbe.getApiKey());
noAuthProbe.setDomain(apiKey.getDomain()); noAuthProbe.setDomain(apiKey.getDomain());
messagePublisher.publishToDomain("/app", apiKey.getDomain(), "NoAuthProbeService.regist", noAuthProbe);
return this.noAuthProbeDAO.save(noAuthProbe); return this.noAuthProbeDAO.save(noAuthProbe);
} }

View File

@ -0,0 +1,102 @@
package com.loafle.overflow.redis.service;
import com.loafle.overflow.commons.model.PublishMessage;
import com.loafle.overflow.commons.service.MessagePublisher;
import com.loafle.overflow.module.domain.model.Domain;
import com.loafle.overflow.module.member.model.Member;
import com.loafle.overflow.module.member.service.MemberService;
import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.ChannelTopic;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Service
public class RedisMessagePublisher implements MessagePublisher {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private ObjectMapper objectMapper;
@Autowired
private MemberService memberService;
private Map<String, ChannelTopic> topics;
public RedisMessagePublisher() {
}
public RedisMessagePublisher(final RedisTemplate<String, Object> redisTemplate, final Map<String, ChannelTopic> topics) {
this.redisTemplate = redisTemplate;
this.topics = topics;
}
public void publishToMember(final String channel, final String memberID, final String method, final Object... params) {
PublishMessage message = new PublishMessage();
message.addTarget(memberID);
this.publish(channel, message, method, params);
}
public void publishToDomain(final String channel, final Domain domain, final String method, final Object... params) {
PublishMessage message = new PublishMessage();
message.setTargets(getMemberListByDomain(domain));
this.publish(channel, message, method, params);
}
public void publishToDomainByProbeKey(final String channel, final String probeKey, final String method, final Object... params) {
PublishMessage message = new PublishMessage();
message.setTargets(getMemberListByProbeKey(probeKey));
this.publish(channel, message, method, params);
}
@Cacheable("memberListByDomain")
protected List<String> getMemberListByDomain(final Domain domain) {
return this.getMemberList(memberService.readAllByDomain(domain));
}
@Cacheable("memberListByProbeKey")
protected List<String> getMemberListByProbeKey(final String probeKey) {
return this.getMemberList(memberService.readAllByProbeKey(probeKey));
}
protected List<String> getMemberList(final List<Member> members) {
List<String> results = new ArrayList<>(members.size());
for (Member member: members) {
results.add(member.getEmail());
}
return results;
}
protected void publish(final String channel, PublishMessage message, final String method, Object... params) {
ChannelTopic topic = this.topics.get(channel);
message.setMessage(new PublishMessage.PublishMessageBody(method, this.getMessageBody(params)));
redisTemplate.convertAndSend(topic.getTopic(), message);
}
protected List<String> getMessageBody(final Object... params) {
List<String> results = new ArrayList<>(params.length);
try {
for (Object param : params) {
results.add(objectMapper.writeValueAsString(param));
}
} catch (IOException e) {
e.printStackTrace();
}
return results;
}
// public void publishToMember(final String message) {
// redisTemplate.convertAndSend(topic.getTopic(), message);
// }
// public void publishToDomain(final String message) {
// redisTemplate.convertAndSend(topic.getTopic(), message);
// }
}

View File

@ -2,30 +2,43 @@ package com.loafle.overflow.spring;
import org.codehaus.jackson.map.DeserializationConfig; import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.context.annotation.*; import org.springframework.context.annotation.*;
import org.springframework.core.io.ClassPathResource; import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.io.Resource;
/** /**
* Created by insanity on 17. 6. 13. * Created by insanity on 17. 6. 13.
*/ */
@Configuration @Configuration
@ComponentScan(basePackages = {"com.loafle.overflow"}, excludeFilters = @ComponentScan.Filter({Configuration.class})) @ComponentScan(basePackages = {"com.loafle.overflow"}, excludeFilters = @ComponentScan.Filter({Configuration.class}))
@Import({JdbcConfiguration.class, MailConfiguration.class}) @Import({
@PropertySource({"classpath:database.properties","classpath:mail.properties"}) JdbcConfiguration.class,
MailConfiguration.class,
RedisConfiguration.class,
CacheConfiguration.class
})
@PropertySource({
"classpath:database.properties",
"classpath:mail.properties",
"classpath:redis.properties",
"classpath:cache.properties"
})
public class AppConfig { public class AppConfig {
// @Bean
// public static PropertyPlaceholderConfigurer propertyPlaceholderConfigurer() {
// PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
//// ppc.setLocation(new ClassPathResource("database.properties"));
// ppc.setLocations(new Resource[] {
// new ClassPathResource("database.properties"),
// new ClassPathResource("mail.properties"),
// new ClassPathResource("redis.properties")
// });
// ppc.setIgnoreUnresolvablePlaceholders(true);
//
// return ppc;
// }
@Bean @Bean
public static PropertyPlaceholderConfigurer propertyPlaceholderConfigurer() { public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer(); return new PropertySourcesPlaceholderConfigurer();
// ppc.setLocation(new ClassPathResource("database.properties"));
ppc.setLocations(new Resource[] {
new ClassPathResource("database.properties"),
new ClassPathResource("mail.properties")
});
ppc.setIgnoreUnresolvablePlaceholders(true);
return ppc;
} }
@Bean @Bean

View File

@ -0,0 +1,26 @@
package com.loafle.overflow.spring;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@EnableCaching
@Configuration
public class CacheConfiguration {
@Value("#{'${cache.cache-names}'.split(',')}")
private String[] cacheNames;
@Value("${cache.caffeine.spec}")
private String spec;
@Bean
public CacheManager cacheManager() {
//A EhCache based Cache manager
CaffeineCacheManager cacheManager = new CaffeineCacheManager(cacheNames);
cacheManager.setAllowNullValues(false);
cacheManager.setCacheSpecification(spec);
return cacheManager;
}
}

View File

@ -0,0 +1,56 @@
package com.loafle.overflow.spring;
import com.loafle.overflow.commons.service.MessagePublisher;
import com.loafle.overflow.redis.service.RedisMessagePublisher;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.ChannelTopic;
import org.springframework.data.redis.serializer.GenericToStringSerializer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Configuration
public class RedisConfiguration {
@Value("${redis.host}")
private String host;
@Value("${redis.port}")
private int port;
@Value("#{'${redis.channels}'.split(',')}")
private List<String> channels;
@Bean
JedisConnectionFactory jedisConnectionFactory() {
JedisConnectionFactory jedisConFactory = new JedisConnectionFactory();
jedisConFactory.setHostName(host);
jedisConFactory.setPort(port);
return jedisConFactory;
}
@Bean
public RedisTemplate<String, Object> redisTemplate() {
final RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(jedisConnectionFactory());
template.setValueSerializer(new GenericToStringSerializer<Object>(Object.class));
return template;
}
@Bean
MessagePublisher messagePublisher() {
return new RedisMessagePublisher(redisTemplate(), topics());
}
@Bean
Map<String, ChannelTopic> topics() {
Map<String, ChannelTopic> topicMap = new HashMap<>(this.channels.size());
for (String channel: this.channels) {
topicMap.put(channel, new ChannelTopic(channel));
}
return topicMap;
}
}

View File

@ -0,0 +1,3 @@
# Caffeine
cache.cache-names:memberListByDomain,memberListByProbeKey
cache.caffeine.spec: initialCapacity=100,maximumSize=500,expireAfterAccess=5m,recordStats

View File

@ -0,0 +1,4 @@
# Redis
redis.host=192.168.1.50
redis.port=6379
redis.channels=/web,/probe,/auth

View File

@ -1,7 +1,10 @@
package com.loafle.overflow.spring; package com.loafle.overflow.spring;
import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer; import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.context.annotation.*; import org.springframework.context.annotation.*;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
@ -14,21 +17,46 @@ import static org.junit.Assert.*;
*/ */
@Configuration @Configuration
@ComponentScan(basePackages = {"com.loafle.overflow"}, excludeFilters = @ComponentScan.Filter({Configuration.class})) @ComponentScan(basePackages = {"com.loafle.overflow"}, excludeFilters = @ComponentScan.Filter({Configuration.class}))
@Import({JdbcConfiguration.class, MailConfiguration.class}) @Import({
@TestPropertySource({"classpath:database.properties","classpath:mail.properties"}) JdbcConfiguration.class,
MailConfiguration.class,
RedisConfiguration.class,
CacheConfiguration.class
})
@TestPropertySource({
"classpath:database.properties",
"classpath:mail.properties",
"classpath:redis.properties",
"classpath:cache.properties"
})
public class AppConfigTest { public class AppConfigTest {
// @Bean
// public static PropertyPlaceholderConfigurer propertyPlaceholderConfigurer() {
// PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
//// ppc.setLocation(new ClassPathResource("database.properties"));
// ppc.setLocations(new Resource[] {
// new ClassPathResource("database.properties"),
// new ClassPathResource("mail.properties"),
// new ClassPathResource("redis.properties")
// });
// ppc.setIgnoreUnresolvablePlaceholders(true);
//
// return ppc;
// }
@Bean @Bean
public static PropertyPlaceholderConfigurer propertyPlaceholderConfigurer() { public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer(); return new PropertySourcesPlaceholderConfigurer();
// ppc.setLocation(new ClassPathResource("database.properties"));
ppc.setLocations(new Resource[] {
new ClassPathResource("database.properties"),
new ClassPathResource("mail.properties")
});
ppc.setIgnoreUnresolvablePlaceholders(true);
return ppc;
} }
@Bean
public ObjectMapper getObjectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
return objectMapper;
}
} }

View File

@ -0,0 +1,3 @@
# Caffeine
cache.cache-names:memberListByDomain,memberListByProbeKey
cache.caffeine.spec: initialCapacity=100,maximumSize=500,expireAfterAccess=5m,recordStats

View File

@ -0,0 +1,4 @@
# Redis
redis.host=192.168.1.50
redis.port=6379
redis.channels=/web,/probe,/auth