first commit

This commit is contained in:
jyjang
2020-04-19 15:37:29 +09:00
parent 11a9f3b645
commit 96631ee507
66 changed files with 2310 additions and 0 deletions

4
app/Dockerfile Executable file
View File

@@ -0,0 +1,4 @@
FROM openjdk:8u212-jdk-alpine
COPY target/*SNAPSHOT.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java","-Xmx400M","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar","--spring.profiles.active=docker"]

103
app/cloudbuild.yaml Executable file
View File

@@ -0,0 +1,103 @@
steps:
### Test
# - id: 'test'
# name: 'gcr.io/cloud-builders/mvn'
# args: [
# 'test',
# '-Dspring.profiles.active=test'
# ]
### Build
- id: 'build'
name: 'gcr.io/cloud-builders/mvn'
args: [
'clean',
'package'
# '-Dmaven.test.skip=true'
]
# waitFor: ['test']
### docker Build
- id: 'docker build'
name: 'gcr.io/cloud-builders/docker'
args:
- 'build'
- '--tag=gcr.io/$PROJECT_ID/$_PROJECT_NAME:latest'
- '.'
### Publish
- id: 'publish'
name: 'gcr.io/cloud-builders/docker'
entrypoint: 'bash'
args:
- '-c'
- |
docker push gcr.io/$PROJECT_ID/$_PROJECT_NAME:latest
### deploy
- id: 'deploy'
name: 'gcr.io/cloud-builders/gcloud'
entrypoint: 'bash'
args:
- '-c'
- |
PROJECT=$$(gcloud config get-value core/project)
gcloud container clusters get-credentials "$${CLOUDSDK_CONTAINER_CLUSTER}" \
--project "$${PROJECT}" \
--zone "$${CLOUDSDK_COMPUTE_ZONE}"
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
name: $_PROJECT_NAME
labels:
app: $_PROJECT_NAME
spec:
ports:
- port: 8080
targetPort: 8080
selector:
app: $_PROJECT_NAME
EOF
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: $_PROJECT_NAME
labels:
app: $_PROJECT_NAME
spec:
replicas: 1
selector:
matchLabels:
app: $_PROJECT_NAME
template:
metadata:
labels:
app: $_PROJECT_NAME
spec:
containers:
- name: $_PROJECT_NAME
image: gcr.io/$PROJECT_ID/$_PROJECT_NAME:latest
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 10
timeoutSeconds: 2
periodSeconds: 5
failureThreshold: 10
livenessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 120
timeoutSeconds: 2
periodSeconds: 5
failureThreshold: 5
EOF
substitutions:
_PROJECT_NAME: app
options:
env:
# # location/name of GKE cluster (used by all kubectl commands)
- CLOUDSDK_COMPUTE_ZONE=asia-northeast1-a
- CLOUDSDK_CONTAINER_CLUSTER=cluster-1

37
app/kubernetes/deployment.yml Executable file
View File

@@ -0,0 +1,37 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
labels:
app: app
spec:
replicas: 1
selector:
matchLabels:
app: app
template:
metadata:
labels:
app: app
spec:
containers:
- name: app
image: username/app:latest
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: '/actuator/health'
port: 8080
initialDelaySeconds: 10
timeoutSeconds: 2
periodSeconds: 5
failureThreshold: 10
livenessProbe:
httpGet:
path: '/actuator/health'
port: 8080
initialDelaySeconds: 120
timeoutSeconds: 2
periodSeconds: 5
failureThreshold: 5

12
app/kubernetes/service.yaml Executable file
View File

@@ -0,0 +1,12 @@
apiVersion: v1
kind: Service
metadata:
name: app
labels:
app: app
spec:
ports:
- port: 8080
targetPort: 8080
selector:
app: app

90
app/pom.xml Executable file
View File

@@ -0,0 +1,90 @@
<?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.1.9.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>fooddelivery</groupId>
<artifactId>app</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>app</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.RELEASE</spring-cloud.version>
<spring-cloud-stream.version>Germantown.SR1</spring-cloud-stream.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- kafka streams -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-kafka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-dependencies</artifactId>
<version>${spring-cloud-stream.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,78 @@
package fooddelivery;
import fooddelivery.config.kafka.KafkaProcessor;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.util.MimeTypeUtils;
import java.text.SimpleDateFormat;
import java.util.Date;
public class AbstractEvent {
String eventType;
String timestamp;
public AbstractEvent(){
this.setEventType(this.getClass().getSimpleName());
SimpleDateFormat defaultSimpleDateFormat = new SimpleDateFormat("YYYYMMddHHmmss");
this.timestamp = defaultSimpleDateFormat.format(new Date());
}
public String toJson(){
ObjectMapper objectMapper = new ObjectMapper();
String json = null;
try {
json = objectMapper.writeValueAsString(this);
} catch (JsonProcessingException e) {
throw new RuntimeException("JSON format exception", e);
}
return json;
}
public void publish(){
this.publish(this.toJson());
}
public void publish(String json){
if( json != null ){
/**
* spring streams 방식
*/
KafkaProcessor processor = Application.applicationContext.getBean(KafkaProcessor.class);
MessageChannel outputChannel = processor.outboundTopic();
outputChannel.send(MessageBuilder
.withPayload(json)
.setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON)
.build());
}
}
public String getEventType() {
return eventType;
}
public void setEventType(String eventType) {
this.eventType = eventType;
}
public String getTimestamp() {
return timestamp;
}
public void setTimestamp(String timestamp) {
this.timestamp = timestamp;
}
public boolean isMe(){
return getEventType().equals(getClass().getSimpleName());
}
}

