A demo application inspired by *ber Eats with the following Google Cloud Products.
Technologies which this demo application uses.
- Golang
- Restful API
- gRPC (Server-side streaming)
- MySQL
- Messaging Queue
- Container (a.k.a Docker)
Set your preferred Google Cloud region name.
export REGION_NAME={{REGION_NAME}}
Set your Google Cloud Project ID
export PROJECT_ID={{PROJECT_ID}}
Set your Artifact Registry repository name
export REPO_NAME={{REPO_NAME}}
Set your Service Account name
export SA_NAME={{SERVICE_ACCOUNT_NAME}}
Set your DB instance name
export DB_INSTANCE_NAME={{DB_INSTANCE_NAME}}
Set your schema id
export SCHEMA_ID={{SCHEMA_ID}}
Set your Topic id
export TOPIC_ID={{TOPIC_ID}}
Set your Subscription id
export SUB_ID={{SUB_ID}}
Enable Google Cloud APIs
gcloud services enable \
run.googleapis.com \
sql-component.googleapis.com \
sqladmin.googleapis.com \
compute.googleapis.com \
pubsub.googleapis.com \
artifactregistry.googleapis.com \
cloudbuild.googleapis.com
Set the project id into gcloud.
gcloud config set project $PROJECT_ID
Create a Service Account and give necessary roles to it.
gcloud iam service-accounts create ${SA_NAME}
gcloud projects add-iam-policy-binding ${PROJECT_ID} --member "serviceAccount:${SA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com" --role "roles/pubsub.publisher"
gcloud projects add-iam-policy-binding ${PROJECT_ID} --member "serviceAccount:${SA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com" --role "roles/pubsub.subscriber"
dd
gcloud projects add-iam-policy-binding ${PROJECT_ID} --member "serviceAccount:${SA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com" --role "roles/cloudsql.client"
Create a MySQL DB Instance.
gcloud sql instances create ${DB_INSTANCE_NAME} --tier=db-custom-1-3840 --region=${REGION_NAME}
Confirm if you could create it.
gcloud sql instances list
Change the root user's password.
gcloud sql users set-password root \
--host="%" \
--instance=${DB_INSTANCE_NAME} \
--prompt-for-password
Connect to the DB instance with the root user.
gcloud sql connect ${DB_INSTANCE_NAME} --user=root
Create your database, the name must be "handson"
CREATE DATABASE handson;
Leave from the DB
QUIT;
Set DB related parameters as env vars.
export DB_USER=root
export DB_PWD={{DB_PASSWORD}}
export DB_INSTANCE=$(gcloud sql instances describe ${DB_INSTANCE_NAME} --format json | jq -r .connectionName)
export DB_CONNECTION="/cloudsql/"$(gcloud sql instances describe ${DB_INSTANCE_NAME} --format json | jq -r .connectionName)
Create the message schema as Protocol buffer type.
gcloud beta pubsub schemas create ${SCHEMA_ID} \
--type=PROTOCOL_BUFFER \
--definition='syntax = "proto3";message ProtocolBuffer {string event_name = 1;string purchaser = 2;int64 order_id = 3;int64 item_id = 4;}'
Verify your schema.
gcloud beta pubsub schemas validate-message \
--message-encoding=JSON \
--message='{"event_name": "Order received", "purchaser": "Taro Yamada", "order_id": 1, "item_id": 1 }' \
--schema-name=${SCHEMA_ID}
Create a topic with the schema.
gcloud beta pubsub topics create ${TOPIC_ID} \
--message-encoding=JSON \
--schema=${SCHEMA_ID}
Create a subscription.
gcloud pubsub subscriptions create ${SUB_ID} \
--topic=${TOPIC_ID}
Note: please make your own Artifact Registry repo in advance, if you don't have it yet.
Create an Artifact Registry's repo.
gcloud artifacts repositories create ${REPO_NAME} --repository-format=docker --location=${REGION_NAME}
Git clone this repo to your local.
git clone git@github.com:kazshinohara/eats-api-demo.git
Build eats service image & Push it to Artifact Registry's repo by Cloud Build.
cd eats-api-demo/eats
gcloud builds submit --tag ${REGION_NAME}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/eats:v1
Build notification server image & Push it to Artifact Registry's repo by Cloud Build.
cd ../notification-server
gcloud builds submit --tag ${REGION_NAME}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/notification-server:v1
Build notification client image in your local.
cd ../notification-client
docker build -t ${REGION_NAME}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/notification-client:v1 .
Set Cloud Run's base configuration.
gcloud config set run/region ${REGION_NAME}
gcloud config set run/platform managed
Deploy Eats.
gcloud run deploy eats \
--image=${REGION_NAME}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/eats:v1 \
--allow-unauthenticated \
--set-env-vars=DB_PWD=${DB_PWD},DB_USER=${DB_USER},DB_CONNECTION=${DB_CONNECTION},PROJECT_ID=${PROJECT_ID},TOPIC_ID=${TOPIC_ID} \
--set-cloudsql-instances=${DB_INSTANCE} \
--service-account="${SA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com"
Get Eats service's url for test access later.
export EATS_URL=$(gcloud run services describe eats --format json | jq -r '.status.address.url')
Deploy Notification server.
gcloud beta run deploy notification-server \
--image=${REGION_NAME}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/notification-server:v1 \
--allow-unauthenticated \
--use-http2 \
--timeout=3600 \
--set-env-vars=PROJECT_ID=${PROJECT_ID},SUB_ID=${SUB_ID} \
--service-account="${SA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com"
Get domain name of Notification server for the client's access.
export NOTIFICATION_DOMAIN=$(gcloud run services describe notification-server --format json | jq -r '.status.address.url' | sed 's|https://||g')
Confirm if Eats service is alive.
curl -X GET ${EATS_URL}/ | jq
{"version":"v1","message":"This is Eats service API"}
Get items list, these items were automatically created in the db when eats service starts up.
curl -X GET ${EATS_URL}/items | jq
[
{
"ID":1,
"CreatedAt":"2021-06-11T16:48:44.379Z",
"UpdatedAt":"2021-06-11T16:48:44.379Z",
"DeletedAt":null,
"name":"Simple Pizza",
"price":1000,
"currency":"JPY"
},
{
"ID":2,
"CreatedAt":"2021-06-11T16:48:44.385Z",
"UpdatedAt":"2021-06-11T16:48:44.385Z",
"DeletedAt":null,
"name":"Normal Pizza",
"price":2000,
"currency":"JPY"
},
{
"ID":3,
"CreatedAt":"2021-06-11T16:48:44.393Z",
"UpdatedAt":"2021-06-11T16:48:44.393Z",
"DeletedAt":null,
"name":"Luxury Pizza",
"price":3000,
"currency":"JPY"
}
]
Create an order.
curl -X POST -d '{"purchaser":"Taro Yamada","item_id":1}' ${EATS_URL}/orders | jq
{
"ID":1,
"CreatedAt":"2021-06-13T16:34:30.286Z",
"UpdatedAt":"2021-06-13T16:34:30.286Z",
"DeletedAt":null,"item_id":1,
"purchaser":"Taro Yamada",
"item_completed":false,
"delivery_completed":false,
"delivery_completed_at":null
}
Get orders list, you could see what you created in the earlier step.
curl -X GET ${EATS_URL}/orders | jq
{
"ID":1,
"CreatedAt":"2021-06-13T16:34:30.286Z",
"UpdatedAt":"2021-06-13T16:34:30.286Z",
"DeletedAt":null,"item_id":1,
"purchaser":"Taro Yamada",
"item_completed":false,
"delivery_completed":false,
"delivery_completed_at":null
}
Update your order changing item_completed flag to true.
curl -X PUT -d '{"purchaser":"Taro Yamada","item_id":1,"item_completed":true}' ${EATS_URL}/orders/1 | jq
{
"ID":1,
"CreatedAt":"2021-06-13T16:34:30.286Z",
"UpdatedAt":"2021-06-13T17:10:53.908Z",
"DeletedAt":null,
"item_id":1,
"purchaser":"Taro Yamada",
"item_completed":true,
"delivery_completed":false,
"delivery_completed_at":null
}
Delete your order.
curl -X DELETE ${EATS_URL}/orders/1 | jq
{"id":"1","message":"deleted"}
Run Notification client in your local.
docker run --name notification-client -e INSECURE=false -e DOMAIN=${NOTIFICATION_DOMAIN} -e PORT=443 ${REGION_NAME}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/notification-client:v1
Create orders, recommend you to try creating 5 ~ 6 orders to get the notification quickly.
curl -X POST -d '{"purchaser":"Taro Yamada","item_id":1}' ${EATS_URL}/orders | jq
In your Notification client, you could see the following messages from Notification server.
2021/06/12 14:40:10 Subscribing...
2021/06/12 14:40:34 {"event_name":"Order received","purchaser":"Taro Yamada","order_id":2,"item_id":1}
2021/06/12 14:40:34 {"event_name":"Order received","purchaser":"Taro Yamada","order_id":3,"item_id":2}
2021/06/12 14:40:34 {"event_name":"Order received","purchaser":"Taro Yamada","order_id":4,"item_id":3}
Let's try Updating your order and confirm if the notification comes.
curl -X PUT -d '{"purchaser":"Taro Yamada","item_id":1,"item_completed":true}' ${EATS_URL}/orders/2 | jq
In your Notification client.
2021/06/13 17:10:47 {"event_name":"Order updated","purchaser":"Taro Yamada","order_id":2,"item_id":3}