View File

@@ -0,0 +1,18 @@
package fooddelivery;
import fooddelivery.config.kafka.KafkaProcessor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableBinding(KafkaProcessor.class)
@EnableFeignClients
public class Application {
protected static ApplicationContext applicationContext;
public static void main(String[] args) {
applicationContext = SpringApplication.run(Application.class, args);
}
}

View File

@@ -0,0 +1,57 @@
package fooddelivery;
import javax.persistence.*;
import org.springframework.beans.BeanUtils;
@Entity
@Table(name="주문_table")
public class Order {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String item;
private Integer 수량;
@PostPersist
public void onPostPersist(){
//Following code causes dependency to external APIs
// it is NOT A GOOD PRACTICE. instead, Event-Policy mapping is recommended.
fooddelivery.external.결제이력 결제이력 = new fooddelivery.external.결제이력();
// mappings goes here
Application.applicationContext.getBean(fooddelivery.external.결제이력Service.class)
.결제(결제이력);
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getItem() {
return item;
}
public void setItem(String item) {
this.item = item;
}
public Integer get수량() {
return 수량;
}
public void set수량(Integer 수량) {
this.수량 = 수량;
}
}

View File

@@ -0,0 +1,22 @@
package fooddelivery;
import fooddelivery.config.kafka.KafkaProcessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.stereotype.Service;
@Service
public class PolicyHandler{
@StreamListener(KafkaProcessor.INPUT)
public void whenever배달시작됨_주문상태변경(@Payload 배달시작됨 배달시작됨){
if(배달시작됨.isMe()){
System.out.println("##### listener 주문상태변경 : " + 배달시작됨.toJson());
}
}
}

View File

@@ -0,0 +1,19 @@
package fooddelivery.config.kafka;
import org.springframework.cloud.stream.annotation.Input;
import org.springframework.cloud.stream.annotation.Output;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.SubscribableChannel;
public interface KafkaProcessor {
String INPUT = "event-in";
String OUTPUT = "event-out";
@Input(INPUT)
SubscribableChannel inboundTopic();
@Output(OUTPUT)
MessageChannel outboundTopic();
}

View File

@@ -0,0 +1,33 @@
package fooddelivery.external;
public class 결제이력 {
private Long id;
private String orderId;
private Double 금액;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
public Double get금액() {
return 금액;
}
public void set금액(Double 금액) {
this.금액 = 금액;
}
}

View File

@@ -0,0 +1,21 @@
package fooddelivery.external;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import java.util.Date;
/**
* Created by uengine on 2018. 11. 21..
*/
@FeignClient(name="pay", url="http://localhost:8082")//, fallback = 결제이력ServiceFallback.class)
public interface 결제이력Service {
@RequestMapping(method= RequestMethod.POST, path="/결제이력s")
public void 결제(@RequestBody 결제이력 결제이력);
}

View File

@@ -0,0 +1,13 @@
package fooddelivery.external;
/**
* Created by uengine on 2020. 4. 18..
*/
public class 결제이력ServiceFallback implements 결제이력Service {
@Override
public void 결제(결제이력 주문) {
//do nothing if you want to forgive it
System.out.println("Circuit breaker has been opened. Fallback returned instead.");
}
}

View File

@@ -0,0 +1,39 @@
package fooddelivery;
public class 배달시작됨 extends AbstractEvent {
private Long id;
private String 요리종류;
private String 배달지주소;
private String orderId;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String get요리종류() {
return 요리종류;
}
public void set요리종류(String 요리종류) {
this.요리종류 = 요리종류;
}
public String get배달지주소() {
return 배달지주소;
}
public void set배달지주소(String 배달지주소) {
this.배달지주소 = 배달지주소;
}
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
}

View File

@@ -0,0 +1,15 @@
package fooddelivery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
@RestController
public class 주문Controller {
}

View File

@@ -0,0 +1,8 @@
package fooddelivery;
import org.springframework.data.repository.PagingAndSortingRepository;
public interface 주문Repository extends PagingAndSortingRepository<Order, Long>{
}

View File

@@ -0,0 +1,34 @@
package fooddelivery;
public class 주문됨 extends AbstractEvent {
private Long id;
private String 품목;
private Integer 수량;
public 주문됨(){
super();
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String get품목() {
return 품목;
}
public void set품목(String 품목) {
this.품목 = 품목;
}
public Integer get수량() {
return 수량;
}
public void set수량(Integer 수량) {
this.수량 = 수량;
}
}

View File

@@ -0,0 +1,18 @@
package fooddelivery;
public class 주문취소됨 extends AbstractEvent {
private Long id;
public 주문취소됨(){
super();
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}

View File

@@ -0,0 +1,82 @@
server:
port: 8080
---
spring:
profiles: default
jpa:
properties:
hibernate:
show_sql: true
format_sql: true
cloud:
stream:
kafka:
binder:
brokers: localhost:9092
streams:
binder:
configuration:
default:
key:
serde: org.apache.kafka.common.serialization.Serdes$StringSerde
value:
serde: org.apache.kafka.common.serialization.Serdes$StringSerde
bindings:
event-in:
group: app
destination: fooddelivery
contentType: application/json
event-out:
destination: fooddelivery
contentType: application/json
logging:
level:
org.hibernate.type: trace
org.springframework.cloud: debug
server:
port: 8081
feign:
hystrix:
enabled: true
# To set thread isolation to SEMAPHORE
#hystrix:
# command:
# default:
# execution:
# isolation:
# strategy: SEMAPHORE
hystrix:
command:
# 전역설정
default:
execution.isolation.thread.timeoutInMilliseconds: 610
---
spring:
profiles: docker
cloud:
stream:
kafka:
binder:
brokers: my-kafka.kafka.svc.cluster.local:9092
streams:
binder:
configuration:
default:
key:
serde: org.apache.kafka.common.serialization.Serdes$StringSerde
value:
serde: org.apache.kafka.common.serialization.Serdes$StringSerde
bindings:
event-in:
group: app
destination: fooddelivery
contentType: application/json
event-out:
destination: fooddelivery
contentType: application/json

View File

@@ -0,0 +1,9 @@
FROM python:2.7-slim
WORKDIR /app
ADD . /app
RUN pip install --trusted-host pypi.python.org -r requirements.txt
ENV NAME World
EXPOSE 8090
CMD ["python", "command-handler.py"]

View File

@@ -0,0 +1,9 @@
FROM python:2.7-slim
WORKDIR /app
ADD . /app
RUN pip install --trusted-host pypi.python.org -r requirements.txt
ENV NAME World
EXPOSE 8090
CMD ["python", "policy-handler.py"]

21
customer/LICENSE Executable file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 msaez-template
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

1
customer/README.md Executable file
View File

@@ -0,0 +1 @@
# python

18
customer/command-handler.py Executable file
View File

@@ -0,0 +1,18 @@
from flask import Flask
from redis import Redis, RedisError
from kafka import KafkaConsumer
import os
import socket
app = Flask(__name__)
@app.route("/customer")
def hello():
html = "<h3>Hello {name}!</h3>" \
"<b>Hostname:</b> {hostname}<br/>"
return html.format(name=os.getenv("NAME", "world"), hostname=socket.gethostname())
if __name__ == "__main__":
app.run(host='0.0.0.0', port=8084)

View File

@@ -0,0 +1,23 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: customer
labels:
app: customer
spec:
replicas: 1
selector:
matchLabels:
app: customer
template:
metadata:
labels:
app: customer
spec:
containers:
- name: command-handler
image: username/customer-command-handler:latest
ports:
- containerPort: 8084
- name: policy-handler
image: username/customer-policy-handler:latest

View File

@@ -0,0 +1,12 @@
apiVersion: v1
kind: Service
metadata:
name: customer
labels:
app: customer
spec:
ports:
- port: 8080
targetPort: 8080
selector:
app: customer

17
customer/policy-handler.py Executable file
View File

@@ -0,0 +1,17 @@
from flask import Flask
from redis import Redis, RedisError
from kafka import KafkaConsumer
import os
import socket
# To consume latest messages and auto-commit offsets
consumer = KafkaConsumer('fooddelivery',
group_id='',
bootstrap_servers=['localhost:9092'])
for message in consumer:
print ("%s:%d:%d: key=%s value=%s" % (message.topic, message.partition,
message.offset, message.key,
message.value))

4
customer/requirements.txt Executable file
View File

@@ -0,0 +1,4 @@
Flask
Redis
kafka-python

4
gateway/Dockerfile Executable file
View File

@@ -0,0 +1,4 @@
FROM openjdk:8u212-jdk-alpine
COPY target/*SNAPSHOT.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java","-Xmx400M","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar","--spring.profiles.active=docker"]

85
gateway/cloudbuild.yaml Executable file
View File

@@ -0,0 +1,85 @@
steps:
- id: 'build'
name: 'gcr.io/cloud-builders/mvn'
args: [
'clean',
'package',
'-Dmaven.test.skip=true'
]
### Build
- id: 'docker build'
name: 'gcr.io/cloud-builders/docker'
entrypoint: 'bash'
args:
- '-c'
- |
echo '$COMMIT_SHA =' $COMMIT_SHA
docker build -t gcr.io/$PROJECT_ID/$_PROJECT_NAME:$COMMIT_SHA .
### Test
### Publish
- id: 'publish'
name: 'gcr.io/cloud-builders/docker'
entrypoint: 'bash'
args:
- '-c'
- |
docker push gcr.io/$PROJECT_ID/$_PROJECT_NAME:$COMMIT_SHA
### deploy
- id: 'deploy'
name: 'gcr.io/cloud-builders/gcloud'
entrypoint: 'bash'
args:
- '-c'
- |
PROJECT=$$(gcloud config get-value core/project)
gcloud container clusters get-credentials "$${CLOUDSDK_CONTAINER_CLUSTER}" \
--project "$${PROJECT}" \
--zone "$${CLOUDSDK_COMPUTE_ZONE}"
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
name: $_PROJECT_NAME
labels:
app: $_PROJECT_NAME
spec:
ports:
- port: 8080
targetPort: 8080
selector:
app: $_PROJECT_NAME
type:
LoadBalancer
EOF
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: $_PROJECT_NAME
labels:
app: $_PROJECT_NAME
spec:
replicas: 1
selector:
matchLabels:
app: $_PROJECT_NAME
template:
metadata:
labels:
app: $_PROJECT_NAME
spec:
containers:
- name: $_PROJECT_NAME
image: gcr.io/$PROJECT_ID/$_PROJECT_NAME:$COMMIT_SHA
ports:
- containerPort: 8080
EOF
substitutions:
_PROJECT_NAME: gateway
options:
env:
# # location/name of GKE cluster (used by all kubectl commands)
- CLOUDSDK_COMPUTE_ZONE=asia-northeast1-a
- CLOUDSDK_CONTAINER_CLUSTER=standard-cluster-1

54
gateway/pom.xml Executable file
View File

@@ -0,0 +1,54 @@
<?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.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>boot-camp-gateway</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>boot-camp-gateway</name>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR2</spring-cloud.version>
</properties>
<dependencies>
<!-- Add Stackdriver Trace Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,18 @@
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
@SpringBootApplication
public class Application {
public static ApplicationContext applicationContext;
public static void main(String[] args) {
applicationContext = SpringApplication.run(Application.class, args);
}
}

View File

@@ -0,0 +1,74 @@
server:
port: 8088
---
spring:
profiles: default
cloud:
gateway:
routes:
- id: app
uri: http://localhost:8081
predicates:
- Path=/주문/** /메뉴판/**/통합주문상태/**
- id: pay
uri: http://localhost:8082
predicates:
- Path=/결제이력/**
- id: store
uri: http://localhost:8083
predicates:
- Path=/주문관리/** /주문상세보기/**
- id: customer
uri: http://localhost:8084
predicates:
- Path=
globalcors:
corsConfigurations:
'[/**]':
allowedOrigins:
- "*"
allowedMethods:
- "*"
allowedHeaders:
- "*"
allowCredentials: true
---
spring:
profiles: docker
cloud:
gateway:
routes:
- id: app
uri: http://app:8080
predicates:
- Path=/주문/** /메뉴판/**/통합주문상태/**
- id: pay
uri: http://pay:8080
predicates:
- Path=/결제이력/**
- id: store
uri: http://store:8080
predicates:
- Path=/주문관리/** /주문상세보기/**
- id: customer
uri: http://customer:8080
predicates:
- Path=
globalcors:
corsConfigurations:
'[/**]':
allowedOrigins:
- "*"
allowedMethods:
- "*"
allowedHeaders:
- "*"
allowCredentials: true
server:
port: 8080

4
pay/Dockerfile Executable file
View File

@@ -0,0 +1,4 @@
FROM openjdk:8u212-jdk-alpine
COPY target/*SNAPSHOT.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java","-Xmx400M","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar","--spring.profiles.active=docker"]

103
pay/cloudbuild.yaml Executable file
View File

@@ -0,0 +1,103 @@
steps:
### Test
# - id: 'test'
# name: 'gcr.io/cloud-builders/mvn'
# args: [
# 'test',
# '-Dspring.profiles.active=test'
# ]
### Build
- id: 'build'
name: 'gcr.io/cloud-builders/mvn'
args: [
'clean',
'package'
# '-Dmaven.test.skip=true'
]
# waitFor: ['test']
### docker Build
- id: 'docker build'
name: 'gcr.io/cloud-builders/docker'
args:
- 'build'
- '--tag=gcr.io/$PROJECT_ID/$_PROJECT_NAME:latest'
- '.'
### Publish
- id: 'publish'
name: 'gcr.io/cloud-builders/docker'
entrypoint: 'bash'
args:
- '-c'
- |
docker push gcr.io/$PROJECT_ID/$_PROJECT_NAME:latest
### deploy
- id: 'deploy'
name: 'gcr.io/cloud-builders/gcloud'
entrypoint: 'bash'
args:
- '-c'
- |
PROJECT=$$(gcloud config get-value core/project)
gcloud container clusters get-credentials "$${CLOUDSDK_CONTAINER_CLUSTER}" \
--project "$${PROJECT}" \
--zone "$${CLOUDSDK_COMPUTE_ZONE}"
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
name: $_PROJECT_NAME
labels:
app: $_PROJECT_NAME
spec:
ports:
- port: 8080
targetPort: 8080
selector:
app: $_PROJECT_NAME
EOF
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: $_PROJECT_NAME
labels:
app: $_PROJECT_NAME
spec:
replicas: 1
selector:
matchLabels:
app: $_PROJECT_NAME
template:
metadata:
labels:
app: $_PROJECT_NAME
spec:
containers:
- name: $_PROJECT_NAME
image: gcr.io/$PROJECT_ID/$_PROJECT_NAME:latest
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 10
timeoutSeconds: 2
periodSeconds: 5
failureThreshold: 10
livenessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 120
timeoutSeconds: 2
periodSeconds: 5
failureThreshold: 5
EOF
substitutions:
_PROJECT_NAME: pay
options:
env:
# # location/name of GKE cluster (used by all kubectl commands)
- CLOUDSDK_COMPUTE_ZONE=asia-northeast1-a
- CLOUDSDK_CONTAINER_CLUSTER=cluster-1

37
pay/kubernetes/deployment.yml Executable file
View File

@@ -0,0 +1,37 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: pay
labels:
app: pay
spec:
replicas: 1
selector:
matchLabels:
app: pay
template:
metadata:
labels:
app: pay
spec:
containers:
- name: pay
image: username/pay:latest
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: '/actuator/health'
port: 8080
initialDelaySeconds: 10
timeoutSeconds: 2
periodSeconds: 5
failureThreshold: 10
livenessProbe:
httpGet:
path: '/actuator/health'
port: 8080
initialDelaySeconds: 120
timeoutSeconds: 2
periodSeconds: 5
failureThreshold: 5

12
pay/kubernetes/service.yaml Executable file
View File

@@ -0,0 +1,12 @@
apiVersion: v1
kind: Service
metadata:
name: pay
labels:
app: pay
spec:
ports:
- port: 8080
targetPort: 8080
selector:
app: pay

90
pay/pom.xml Executable file
View File

@@ -0,0 +1,90 @@
<?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.1.9.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>fooddelivery</groupId>
<artifactId>pay</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>pay</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.RELEASE</spring-cloud.version>
<spring-cloud-stream.version>Germantown.SR1</spring-cloud-stream.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- kafka streams -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-kafka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-dependencies</artifactId>
<version>${spring-cloud-stream.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,78 @@
package fooddelivery;
import fooddelivery.config.kafka.KafkaProcessor;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.util.MimeTypeUtils;
import java.text.SimpleDateFormat;
import java.util.Date;
public class AbstractEvent {
String eventType;
String timestamp;
public AbstractEvent(){
this.setEventType(this.getClass().getSimpleName());
SimpleDateFormat defaultSimpleDateFormat = new SimpleDateFormat("YYYYMMddHHmmss");
this.timestamp = defaultSimpleDateFormat.format(new Date());
}
public String toJson(){
ObjectMapper objectMapper = new ObjectMapper();
String json = null;
try {
json = objectMapper.writeValueAsString(this);
} catch (JsonProcessingException e) {
throw new RuntimeException("JSON format exception", e);
}
return json;
}
public void publish(){
this.publish(this.toJson());
}
public void publish(String json){
if( json != null ){
/**
* spring streams 방식
*/
KafkaProcessor processor = Application.applicationContext.getBean(KafkaProcessor.class);
MessageChannel outputChannel = processor.outboundTopic();
outputChannel.send(MessageBuilder
.withPayload(json)
.setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON)
.build());
}
}
public String getEventType() {
return eventType;
}
public void setEventType(String eventType) {
this.eventType = eventType;
}
public String getTimestamp() {
return timestamp;
}
public void setTimestamp(String timestamp) {
this.timestamp = timestamp;
}
public boolean isMe(){
return getEventType().equals(getClass().getSimpleName());
}
}

View File

@@ -0,0 +1,18 @@
package fooddelivery;
import fooddelivery.config.kafka.KafkaProcessor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableBinding(KafkaProcessor.class)
@EnableFeignClients
public class Application {
protected static ApplicationContext applicationContext;
public static void main(String[] args) {
applicationContext = SpringApplication.run(Application.class, args);
}
}

View File

@@ -0,0 +1,22 @@
package fooddelivery;
import fooddelivery.config.kafka.KafkaProcessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.stereotype.Service;
@Service
public class PolicyHandler{
@StreamListener(KafkaProcessor.INPUT)
public void whenever주문취소됨_결재취소함(@Payload 주문취소됨 주문취소됨){
if(주문취소됨.isMe()){
System.out.println("##### listener 결재취소함 : " + 주문취소됨.toJson());
}
}
}

View File

@@ -0,0 +1,19 @@
package fooddelivery.config.kafka;
import org.springframework.cloud.stream.annotation.Input;
import org.springframework.cloud.stream.annotation.Output;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.SubscribableChannel;
public interface KafkaProcessor {
String INPUT = "event-in";
String OUTPUT = "event-out";
@Input(INPUT)
SubscribableChannel inboundTopic();
@Output(OUTPUT)
MessageChannel outboundTopic();
}

View File

@@ -0,0 +1,34 @@
package fooddelivery;
public class 결제승인됨 extends AbstractEvent {
private Long id;
private String orderId;
private Double 금액;
public 결제승인됨(){
super();
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
public Double get금액() {
return 금액;
}
public void set금액(Double 금액) {
this.금액 = 금액;
}
}

View File

@@ -0,0 +1,57 @@
package fooddelivery;
import javax.persistence.*;
import org.springframework.beans.BeanUtils;
import java.util.List;
@Entity
@Table(name="결제이력_table")
public class 결제이력 {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String orderId;
private Double 금액;
@PrePersist
public void onPrePersist(){
결제승인됨 결제승인됨 = new 결제승인됨();
BeanUtils.copyProperties(this, 결제승인됨);
결제승인됨.publish();
try {
Thread.currentThread().sleep((long) (400 + Math.random() * 220));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
public Double get금액() {
return 금액;
}
public void set금액(Double 금액) {
this.금액 = 금액;
}
}

View File

@@ -0,0 +1,15 @@
package fooddelivery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
@RestController
public class 결제이력Controller {
}

View File

@@ -0,0 +1,8 @@
package fooddelivery;
import org.springframework.data.repository.PagingAndSortingRepository;
public interface 결제이력Repository extends PagingAndSortingRepository<결제이력, Long>{
}

View File

@@ -0,0 +1,18 @@
package fooddelivery;
public class 결제취소됨 extends AbstractEvent {
private Long id;
public 결제취소됨(){
super();
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}

View File

@@ -0,0 +1,15 @@
package fooddelivery;
public class 주문취소됨 extends AbstractEvent {
private Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}

View File

@@ -0,0 +1,64 @@
server:
port: 8080
---
spring:
profiles: default
jpa:
properties:
hibernate:
show_sql: true
format_sql: true
cloud:
stream:
kafka:
binder:
brokers: localhost:9092
streams:
binder:
configuration:
default:
key:
serde: org.apache.kafka.common.serialization.Serdes$StringSerde
value:
serde: org.apache.kafka.common.serialization.Serdes$StringSerde
bindings:
event-in:
group: pay
destination: fooddelivery
contentType: application/json
event-out:
destination: fooddelivery
contentType: application/json
logging:
level:
org.hibernate.type: trace
org.springframework.cloud: debug
server:
port: 8082
---
spring:
profiles: docker
cloud:
stream:
kafka:
binder:
brokers: my-kafka.kafka.svc.cluster.local:9092
streams:
binder:
configuration:
default:
key:
serde: org.apache.kafka.common.serialization.Serdes$StringSerde
value:
serde: org.apache.kafka.common.serialization.Serdes$StringSerde
bindings:
event-in:
group: pay
destination: fooddelivery
contentType: application/json
event-out:
destination: fooddelivery
contentType: application/json

4
store/Dockerfile Executable file
View File

@@ -0,0 +1,4 @@
FROM openjdk:8u212-jdk-alpine
COPY target/*SNAPSHOT.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java","-Xmx400M","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar","--spring.profiles.active=docker"]

103
store/cloudbuild.yaml Executable file
View File

@@ -0,0 +1,103 @@
steps:
### Test
# - id: 'test'
# name: 'gcr.io/cloud-builders/mvn'
# args: [
# 'test',
# '-Dspring.profiles.active=test'
# ]
### Build
- id: 'build'
name: 'gcr.io/cloud-builders/mvn'
args: [
'clean',
'package'
# '-Dmaven.test.skip=true'
]
# waitFor: ['test']
### docker Build
- id: 'docker build'
name: 'gcr.io/cloud-builders/docker'
args:
- 'build'
- '--tag=gcr.io/$PROJECT_ID/$_PROJECT_NAME:latest'
- '.'
### Publish
- id: 'publish'
name: 'gcr.io/cloud-builders/docker'
entrypoint: 'bash'
args:
- '-c'
- |
docker push gcr.io/$PROJECT_ID/$_PROJECT_NAME:latest
### deploy
- id: 'deploy'
name: 'gcr.io/cloud-builders/gcloud'
entrypoint: 'bash'
args:
- '-c'
- |
PROJECT=$$(gcloud config get-value core/project)
gcloud container clusters get-credentials "$${CLOUDSDK_CONTAINER_CLUSTER}" \
--project "$${PROJECT}" \
--zone "$${CLOUDSDK_COMPUTE_ZONE}"
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
name: $_PROJECT_NAME
labels:
app: $_PROJECT_NAME
spec:
ports:
- port: 8080
targetPort: 8080
selector:
app: $_PROJECT_NAME
EOF
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: $_PROJECT_NAME
labels:
app: $_PROJECT_NAME
spec:
replicas: 1
selector:
matchLabels:
app: $_PROJECT_NAME
template:
metadata:
labels:
app: $_PROJECT_NAME
spec:
containers:
- name: $_PROJECT_NAME
image: gcr.io/$PROJECT_ID/$_PROJECT_NAME:latest
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 10
timeoutSeconds: 2
periodSeconds: 5
failureThreshold: 10
livenessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 120
timeoutSeconds: 2
periodSeconds: 5
failureThreshold: 5
EOF
substitutions:
_PROJECT_NAME: store
options:
env:
# # location/name of GKE cluster (used by all kubectl commands)
- CLOUDSDK_COMPUTE_ZONE=asia-northeast1-a
- CLOUDSDK_CONTAINER_CLUSTER=cluster-1

37
store/kubernetes/deployment.yml Executable file
View File

@@ -0,0 +1,37 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: store
labels:
app: store
spec:
replicas: 1
selector:
matchLabels:
app: store
template:
metadata:
labels:
app: store
spec:
containers:
- name: store
image: username/store:latest
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: '/actuator/health'
port: 8080
initialDelaySeconds: 10
timeoutSeconds: 2
periodSeconds: 5
failureThreshold: 10
livenessProbe:
httpGet:
path: '/actuator/health'
port: 8080
initialDelaySeconds: 120
timeoutSeconds: 2
periodSeconds: 5
failureThreshold: 5

12
store/kubernetes/service.yaml Executable file
View File

@@ -0,0 +1,12 @@
apiVersion: v1
kind: Service
metadata:
name: store
labels:
app: store
spec:
ports:
- port: 8080
targetPort: 8080
selector:
app: store

90
store/pom.xml Executable file
View File

@@ -0,0 +1,90 @@
<?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.1.9.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>fooddelivery</groupId>
<artifactId>store</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>store</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.RELEASE</spring-cloud.version>
<spring-cloud-stream.version>Germantown.SR1</spring-cloud-stream.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- kafka streams -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-kafka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-dependencies</artifactId>
<version>${spring-cloud-stream.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,78 @@
package fooddelivery;
import fooddelivery.config.kafka.KafkaProcessor;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.util.MimeTypeUtils;
import java.text.SimpleDateFormat;
import java.util.Date;
public class AbstractEvent {
String eventType;
String timestamp;
public AbstractEvent(){
this.setEventType(this.getClass().getSimpleName());
SimpleDateFormat defaultSimpleDateFormat = new SimpleDateFormat("YYYYMMddHHmmss");
this.timestamp = defaultSimpleDateFormat.format(new Date());
}
public String toJson(){
ObjectMapper objectMapper = new ObjectMapper();
String json = null;
try {
json = objectMapper.writeValueAsString(this);
} catch (JsonProcessingException e) {
throw new RuntimeException("JSON format exception", e);
}
return json;
}
public void publish(){
this.publish(this.toJson());
}
public void publish(String json){
if( json != null ){
/**
* spring streams 방식
*/
KafkaProcessor processor = Application.applicationContext.getBean(KafkaProcessor.class);
MessageChannel outputChannel = processor.outboundTopic();
outputChannel.send(MessageBuilder
.withPayload(json)
.setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON)
.build());
}
}
public String getEventType() {
return eventType;
}
public void setEventType(String eventType) {
this.eventType = eventType;
}
public String getTimestamp() {
return timestamp;
}
public void setTimestamp(String timestamp) {
this.timestamp = timestamp;
}
public boolean isMe(){
return getEventType().equals(getClass().getSimpleName());
}
}

View File

@@ -0,0 +1,18 @@
package fooddelivery;
import fooddelivery.config.kafka.KafkaProcessor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableBinding(KafkaProcessor.class)
@EnableFeignClients
public class Application {
protected static ApplicationContext applicationContext;
public static void main(String[] args) {
applicationContext = SpringApplication.run(Application.class, args);
}
}

View File

@@ -0,0 +1,29 @@
package fooddelivery;
import fooddelivery.config.kafka.KafkaProcessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.stereotype.Service;
@Service
public class PolicyHandler{
@StreamListener(KafkaProcessor.INPUT)
public void whenever결제승인됨_주문정보받음(@Payload 결제승인됨 결제승인됨){
if(결제승인됨.isMe()){
System.out.println("##### listener 주문정보받음 : " + 결제승인됨.toJson());
}
}
@StreamListener(KafkaProcessor.INPUT)
public void whenever결제취소됨_주문취소처리(@Payload 결제취소됨 결제취소됨){
if(결제취소됨.isMe()){
System.out.println("##### listener 주문취소처리 : " + 결제취소됨.toJson());
}
}
}

View File

@@ -0,0 +1,19 @@
package fooddelivery.config.kafka;
import org.springframework.cloud.stream.annotation.Input;
import org.springframework.cloud.stream.annotation.Output;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.SubscribableChannel;
public interface KafkaProcessor {
String INPUT = "event-in";
String OUTPUT = "event-out";
@Input(INPUT)
SubscribableChannel inboundTopic();
@Output(OUTPUT)
MessageChannel outboundTopic();
}

View File

@@ -0,0 +1,43 @@
package fooddelivery;
import javax.persistence.PrePersist;
public class 결제승인됨 extends AbstractEvent {
private Long id;
private String orderId;
private Double 금액;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
public Double get금액() {
return 금액;
}
public void set금액(Double 금액) {
this.금액 = 금액;
}
@PrePersist
public void delay(){
try {
Thread.currentThread().sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,15 @@
package fooddelivery;
public class 결제취소됨 extends AbstractEvent {
private Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}

View File

@@ -0,0 +1,42 @@
package fooddelivery;
public class 배달시작됨 extends AbstractEvent {
private Long id;
private String 요리종류;
private String 배달지주소;
private String orderId;
public 배달시작됨(){
super();
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String get요리종류() {
return 요리종류;
}
public void set요리종류(String 요리종류) {
this.요리종류 = 요리종류;
}
public String get배달지주소() {
return 배달지주소;
}
public void set배달지주소(String 배달지주소) {
this.배달지주소 = 배달지주소;
}
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
}

View File

@@ -0,0 +1,45 @@
package fooddelivery;
import javax.persistence.*;
import org.springframework.beans.BeanUtils;
import java.util.List;
@Entity
@Table(name="주문관리_table")
public class 주문관리 {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
@PostPersist
public void onPostPersist(){
배달시작됨 배달시작됨 = new 배달시작됨();
BeanUtils.copyProperties(this, 배달시작됨);
배달시작됨.publish();
}
@PrePersist
public void onPrePersist(){
쿠폰발행됨 쿠폰발행됨 = new 쿠폰발행됨();
BeanUtils.copyProperties(this, 쿠폰발행됨);
쿠폰발행됨.publish();
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}

View File

@@ -0,0 +1,15 @@
package fooddelivery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
@RestController
public class 주문관리Controller {
}

View File

@@ -0,0 +1,8 @@
package fooddelivery;
import org.springframework.data.repository.PagingAndSortingRepository;
public interface 주문관리Repository extends PagingAndSortingRepository<주문관리, Long>{
}

View File

@@ -0,0 +1,42 @@
package fooddelivery;
public class 쿠폰발행됨 extends AbstractEvent {
private Long id;
private String 요리종류;
private String 배달지주소;
private String orderId;
public 쿠폰발행됨(){
super();
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String get요리종류() {
return 요리종류;
}
public void set요리종류(String 요리종류) {
this.요리종류 = 요리종류;
}
public String get배달지주소() {
return 배달지주소;
}
public void set배달지주소(String 배달지주소) {
this.배달지주소 = 배달지주소;
}
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
}

View File

@@ -0,0 +1,64 @@
server:
port: 8080
---
spring:
profiles: default
jpa:
properties:
hibernate:
show_sql: true
format_sql: true
cloud:
stream:
kafka:
binder:
brokers: localhost:9092
streams:
binder:
configuration:
default:
key:
serde: org.apache.kafka.common.serialization.Serdes$StringSerde
value:
serde: org.apache.kafka.common.serialization.Serdes$StringSerde
bindings:
event-in:
group: store
destination: fooddelivery
contentType: application/json
event-out:
destination: fooddelivery
contentType: application/json
logging:
level:
org.hibernate.type: trace
org.springframework.cloud: debug
server:
port: 8083
---
spring:
profiles: docker
cloud:
stream:
kafka:
binder:
brokers: my-kafka.kafka.svc.cluster.local:9092
streams:
binder:
configuration:
default:
key:
serde: org.apache.kafka.common.serialization.Serdes$StringSerde
value:
serde: org.apache.kafka.common.serialization.Serdes$StringSerde
bindings:
event-in:
group: store
destination: fooddelivery
contentType: application/json
event-out:
destination: fooddelivery
contentType: application/json