├── .github └── workflows │ └── release.yml ├── .gitignore ├── .talismanrc ├── README.md ├── docker-compose.yml ├── efk-stack ├── README.md ├── efk │ └── es_values.yaml └── services │ ├── frontend-deployment.yaml │ ├── frontend-service.yaml │ ├── gateway-deployment.yaml │ ├── gateway-service.yaml │ ├── ingress-frontend-service.yaml │ ├── ingress-service.yaml │ ├── service-one-deployment.yaml │ ├── service-one-service.yaml │ ├── service-two-deployment.yaml │ └── service-two-service.yaml ├── eureka-server ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── mynotes │ │ └── microservices │ │ └── demo │ │ └── eureka │ │ └── EurekaServerApplication.java │ └── resources │ └── application.yml ├── frontend ├── .browserslistrc ├── .dockerignore ├── .editorconfig ├── .gitignore ├── Dockerfile ├── README.md ├── angular.json ├── e2e │ ├── protractor.conf.js │ ├── src │ │ ├── app.e2e-spec.ts │ │ └── app.po.ts │ └── tsconfig.json ├── karma.conf.js ├── package-lock.json ├── package.json ├── proxy.conf.json ├── src │ ├── app │ │ ├── app-routing.module.ts │ │ ├── app.component.css │ │ ├── app.component.html │ │ ├── app.component.spec.ts │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ ├── home │ │ │ ├── home.component.css │ │ │ ├── home.component.html │ │ │ ├── home.component.spec.ts │ │ │ └── home.component.ts │ │ ├── reactive │ │ │ ├── reactive.component.css │ │ │ ├── reactive.component.html │ │ │ ├── reactive.component.spec.ts │ │ │ └── reactive.component.ts │ │ ├── rest │ │ │ ├── rest.component.css │ │ │ ├── rest.component.html │ │ │ ├── rest.component.spec.ts │ │ │ └── rest.component.ts │ │ ├── services │ │ │ ├── error-handler.service.spec.ts │ │ │ ├── error-handler.service.ts │ │ │ ├── reactive.service.spec.ts │ │ │ ├── reactive.service.ts │ │ │ ├── rest.service.spec.ts │ │ │ ├── rest.service.ts │ │ │ ├── sse.service.spec.ts │ │ │ ├── sse.service.ts │ │ │ ├── websocket.service.spec.ts │ │ │ └── websocket.service.ts │ │ └── websocket │ │ │ ├── websocket.component.css │ │ │ ├── websocket.component.html │ │ │ ├── websocket.component.spec.ts │ │ │ └── websocket.component.ts │ ├── assets │ │ └── .gitkeep │ ├── environments │ │ ├── environment.prod.ts │ │ └── environment.ts │ ├── favicon.ico │ ├── index.html │ ├── main.ts │ ├── polyfills.ts │ ├── styles.css │ └── test.ts ├── tsconfig.app.json ├── tsconfig.json ├── tsconfig.spec.json └── tslint.json ├── gateway ├── .gitignore ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── mynotes │ │ │ └── microservices │ │ │ └── demo │ │ │ └── gateway │ │ │ └── GatewayApplication.java │ └── resources │ │ ├── application.yml │ │ └── bootstrap.yml │ └── test │ ├── java │ └── com │ │ └── mynotes │ │ └── microservices │ │ └── demo │ │ └── gateway │ │ ├── GatewayApplicationBaseIT.java │ │ └── TestDownstreamCallIT.java │ └── resources │ └── application.yml ├── helm ├── README.md └── spring-microservice-demo │ ├── Chart.lock │ ├── Chart.yaml │ └── values.yaml ├── k8s ├── all-service │ ├── frontend-deployment.yaml │ ├── frontend-service.yaml │ ├── gateway-deployment.yaml │ ├── gateway-service.yaml │ ├── ingress-frontend-service.yaml │ ├── ingress-service.yaml │ ├── reactive-deployment.yaml │ ├── reactive-service.yaml │ ├── service-one-deployment.yaml │ ├── service-one-service.yaml │ ├── service-two-deployment.yaml │ ├── service-two-service.yaml │ ├── websocket-deployment.yaml │ └── websocket-service.yaml ├── k8s-dashboard-user │ ├── README.md │ ├── role.yaml │ └── service.yaml └── spring-k8s-loadbalancer-test │ ├── README.md │ ├── audit-policy.yaml │ ├── cleanup.sh │ ├── install.sh │ ├── kind-config.yaml │ ├── service-account.yaml │ ├── service-one-deployment.yaml │ ├── service-one-service.yaml │ ├── service-two-deployment.yaml │ ├── service-two-service.yaml │ └── test.sh ├── pom.xml ├── prometheus-grafana ├── README.md ├── config │ ├── frontend │ │ └── nginx.conf │ ├── grafana │ │ └── provisioning │ │ │ ├── dashboards │ │ │ ├── dashboard.yml │ │ │ └── jvm-micrometer_rev9.json │ │ │ └── datasources │ │ │ └── datasource.yml │ └── prometheus │ │ └── prometheus.yml └── docker-compose.yml ├── reactive-service ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── mynotes │ │ └── microservices │ │ └── demo │ │ └── reactive │ │ ├── Application.java │ │ ├── GreetReactiveController.java │ │ ├── Greeting.java │ │ ├── HopController.java │ │ ├── Person.java │ │ ├── PersonController.java │ │ └── ReactiveController.java │ └── resources │ ├── application.yml │ └── bootstrap.yml ├── service-one ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── mynotes │ │ └── microservices │ │ └── demo │ │ └── serviceone │ │ ├── Fabric8ServicesListSupplier.java │ │ ├── HopController.java │ │ ├── ServiceOneApplication.java │ │ ├── ServiceOneController.java │ │ └── interceptors │ │ ├── LoggingRequestResponse.java │ │ └── RestTemplateCopyHeaders.java │ └── resources │ ├── META-INF │ └── spring │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ ├── application.yml │ └── bootstrap.yml ├── service-two ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── mynotes │ │ └── microservices │ │ └── demo │ │ └── servicetwo │ │ ├── ServiceTwoApplication.java │ │ └── ServiceTwoController.java │ └── resources │ ├── application.yml │ └── bootstrap.yml ├── tracing-zipkins ├── README.md ├── config │ └── frontend │ │ └── nginx.conf ├── docker-compose.yml └── old │ └── docker-compose.yml ├── uml-diagrams └── forwarded-for │ ├── ip-with-forward-headers-stratergy-empty.puml │ ├── ip-with-forward-headers-stratergy-framework.puml │ ├── ip-with-forward-headers-stratergy-native.puml │ └── ip-with-forward-headers-stratergy-none.puml └── websocket-service ├── pom.xml └── src └── main ├── java └── com │ └── mynotes │ └── microservices │ └── demo │ └── websocket │ ├── Greeting.java │ ├── GreetingController.java │ ├── HelloMessage.java │ ├── WebSocketConfig.java │ └── WebsocketServiceApplication.java └── resources ├── application.yml ├── bootstrap.yml └── static ├── app.js ├── index.html └── main.css /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Maven 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven 3 | 4 | name: Java CI with Maven 5 | 6 | on: 7 | push: 8 | branches: 9 | - master 10 | - spring-boot-2-line 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v2 18 | 19 | - name: Build 20 | uses: actions/setup-java@v1 21 | with: 22 | java-version: 17.0.x 23 | - run: mvn clean install 24 | 25 | - name: Build Images using JIB 26 | run: mvn -pl eureka-server,service-one,service-two,reactive-service,gateway -Dmaven.test.skip=true package jib:build -Djib.to.auth.username=${{ secrets.DOCKERHUB_USERNAME }} -Djib.to.auth.password=${{ secrets.DOCKERHUB_PASSWORD }} 27 | 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | /target 3 | /.settings 4 | .classpath 5 | .project 6 | /.sts4-cache 7 | .attach_pid* 8 | .idea/ 9 | **/target/** 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | .DS_Store 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.ear 17 | 18 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 19 | hs_err_pid* 20 | 21 | .mvn 22 | *.iml 23 | 24 | **/*.tgz 25 | helm/test.yaml -------------------------------------------------------------------------------- /.talismanrc: -------------------------------------------------------------------------------- 1 | fileignoreconfig: 2 | - filename: .github/workflows/release.yml 3 | checksum: 28b00054dce13299fa34d1f7062d3c8269308d55894e8a7ceb70a8813e369596 4 | version: "" 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Demo 2 | This project is a POC for microservices which has different flavours of backend written in **REST**, **Reactive** and **Websockets**. The frotnend is written in **Angular 8**. 3 | 4 | The project can be run using docker-compose or deployed in Kubernetes. 5 | 6 | ## How To Run Locally 7 | 8 | ### Build and Run Backend 9 | To build docker images for backend run following command: 10 | ```sh 11 | mvn -pl eureka-server,service-one,service-two,reactive-service,websocket-service,gateway -Dmaven.test.skip=true package jib:dockerBuild 12 | ``` 13 | or 14 | ``` 15 | mvn -pl service-one -Dmaven.test.skip=true package jib:dockerBuild 16 | ``` 17 | 18 | `dockerBuild` requires your system to have the docker demon and will build images locally. `build` is more efficient but wont build images locally. 19 | You might need to change the `docker.image.url` in pom.xml if you are using `build`. 20 | 21 | Now, just run all the service using docker-compose: 22 | 23 | ```sh 24 | docker-compose up -d 25 | ``` 26 | 27 | ### Build and Run Frontend 28 | 29 | Go to frontend folder and run 30 | 31 | ```sh 32 | npm install 33 | ``` 34 | 35 | Then to start run 36 | 37 | ```sh 38 | npm run start 39 | ``` 40 | 41 | ## How To Run on Kubernetes 42 | 43 | ### Build Docker Images 44 | 45 | To build docker images for backend run following command: 46 | ```sh 47 | mvn -pl eureka-server,service-one,service-two,reactive-service,websocket-service,gateway -Dmaven.test.skip=true package jib:dockerBuild 48 | ``` 49 | 50 | ### Build Frontend Image 51 | 52 | To build docker image for frontend, run the following command inside `frontend` folder. 53 | ```sh 54 | docker build -t dhananjay12/demo-frontend:latest . 55 | ``` 56 | 57 | **Note** :- Whatever image `prefix` name you give, change in the `k8s` accordingly. 58 | 59 | ### Install local Ingress 60 | 61 | https://kubernetes.github.io/ingress-nginx/deploy/ 62 | 63 | ``` 64 | kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.41.2/deploy/static/provider/cloud/deploy.yaml 65 | ``` 66 | If you want to delete later just use `delete` afterwards. 67 | 68 | ``` 69 | kubectl delete -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.41.2/deploy/static/provider/cloud/deploy.yaml 70 | ``` 71 | 72 | ### Run on K8 73 | 74 | ``` 75 | kubectl apply -f k8s 76 | ``` 77 | 78 | Open - `localhost` 79 | 80 | ## Endpoints 81 | 82 | ### Service One (REST) 83 | 84 | **Endpoint** - `/hello`, `/headers` and `/hop/{status}`. 85 | 86 | If calling via gateway then - `/api/service-one/hello` 87 | 88 | `/hop/{status}` internally calls `service-two` `/status/{status}` endpoint and the response will be that code. 89 | 90 | ### Service Two (REST) 91 | 92 | **Endpoint** - `/hello` and `/status/{status}` 93 | 94 | If calling via gateway then - `/api/service-two/hello` 95 | 96 | 97 | ### Reactive Service (Web Flux) 98 | 99 | **Endpoint** - `/greetings/sse` and `/greetings` 100 | 101 | If calling via gateway then - `/reactive-web-producer/greetings/sse` 102 | 103 | ### WebSocket Service (Websocket) 104 | 105 | **Websocker Connect Endpoint** - `/gs-guide-websocket` 106 | **Topic** - `/topic/greetings` 107 | **Message Mapping** - `/hello` 108 | 109 | If calling via gateway then - `/websocketservice/gs-guide-websocket` 110 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.7" 2 | 3 | x-common-variables: &common-variables 4 | eureka.client.serviceUrl.defaultZone: http://eureka:8080/eureka 5 | eureka.instance.non-secure-port: 8080 6 | spring.sleuth.propagation.type: w3c,b3 7 | #management.tracing.propagation.type: "B3" 8 | services: 9 | eureka: 10 | image: "dhananjay12/demo-eureka-server:latest" 11 | environment: 12 | <<: *common-variables 13 | ports: 14 | - "8761:8080" 15 | gateway: 16 | image: "dhananjay12/demo-gateway:latest" 17 | environment: 18 | <<: *common-variables 19 | ports: 20 | - "8080:8080" 21 | service-one: 22 | image: "dhananjay12/demo-service-one:latest" 23 | environment: 24 | <<: *common-variables 25 | ports: 26 | - "8100:8080" 27 | service-two: 28 | image: "dhananjay12/demo-service-two:latest" 29 | environment: 30 | <<: *common-variables 31 | ports: 32 | - "8200:8080" 33 | reactive-service: 34 | image: "dhananjay12/demo-reactive-service:latest" 35 | environment: 36 | <<: *common-variables 37 | ports: 38 | - "8300:8080" 39 | # websocket-service: 40 | # image: "dhananjay12/demo-websocket-service:latest" 41 | # environment: 42 | # <<: *common-variables 43 | # ports: 44 | # - "8400:8080" 45 | -------------------------------------------------------------------------------- /efk-stack/README.md: -------------------------------------------------------------------------------- 1 | # Setup 2 | 3 | ## Install/Remove local Ingress 4 | 5 | https://kubernetes.github.io/ingress-nginx/deploy/#using-helm 6 | 7 | ``` 8 | helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx 9 | helm install local-ingress ingress-nginx/ingress-nginx 10 | ``` 11 | If you want to delete later just use `delete` afterwards. 12 | 13 | ``` 14 | helm delete local-ingress 15 | ``` 16 | 17 | ## Install/Remove Services 18 | 19 | ``` 20 | kubectl apply -f services 21 | ``` 22 | 23 | For removing 24 | ``` 25 | kubectl delete -f services 26 | ``` 27 | 28 | ## Install Elasticsearch 29 | 30 | https://github.com/elastic/helm-charts/blob/master/elasticsearch/README.md 31 | 32 | Add the elasticsearch helm repo 33 | ``` 34 | helm repo add elastic https://helm.elastic.co 35 | ``` 36 | Install with custom values 37 | ``` 38 | helm install elasticsearch elastic/elasticsearch -f efk/es_values.yaml 39 | ``` 40 | 41 | For local this could be tricky. So we override some values based on eleastic search team recommendations here 42 | https://github.com/elastic/helm-charts/tree/master/elasticsearch/examples/docker-for-mac 43 | 44 | 45 | ## Install Kibana 46 | 47 | https://github.com/elastic/helm-charts/blob/master/kibana/README.md 48 | 49 | We dont need to override anything for this. 50 | ``` 51 | helm install kibana elastic/kibana 52 | ``` 53 | 54 | ## Install FluentD 55 | 56 | https://bitnami.com/stack/fluentd/helm 57 | 58 | 59 | ``` 60 | helm repo add bitnami https://charts.bitnami.com/bitnami 61 | helm install fluentd bitnami/fluentd 62 | ``` -------------------------------------------------------------------------------- /efk-stack/efk/es_values.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Permit co-located instances for solitary minikube virtual machines. 3 | antiAffinity: "soft" 4 | 5 | # Shrink default JVM heap. 6 | esJavaOpts: "-Xmx128m -Xms128m" 7 | 8 | # Allocate smaller chunks of memory per pod. 9 | resources: 10 | requests: 11 | cpu: "100m" 12 | memory: "512M" 13 | limits: 14 | cpu: "1000m" 15 | memory: "512M" 16 | 17 | # Request smaller persistent volumes for local. This is for Docker Desktop 18 | # https://github.com/elastic/helm-charts/blob/master/elasticsearch/examples/docker-for-mac/values.yaml 19 | volumeClaimTemplate: 20 | accessModes: [ "ReadWriteOnce" ] 21 | storageClassName: "hostpath" 22 | resources: 23 | requests: 24 | storage: 100M -------------------------------------------------------------------------------- /efk-stack/services/frontend-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: frontend 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: frontend 10 | template: 11 | metadata: 12 | labels: 13 | app: frontend 14 | spec: 15 | containers: 16 | - name: frontend 17 | image: dhananjay12/demo-frontend:latest 18 | ports: 19 | - containerPort: 80 20 | imagePullPolicy: Always 21 | -------------------------------------------------------------------------------- /efk-stack/services/frontend-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | name: frontend 6 | name: frontend 7 | spec: 8 | type: ClusterIP 9 | selector: 10 | app: frontend 11 | ports: 12 | - port: 80 13 | targetPort: 80 14 | protocol: TCP 15 | -------------------------------------------------------------------------------- /efk-stack/services/gateway-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: demo-gateway 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: demo-gateway 10 | template: 11 | metadata: 12 | labels: 13 | app: demo-gateway 14 | spec: 15 | containers: 16 | - env: 17 | - name: EUREKA_CLIENT_ENABLED 18 | value: "false" 19 | - name: SPRING_CLOUD_KUBERNETES_ENABLED 20 | value: "true" 21 | - name: SPRING_CLOUD_KUBERNETES_DISCOVERY_ENABLED 22 | value: "true" 23 | - name: SPRING_CLOUD_KUBERNETES_RELOAD_ENABLED 24 | value: "true" 25 | image: dhananjay12/demo-gateway:latest 26 | name: demo-gateway 27 | imagePullPolicy: Always 28 | resources: 29 | requests: 30 | memory: "256Mi" 31 | limits: 32 | memory: "512Mi" 33 | ports: 34 | - containerPort: 8080 35 | -------------------------------------------------------------------------------- /efk-stack/services/gateway-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | name: gateway 6 | name: gateway 7 | spec: 8 | type: ClusterIP 9 | selector: 10 | app: gateway 11 | ports: 12 | - port: 8080 13 | targetPort: 8080 14 | protocol: TCP 15 | -------------------------------------------------------------------------------- /efk-stack/services/ingress-frontend-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: Ingress 3 | metadata: 4 | name: ingress-frontend-service 5 | annotations: 6 | ingress.kubernetes.io/rewrite-target: / 7 | spec: 8 | rules: 9 | - http: 10 | paths: 11 | - path: / 12 | pathType: Prefix 13 | backend: 14 | service: 15 | name: frontend 16 | port: 17 | number: 80 18 | -------------------------------------------------------------------------------- /efk-stack/services/ingress-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: Ingress 3 | metadata: 4 | name: ingress-backend-service 5 | annotations: 6 | nginx.ingress.kubernetes.io/use-regex: "true" 7 | spec: 8 | rules: 9 | - http: 10 | paths: 11 | - path: /api/.* 12 | pathType: Prefix 13 | backend: 14 | service: 15 | name: gateway 16 | port: 17 | number: 8080 18 | - path: /websocketservice/.* 19 | pathType: Prefix 20 | backend: 21 | service: 22 | name: gateway 23 | port: 24 | number: 8080 -------------------------------------------------------------------------------- /efk-stack/services/service-one-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: demo-service-one 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: demo-service-one 10 | template: 11 | metadata: 12 | labels: 13 | app: demo-service-one 14 | spec: 15 | containers: 16 | - env: 17 | - name: EUREKA_CLIENT_ENABLED 18 | value: "false" 19 | - name: SPRING_CLOUD_KUBERNETES_ENABLED 20 | value: "true" 21 | - name: SPRING_CLOUD_KUBERNETES_DISCOVERY_ENABLED 22 | value: "true" 23 | - name: SPRING_CLOUD_KUBERNETES_RELOAD_ENABLED 24 | value: "true" 25 | image: dhananjay12/demo-service-one:latest 26 | name: demo-service-one 27 | imagePullPolicy: Always 28 | resources: 29 | requests: 30 | memory: "256Mi" 31 | limits: 32 | memory: "512Mi" 33 | ports: 34 | - containerPort: 8080 35 | -------------------------------------------------------------------------------- /efk-stack/services/service-one-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | name: service-one 6 | name: service-one 7 | spec: 8 | type: ClusterIP 9 | selector: 10 | app: service-one 11 | ports: 12 | - port: 8080 13 | targetPort: 8080 14 | protocol: TCP 15 | -------------------------------------------------------------------------------- /efk-stack/services/service-two-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: demo-service-two 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: demo-service-two 10 | template: 11 | metadata: 12 | labels: 13 | app: demo-service-two 14 | spec: 15 | containers: 16 | - env: 17 | - name: EUREKA_CLIENT_ENABLED 18 | value: "false" 19 | - name: SPRING_CLOUD_KUBERNETES_ENABLED 20 | value: "true" 21 | - name: SPRING_CLOUD_KUBERNETES_DISCOVERY_ENABLED 22 | value: "true" 23 | - name: SPRING_CLOUD_KUBERNETES_RELOAD_ENABLED 24 | value: "true" 25 | image: dhananjay12/demo-service-two:latest 26 | name: demo-service-two 27 | imagePullPolicy: Always 28 | resources: 29 | requests: 30 | memory: "256Mi" 31 | limits: 32 | memory: "512Mi" 33 | ports: 34 | - containerPort: 8080 35 | -------------------------------------------------------------------------------- /efk-stack/services/service-two-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | name: service-two 6 | name: service-two 7 | spec: 8 | type: ClusterIP 9 | selector: 10 | app: service-two 11 | ports: 12 | - port: 8080 13 | targetPort: 8080 14 | protocol: TCP 15 | -------------------------------------------------------------------------------- /eureka-server/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.mynotes.microservices.demo 7 | eureka-server 8 | eureka-server 9 | jar 10 | Demo project for Spring Cloud eureka 11 | 12 | 13 | org.springframework.boot 14 | spring-boot-starter-parent 15 | 3.0.2 16 | 17 | 18 | 19 | 20 | UTF-8 21 | UTF-8 22 | 17 23 | 2022.0.1 24 | false 25 | false 26 | dhananjay12 27 | demo 28 | 29 | 30 | 31 | 32 | 33 | org.springframework.cloud 34 | spring-cloud-dependencies 35 | ${spring-cloud.version} 36 | pom 37 | import 38 | 39 | 40 | 41 | 42 | 43 | 44 | org.springframework.cloud 45 | spring-cloud-starter-netflix-eureka-server 46 | 47 | 48 | 49 | 50 | ${project.artifactId} 51 | 52 | 53 | org.springframework.boot 54 | spring-boot-maven-plugin 55 | 56 | 57 | org.apache.maven.plugins 58 | maven-surefire-plugin 59 | 60 | 61 | 62 | ${surefireArgLine} 63 | ${skip.unit.tests} 64 | 65 | 66 | **/*IT.java 67 | **/*It.java 68 | 69 | 70 | 71 | 72 | 73 | org.apache.maven.plugins 74 | maven-failsafe-plugin 75 | 76 | 77 | 78 | ${failsafeArgLine} 79 | ${skip.integration.tests} 80 | 81 | **/*It.java 82 | **/*IT.java 83 | 84 | 85 | 86 | 87 | pl.project13.maven 88 | git-commit-id-plugin 89 | 4.0.0 90 | 91 | 92 | get-the-git-infos 93 | initialize 94 | 95 | revision 96 | 97 | 98 | 99 | 100 | false 101 | yyyy-MM-dd'T'HH:mm:ss'Z' 102 | GMT 103 | 104 | ^git.commit.time$ 105 | 106 | 107 | 108 | 109 | com.google.cloud.tools 110 | jib-maven-plugin 111 | 2.6.0 112 | 113 | 114 | eclipse-temurin:17-jre-alpine 115 | 116 | 117 | ${docker.image.url}/${docker.image.prefix}-${project.artifactId} 118 | 119 | ${project.version} 120 | 121 | 122 | 123 | ${git.commit.time} 124 | ${git.commit.time} 125 | com.mynotes.microservices.demo.eureka.EurekaServerApplication 126 | 127 | 8080 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /eureka-server/src/main/java/com/mynotes/microservices/demo/eureka/EurekaServerApplication.java: -------------------------------------------------------------------------------- 1 | package com.mynotes.microservices.demo.eureka; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; 6 | 7 | @SpringBootApplication 8 | @EnableEurekaServer 9 | public class EurekaServerApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(EurekaServerApplication.class, args); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /eureka-server/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | eureka: 2 | client: 3 | registerWithEureka: false 4 | fetchRegistry: false 5 | server: 6 | waitTimeInMsWhenSyncEmpty: 0 7 | 8 | spring: 9 | application: 10 | name: eureka-server 11 | -------------------------------------------------------------------------------- /frontend/.browserslistrc: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # You can see what browsers were selected by your queries by running: 6 | # npx browserslist 7 | 8 | > 0.5% 9 | last 2 versions 10 | Firefox ESR 11 | not dead 12 | not IE 9-11 # For IE 9-11 support, remove 'not'. -------------------------------------------------------------------------------- /frontend/.dockerignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | node_modules/ 3 | .git 4 | .gitignore 5 | -------------------------------------------------------------------------------- /frontend/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | # Only exists if Bazel was run 8 | /bazel-out 9 | 10 | # dependencies 11 | /node_modules 12 | 13 | # profiling files 14 | chrome-profiler-events*.json 15 | speed-measure-plugin*.json 16 | 17 | # IDEs and editors 18 | /.idea 19 | .project 20 | .classpath 21 | .c9/ 22 | *.launch 23 | .settings/ 24 | *.sublime-workspace 25 | 26 | # IDE - VSCode 27 | .vscode/* 28 | !.vscode/settings.json 29 | !.vscode/tasks.json 30 | !.vscode/launch.json 31 | !.vscode/extensions.json 32 | .history/* 33 | 34 | # misc 35 | /.sass-cache 36 | /connect.lock 37 | /coverage 38 | /libpeerconnection.log 39 | npm-debug.log 40 | yarn-error.log 41 | testem.log 42 | /typings 43 | 44 | # System Files 45 | .DS_Store 46 | Thumbs.db 47 | -------------------------------------------------------------------------------- /frontend/Dockerfile: -------------------------------------------------------------------------------- 1 | ############# 2 | ### build ### 3 | ############# 4 | 5 | # base image 6 | FROM node:14.15.0 as build 7 | 8 | # set working directory 9 | WORKDIR /app 10 | 11 | # add `/app/node_modules/.bin` to $PATH 12 | ENV PATH /app/node_modules/.bin:$PATH 13 | 14 | # install and cache app dependencies 15 | COPY package.json /app/package.json 16 | RUN npm install 17 | RUN npm install -g @angular/cli@11.0.1 18 | 19 | # add app 20 | COPY . /app 21 | 22 | # generate build 23 | RUN ng build --output-path=dist 24 | 25 | ############ 26 | ### prod ### 27 | ############ 28 | 29 | # base image 30 | FROM nginx:1.16.0-alpine 31 | 32 | # copy artifact build from the 'build environment' 33 | COPY --from=build /app/dist /usr/share/nginx/html 34 | 35 | # expose port 80 36 | EXPOSE 80 37 | 38 | # run nginx 39 | CMD ["nginx", "-g", "daemon off;"] 40 | -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | # Frontend 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.2.2. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 12 | 13 | ## Build 14 | 15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build. 16 | 17 | ## Running unit tests 18 | 19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 20 | 21 | ## Running end-to-end tests 22 | 23 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). 24 | 25 | ## Further help 26 | 27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). 28 | -------------------------------------------------------------------------------- /frontend/angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "frontend": { 7 | "projectType": "application", 8 | "schematics": {}, 9 | "root": "", 10 | "sourceRoot": "src", 11 | "prefix": "app", 12 | "architect": { 13 | "build": { 14 | "builder": "@angular-devkit/build-angular:browser", 15 | "options": { 16 | "outputPath": "dist/frontend", 17 | "index": "src/index.html", 18 | "main": "src/main.ts", 19 | "polyfills": "src/polyfills.ts", 20 | "tsConfig": "tsconfig.app.json", 21 | "aot": true, 22 | "assets": [ 23 | "src/favicon.ico", 24 | "src/assets" 25 | ], 26 | "styles": [ 27 | "src/styles.css" 28 | ], 29 | "scripts": [] 30 | }, 31 | "configurations": { 32 | "production": { 33 | "fileReplacements": [ 34 | { 35 | "replace": "src/environments/environment.ts", 36 | "with": "src/environments/environment.prod.ts" 37 | } 38 | ], 39 | "optimization": true, 40 | "outputHashing": "all", 41 | "sourceMap": false, 42 | "namedChunks": false, 43 | "extractLicenses": true, 44 | "vendorChunk": false, 45 | "buildOptimizer": true, 46 | "budgets": [ 47 | { 48 | "type": "initial", 49 | "maximumWarning": "2mb", 50 | "maximumError": "5mb" 51 | }, 52 | { 53 | "type": "anyComponentStyle", 54 | "maximumWarning": "6kb", 55 | "maximumError": "10kb" 56 | } 57 | ] 58 | } 59 | } 60 | }, 61 | "serve": { 62 | "builder": "@angular-devkit/build-angular:dev-server", 63 | "options": { 64 | "browserTarget": "frontend:build" 65 | }, 66 | "configurations": { 67 | "production": { 68 | "browserTarget": "frontend:build:production" 69 | } 70 | } 71 | }, 72 | "extract-i18n": { 73 | "builder": "@angular-devkit/build-angular:extract-i18n", 74 | "options": { 75 | "browserTarget": "frontend:build" 76 | } 77 | }, 78 | "test": { 79 | "builder": "@angular-devkit/build-angular:karma", 80 | "options": { 81 | "main": "src/test.ts", 82 | "polyfills": "src/polyfills.ts", 83 | "tsConfig": "tsconfig.spec.json", 84 | "karmaConfig": "karma.conf.js", 85 | "assets": [ 86 | "src/favicon.ico", 87 | "src/assets" 88 | ], 89 | "styles": [ 90 | "src/styles.css" 91 | ], 92 | "scripts": [] 93 | } 94 | }, 95 | "lint": { 96 | "builder": "@angular-devkit/build-angular:tslint", 97 | "options": { 98 | "tsConfig": [ 99 | "tsconfig.app.json", 100 | "tsconfig.spec.json", 101 | "e2e/tsconfig.json" 102 | ], 103 | "exclude": [ 104 | "**/node_modules/**" 105 | ] 106 | } 107 | }, 108 | "e2e": { 109 | "builder": "@angular-devkit/build-angular:protractor", 110 | "options": { 111 | "protractorConfig": "e2e/protractor.conf.js", 112 | "devServerTarget": "frontend:serve" 113 | }, 114 | "configurations": { 115 | "production": { 116 | "devServerTarget": "frontend:serve:production" 117 | } 118 | } 119 | } 120 | } 121 | } 122 | }, 123 | "defaultProject": "frontend", 124 | "cli": { 125 | "analytics": false 126 | } 127 | } -------------------------------------------------------------------------------- /frontend/e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Protractor configuration file, see link for more information 3 | // https://github.com/angular/protractor/blob/master/lib/config.ts 4 | 5 | const { SpecReporter } = require('jasmine-spec-reporter'); 6 | 7 | /** 8 | * @type { import("protractor").Config } 9 | */ 10 | exports.config = { 11 | allScriptsTimeout: 11000, 12 | specs: [ 13 | './src/**/*.e2e-spec.ts' 14 | ], 15 | capabilities: { 16 | 'browserName': 'chrome' 17 | }, 18 | directConnect: true, 19 | baseUrl: 'http://localhost:4200/', 20 | framework: 'jasmine', 21 | jasmineNodeOpts: { 22 | showColors: true, 23 | defaultTimeoutInterval: 30000, 24 | print: function() {} 25 | }, 26 | onPrepare() { 27 | require('ts-node').register({ 28 | project: require('path').join(__dirname, './tsconfig.json') 29 | }); 30 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 31 | } 32 | }; -------------------------------------------------------------------------------- /frontend/e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | import { browser, logging } from 'protractor'; 3 | 4 | describe('workspace-project App', () => { 5 | let page: AppPage; 6 | 7 | beforeEach(() => { 8 | page = new AppPage(); 9 | }); 10 | 11 | it('should display welcome message', () => { 12 | page.navigateTo(); 13 | expect(page.getTitleText()).toEqual('Welcome to frontend!'); 14 | }); 15 | 16 | afterEach(async () => { 17 | // Assert that there are no errors emitted from the browser 18 | const logs = await browser.manage().logs().get(logging.Type.BROWSER); 19 | expect(logs).not.toContain(jasmine.objectContaining({ 20 | level: logging.Level.SEVERE, 21 | } as logging.Entry)); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /frontend/e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get(browser.baseUrl) as Promise; 6 | } 7 | 8 | getTitleText() { 9 | return element(by.css('app-root h1')).getText() as Promise; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /frontend/e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "module": "commonjs", 6 | "target": "es2018", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /frontend/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, './coverage/frontend'), 20 | reports: ['html', 'lcovonly', 'text-summary'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false, 30 | restartOnFileChange: true 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve --proxy-config proxy.conf.json", 7 | "build": "ng build", 8 | "test": "ng test", 9 | "lint": "ng lint", 10 | "e2e": "ng e2e" 11 | }, 12 | "private": true, 13 | "dependencies": { 14 | "@angular/animations": "~11.0.0", 15 | "@angular/common": "~11.0.0", 16 | "@angular/compiler": "~11.0.0", 17 | "@angular/core": "~11.0.0", 18 | "@angular/forms": "~11.0.0", 19 | "@angular/platform-browser": "~11.0.0", 20 | "@angular/platform-browser-dynamic": "~11.0.0", 21 | "@angular/router": "~11.0.0", 22 | "jquery": "^3.4.1", 23 | "net": "^1.0.2", 24 | "rxjs": "~6.6.3", 25 | "sockjs-client": "^1.3.0", 26 | "stompjs": "^2.3.3", 27 | "tslib": "^2.0.0", 28 | "zone.js": "~0.10.2" 29 | }, 30 | "devDependencies": { 31 | "@angular-devkit/build-angular": "~0.1100.1", 32 | "@angular/cli": "~11.0.1", 33 | "@angular/compiler-cli": "~11.0.0", 34 | "@angular/language-service": "~11.0.0", 35 | "@types/node": "^12.11.1", 36 | "@types/jasmine": "~3.6.0", 37 | "@types/jasminewd2": "~2.0.3", 38 | "codelyzer": "^6.0.0", 39 | "jasmine-core": "~3.6.0", 40 | "jasmine-spec-reporter": "~5.0.0", 41 | "karma": "~5.0.0", 42 | "karma-chrome-launcher": "~3.1.0", 43 | "karma-coverage-istanbul-reporter": "~3.0.2", 44 | "karma-jasmine": "~4.0.0", 45 | "karma-jasmine-html-reporter": "^1.5.0", 46 | "protractor": "~7.0.0", 47 | "ts-node": "~7.0.0", 48 | "tslint": "~6.1.0", 49 | "typescript": "~4.0.5" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /frontend/proxy.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "/api": { 3 | "target": "http://localhost:8080/", 4 | "secure": false, 5 | "changeOrigin": true, 6 | "logLevel": "debug", 7 | "headers": { "host": "localhost" } 8 | }, 9 | "/websocketservice": { 10 | "target": "http://localhost:8080/", 11 | "secure": false, 12 | "changeOrigin": true, 13 | "logLevel": "debug", 14 | "headers": { "host": "localhost" } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /frontend/src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | 4 | 5 | const routes: Routes = []; 6 | 7 | @NgModule({ 8 | imports: [RouterModule.forRoot(routes, { relativeLinkResolution: 'legacy' })], 9 | exports: [RouterModule] 10 | }) 11 | export class AppRoutingModule { } 12 | -------------------------------------------------------------------------------- /frontend/src/app/app.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dhananjay12/spring-microservice-demo/e1dec03a50af3eb9e81a079b3cf9a892e6437be4/frontend/src/app/app.component.css -------------------------------------------------------------------------------- /frontend/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /frontend/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, waitForAsync } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('AppComponent', () => { 6 | beforeEach(waitForAsync(() => { 7 | TestBed.configureTestingModule({ 8 | imports: [ 9 | RouterTestingModule 10 | ], 11 | declarations: [ 12 | AppComponent 13 | ], 14 | }).compileComponents(); 15 | })); 16 | 17 | it('should create the app', () => { 18 | const fixture = TestBed.createComponent(AppComponent); 19 | const app = fixture.debugElement.componentInstance; 20 | expect(app).toBeTruthy(); 21 | }); 22 | 23 | it(`should have as title 'frontend'`, () => { 24 | const fixture = TestBed.createComponent(AppComponent); 25 | const app = fixture.debugElement.componentInstance; 26 | expect(app.title).toEqual('frontend'); 27 | }); 28 | 29 | it('should render title in a h1 tag', () => { 30 | const fixture = TestBed.createComponent(AppComponent); 31 | fixture.detectChanges(); 32 | const compiled = fixture.debugElement.nativeElement; 33 | expect(compiled.querySelector('h1').textContent).toContain('Welcome to frontend!'); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /frontend/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | templateUrl: './app.component.html', 6 | styleUrls: ['./app.component.css'] 7 | }) 8 | export class AppComponent { 9 | title = 'frontend'; 10 | } 11 | -------------------------------------------------------------------------------- /frontend/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | 4 | import { AppRoutingModule } from './app-routing.module'; 5 | import { AppComponent } from './app.component'; 6 | import { HomeComponent } from './home/home.component'; 7 | import { RestComponent } from './rest/rest.component'; 8 | import { WebsocketComponent } from './websocket/websocket.component'; 9 | import { ReactiveComponent } from './reactive/reactive.component'; 10 | import { RouterModule } from '@angular/router'; 11 | import { HttpClientModule } from '@angular/common/http'; 12 | import { RestService } from './services/rest.service'; 13 | import { WebsocketService } from './services/websocket.service'; 14 | import { ReactiveService } from './services/reactive.service'; 15 | import { FormsModule } from '@angular/forms'; 16 | 17 | @NgModule({ 18 | declarations: [ 19 | AppComponent, 20 | HomeComponent, 21 | RestComponent, 22 | WebsocketComponent, 23 | ReactiveComponent 24 | ], 25 | imports: [ 26 | BrowserModule, 27 | AppRoutingModule, 28 | FormsModule, 29 | HttpClientModule, 30 | RouterModule.forRoot([ 31 | { path: '', component: HomeComponent }, 32 | { path: 'rest', component: RestComponent }, 33 | { path: 'reactive', component: ReactiveComponent }, 34 | { path: 'websocket', component: WebsocketComponent } 35 | ], { relativeLinkResolution: 'legacy' }) 36 | ], 37 | providers: [RestService,WebsocketService,ReactiveService], 38 | bootstrap: [AppComponent] 39 | }) 40 | export class AppModule { } 41 | -------------------------------------------------------------------------------- /frontend/src/app/home/home.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dhananjay12/spring-microservice-demo/e1dec03a50af3eb9e81a079b3cf9a892e6437be4/frontend/src/app/home/home.component.css -------------------------------------------------------------------------------- /frontend/src/app/home/home.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Welcome

4 |

New Gateway Demo

5 |
6 |
7 |
8 |
9 |
10 |

REST service

11 |

Simple REST calls to Backend services

12 | Test 13 |
14 |
15 |
16 |
17 |
18 |
19 |

Reactive service

20 |

Backend service are reactive

21 | Test 22 |
23 |
24 |
25 |
26 |
27 |
28 |

Websocket service

29 |

Websocket service

30 | Test 31 |
32 |
33 |
34 |
35 |
-------------------------------------------------------------------------------- /frontend/src/app/home/home.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; 2 | 3 | import { HomeComponent } from './home.component'; 4 | 5 | describe('HomeComponent', () => { 6 | let component: HomeComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(waitForAsync(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ HomeComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(HomeComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/src/app/home/home.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-home', 5 | templateUrl: './home.component.html', 6 | styleUrls: ['./home.component.css'] 7 | }) 8 | export class HomeComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /frontend/src/app/reactive/reactive.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dhananjay12/spring-microservice-demo/e1dec03a50af3eb9e81a079b3cf9a892e6437be4/frontend/src/app/reactive/reactive.component.css -------------------------------------------------------------------------------- /frontend/src/app/reactive/reactive.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Reactive Backend

4 |
5 | Home 6 |
7 |
8 |
9 |
10 |

SSE Result

11 |
{{ value }}
12 |
13 |
14 |
15 |
16 | 17 |
-------------------------------------------------------------------------------- /frontend/src/app/reactive/reactive.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; 2 | 3 | import { ReactiveComponent } from './reactive.component'; 4 | 5 | describe('ReactiveComponent', () => { 6 | let component: ReactiveComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(waitForAsync(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ReactiveComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ReactiveComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/src/app/reactive/reactive.component.ts: -------------------------------------------------------------------------------- 1 | import { ReactiveService } from './../services/reactive.service'; 2 | import { Component, OnInit } from '@angular/core'; 3 | import { Observable } from 'rxjs'; 4 | 5 | @Component({ 6 | selector: 'app-reactive', 7 | templateUrl: './reactive.component.html', 8 | styleUrls: ['./reactive.component.css'] 9 | }) 10 | export class ReactiveComponent implements OnInit { 11 | 12 | values: Array = []; 13 | 14 | constructor(private service: ReactiveService) { } 15 | 16 | ngOnInit() { 17 | this.service.getSSE().subscribe(response => {this.values.push(response.data)}) 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /frontend/src/app/rest/rest.component.css: -------------------------------------------------------------------------------- 1 | .blue { 2 | color:blue; 3 | } -------------------------------------------------------------------------------- /frontend/src/app/rest/rest.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

REST CALL

4 |
5 | Home 6 |
7 |
8 |
9 |
10 |

Service One

11 |
1- GET /hello
12 |
Response:
13 |

{{serviceOneResult}}

14 | 15 |
2- GET /hop/status
16 | 17 | 18 |
Response:
19 |

{{hopResult}}

20 | 21 |
3- GET /message
22 | 23 | 24 |
Response:
25 |

{{messageResult}}

26 | 27 |
4 GET /headers
28 | 29 | 30 | 31 |
Request headers to Downstream Service :
32 |
33 | {{ item.key }} :: {{ item.value }} 34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |

Service Two

42 |
1- GET /hello
43 |
Response:
44 |

{{serviceTwoResult}}

45 | 46 |
2- GET /calculate/divide
47 | 48 | 49 | 50 |
Response:
51 |

{{divisionResult}}

52 |
53 |
54 | 55 |
56 | 57 |
58 | -------------------------------------------------------------------------------- /frontend/src/app/rest/rest.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; 2 | 3 | import { RestComponent } from './rest.component'; 4 | 5 | describe('RestComponent', () => { 6 | let component: RestComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(waitForAsync(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ RestComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(RestComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/src/app/rest/rest.component.ts: -------------------------------------------------------------------------------- 1 | import { RestService } from './../services/rest.service'; 2 | import { Component, OnInit } from '@angular/core'; 3 | 4 | 5 | @Component({ 6 | selector: 'app-rest', 7 | templateUrl: './rest.component.html', 8 | styleUrls: ['./rest.component.css'] 9 | }) 10 | export class RestComponent implements OnInit { 11 | 12 | serviceOneResult: string; 13 | serviceTwoResult: string; 14 | userResultObj: any; 15 | headerResult: any 16 | hopResult: string; 17 | messageResult: string; 18 | divisionResult: string; 19 | 20 | constructor(private service: RestService) { } 21 | 22 | ngOnInit() { 23 | this.service.helloFromServiceOne().subscribe(result=> this.serviceOneResult=result); 24 | this.service.helloFromServiceTwo().subscribe(result=> this.serviceTwoResult=result); 25 | } 26 | 27 | hop(status){ 28 | this.service.hop(status).subscribe(result=> this.hopResult=result); 29 | } 30 | 31 | sendHeader(headerKey,HeaderValue){ 32 | this.service.sendHeader(headerKey,HeaderValue).subscribe(result=> this.headerResult=result); 33 | } 34 | 35 | messageServiceOne(text){ 36 | this.service.messageServiceOne(text).subscribe(result=> this.messageResult=result); 37 | } 38 | 39 | divisionServiceTwo(num1,num2){ 40 | this.service.divisionServiceTwo(num1,num2).subscribe(result=> this.divisionResult=result); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /frontend/src/app/services/error-handler.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { ErrorHandlerService } from './error-handler.service'; 4 | 5 | describe('ErrorHandlerService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: ErrorHandlerService = TestBed.get(ErrorHandlerService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /frontend/src/app/services/error-handler.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; 3 | 4 | import { Observable, throwError } from 'rxjs'; 5 | import { catchError, retry } from 'rxjs/operators'; 6 | 7 | @Injectable({ 8 | providedIn: 'root' 9 | }) 10 | export class ErrorHandlerService { 11 | 12 | constructor() { } 13 | 14 | public handleError(error: HttpErrorResponse) { 15 | if (error.error instanceof ErrorEvent) { 16 | // A client-side or network error occurred. Handle it accordingly. 17 | console.error('An error occurred:', error.error.message); 18 | } else { 19 | // The backend returned an unsuccessful response code. 20 | // The response body may contain clues as to what went wrong, 21 | console.error( 22 | `Backend returned code ${error.status}, ` + 23 | `body was: ${error.error}`); 24 | } 25 | // return an observable with a user-facing error message 26 | return throwError( 27 | 'Something bad happened; please try again later.'); 28 | }; 29 | 30 | } -------------------------------------------------------------------------------- /frontend/src/app/services/reactive.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { ReactiveService } from './reactive.service'; 4 | 5 | describe('ReactiveService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: ReactiveService = TestBed.get(ReactiveService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /frontend/src/app/services/reactive.service.ts: -------------------------------------------------------------------------------- 1 | import { SseService } from "./sse.service"; 2 | import { Injectable, NgZone } from "@angular/core"; 3 | import { HttpClient, HttpHeaders } from "@angular/common/http"; 4 | import { ErrorHandlerService } from "./error-handler.service"; 5 | import { Observable } from "rxjs"; 6 | 7 | @Injectable({ 8 | providedIn: "root" 9 | }) 10 | export class ReactiveService { 11 | constructor( 12 | private httpClient: HttpClient, 13 | private errorHandlerService: ErrorHandlerService, 14 | private sseService: SseService, 15 | private zone: NgZone 16 | ) {} 17 | 18 | getSSE() { 19 | return Observable.create(observer => { 20 | const eventSource = this.sseService.getEventSource( 21 | "/api/reactive-service/greetings/sse" 22 | ); 23 | 24 | eventSource.onmessage = event => { 25 | this.zone.run(() => { 26 | observer.next(event); 27 | }); 28 | }; 29 | 30 | eventSource.onerror = error => { 31 | this.zone.run(() => { 32 | observer.error(error); 33 | }); 34 | }; 35 | }); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /frontend/src/app/services/rest.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { RestService } from './rest.service'; 4 | 5 | describe('RestService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: RestService = TestBed.get(RestService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /frontend/src/app/services/rest.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from "@angular/core"; 2 | import { HttpClient, HttpHeaders } from "@angular/common/http"; 3 | import { ErrorHandlerService } from "./error-handler.service"; 4 | import { catchError } from "rxjs/operators"; 5 | 6 | @Injectable({ 7 | providedIn: "root" 8 | }) 9 | export class RestService { 10 | constructor( 11 | private httpClient: HttpClient, 12 | private errorHandlerService: ErrorHandlerService 13 | ) {} 14 | 15 | hop(status) { 16 | const headers = new HttpHeaders().set( 17 | "Content-Type", 18 | "text/plain; charset=utf-8" 19 | ); 20 | 21 | return this.httpClient 22 | .get("/api/service-one/hop/"+status, { 23 | headers, 24 | responseType: "text" 25 | }) 26 | .pipe(catchError(this.errorHandlerService.handleError)); 27 | } 28 | 29 | sendHeader(headerKey,HeaderValue){ 30 | const headers = new HttpHeaders().set( 31 | headerKey,HeaderValue 32 | ); 33 | 34 | return this.httpClient 35 | .get("/api/service-one/headers", { 36 | headers 37 | }) 38 | .pipe(catchError(this.errorHandlerService.handleError)); 39 | } 40 | 41 | helloFromServiceOne() { 42 | const headers = new HttpHeaders().set( 43 | "Content-Type", 44 | "text/plain; charset=utf-8" 45 | ); 46 | 47 | return this.httpClient 48 | .get("/api/service-one/hello", { 49 | headers, 50 | responseType: "text" 51 | }) 52 | .pipe(catchError(this.errorHandlerService.handleError)); 53 | } 54 | 55 | helloFromServiceTwo() { 56 | const headers = new HttpHeaders().set( 57 | "Content-Type", 58 | "text/plain; charset=utf-8" 59 | ); 60 | 61 | return this.httpClient 62 | .get("/api/service-two/hello", { 63 | headers, 64 | responseType: "text" 65 | }) 66 | .pipe(catchError(this.errorHandlerService.handleError)); 67 | } 68 | 69 | messageServiceOne(text) { 70 | const headers = new HttpHeaders().set( 71 | "Content-Type", 72 | "text/plain; charset=utf-8" 73 | ); 74 | return this.httpClient 75 | .get("/api/service-one/message/"+text, { 76 | headers, 77 | responseType: "text" 78 | }) 79 | .pipe(catchError(this.errorHandlerService.handleError)); 80 | } 81 | 82 | divisionServiceTwo(num1,num2) { 83 | const headers = new HttpHeaders().set( 84 | "Content-Type", 85 | "text/plain; charset=utf-8" 86 | ); 87 | return this.httpClient 88 | .get("/api/service-two/calculate/divide/"+num1+"/"+num2, { 89 | headers, 90 | responseType: "text" 91 | }) 92 | .pipe(catchError(this.errorHandlerService.handleError)); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /frontend/src/app/services/sse.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { SseService } from './sse.service'; 4 | 5 | describe('SseService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: SseService = TestBed.get(SseService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /frontend/src/app/services/sse.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | @Injectable({ 4 | providedIn: 'root' 5 | }) 6 | export class SseService { 7 | 8 | getEventSource(url:string):EventSource{ 9 | return new EventSource(url); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /frontend/src/app/services/websocket.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { WebsocketService } from './websocket.service'; 4 | 5 | describe('WebsocketService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: WebsocketService = TestBed.get(WebsocketService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /frontend/src/app/services/websocket.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | @Injectable({ 4 | providedIn: 'root' 5 | }) 6 | export class WebsocketService { 7 | 8 | constructor() { } 9 | } 10 | -------------------------------------------------------------------------------- /frontend/src/app/websocket/websocket.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dhananjay12/spring-microservice-demo/e1dec03a50af3eb9e81a079b3cf9a892e6437be4/frontend/src/app/websocket/websocket.component.css -------------------------------------------------------------------------------- /frontend/src/app/websocket/websocket.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

WEBSOCKET

4 |
5 | Home 6 |
7 |
8 |
9 |
10 | 11 | 19 | 27 |
28 |
29 |
30 |
31 |
32 |
33 | 34 | 40 |
41 |
42 | 45 |
46 |
47 |
48 |
49 |
50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 |
{{ value }}
58 | 59 |
Greetings
60 |
61 |
62 |
63 | -------------------------------------------------------------------------------- /frontend/src/app/websocket/websocket.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; 2 | 3 | import { WebsocketComponent } from './websocket.component'; 4 | 5 | describe('WebsocketComponent', () => { 6 | let component: WebsocketComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(waitForAsync(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ WebsocketComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(WebsocketComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/src/app/websocket/websocket.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import * as Stomp from 'stompjs'; 3 | import * as SockJS from 'sockjs-client'; 4 | 5 | @Component({ 6 | selector: 'app-websocket', 7 | templateUrl: './websocket.component.html', 8 | styleUrls: ['./websocket.component.css'] 9 | }) 10 | export class WebsocketComponent implements OnInit { 11 | 12 | greetings: Array = []; 13 | name: string; 14 | webSocketEndPoint: string = '/websocketservice/gs-guide-websocket'; 15 | topic: string = "/topic/greetings"; 16 | stompClient: any; 17 | buttonDisabled: boolean = false; 18 | ngOnInit() { 19 | } 20 | 21 | connect(){ 22 | this.buttonDisabled=true; 23 | let ws = new SockJS(this.webSocketEndPoint); 24 | this.stompClient = Stomp.over(ws); 25 | const _this = this; 26 | _this.stompClient.connect({}, function (frame) { 27 | _this.stompClient.subscribe(_this.topic, function (sdkEvent) { 28 | _this.handleMessage(sdkEvent); 29 | }); 30 | //_this.stompClient.reconnect_delay = 2000; 31 | }, this.errorCallBack); 32 | } 33 | 34 | disconnect(){ 35 | // this.webSocketAPI._disconnect(); 36 | if (this.stompClient !== null) { 37 | this.stompClient.disconnect(); 38 | this.buttonDisabled=false; 39 | } 40 | } 41 | 42 | sendMessage(){ 43 | //this.webSocketAPI._send(this.name); 44 | console.log("calling logout api via web socket"); 45 | this.stompClient.send("/app/hello", {}, JSON.stringify(this.name)); 46 | } 47 | 48 | handleMessage(message){ 49 | this.greetings.push(JSON.parse(message.body).content); 50 | } 51 | 52 | // on error, schedule a reconnection attempt 53 | errorCallBack(error) { 54 | console.log("errorCallBack -> " + error) 55 | setTimeout(() => { 56 | this.connect(); 57 | }, 5000); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /frontend/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dhananjay12/spring-microservice-demo/e1dec03a50af3eb9e81a079b3cf9a892e6437be4/frontend/src/assets/.gitkeep -------------------------------------------------------------------------------- /frontend/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /frontend/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false 7 | }; 8 | 9 | /* 10 | * For easier debugging in development mode, you can import the following file 11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 12 | * 13 | * This import should be commented out in production mode because it will have a negative impact 14 | * on performance if an error is thrown. 15 | */ 16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 17 | -------------------------------------------------------------------------------- /frontend/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dhananjay12/spring-microservice-demo/e1dec03a50af3eb9e81a079b3cf9a892e6437be4/frontend/src/favicon.ico -------------------------------------------------------------------------------- /frontend/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Frontend 6 | 7 | 8 | 9 | 10 | 11 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /frontend/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.error(err)); 13 | -------------------------------------------------------------------------------- /frontend/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/guide/browser-support 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 22 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 23 | 24 | /** 25 | * Web Animations `@angular/platform-browser/animations` 26 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 27 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 28 | */ 29 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 30 | 31 | /** 32 | * By default, zone.js will patch all possible macroTask and DomEvents 33 | * user can disable parts of macroTask/DomEvents patch by setting following flags 34 | * because those flags need to be set before `zone.js` being loaded, and webpack 35 | * will put import in the top of bundle, so user need to create a separate file 36 | * in this directory (for example: zone-flags.ts), and put the following flags 37 | * into that file, and then add the following code before importing zone.js. 38 | * import './zone-flags.ts'; 39 | * 40 | * The flags allowed in zone-flags.ts are listed here. 41 | * 42 | * The following flags will work for all browsers. 43 | * 44 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 45 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 46 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 47 | * 48 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 49 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 50 | * 51 | * (window as any).__Zone_enable_cross_context_check = true; 52 | * 53 | */ 54 | 55 | /*************************************************************************************************** 56 | * Zone JS is required by default for Angular itself. 57 | */ 58 | import 'zone.js/dist/zone'; // Included with Angular CLI. 59 | 60 | 61 | /*************************************************************************************************** 62 | * APPLICATION IMPORTS 63 | */ 64 | -------------------------------------------------------------------------------- /frontend/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /frontend/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting() 16 | ); 17 | // Then we find all the tests. 18 | const context = require.context('./', true, /\.spec\.ts$/); 19 | // And load the modules. 20 | context.keys().map(context); 21 | -------------------------------------------------------------------------------- /frontend/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/app", 5 | "types": [] 6 | }, 7 | "files": [ 8 | "src/main.ts", 9 | "src/polyfills.ts" 10 | ], 11 | "include": [ 12 | "src/**/*.d.ts" 13 | ], 14 | "exclude": [ 15 | "src/test.ts", 16 | "src/**/*.spec.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "downlevelIteration": true, 9 | "experimentalDecorators": true, 10 | "module": "es2020", 11 | "moduleResolution": "node", 12 | "importHelpers": true, 13 | "target": "es2015", 14 | "typeRoots": [ 15 | "node_modules/@types" 16 | ], 17 | "lib": [ 18 | "es2018", 19 | "dom" 20 | ] 21 | }, 22 | "angularCompilerOptions": { 23 | "fullTemplateTypeCheck": true, 24 | "strictInjectionParameters": true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /frontend/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts", 12 | "src/polyfills.ts" 13 | ], 14 | "include": [ 15 | "src/**/*.spec.ts", 16 | "src/**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /frontend/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint:recommended", 3 | "rules": { 4 | "align": { 5 | "options": [ 6 | "parameters", 7 | "statements" 8 | ] 9 | }, 10 | "array-type": false, 11 | "arrow-parens": false, 12 | "arrow-return-shorthand": true, 13 | "deprecation": { 14 | "severity": "warning" 15 | }, 16 | "component-class-suffix": true, 17 | "contextual-lifecycle": true, 18 | "curly": true, 19 | "directive-class-suffix": true, 20 | "directive-selector": [ 21 | true, 22 | "attribute", 23 | "app", 24 | "camelCase" 25 | ], 26 | "component-selector": [ 27 | true, 28 | "element", 29 | "app", 30 | "kebab-case" 31 | ], 32 | "eofline": true, 33 | "import-blacklist": [ 34 | true, 35 | "rxjs/Rx" 36 | ], 37 | "import-spacing": true, 38 | "indent": { 39 | "options": [ 40 | "spaces" 41 | ] 42 | }, 43 | "interface-name": false, 44 | "max-classes-per-file": false, 45 | "max-line-length": [ 46 | true, 47 | 140 48 | ], 49 | "member-access": false, 50 | "member-ordering": [ 51 | true, 52 | { 53 | "order": [ 54 | "static-field", 55 | "instance-field", 56 | "static-method", 57 | "instance-method" 58 | ] 59 | } 60 | ], 61 | "no-consecutive-blank-lines": false, 62 | "no-console": [ 63 | true, 64 | "debug", 65 | "info", 66 | "time", 67 | "timeEnd", 68 | "trace" 69 | ], 70 | "no-empty": false, 71 | "no-inferrable-types": [ 72 | true, 73 | "ignore-params" 74 | ], 75 | "no-non-null-assertion": true, 76 | "no-redundant-jsdoc": true, 77 | "no-switch-case-fall-through": true, 78 | "no-var-requires": false, 79 | "object-literal-key-quotes": [ 80 | true, 81 | "as-needed" 82 | ], 83 | "object-literal-sort-keys": false, 84 | "ordered-imports": false, 85 | "quotemark": [ 86 | true, 87 | "single" 88 | ], 89 | "trailing-comma": false, 90 | "no-conflicting-lifecycle": true, 91 | "no-host-metadata-property": true, 92 | "no-input-rename": true, 93 | "no-inputs-metadata-property": true, 94 | "no-output-native": true, 95 | "no-output-on-prefix": true, 96 | "no-output-rename": true, 97 | "semicolon": { 98 | "options": [ 99 | "always" 100 | ] 101 | }, 102 | "space-before-function-paren": { 103 | "options": { 104 | "anonymous": "never", 105 | "asyncArrow": "always", 106 | "constructor": "never", 107 | "method": "never", 108 | "named": "never" 109 | } 110 | }, 111 | "no-outputs-metadata-property": true, 112 | "template-banana-in-box": true, 113 | "template-no-negated-async": true, 114 | "typedef-whitespace": { 115 | "options": [ 116 | { 117 | "call-signature": "nospace", 118 | "index-signature": "nospace", 119 | "parameter": "nospace", 120 | "property-declaration": "nospace", 121 | "variable-declaration": "nospace" 122 | }, 123 | { 124 | "call-signature": "onespace", 125 | "index-signature": "onespace", 126 | "parameter": "onespace", 127 | "property-declaration": "onespace", 128 | "variable-declaration": "onespace" 129 | } 130 | ] 131 | }, 132 | "use-lifecycle-interface": true, 133 | "use-pipe-transform-interface": true, 134 | "variable-name": { 135 | "options": [ 136 | "ban-keywords", 137 | "check-format", 138 | "allow-pascal-case" 139 | ] 140 | }, 141 | "whitespace": { 142 | "options": [ 143 | "check-branch", 144 | "check-decl", 145 | "check-operator", 146 | "check-separator", 147 | "check-type", 148 | "check-typecast" 149 | ] 150 | } 151 | }, 152 | "rulesDirectory": [ 153 | "codelyzer" 154 | ] 155 | } -------------------------------------------------------------------------------- /gateway/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/** 5 | !**/src/test/** 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | 30 | ### VS Code ### 31 | .vscode/ 32 | -------------------------------------------------------------------------------- /gateway/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | com.mynotes.microservices.demo 6 | gateway 7 | gateway 8 | Demo project for Spring Boot 9 | 10 | org.springframework.boot 11 | spring-boot-starter-parent 12 | 3.0.2 13 | 14 | 15 | 16 | 17 | 18 | UTF-8 19 | UTF-8 20 | 17 21 | 2022.0.1 22 | false 23 | false 24 | dhananjay12 25 | demo 26 | 5.14.0 27 | 1.0.1 28 | 29 | 30 | 31 | 32 | 33 | org.springframework.cloud 34 | spring-cloud-dependencies 35 | ${spring-cloud.version} 36 | pom 37 | import 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | org.springframework.cloud 46 | spring-cloud-starter-gateway 47 | 48 | 49 | io.micrometer 50 | micrometer-tracing-bridge-brave 51 | 52 | 53 | io.zipkin.reporter2 54 | zipkin-reporter-brave 55 | 56 | 57 | org.springframework.boot 58 | spring-boot-starter-actuator 59 | 60 | 61 | io.micrometer 62 | micrometer-registry-prometheus 63 | runtime 64 | 65 | 66 | org.springframework.cloud 67 | spring-cloud-starter-kubernetes-fabric8-all 68 | 69 | 70 | org.springframework.cloud 71 | spring-cloud-starter-loadbalancer 72 | 73 | 74 | org.springframework.cloud 75 | spring-cloud-starter-netflix-eureka-client 76 | 77 | 78 | org.springframework.boot 79 | spring-boot-starter-test 80 | test 81 | 82 | 83 | org.mock-server 84 | mockserver-netty 85 | ${mockserver-netty.version} 86 | test 87 | 88 | 89 | 90 | 91 | ${project.artifactId} 92 | 93 | 94 | org.springframework.boot 95 | spring-boot-maven-plugin 96 | 97 | 98 | org.apache.maven.plugins 99 | maven-surefire-plugin 100 | 101 | 102 | 103 | ${surefireArgLine} 104 | ${skip.unit.tests} 105 | 106 | 107 | **/*IT.java 108 | **/*It.java 109 | 110 | 111 | 112 | 113 | 114 | org.apache.maven.plugins 115 | maven-failsafe-plugin 116 | 117 | 118 | 119 | ${failsafeArgLine} 120 | ${skip.integration.tests} 121 | 122 | **/*It.java 123 | **/*IT.java 124 | 125 | 126 | 127 | 128 | pl.project13.maven 129 | git-commit-id-plugin 130 | 4.0.0 131 | 132 | 133 | get-the-git-infos 134 | initialize 135 | 136 | revision 137 | 138 | 139 | 140 | 141 | false 142 | yyyy-MM-dd'T'HH:mm:ss'Z' 143 | GMT 144 | 145 | ^git.commit.time$ 146 | 147 | 148 | 149 | 150 | com.google.cloud.tools 151 | jib-maven-plugin 152 | 2.6.0 153 | 154 | 155 | eclipse-temurin:17-jre-alpine 156 | 157 | 158 | ${docker.image.url}/${docker.image.prefix}-${project.artifactId} 159 | 160 | ${project.version} 161 | 162 | 163 | 164 | ${git.commit.time} 165 | ${git.commit.time} 166 | com.mynotes.microservices.demo.gateway.GatewayApplication 167 | 168 | 8080 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | -------------------------------------------------------------------------------- /gateway/src/main/java/com/mynotes/microservices/demo/gateway/GatewayApplication.java: -------------------------------------------------------------------------------- 1 | package com.mynotes.microservices.demo.gateway; 2 | 3 | import brave.Tracer; 4 | import brave.baggage.BaggageField; 5 | import brave.propagation.TraceContext; 6 | import java.util.Optional; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.stereotype.Component; 10 | import reactor.core.publisher.Mono; 11 | 12 | import org.springframework.boot.SpringApplication; 13 | import org.springframework.boot.autoconfigure.SpringBootApplication; 14 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 15 | import org.springframework.cloud.gateway.filter.GatewayFilterChain; 16 | import org.springframework.cloud.gateway.filter.GlobalFilter; 17 | import org.springframework.scheduling.annotation.EnableScheduling; 18 | import org.springframework.core.Ordered; 19 | import org.springframework.web.server.ServerWebExchange; 20 | 21 | 22 | @SpringBootApplication 23 | @EnableDiscoveryClient 24 | @EnableScheduling 25 | public class GatewayApplication { 26 | 27 | 28 | public static void main(String[] args) { 29 | SpringApplication.run(GatewayApplication.class, args); 30 | } 31 | 32 | } 33 | 34 | @Component 35 | class CustomGlobalFilter implements GlobalFilter, Ordered { 36 | 37 | private static final Logger log = LoggerFactory.getLogger(CustomGlobalFilter.class); 38 | private final Tracer tracer; 39 | 40 | CustomGlobalFilter(Tracer tracer) { 41 | this.tracer = tracer; 42 | } 43 | 44 | @Override 45 | public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { 46 | log.info("Custom global filter"); 47 | BaggageField.getByName(context(), "test").updateValue("123"); 48 | tracer.currentSpanCustomizer().tag("test", "123"); 49 | return chain.filter(exchange); 50 | } 51 | 52 | @Override 53 | public int getOrder() { 54 | return -1; 55 | } 56 | 57 | private TraceContext context() { 58 | return Optional.ofNullable(tracer.currentSpan()).orElseGet(tracer::newTrace).context(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /gateway/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | main: 3 | banner-mode: off 4 | application: 5 | name: gateway 6 | cloud: 7 | gateway: 8 | discovery: 9 | locator: 10 | lowerCaseServiceId: true 11 | enabled: true 12 | predicates: 13 | - name: Path 14 | args: 15 | pattern: "'/api/'+serviceId.toLowerCase()+'/**'" 16 | filters: 17 | - name: RewritePath 18 | args: 19 | regexp: "'/api/' + serviceId.toLowerCase() + '/(?.*)'" 20 | replacement: "'/${remaining}'" 21 | routes: 22 | - id: websocket-sockjs-route 23 | uri: http://websocketservice 24 | order: -1 25 | predicates: 26 | - Path=/websocketservice/info/** 27 | filters: 28 | - StripPrefix=1 29 | - id: websocket_route 30 | uri: lb:ws://websocketservice 31 | order: -1 32 | predicates: 33 | - Path=/websocketservice/** 34 | filters: 35 | - StripPrefix=1 36 | kubernetes: 37 | enabled: false 38 | reload: 39 | enabled: false 40 | config: 41 | enabled: false 42 | zipkin: 43 | enabled: false 44 | 45 | management: 46 | endpoints: 47 | web: 48 | exposure: 49 | include: '*' 50 | 51 | eureka: 52 | client: 53 | enabled: true 54 | serviceUrl: 55 | defaultZone: http://localhost:8761/eureka/ 56 | -------------------------------------------------------------------------------- /gateway/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | #Disable kubernetes initially. https://docs.spring.io/spring-cloud-kubernetes/docs/current/reference/html/index.html#kubernetes-ecosystem-awareness 2 | spring: 3 | cloud: 4 | kubernetes: 5 | enabled: false 6 | config: 7 | enabled: false 8 | secrets: 9 | enabled: false -------------------------------------------------------------------------------- /gateway/src/test/java/com/mynotes/microservices/demo/gateway/GatewayApplicationBaseIT.java: -------------------------------------------------------------------------------- 1 | package com.mynotes.microservices.demo.gateway; 2 | 3 | import ch.qos.logback.classic.Logger; 4 | import java.time.Duration; 5 | import org.junit.jupiter.api.AfterAll; 6 | import org.junit.jupiter.api.AfterEach; 7 | import org.junit.jupiter.api.BeforeAll; 8 | import org.junit.jupiter.api.BeforeEach; 9 | import org.mockserver.integration.ClientAndServer; 10 | import org.mockserver.socket.PortFactory; 11 | import org.springframework.boot.logging.LogLevel; 12 | import org.springframework.boot.logging.LoggingSystem; 13 | import org.springframework.boot.test.web.server.LocalServerPort; 14 | import org.springframework.test.web.reactive.server.WebTestClient; 15 | 16 | public class GatewayApplicationBaseIT { 17 | 18 | protected static int mockServerPort; 19 | protected static ClientAndServer mockServer; 20 | protected WebTestClient webClient; 21 | @LocalServerPort 22 | private int port; 23 | 24 | @BeforeAll 25 | public static void beforeClass() { 26 | 27 | LoggingSystem.get(ClassLoader.getSystemClassLoader()).setLogLevel(Logger.ROOT_LOGGER_NAME, LogLevel.TRACE); 28 | 29 | mockServerPort = PortFactory.findFreePort(); 30 | 31 | System.setProperty("it.mock.server.port", String.valueOf(mockServerPort)); 32 | 33 | } 34 | 35 | @AfterAll 36 | public static void afterClass() { 37 | System.clearProperty("it.mock.server.port"); 38 | } 39 | 40 | @BeforeEach 41 | public void setUp() { 42 | webClient = WebTestClient.bindToServer() 43 | .responseTimeout(Duration.ofSeconds(5)) 44 | .baseUrl("http://localhost:" + port) 45 | .build(); 46 | } 47 | 48 | @AfterEach 49 | public void afterEach() { 50 | mockServer.stop(); 51 | webClient.delete(); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /gateway/src/test/java/com/mynotes/microservices/demo/gateway/TestDownstreamCallIT.java: -------------------------------------------------------------------------------- 1 | package com.mynotes.microservices.demo.gateway; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.junit.jupiter.api.extension.ExtendWith; 5 | import org.mockserver.integration.ClientAndServer; 6 | import org.springframework.boot.test.autoconfigure.actuate.observability.AutoConfigureObservability; 7 | import org.springframework.boot.test.context.SpringBootTest; 8 | import org.springframework.test.context.junit.jupiter.SpringExtension; 9 | 10 | import static org.mockserver.model.HttpRequest.request; 11 | import static org.mockserver.model.HttpResponse.response; 12 | import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; 13 | 14 | @ExtendWith(SpringExtension.class) 15 | @SpringBootTest(webEnvironment = RANDOM_PORT) 16 | @AutoConfigureObservability 17 | public class TestDownstreamCallIT extends GatewayApplicationBaseIT { 18 | 19 | @Test 20 | void test(){ 21 | mockServer = ClientAndServer.startClientAndServer(mockServerPort); 22 | mockServer.when( 23 | request() 24 | .withPath("/test") 25 | ) 26 | .respond(response() 27 | .withStatusCode(200)); 28 | webClient.get().uri("/test") 29 | .exchange() 30 | .expectStatus().isOk(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /gateway/src/test/resources/application.yml: -------------------------------------------------------------------------------- 1 | eureka: 2 | client: 3 | enabled: false 4 | 5 | spring: 6 | cloud: 7 | gateway: 8 | routes: 9 | - id: test 10 | uri: http://localhost:${it.mock.server.port} 11 | order: 1 12 | predicates: 13 | - Path=/test 14 | management: 15 | tracing: 16 | baggage: 17 | correlation: 18 | fields: 19 | - test 20 | remote-fields: 21 | - test -------------------------------------------------------------------------------- /helm/README.md: -------------------------------------------------------------------------------- 1 | ## To install 2 | ``` 3 | helm install spring-microservice-demo 4 | ``` 5 | 6 | ``` 7 | helm delete 8 | ``` 9 | 10 | -------------------------------------------------------------------------------- /helm/spring-microservice-demo/Chart.lock: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - name: ingress-nginx 3 | repository: https://kubernetes.github.io/ingress-nginx 4 | version: 3.23.0 5 | - name: generic-chart 6 | repository: https://dhananjay12.github.io/learn-helm 7 | version: 0.0.1 8 | - name: generic-chart 9 | repository: https://dhananjay12.github.io/learn-helm 10 | version: 0.0.1 11 | - name: generic-chart 12 | repository: https://dhananjay12.github.io/learn-helm 13 | version: 0.0.1 14 | digest: sha256:93a6ec99c7a3f9e275dfec88508b6687bf56bfc006125663267939c2afd117a0 15 | generated: "2021-10-09T12:16:03.660909+02:00" 16 | -------------------------------------------------------------------------------- /helm/spring-microservice-demo/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: spring-microservice-demo 3 | description: A Helm Chart for spring-microservice-demo 4 | version: 0.0.1 5 | appVersion: 0.0.1 6 | keywords: 7 | - spring 8 | - helm 9 | maintainers: 10 | - name: Dhananjay Singh 11 | email: dhananjay@backbase.com 12 | 13 | dependencies: 14 | - name: ingress-nginx 15 | repository: https://kubernetes.github.io/ingress-nginx 16 | version: 3.23.0 17 | - name: generic-chart 18 | repository: https://dhananjay12.github.io/learn-helm 19 | version: 0.0.1 20 | alias: gateway 21 | condition: gateway.enabled 22 | - name: generic-chart 23 | repository: https://dhananjay12.github.io/learn-helm 24 | version: 0.0.1 25 | alias: service-one 26 | condition: service-one.enabled 27 | - name: generic-chart 28 | repository: https://dhananjay12.github.io/learn-helm 29 | version: 0.0.1 30 | alias: service-two 31 | condition: service-two.enabled 32 | # - name: generic-chart 33 | # repository: https://dhananjay12.github.io/learn-helm 34 | # version: 0.0.1 35 | # alias: reactive-service 36 | # condition: reactive-service.enabled 37 | # - name: generic-chart 38 | # repository: https://dhananjay12.github.io/learn-helm 39 | # version: 0.0.1 40 | # alias: websocket-service 41 | # condition: websocket-service.enabled 42 | # - name: generic-chart 43 | # repository: https://dhananjay12.github.io/learn-helm 44 | # version: 0.0.1 45 | # alias: frontend 46 | # condition: frontend.enabled -------------------------------------------------------------------------------- /helm/spring-microservice-demo/values.yaml: -------------------------------------------------------------------------------- 1 | 2 | gateway: 3 | enabled: true 4 | image: 5 | repository: dhananjay12/demo-gateway 6 | tag: latest #2.7.7 7 | pullPolicy: Always 8 | containerPort: 8080 9 | service: 10 | httpPort: 8080 11 | targetPort: 8080 12 | extraEnv: 13 | "EUREKA_CLIENT_ENABLED": "false" 14 | "SPRING_CLOUD_KUBERNETES_ENABLED": "true" 15 | "SPRING_CLOUD_KUBERNETES_LOADBALANCER_MODE": "SERVICE" 16 | "spring.cloud.gateway.httpserver.wiretap": "true" 17 | "spring.cloud.gateway.httpclient.wiretap": "true" 18 | "logging.level.reactor.netty": "DEBUG" 19 | ingress: 20 | enabled: true 21 | annotations: 22 | "kubernetes.io/ingress.class": "nginx" 23 | "nginx.ingress.kubernetes.io/proxy-body-size": "50m" 24 | "nginx.ingress.kubernetes.io/ssl-redirect": "false" 25 | # "nginx.ingress.kubernetes.io/use-regex": "true" 26 | hosts: 27 | - host: "kubernetes.docker.internal" 28 | paths: 29 | - / 30 | 31 | service-one: 32 | enabled: true 33 | image: 34 | repository: dhananjay12/demo-service-one 35 | tag: latest #2.7.7 36 | pullPolicy: Always 37 | containerPort: 8080 38 | service: 39 | httpPort: 8080 40 | targetPort: 8080 41 | extraEnv: 42 | "EUREKA_CLIENT_ENABLED": "false" 43 | "SPRING_CLOUD_KUBERNETES_ENABLED": "true" 44 | "SPRING_CLOUD_KUBERNETES_LOADBALANCER_MODE": "SERVICE" 45 | "logging.level.com.mynotes.microservices.demo": "DEBUG" 46 | "server.forward-headers-strategy": "NATIVE" 47 | ingress: 48 | enabled: false 49 | 50 | service-two: 51 | enabled: true 52 | image: 53 | repository: dhananjay12/demo-service-two 54 | tag: latest #2.7.7 55 | pullPolicy: Always 56 | containerPort: 8080 57 | service: 58 | httpPort: 8080 59 | targetPort: 8080 60 | extraEnv: 61 | "EUREKA_CLIENT_ENABLED": "false" 62 | "SPRING_CLOUD_KUBERNETES_ENABLED": "true" 63 | "SPRING_CLOUD_KUBERNETES_LOADBALANCER_MODE": "SERVICE" 64 | "logging.level.com.mynotes.microservices.demo": "DEBUG" 65 | "server.forward-headers-strategy": "FRAMEWORK" 66 | ingress: 67 | enabled: false 68 | 69 | #reactive-service: 70 | # enabled: false 71 | # image: 72 | # repository: dhananjay12/demo-reactive-service 73 | # tag: 2.7.7 74 | # pullPolicy: Always 75 | # containerPort: 8080 76 | # service: 77 | # httpPort: 8080 78 | # targetPort: 8080 79 | # extraEnv: 80 | # "EUREKA_CLIENT_ENABLED": "false" 81 | # "SPRING_CLOUD_KUBERNETES_ENABLED": "true" 82 | # "SPRING_CLOUD_KUBERNETES_LOADBALANCER_MODE": "SERVICE" 83 | # 84 | #websocket-service: 85 | # enabled: false 86 | # image: 87 | # repository: dhananjay12/demo-websocket-service 88 | # tag: 2.7.7 89 | # pullPolicy: Always 90 | # containerPort: 8080 91 | # service: 92 | # httpPort: 8080 93 | # targetPort: 8080 94 | # extraEnv: 95 | # "EUREKA_CLIENT_ENABLED": "false" 96 | # "SPRING_CLOUD_KUBERNETES_ENABLED": "true" 97 | # "SPRING_CLOUD_KUBERNETES_LOADBALANCER_MODE": "SERVICE" 98 | # 99 | #frontend: 100 | # enabled: false 101 | # image: 102 | # repository: dhananjay12/demo-frontend 103 | # tag: latest 104 | # pullPolicy: Always 105 | # containerPort: 80 106 | # service: 107 | # httpPort: 80 108 | # targetPort: 80 109 | # extraEnv: 110 | # "EUREKA_CLIENT_ENABLED": "false" 111 | # "SPRING_CLOUD_KUBERNETES_ENABLED": "true" 112 | # "SPRING_CLOUD_KUBERNETES_LOADBALANCER_MODE": "SERVICE" -------------------------------------------------------------------------------- /k8s/all-service/frontend-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: frontend 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: frontend 10 | template: 11 | metadata: 12 | labels: 13 | app: frontend 14 | spec: 15 | containers: 16 | - name: frontend 17 | image: dhananjay12/demo-frontend:latest 18 | ports: 19 | - containerPort: 80 20 | imagePullPolicy: Always 21 | -------------------------------------------------------------------------------- /k8s/all-service/frontend-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | name: frontend 6 | name: frontend 7 | spec: 8 | type: ClusterIP 9 | selector: 10 | app: frontend 11 | ports: 12 | - port: 80 13 | targetPort: 80 14 | protocol: TCP 15 | -------------------------------------------------------------------------------- /k8s/all-service/gateway-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: gateway 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: gateway 10 | template: 11 | metadata: 12 | labels: 13 | app: gateway 14 | spec: 15 | containers: 16 | - env: 17 | - name: EUREKA_CLIENT_ENABLED 18 | value: "false" 19 | - name: SPRING_CLOUD_KUBERNETES_ENABLED 20 | value: "true" 21 | - name: SPRING_CLOUD_KUBERNETES_DISCOVERY_ENABLED 22 | value: "true" 23 | - name: SPRING_CLOUD_KUBERNETES_RELOAD_ENABLED 24 | value: "true" 25 | image: dhananjay12/demo-gateway:latest 26 | name: gateway 27 | imagePullPolicy: Always 28 | resources: 29 | requests: 30 | memory: "256Mi" 31 | limits: 32 | memory: "512Mi" 33 | ports: 34 | - containerPort: 8080 35 | -------------------------------------------------------------------------------- /k8s/all-service/gateway-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | name: gateway 6 | name: gateway 7 | spec: 8 | type: ClusterIP 9 | selector: 10 | app: gateway 11 | ports: 12 | - port: 8080 13 | targetPort: 8080 14 | protocol: TCP 15 | -------------------------------------------------------------------------------- /k8s/all-service/ingress-frontend-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: Ingress 3 | metadata: 4 | name: ingress-frontend-service 5 | annotations: 6 | ingress.kubernetes.io/rewrite-target: / 7 | spec: 8 | rules: 9 | - http: 10 | paths: 11 | - path: / 12 | pathType: Prefix 13 | backend: 14 | service: 15 | name: frontend 16 | port: 17 | number: 80 18 | -------------------------------------------------------------------------------- /k8s/all-service/ingress-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: Ingress 3 | metadata: 4 | name: ingress-backend-service 5 | annotations: 6 | nginx.ingress.kubernetes.io/use-regex: "true" 7 | spec: 8 | rules: 9 | - http: 10 | paths: 11 | - path: /api/.* 12 | pathType: Prefix 13 | backend: 14 | service: 15 | name: gateway 16 | port: 17 | number: 8080 18 | - path: /websocketservice/.* 19 | pathType: Prefix 20 | backend: 21 | service: 22 | name: gateway 23 | port: 24 | number: 8080 -------------------------------------------------------------------------------- /k8s/all-service/reactive-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: reactive-service 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: reactive-service 10 | template: 11 | metadata: 12 | labels: 13 | app: reactive-service 14 | spec: 15 | containers: 16 | - env: 17 | - name: EUREKA_CLIENT_ENABLED 18 | value: "false" 19 | - name: SPRING_CLOUD_KUBERNETES_ENABLED 20 | value: "true" 21 | - name: SPRING_CLOUD_KUBERNETES_DISCOVERY_ENABLED 22 | value: "true" 23 | - name: SPRING_CLOUD_KUBERNETES_RELOAD_ENABLED 24 | value: "true" 25 | image: dhananjay12/demo-reactive-service:latest 26 | name: reactive-service 27 | imagePullPolicy: Always 28 | resources: 29 | requests: 30 | memory: "256Mi" 31 | limits: 32 | memory: "512Mi" 33 | ports: 34 | - containerPort: 8080 35 | -------------------------------------------------------------------------------- /k8s/all-service/reactive-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | name: reactive-service 6 | name: reactive-service 7 | spec: 8 | type: ClusterIP 9 | selector: 10 | app: reactive-service 11 | ports: 12 | - port: 8080 13 | targetPort: 8080 14 | protocol: TCP 15 | -------------------------------------------------------------------------------- /k8s/all-service/service-one-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: service-one 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: service-one 10 | template: 11 | metadata: 12 | labels: 13 | app: service-one 14 | spec: 15 | containers: 16 | - env: 17 | - name: EUREKA_CLIENT_ENABLED 18 | value: "false" 19 | - name: SPRING_CLOUD_KUBERNETES_ENABLED 20 | value: "true" 21 | - name: SPRING_CLOUD_KUBERNETES_DISCOVERY_ENABLED 22 | value: "true" 23 | - name: SPRING_CLOUD_KUBERNETES_RELOAD_ENABLED 24 | value: "true" 25 | image: dhananjay12/demo-service-one:latest 26 | name: service-one 27 | imagePullPolicy: Always 28 | resources: 29 | requests: 30 | memory: "256Mi" 31 | limits: 32 | memory: "512Mi" 33 | ports: 34 | - containerPort: 8080 35 | -------------------------------------------------------------------------------- /k8s/all-service/service-one-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | name: service-one 6 | name: service-one 7 | spec: 8 | type: ClusterIP 9 | selector: 10 | app: service-one 11 | ports: 12 | - port: 8080 13 | targetPort: 8080 14 | protocol: TCP 15 | -------------------------------------------------------------------------------- /k8s/all-service/service-two-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: service-two 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: service-two 10 | template: 11 | metadata: 12 | labels: 13 | app: service-two 14 | spec: 15 | containers: 16 | - env: 17 | - name: EUREKA_CLIENT_ENABLED 18 | value: "false" 19 | - name: SPRING_CLOUD_KUBERNETES_ENABLED 20 | value: "true" 21 | - name: SPRING_CLOUD_KUBERNETES_DISCOVERY_ENABLED 22 | value: "true" 23 | - name: SPRING_CLOUD_KUBERNETES_RELOAD_ENABLED 24 | value: "true" 25 | image: dhananjay12/demo-service-two:latest 26 | name: service-two 27 | imagePullPolicy: Always 28 | resources: 29 | requests: 30 | memory: "256Mi" 31 | limits: 32 | memory: "512Mi" 33 | ports: 34 | - containerPort: 8080 35 | -------------------------------------------------------------------------------- /k8s/all-service/service-two-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | name: service-two 6 | name: service-two 7 | spec: 8 | type: ClusterIP 9 | selector: 10 | app: service-two 11 | ports: 12 | - port: 8080 13 | targetPort: 8080 14 | protocol: TCP 15 | -------------------------------------------------------------------------------- /k8s/all-service/websocket-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: websocketservice 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: websocketservice 10 | template: 11 | metadata: 12 | labels: 13 | app: websocketservice 14 | spec: 15 | containers: 16 | - env: 17 | - name: EUREKA_CLIENT_ENABLED 18 | value: "false" 19 | - name: SPRING_CLOUD_KUBERNETES_ENABLED 20 | value: "true" 21 | - name: SPRING_CLOUD_KUBERNETES_DISCOVERY_ENABLED 22 | value: "true" 23 | - name: SPRING_CLOUD_KUBERNETES_RELOAD_ENABLED 24 | value: "true" 25 | image: dhananjay12/demo-websocket-service:latest 26 | name: websocketservice 27 | imagePullPolicy: Always 28 | resources: 29 | requests: 30 | memory: "256Mi" 31 | limits: 32 | memory: "512Mi" 33 | ports: 34 | - containerPort: 8080 35 | -------------------------------------------------------------------------------- /k8s/all-service/websocket-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | name: websocketservice 6 | name: websocketservice 7 | spec: 8 | type: ClusterIP 9 | selector: 10 | app: websocketservice 11 | ports: 12 | - port: 8080 13 | targetPort: 8080 14 | protocol: TCP 15 | -------------------------------------------------------------------------------- /k8s/k8s-dashboard-user/README.md: -------------------------------------------------------------------------------- 1 | ## User for K8s dashboard 2 | 3 | If you are following https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/ you will also 4 | need a user to access the dashboard. While its linked in the doc, install these yaml. 5 | 6 | https://github.com/kubernetes/dashboard/blob/master/docs/user/access-control/creating-sample-user.md 7 | 8 | This is a one time activity if your K8s cluster hasnt changed. 9 | 10 | ``` 11 | kubectl apply -f k8s-dashboard-user 12 | 13 | kubectl -n kubernetes-dashboard create token admin-user 14 | ``` 15 | 16 | To open dashboard 17 | ``` 18 | kubectl proxy 19 | 20 | ``` 21 | 22 | http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/#/login -------------------------------------------------------------------------------- /k8s/k8s-dashboard-user/role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: admin-user 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: ClusterRole 8 | name: cluster-admin 9 | subjects: 10 | - kind: ServiceAccount 11 | name: admin-user 12 | namespace: kubernetes-dashboard -------------------------------------------------------------------------------- /k8s/k8s-dashboard-user/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: admin-user 5 | namespace: kubernetes-dashboard -------------------------------------------------------------------------------- /k8s/spring-k8s-loadbalancer-test/README.md: -------------------------------------------------------------------------------- 1 | Pre-reqs 2 | ======== 3 | - A Docker daemon running 4 | - `kind` installed (e.g `brew install kind`) 5 | 6 | Setup Details 7 | ============= 8 | - sets up a Kind Kubernetes test cluster 9 | - configs it to audit requests to the Kubernetes API Server: get, watch and describe Services and Endpoints 10 | - install test apps 11 | - sets up a port-forward to the `service-one` 12 | - make 10 curl requests to `service-one` (`/hop/headers`) that will result in a call to `service-two` (`/headers`) 13 | - log the curl request-response and the kubernetes api-requests defined above 14 | 15 | 16 | To install 17 | ========== 18 | Note; this will delete the default `kind` container 19 | 20 | Run `./install.sh` 21 | 22 | To run the test 23 | =============== 24 | Run `./test.sh` -------------------------------------------------------------------------------- /k8s/spring-k8s-loadbalancer-test/audit-policy.yaml: -------------------------------------------------------------------------------- 1 | # Log all requests at the Metadata level. 2 | apiVersion: audit.k8s.io/v1 3 | kind: Policy 4 | rules: 5 | # - level: Metadata 6 | - level: RequestResponse 7 | verbs: ["get","watch","describe"] 8 | resources: 9 | - group: "" 10 | resources: ["endpoints", "services"] 11 | namespaces: ["test"] -------------------------------------------------------------------------------- /k8s/spring-k8s-loadbalancer-test/cleanup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -xe 3 | 4 | kubectl -n test delete -f service-account.yaml 5 | kubectl -n test delete -f service-one-deployment.yaml 6 | kubectl -n test delete -f service-one-service.yaml 7 | kubectl -n test delete -f service-two-deployment.yaml 8 | kubectl -n test delete -f service-two-service.yaml 9 | kind delete cluster -------------------------------------------------------------------------------- /k8s/spring-k8s-loadbalancer-test/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -xe 3 | 4 | kind delete cluster 5 | kind create cluster --config kind-config.yaml 6 | kubectl create ns test 7 | kubectl -n test apply -f service-account.yaml 8 | kubectl -n test apply -f service-one-deployment.yaml 9 | kubectl -n test apply -f service-one-service.yaml 10 | kubectl -n test apply -f service-two-deployment.yaml 11 | kubectl -n test apply -f service-two-service.yaml 12 | 13 | 14 | #echo "Wating for deployments to be available..." 15 | #kubectl wait deployment -n test test-service --for condition=Available=True --timeout=90s 16 | #kubectl wait deployment -n test reactive-service --for condition=Available=True --timeout=90s 17 | #kubectl wait deployment -n test token-converter --for condition=Available=True --timeout=90s 18 | # 19 | #echo "Ready for tests..." 20 | #echo " run ./test.sh" -------------------------------------------------------------------------------- /k8s/spring-k8s-loadbalancer-test/kind-config.yaml: -------------------------------------------------------------------------------- 1 | kind: Cluster 2 | apiVersion: kind.x-k8s.io/v1alpha4 3 | nodes: 4 | - role: control-plane 5 | kubeadmConfigPatches: 6 | - | 7 | kind: ClusterConfiguration 8 | apiServer: 9 | # enable auditing flags on the API server 10 | extraArgs: 11 | audit-log-path: /var/log/kubernetes/kube-apiserver-audit.log 12 | audit-policy-file: /etc/kubernetes/policies/audit-policy.yaml 13 | # mount new files / directories on the control plane 14 | extraVolumes: 15 | - name: audit-policies 16 | hostPath: /etc/kubernetes/policies 17 | mountPath: /etc/kubernetes/policies 18 | readOnly: true 19 | pathType: "DirectoryOrCreate" 20 | - name: "audit-logs" 21 | hostPath: "/var/log/kubernetes" 22 | mountPath: "/var/log/kubernetes" 23 | readOnly: false 24 | pathType: DirectoryOrCreate 25 | # mount the local file on the control plane 26 | extraMounts: 27 | - hostPath: ./audit-policy.yaml 28 | containerPath: /etc/kubernetes/policies/audit-policy.yaml 29 | readOnly: true -------------------------------------------------------------------------------- /k8s/spring-k8s-loadbalancer-test/service-account.yaml: -------------------------------------------------------------------------------- 1 | kind: Role 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | metadata: 4 | name: namespace-reader 5 | rules: 6 | - apiGroups: [""] 7 | resources: ["configmaps", "pods", "services", "endpoints", "secrets"] 8 | verbs: ["get", "list", "watch"] 9 | 10 | --- 11 | 12 | kind: RoleBinding 13 | apiVersion: rbac.authorization.k8s.io/v1 14 | metadata: 15 | name: namespace-reader-binding 16 | subjects: 17 | - kind: ServiceAccount 18 | name: default 19 | apiGroup: "" 20 | roleRef: 21 | kind: Role 22 | name: namespace-reader 23 | apiGroup: "" -------------------------------------------------------------------------------- /k8s/spring-k8s-loadbalancer-test/service-one-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: service-one 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: service-one 10 | template: 11 | metadata: 12 | labels: 13 | app: service-one 14 | spec: 15 | containers: 16 | - env: 17 | # Make switch from Eureka to Kubernetes 18 | - name: eureka.client.enabled 19 | value: "false" 20 | - name: spring.cloud.kubernetes.enabled 21 | value: "true" 22 | - name: spring.cloud.kubernetes.loadbalancer.mode 23 | value: "SERVICE" 24 | # Debugging and management 25 | - name: logging.level.org.springframework.cloud 26 | value: "DEBUG" 27 | - name: management.endpoints.enabled-by-default 28 | value: "true" 29 | - name: management.endpoints.web.exposure.include 30 | value: "*" 31 | # Spring K8s Load Balancer 32 | - name: loadbalancer.client.name 33 | value: "service-two" 34 | image: dhananjay12/demo-service-one:3.0.2-fix 35 | name: service-one 36 | imagePullPolicy: Always 37 | resources: 38 | requests: 39 | memory: "256Mi" 40 | limits: 41 | memory: "512Mi" 42 | ports: 43 | - containerPort: 8080 44 | -------------------------------------------------------------------------------- /k8s/spring-k8s-loadbalancer-test/service-one-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | name: service-one 6 | name: service-one 7 | spec: 8 | type: LoadBalancer 9 | selector: 10 | app: service-one 11 | ports: 12 | - port: 8080 13 | targetPort: 8080 14 | protocol: TCP 15 | -------------------------------------------------------------------------------- /k8s/spring-k8s-loadbalancer-test/service-two-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: service-two 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: service-two 10 | template: 11 | metadata: 12 | labels: 13 | app: service-two 14 | spec: 15 | containers: 16 | - env: 17 | # Make switch from Eureka to Kubernetes 18 | - name: eureka.client.enabled 19 | value: "false" 20 | - name: spring.cloud.kubernetes.enabled 21 | value: "true" 22 | - name: spring.cloud.kubernetes.loadbalancer.mode 23 | value: "SERVICE" 24 | # Debugging and management 25 | - name: logging.level.org.springframework.cloud 26 | value: "DEBUG" 27 | - name: management.endpoints.enabled-by-default 28 | value: "true" 29 | - name: management.endpoints.web.exposure.include 30 | value: "*" 31 | image: dhananjay12/demo-service-two:3.0.2 32 | name: service-two 33 | imagePullPolicy: Always 34 | resources: 35 | requests: 36 | memory: "256Mi" 37 | limits: 38 | memory: "512Mi" 39 | ports: 40 | - containerPort: 8080 41 | -------------------------------------------------------------------------------- /k8s/spring-k8s-loadbalancer-test/service-two-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | name: service-two 6 | name: service-two 7 | spec: 8 | type: ClusterIP 9 | selector: 10 | app: service-two 11 | ports: 12 | - port: 8080 13 | targetPort: 8080 14 | protocol: TCP 15 | -------------------------------------------------------------------------------- /k8s/spring-k8s-loadbalancer-test/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | kubectl port-forward -n test deployment/service-one 38080:8080 2>/dev/null & 3 | sleep 3 4 | 5 | docker exec kind-control-plane tail -n0 -F /var/log/kubernetes/kube-apiserver-audit.log & 6 | 7 | for ((i=0;i<10;i++)); 8 | do 9 | printf ">> Attempting curl request: %s \n" "$i" 10 | curl -s -o /dev/null -w "<<< Curl reponse code: %{http_code} \n\n\n" http://localhost:38080/hop/headers 11 | sleep 1 12 | done 13 | 14 | sleep 1 15 | pkill -P $$ -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.mynotes.microservices.demo 8 | spring-microservice-demo-root 9 | 0.0.1-SNAPSHOT 10 | pom 11 | 12 | spring-microservice-demo 13 | Demo project for spring microservices k8s 14 | 15 | 16 | eureka-server 17 | service-one 18 | service-two 19 | gateway 20 | reactive-service 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /prometheus-grafana/README.md: -------------------------------------------------------------------------------- 1 | ## Services 2 | 3 | All java images have port `8080` and exposed on different ports. 4 | 5 | Frontend - http://localhost 6 | 7 | Eureka - http://localhost:8761/ 8 | 9 | Gateway routes - http://localhost:8080/actuator/gateway/routes 10 | 11 | ## Prometheus and Grafana 12 | 13 | Setup taken from - https://github.com/vegasbrianc/prometheus 14 | 15 | Prometheus is configured to fetch metrics from `gateway`, `service-one` and `service-two`. 16 | 17 | Prometheus endpoint - http://localhost:9090 18 | 19 | Prometheus targets - http://localhost:9090/targets 20 | 21 | Grafana - http://localhost:3000/ 22 | 23 | Grafana is setup to use the Prometheus as datasource. A dashboard too will be 24 | imported (https://grafana.com/grafana/dashboards/4701). 25 | 26 | Note - Had to replace `${DS_PROMETHEUS` to `DS_PROMETHEUS` from the default dashboard json. 27 | Importing directly wont have this issue. 28 | -------------------------------------------------------------------------------- /prometheus-grafana/config/frontend/nginx.conf: -------------------------------------------------------------------------------- 1 | events{ 2 | 3 | } 4 | http { 5 | 6 | types { 7 | module js; 8 | } 9 | 10 | include /etc/nginx/mime.types; 11 | 12 | upstream gateway { 13 | server gateway:8080; 14 | } 15 | 16 | server { 17 | 18 | location /api { 19 | proxy_pass http://gateway; 20 | } 21 | 22 | location /websocketservice { 23 | proxy_pass http://gateway; 24 | } 25 | 26 | location / { 27 | root /usr/share/nginx/html; 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /prometheus-grafana/config/grafana/provisioning/dashboards/dashboard.yml: -------------------------------------------------------------------------------- 1 | apiVersion: 1 2 | providers: 3 | - name: 'Prometheus' 4 | orgId: 1 5 | folder: '' 6 | type: file 7 | disableDeletion: false 8 | editable: true 9 | options: 10 | path: /etc/grafana/provisioning/dashboards -------------------------------------------------------------------------------- /prometheus-grafana/config/grafana/provisioning/datasources/datasource.yml: -------------------------------------------------------------------------------- 1 | # config file version 2 | apiVersion: 1 3 | 4 | # list of datasources that should be deleted from the database 5 | deleteDatasources: 6 | - name: DS_PROMETHEUS 7 | orgId: 1 8 | 9 | # list of datasources to insert/update depending 10 | # whats available in the database 11 | datasources: 12 | # name of the datasource. Required 13 | - name: DS_PROMETHEUS 14 | # datasource type. Required 15 | type: prometheus 16 | # access mode. direct or proxy. Required 17 | access: proxy 18 | # org id. will default to orgId 1 if not specified 19 | orgId: 1 20 | # url 21 | url: http://prometheus:9090 22 | # database password, if used 23 | password: 24 | # database user, if used 25 | user: 26 | # database name, if used 27 | database: 28 | # enable/disable basic auth 29 | basicAuth: false 30 | # basic auth username, if used 31 | basicAuthUser: 32 | # basic auth password, if used 33 | basicAuthPassword: 34 | # enable/disable with credentials headers 35 | withCredentials: 36 | # mark as default datasource. Max one per org 37 | isDefault: true 38 | # fields that will be converted to json and stored in json_data 39 | jsonData: 40 | graphiteVersion: "1.1" 41 | tlsAuth: false 42 | tlsAuthWithCACert: false 43 | # json object of data that will be encrypted. 44 | secureJsonData: 45 | tlsCACert: "..." 46 | tlsClientCert: "..." 47 | tlsClientKey: "..." 48 | version: 1 49 | # allow users to edit datasources from the UI. 50 | editable: true -------------------------------------------------------------------------------- /prometheus-grafana/config/prometheus/prometheus.yml: -------------------------------------------------------------------------------- 1 | global: 2 | scrape_interval: 10s 3 | 4 | scrape_configs: 5 | - job_name: 'gateway' 6 | metrics_path: '/actuator/prometheus' 7 | scrape_interval: 5s 8 | static_configs: 9 | - targets: ['gateway:8080'] 10 | - job_name: 'service-one' 11 | metrics_path: '/actuator/prometheus' 12 | scrape_interval: 5s 13 | static_configs: 14 | - targets: ['service-one:8080'] 15 | - job_name: 'service-two' 16 | metrics_path: '/actuator/prometheus' 17 | scrape_interval: 5s 18 | static_configs: 19 | - targets: ['service-two:8080'] 20 | - job_name: "node-exporter" 21 | scrape_interval: "15s" 22 | static_configs: 23 | - targets: ['node-exporter:9100'] 24 | -------------------------------------------------------------------------------- /prometheus-grafana/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.7" 2 | 3 | x-common-variables: &common-variables 4 | eureka.client.serviceUrl.defaultZone: http://eureka:8080/eureka 5 | eureka.instance.non-secure-port: 8080 6 | 7 | services: 8 | eureka: 9 | image: "dhananjay12/demo-eureka-server:latest" 10 | environment: 11 | <<: *common-variables 12 | ports: 13 | - "8761:8080" 14 | frontend: 15 | image: "dhananjay12/demo-frontend:latest" 16 | volumes: 17 | - ./config/frontend/nginx.conf:/etc/nginx/nginx.conf 18 | ports: 19 | - "80:80" 20 | gateway: 21 | image: "dhananjay12/demo-gateway:latest" 22 | environment: 23 | <<: *common-variables 24 | ports: 25 | - "8080:8080" 26 | service-one: 27 | image: "dhananjay12/demo-service-one:latest" 28 | environment: 29 | <<: *common-variables 30 | ports: 31 | - "8100:8080" 32 | service-two: 33 | image: "dhananjay12/demo-service-two:latest" 34 | environment: 35 | <<: *common-variables 36 | ports: 37 | - "8200:8080" 38 | 39 | prometheus: 40 | image: prom/prometheus 41 | volumes: 42 | - ./config/prometheus/:/etc/prometheus/ 43 | command: 44 | - '--config.file=/etc/prometheus/prometheus.yml' 45 | - '--storage.tsdb.path=/prometheus' 46 | ports: 47 | - 9090:9090 48 | 49 | node-exporter: 50 | image: prom/node-exporter:latest 51 | ports: 52 | - 9100:9100 53 | 54 | grafana: 55 | image: grafana/grafana:latest 56 | ports: 57 | - 3000:3000 58 | depends_on: 59 | - prometheus 60 | volumes: 61 | - grafana_data:/var/lib/grafana 62 | - ./config/grafana/provisioning/:/etc/grafana/provisioning 63 | environment: 64 | - GF_SECURITY_ADMIN_USER=admin 65 | - GF_SECURITY_ADMIN_PASSWORD=admin 66 | 67 | volumes: 68 | grafana_data: -------------------------------------------------------------------------------- /reactive-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.mynotes.microservices.demo 7 | reactive-service 8 | jar 9 | reactive-service 10 | Demo project for Spring Boot 11 | 12 | 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 3.0.2 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 17 25 | 2022.0.1 26 | false 27 | false 28 | dhananjay12 29 | demo 30 | 31 | 32 | 33 | 34 | 35 | org.springframework.cloud 36 | spring-cloud-dependencies 37 | ${spring-cloud.version} 38 | pom 39 | import 40 | 41 | 42 | 43 | 44 | 45 | 46 | org.springframework.boot 47 | spring-boot-starter-webflux 48 | 49 | 50 | io.zipkin.reporter2 51 | zipkin-reporter-brave 52 | 53 | 54 | io.micrometer 55 | micrometer-tracing-bridge-brave 56 | 57 | 58 | org.springframework.boot 59 | spring-boot-starter-actuator 60 | 61 | 62 | io.micrometer 63 | micrometer-registry-prometheus 64 | runtime 65 | 66 | 67 | org.springframework.cloud 68 | spring-cloud-starter-kubernetes-fabric8-all 69 | 70 | 71 | org.springframework.cloud 72 | spring-cloud-starter-loadbalancer 73 | 74 | 75 | org.springframework.cloud 76 | spring-cloud-starter-netflix-eureka-client 77 | 78 | 79 | org.projectlombok 80 | lombok 81 | true 82 | 83 | 84 | org.springframework.boot 85 | spring-boot-starter-test 86 | test 87 | 88 | 89 | io.projectreactor 90 | reactor-test 91 | test 92 | 93 | 94 | 95 | 96 | ${project.artifactId} 97 | 98 | 99 | org.springframework.boot 100 | spring-boot-maven-plugin 101 | 102 | 103 | org.apache.maven.plugins 104 | maven-surefire-plugin 105 | 106 | 107 | 108 | ${surefireArgLine} 109 | ${skip.unit.tests} 110 | 111 | 112 | **/*IT.java 113 | **/*It.java 114 | 115 | 116 | 117 | 118 | 119 | org.apache.maven.plugins 120 | maven-failsafe-plugin 121 | 122 | 123 | 124 | ${failsafeArgLine} 125 | ${skip.integration.tests} 126 | 127 | **/*It.java 128 | **/*IT.java 129 | 130 | 131 | 132 | 133 | pl.project13.maven 134 | git-commit-id-plugin 135 | 4.0.0 136 | 137 | 138 | get-the-git-infos 139 | initialize 140 | 141 | revision 142 | 143 | 144 | 145 | 146 | false 147 | yyyy-MM-dd'T'HH:mm:ss'Z' 148 | GMT 149 | 150 | ^git.commit.time$ 151 | 152 | 153 | 154 | 155 | com.google.cloud.tools 156 | jib-maven-plugin 157 | 2.6.0 158 | 159 | 160 | eclipse-temurin:17-jre-alpine 161 | 162 | 163 | ${docker.image.url}/${docker.image.prefix}-${project.artifactId} 164 | 165 | ${project.version} 166 | 167 | 168 | 169 | ${git.commit.time} 170 | ${git.commit.time} 171 | com.mynotes.microservices.demo.reactive.Application 172 | 173 | 8080 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | -------------------------------------------------------------------------------- /reactive-service/src/main/java/com/mynotes/microservices/demo/reactive/Application.java: -------------------------------------------------------------------------------- 1 | package com.mynotes.microservices.demo.reactive; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.client.loadbalancer.LoadBalanced; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.http.HttpHeaders; 8 | import org.springframework.http.MediaType; 9 | import org.springframework.web.reactive.function.client.WebClient; 10 | 11 | @SpringBootApplication 12 | public class Application { 13 | 14 | public static void main(String[] args) { 15 | SpringApplication.run(Application.class, args); 16 | } 17 | 18 | @LoadBalanced 19 | @Bean 20 | public WebClient.Builder webClient(){ 21 | return WebClient.builder() 22 | .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /reactive-service/src/main/java/com/mynotes/microservices/demo/reactive/GreetReactiveController.java: -------------------------------------------------------------------------------- 1 | package com.mynotes.microservices.demo.reactive; 2 | 3 | import java.time.Duration; 4 | import java.time.Instant; 5 | 6 | import org.reactivestreams.Publisher; 7 | import org.springframework.http.MediaType; 8 | import org.springframework.web.bind.annotation.GetMapping; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | import reactor.core.publisher.Flux; 12 | 13 | @RestController 14 | public class GreetReactiveController { 15 | 16 | @GetMapping("/greetings") 17 | public Publisher greetingPublisher() { 18 | Flux greetingFlux = Flux.generate(sink -> sink.next(new Greeting("Hello"))).take(50); 19 | return greetingFlux; 20 | } 21 | 22 | @GetMapping(value = "/greetings/sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE) 23 | public Publisher sseGreetings() { 24 | Flux delayElements = Flux 25 | .generate(sink -> sink.next(new Greeting("Hello @" + Instant.now().toString()))) 26 | .delayElements(Duration.ofSeconds(5)); 27 | return delayElements; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /reactive-service/src/main/java/com/mynotes/microservices/demo/reactive/Greeting.java: -------------------------------------------------------------------------------- 1 | package com.mynotes.microservices.demo.reactive; 2 | 3 | public class Greeting { 4 | 5 | private String msg; 6 | 7 | public Greeting() {} 8 | 9 | public Greeting(String msg) { 10 | this.msg = msg; 11 | } 12 | 13 | public String getMsg() { 14 | return msg; 15 | } 16 | 17 | public void setMsg(String msg) { 18 | this.msg = msg; 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /reactive-service/src/main/java/com/mynotes/microservices/demo/reactive/HopController.java: -------------------------------------------------------------------------------- 1 | package com.mynotes.microservices.demo.reactive; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import java.util.stream.Collectors; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.util.MultiValueMap; 8 | import org.springframework.web.bind.annotation.RequestHeader; 9 | import org.springframework.web.bind.annotation.RequestMapping; 10 | import org.springframework.web.bind.annotation.RequestMethod; 11 | import org.springframework.web.bind.annotation.ResponseBody; 12 | import org.springframework.web.bind.annotation.RestController; 13 | import org.springframework.web.reactive.function.client.WebClient; 14 | import reactor.core.publisher.Mono; 15 | 16 | @RestController 17 | @RequestMapping("/hop") 18 | @Slf4j 19 | public class HopController { 20 | 21 | private final WebClient webClient; 22 | 23 | public HopController(WebClient.Builder builder) { 24 | this.webClient = builder.build(); 25 | } 26 | 27 | @RequestMapping(value = "/headers/service-one", method = RequestMethod.GET) 28 | @ResponseBody 29 | public Mono>> hopHeadersToReactive(@RequestHeader MultiValueMap headers) { 30 | 31 | log.info("hop headers to reactive"); 32 | 33 | Map> result = new HashMap<>(); 34 | Map requestHeaders = new HashMap<>(); 35 | 36 | headers.forEach((key, value) -> { 37 | requestHeaders.put(key, value.stream().collect(Collectors.joining("|"))); 38 | }); 39 | result.put("reactive-service",requestHeaders); 40 | 41 | 42 | return this.webClient.get().uri("http://service-one/headers").retrieve().bodyToMono(Map.class) 43 | .doOnSuccess(response -> result.put("service-one", response)) 44 | .thenReturn(result); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /reactive-service/src/main/java/com/mynotes/microservices/demo/reactive/Person.java: -------------------------------------------------------------------------------- 1 | package com.mynotes.microservices.demo.reactive; 2 | 3 | public class Person { 4 | 5 | private int id; 6 | 7 | private String name; 8 | 9 | public Person() { 10 | 11 | } 12 | 13 | public Person(int id, String name) { 14 | super(); 15 | this.id = id; 16 | this.name = name; 17 | } 18 | 19 | public int getId() { 20 | return id; 21 | } 22 | 23 | public String getName() { 24 | return name; 25 | } 26 | 27 | public void setId(int id) { 28 | this.id = id; 29 | } 30 | 31 | public void setName(String name) { 32 | this.name = name; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /reactive-service/src/main/java/com/mynotes/microservices/demo/reactive/PersonController.java: -------------------------------------------------------------------------------- 1 | package com.mynotes.microservices.demo.reactive; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.PathVariable; 8 | import org.springframework.web.bind.annotation.RequestParam; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | @RestController 12 | public class PersonController { 13 | 14 | private static List personList = new ArrayList<>(); 15 | 16 | static { 17 | personList.add(new Person(1, "John")); 18 | personList.add(new Person(2, "Jane")); 19 | personList.add(new Person(3, "Max")); 20 | personList.add(new Person(4, "Alex")); 21 | personList.add(new Person(5, "Aloy")); 22 | personList.add(new Person(6, "Sarah")); 23 | } 24 | 25 | @GetMapping("/person/{id}") 26 | public Person getPerson(@PathVariable int id, @RequestParam(defaultValue = "2") int delay) 27 | throws InterruptedException { 28 | Thread.sleep(delay * 1000); 29 | return personList.stream().filter((person) -> person.getId() == id).findFirst().get(); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /reactive-service/src/main/java/com/mynotes/microservices/demo/reactive/ReactiveController.java: -------------------------------------------------------------------------------- 1 | package com.mynotes.microservices.demo.reactive; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import java.util.stream.Collectors; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.util.MultiValueMap; 8 | import org.springframework.web.bind.annotation.GetMapping; 9 | import org.springframework.web.bind.annotation.RequestHeader; 10 | import org.springframework.web.bind.annotation.ResponseBody; 11 | import org.springframework.web.bind.annotation.RestController; 12 | 13 | @RestController 14 | @Slf4j 15 | public class ReactiveController { 16 | 17 | @GetMapping("/headers") 18 | @ResponseBody 19 | public Map headers(@RequestHeader MultiValueMap headers) { 20 | 21 | Map map = new HashMap<>(); 22 | 23 | headers.forEach((key, value) -> { 24 | log.info(String.format("Header '%s' = %s", key, value.stream().collect(Collectors.joining("|")))); 25 | map.put(key, value.stream().collect(Collectors.joining("|"))); 26 | }); 27 | 28 | return map; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /reactive-service/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | management: 2 | endpoints: 3 | web: 4 | exposure: 5 | include: '*' 6 | 7 | eureka: 8 | client: 9 | enabled: true 10 | serviceUrl: 11 | defaultZone: http://localhost:8080/eureka/ 12 | 13 | spring: 14 | main: 15 | banner-mode: off 16 | application: 17 | name: reactive-service 18 | cloud: 19 | kubernetes: 20 | enabled: false 21 | config: 22 | enable-api: false 23 | enableApi: false 24 | enabled: false 25 | reload: 26 | enabled: false 27 | zipkin: 28 | enabled: false -------------------------------------------------------------------------------- /reactive-service/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | #Disable kubernetes initially. https://docs.spring.io/spring-cloud-kubernetes/docs/current/reference/html/index.html#kubernetes-ecosystem-awareness 2 | spring: 3 | cloud: 4 | kubernetes: 5 | enabled: false 6 | config: 7 | enabled: false 8 | secrets: 9 | enabled: false -------------------------------------------------------------------------------- /service-one/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | com.mynotes.microservices.demo 6 | service-one 7 | jar 8 | service-one 9 | Demo project for Spring 10 | 11 | 12 | org.springframework.boot 13 | spring-boot-starter-parent 14 | 3.0.2 15 | 16 | 17 | 18 | 19 | 20 | 21 | org.springframework.cloud 22 | spring-cloud-dependencies 23 | ${spring-cloud.version} 24 | pom 25 | import 26 | 27 | 28 | 29 | 30 | 31 | UTF-8 32 | UTF-8 33 | 17 34 | 2022.0.1 35 | false 36 | false 37 | dhananjay12 38 | demo 39 | 40 | 41 | 42 | 43 | 44 | org.springframework.boot 45 | spring-boot-starter-web 46 | 47 | 48 | io.micrometer 49 | micrometer-tracing-bridge-brave 50 | 51 | 52 | io.zipkin.reporter2 53 | zipkin-reporter-brave 54 | 55 | 56 | org.springframework.boot 57 | spring-boot-starter-actuator 58 | 59 | 60 | io.micrometer 61 | micrometer-registry-prometheus 62 | runtime 63 | 64 | 65 | org.springframework.cloud 66 | spring-cloud-starter-kubernetes-fabric8-all 67 | 68 | 69 | org.springframework.cloud 70 | spring-cloud-starter-loadbalancer 71 | 72 | 73 | org.springframework.cloud 74 | spring-cloud-starter-netflix-eureka-client 75 | 76 | 77 | org.projectlombok 78 | lombok 79 | true 80 | 81 | 82 | org.springframework.boot 83 | spring-boot-starter-test 84 | test 85 | 86 | 87 | 97 | 98 | 99 | 100 | ${project.artifactId} 101 | 102 | 103 | org.springframework.boot 104 | spring-boot-maven-plugin 105 | 106 | 107 | org.apache.maven.plugins 108 | maven-surefire-plugin 109 | 110 | 111 | 112 | ${surefireArgLine} 113 | ${skip.unit.tests} 114 | 115 | 116 | **/*IT.java 117 | **/*It.java 118 | 119 | 120 | 121 | 122 | 123 | org.apache.maven.plugins 124 | maven-failsafe-plugin 125 | 126 | 127 | 128 | ${failsafeArgLine} 129 | ${skip.integration.tests} 130 | 131 | **/*It.java 132 | **/*IT.java 133 | 134 | 135 | 136 | 137 | pl.project13.maven 138 | git-commit-id-plugin 139 | 4.0.0 140 | 141 | 142 | get-the-git-infos 143 | initialize 144 | 145 | revision 146 | 147 | 148 | 149 | 150 | false 151 | yyyy-MM-dd'T'HH:mm:ss'Z' 152 | GMT 153 | 154 | ^git.commit.time$ 155 | 156 | 157 | 158 | 159 | com.google.cloud.tools 160 | jib-maven-plugin 161 | 2.6.0 162 | 163 | 164 | eclipse-temurin:17-jre-alpine 165 | 166 | 167 | ${docker.image.url}/${docker.image.prefix}-${project.artifactId} 168 | 169 | ${project.version} 170 | 171 | 172 | 173 | ${git.commit.time} 174 | ${git.commit.time} 175 | com.mynotes.microservices.demo.serviceone.ServiceOneApplication 176 | 177 | 8080 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | -------------------------------------------------------------------------------- /service-one/src/main/java/com/mynotes/microservices/demo/serviceone/Fabric8ServicesListSupplier.java: -------------------------------------------------------------------------------- 1 | package com.mynotes.microservices.demo.serviceone; 2 | 3 | /* 4 | * Copyright 2013-2020 the original author or authors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * https://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | 20 | 21 | import java.util.ArrayList; 22 | import java.util.List; 23 | 24 | import io.fabric8.kubernetes.api.model.Service; 25 | import io.fabric8.kubernetes.client.KubernetesClient; 26 | import org.springframework.cloud.kubernetes.fabric8.loadbalancer.Fabric8ServiceInstanceMapper; 27 | import reactor.core.publisher.Flux; 28 | 29 | import org.springframework.cloud.client.ServiceInstance; 30 | import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties; 31 | import org.springframework.cloud.kubernetes.commons.loadbalancer.KubernetesServicesListSupplier; 32 | import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; 33 | import org.springframework.core.env.Environment; 34 | import org.springframework.util.StringUtils; 35 | 36 | /** 37 | * Implementation of {@link ServiceInstanceListSupplier} for load balancer in SERVICE 38 | * mode. 39 | * 40 | * @author Piotr Minkowski 41 | */ 42 | public class Fabric8ServicesListSupplier extends KubernetesServicesListSupplier { 43 | 44 | private final KubernetesClient kubernetesClient; 45 | 46 | Fabric8ServicesListSupplier(Environment environment, KubernetesClient kubernetesClient, 47 | Fabric8ServiceInstanceMapper mapper, KubernetesDiscoveryProperties discoveryProperties) { 48 | super(environment, mapper, discoveryProperties); 49 | this.kubernetesClient = kubernetesClient; 50 | } 51 | 52 | @Override 53 | public Flux> get() { 54 | List result = new ArrayList<>(); 55 | if (discoveryProperties.allNamespaces()) { 56 | List services = this.kubernetesClient.services().inAnyNamespace() 57 | .withField("metadata.name", this.getServiceId()).list().getItems(); 58 | services.forEach(service -> result.add(mapper.map(service))); 59 | } 60 | else { 61 | Service service = StringUtils.hasText(this.kubernetesClient.getNamespace()) 62 | ? this.kubernetesClient.services().inNamespace(this.kubernetesClient.getNamespace()) 63 | .withName(this.getServiceId()).get() 64 | : this.kubernetesClient.services().withName(this.getServiceId()).get(); 65 | if (service != null) { 66 | result.add(mapper.map(service)); 67 | } 68 | } 69 | return Flux.defer(() -> Flux.just(result)); 70 | } 71 | 72 | } -------------------------------------------------------------------------------- /service-one/src/main/java/com/mynotes/microservices/demo/serviceone/HopController.java: -------------------------------------------------------------------------------- 1 | package com.mynotes.microservices.demo.serviceone; 2 | 3 | import jakarta.servlet.http.HttpServletRequest; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | import java.util.stream.Collectors; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.context.ApplicationContext; 9 | import org.springframework.util.MultiValueMap; 10 | import org.springframework.web.bind.annotation.*; 11 | import org.springframework.web.client.RestTemplate; 12 | 13 | 14 | @RestController 15 | @RequestMapping("/hop") 16 | @Slf4j 17 | public class HopController { 18 | 19 | ApplicationContext context; 20 | 21 | RestTemplate restTemplate; 22 | 23 | public HopController(RestTemplate rest,ApplicationContext context){ 24 | 25 | this.restTemplate = rest; 26 | this.context = context; 27 | } 28 | 29 | @RequestMapping(value = "/{status}", method = RequestMethod.GET) 30 | @ResponseBody 31 | public String hop(@PathVariable("status") Integer status) { 32 | 33 | log.info("hop status passed" + status); 34 | 35 | String result = restTemplate.getForObject("http://service-two/status/" + status, String.class); 36 | 37 | return "Service one" + status + " ++++ " + result; 38 | } 39 | 40 | @RequestMapping(value = "/message/{text}", method = RequestMethod.GET) 41 | @ResponseBody 42 | public String text(@PathVariable("text") String text) { 43 | 44 | log.info("Text passed==" + text); 45 | 46 | String result = restTemplate.getForObject("http://service-two/message/" + text, String.class); 47 | 48 | return "Service one+++++" + result; 49 | } 50 | 51 | @RequestMapping(value = "/headers", method = RequestMethod.GET) 52 | @ResponseBody 53 | public Map> hopHeaders(@RequestHeader MultiValueMap headers) { 54 | 55 | log.info("hop headers"); 56 | 57 | Map> result = new HashMap<>(); 58 | Map requestHeaders = new HashMap<>(); 59 | 60 | headers.forEach((key, value) -> { 61 | requestHeaders.put(key, value.stream().collect(Collectors.joining("|"))); 62 | }); 63 | result.put("service-one",requestHeaders); 64 | 65 | 66 | Map response = restTemplate.getForEntity("http://service-two/headers", Map.class).getBody(); 67 | 68 | result.put("service-two", response); 69 | 70 | return result; 71 | } 72 | 73 | @RequestMapping(value = "/headers/reactive", method = RequestMethod.GET) 74 | @ResponseBody 75 | public Map> hopHeadersToReactive(@RequestHeader MultiValueMap headers) { 76 | 77 | log.info("hop headers to reactive"); 78 | 79 | Map> result = new HashMap<>(); 80 | Map requestHeaders = new HashMap<>(); 81 | 82 | headers.forEach((key, value) -> { 83 | requestHeaders.put(key, value.stream().collect(Collectors.joining("|"))); 84 | }); 85 | result.put("service-one",requestHeaders); 86 | 87 | 88 | Map response = restTemplate.getForEntity("http://reactive-service/headers", Map.class).getBody(); 89 | 90 | result.put("reactive-service", response); 91 | 92 | return result; 93 | } 94 | 95 | @RequestMapping(value = "/ip", method = RequestMethod.GET) 96 | @ResponseBody 97 | public Map> hopIp(@RequestHeader MultiValueMap headers, 98 | HttpServletRequest httpServletRequest) { 99 | 100 | Map> result = new HashMap<>(); 101 | 102 | Map serviceOneResult = new HashMap<>(); 103 | 104 | serviceOneResult.put("server.forward-headers-strategy",context.getEnvironment().getProperty("server.forward-headers-strategy")); 105 | serviceOneResult.put("x-forwarded-For",headers.getFirst("x-forwarded-for")); 106 | serviceOneResult.put("forwarded",headers.getFirst("forwarded")); 107 | serviceOneResult.put("httpServletRequest.getRequestURI()",httpServletRequest.getRequestURI()); 108 | serviceOneResult.put("httpServletRequest.getRemoteAddr()",httpServletRequest.getRemoteAddr()); 109 | 110 | result.put("service-one",serviceOneResult); 111 | 112 | 113 | Map response = restTemplate.getForEntity("http://service-two/ip", Map.class).getBody(); 114 | 115 | result.put("service-two", response); 116 | 117 | return result; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /service-one/src/main/java/com/mynotes/microservices/demo/serviceone/ServiceOneApplication.java: -------------------------------------------------------------------------------- 1 | package com.mynotes.microservices.demo.serviceone; 2 | 3 | import com.mynotes.microservices.demo.serviceone.interceptors.LoggingRequestResponse; 4 | import com.mynotes.microservices.demo.serviceone.interceptors.RestTemplateCopyHeaders; 5 | import io.fabric8.kubernetes.client.KubernetesClient; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.springframework.boot.SpringApplication; 10 | import org.springframework.boot.autoconfigure.SpringBootApplication; 11 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 12 | import org.springframework.cloud.client.loadbalancer.LoadBalanced; 13 | import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties; 14 | import org.springframework.cloud.kubernetes.fabric8.loadbalancer.Fabric8ServiceInstanceMapper; 15 | import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; 16 | import org.springframework.context.ConfigurableApplicationContext; 17 | import org.springframework.context.annotation.Bean; 18 | import org.springframework.context.annotation.Primary; 19 | import org.springframework.core.env.Environment; 20 | import org.springframework.http.client.BufferingClientHttpRequestFactory; 21 | import org.springframework.http.client.ClientHttpRequestFactory; 22 | import org.springframework.http.client.ClientHttpRequestInterceptor; 23 | import org.springframework.http.client.SimpleClientHttpRequestFactory; 24 | import org.springframework.util.CollectionUtils; 25 | import org.springframework.web.client.RestTemplate; 26 | 27 | 28 | @SpringBootApplication 29 | @EnableDiscoveryClient 30 | @Slf4j 31 | public class ServiceOneApplication { 32 | 33 | public static void main(String[] args) { 34 | SpringApplication.run(ServiceOneApplication.class, args); 35 | 36 | } 37 | 38 | @LoadBalanced 39 | @Bean 40 | RestTemplate restTemplate() { 41 | RestTemplate restTemplate = null; 42 | log.info("Is Debug Enabled:" + log.isDebugEnabled()); 43 | if (log.isDebugEnabled()) { 44 | ClientHttpRequestFactory factory 45 | = new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()); 46 | restTemplate = new RestTemplate(factory); 47 | List interceptors = getInterceptors(restTemplate); 48 | interceptors.add(new RestTemplateCopyHeaders()); 49 | interceptors.add(new LoggingRequestResponse()); 50 | restTemplate.setInterceptors(interceptors); 51 | } else { 52 | restTemplate = new RestTemplate(); 53 | List interceptors = getInterceptors(restTemplate); 54 | interceptors.add(new RestTemplateCopyHeaders()); 55 | restTemplate.setInterceptors(interceptors); 56 | } 57 | 58 | return restTemplate; 59 | } 60 | 61 | private List getInterceptors(RestTemplate restTemplate) { 62 | List interceptors = restTemplate.getInterceptors(); 63 | if (CollectionUtils.isEmpty(interceptors)) { 64 | interceptors = new ArrayList<>(); 65 | } 66 | return interceptors; 67 | } 68 | 69 | @Bean 70 | @Primary 71 | public ServiceInstanceListSupplier fabric8ServicesListSupplier( 72 | Environment environment, KubernetesClient kubernetesClient, Fabric8ServiceInstanceMapper mapper, 73 | KubernetesDiscoveryProperties discoveryProperties, 74 | ConfigurableApplicationContext context) { 75 | return ServiceInstanceListSupplier.builder().withBase(new Fabric8ServicesListSupplier( 76 | environment, kubernetesClient, mapper, discoveryProperties)) 77 | .withCaching().build(context); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /service-one/src/main/java/com/mynotes/microservices/demo/serviceone/ServiceOneController.java: -------------------------------------------------------------------------------- 1 | package com.mynotes.microservices.demo.serviceone; 2 | 3 | import jakarta.servlet.http.HttpServletRequest; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | import java.util.stream.Collectors; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.context.ApplicationContext; 10 | import org.springframework.util.MultiValueMap; 11 | import org.springframework.web.bind.annotation.GetMapping; 12 | import org.springframework.web.bind.annotation.PathVariable; 13 | import org.springframework.web.bind.annotation.RequestHeader; 14 | import org.springframework.web.bind.annotation.RequestMapping; 15 | import org.springframework.web.bind.annotation.RequestMethod; 16 | import org.springframework.web.bind.annotation.ResponseBody; 17 | import org.springframework.web.bind.annotation.RestController; 18 | import org.springframework.web.client.RestTemplate; 19 | 20 | 21 | @RestController 22 | @Slf4j 23 | public class ServiceOneController { 24 | 25 | ApplicationContext context; 26 | 27 | RestTemplate restTemplate; 28 | 29 | public ServiceOneController(RestTemplate rest,ApplicationContext context){ 30 | this.restTemplate = rest; 31 | this.context = context; 32 | } 33 | 34 | @RequestMapping(value = "/hello", method = RequestMethod.GET) 35 | @ResponseBody 36 | public String hello() { 37 | 38 | return "Hello from service one"; 39 | } 40 | 41 | @GetMapping("/headers") 42 | @ResponseBody 43 | public Map headers(@RequestHeader MultiValueMap headers) { 44 | 45 | Map map = new HashMap<>(); 46 | 47 | headers.forEach((key, value) -> { 48 | log.info(String.format("Header '%s' = %s", key, value.stream().collect(Collectors.joining("|")))); 49 | map.put(key, value.stream().collect(Collectors.joining("|"))); 50 | }); 51 | 52 | return map; 53 | } 54 | 55 | @RequestMapping(value = "/ip", method = RequestMethod.GET) 56 | @ResponseBody 57 | public Map ip(@RequestHeader MultiValueMap headers, 58 | HttpServletRequest httpServletRequest) { 59 | 60 | Map serviceOneResult = new HashMap<>(); 61 | 62 | serviceOneResult.put("server.forward-headers-strategy",context.getEnvironment().getProperty("server.forward-headers-strategy")); 63 | serviceOneResult.put("x-forwarded-For",headers.getFirst("x-forwarded-for")); 64 | serviceOneResult.put("forwarded",headers.getFirst("forwarded")); 65 | serviceOneResult.put("httpServletRequest.getRequestURI()",httpServletRequest.getRequestURI()); 66 | serviceOneResult.put("httpServletRequest.getRemoteAddr()",httpServletRequest.getRemoteAddr()); 67 | 68 | return serviceOneResult; 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /service-one/src/main/java/com/mynotes/microservices/demo/serviceone/interceptors/LoggingRequestResponse.java: -------------------------------------------------------------------------------- 1 | package com.mynotes.microservices.demo.serviceone.interceptors; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStreamReader; 6 | import java.nio.charset.StandardCharsets; 7 | import java.util.stream.Collectors; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.springframework.http.HttpRequest; 10 | import org.springframework.http.client.ClientHttpRequestExecution; 11 | import org.springframework.http.client.ClientHttpRequestInterceptor; 12 | import org.springframework.http.client.ClientHttpResponse; 13 | 14 | @Slf4j 15 | public class LoggingRequestResponse implements ClientHttpRequestInterceptor { 16 | 17 | @Override 18 | public ClientHttpResponse intercept( 19 | HttpRequest req, byte[] reqBody, ClientHttpRequestExecution ex) throws IOException { 20 | 21 | log.debug("===========================request begin================================================"); 22 | log.debug("URI : {}", req.getURI()); 23 | log.debug("Method : {}", req.getMethod()); 24 | log.debug("Headers : {}", req.getHeaders() ); 25 | log.debug("Request body: {}", new String(reqBody, "UTF-8")); 26 | log.debug("==========================request end================================================"); 27 | ClientHttpResponse response = ex.execute(req, reqBody); 28 | InputStreamReader isr = new InputStreamReader( 29 | response.getBody(), StandardCharsets.UTF_8); 30 | String body = new BufferedReader(isr).lines() 31 | .collect(Collectors.joining("\n")); 32 | log.info("============================response begin=========================================="); 33 | log.debug("Status code : {}", response.getStatusCode()); 34 | log.debug("Status text : {}", response.getStatusText()); 35 | log.debug("Headers : {}", response.getHeaders()); 36 | log.debug("Response body: {}", body); 37 | log.info("=======================response end================================================="); 38 | return response; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /service-one/src/main/java/com/mynotes/microservices/demo/serviceone/interceptors/RestTemplateCopyHeaders.java: -------------------------------------------------------------------------------- 1 | package com.mynotes.microservices.demo.serviceone.interceptors; 2 | 3 | import jakarta.servlet.http.HttpServletRequest; 4 | import java.io.IOException; 5 | import java.util.Enumeration; 6 | import java.util.Optional; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.http.HttpRequest; 9 | import org.springframework.http.client.ClientHttpRequestExecution; 10 | import org.springframework.http.client.ClientHttpRequestInterceptor; 11 | import org.springframework.http.client.ClientHttpResponse; 12 | import org.springframework.web.context.request.RequestContextHolder; 13 | import org.springframework.web.context.request.ServletRequestAttributes; 14 | 15 | 16 | @Slf4j 17 | public class RestTemplateCopyHeaders implements ClientHttpRequestInterceptor { 18 | @Override 19 | public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { 20 | Optional incomingRequest = getCurrentHttpRequest(); 21 | if(incomingRequest.isPresent()){ 22 | Enumeration headers = incomingRequest.get().getHeaderNames(); 23 | while (headers.hasMoreElements()){ 24 | String originalRequestHeader = headers.nextElement(); 25 | String originalRequestHeaderValue = incomingRequest.get().getHeader(originalRequestHeader); 26 | log.info("Original request header={} with value={}",originalRequestHeader,originalRequestHeaderValue); 27 | if(!request.getHeaders().containsKey(originalRequestHeader)){ 28 | log.info("Setting request header={} with value={} to new request",originalRequestHeader,originalRequestHeaderValue); 29 | request.getHeaders().add(originalRequestHeader, originalRequestHeaderValue); 30 | } 31 | } 32 | } 33 | return execution.execute(request,body); 34 | 35 | } 36 | 37 | 38 | private Optional getCurrentHttpRequest() { 39 | return 40 | Optional.ofNullable( 41 | RequestContextHolder.getRequestAttributes() 42 | ) 43 | .filter(ServletRequestAttributes.class::isInstance) 44 | .map(ServletRequestAttributes.class::cast) 45 | .map(ServletRequestAttributes::getRequest); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /service-one/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dhananjay12/spring-microservice-demo/e1dec03a50af3eb9e81a079b3cf9a892e6437be4/service-one/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports -------------------------------------------------------------------------------- /service-one/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | 2 | management: 3 | 4 | endpoints: 5 | web: 6 | exposure: 7 | include: '*' 8 | 9 | eureka: 10 | client: 11 | enabled: true 12 | serviceUrl: 13 | defaultZone: http://localhost:8080/eureka/ 14 | 15 | spring: 16 | main: 17 | banner-mode: off 18 | application: 19 | name: service-one 20 | cloud: 21 | kubernetes: 22 | enabled: false 23 | reload: 24 | enabled: false 25 | config: 26 | enabled: false -------------------------------------------------------------------------------- /service-one/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | #Disable kubernetes initially. https://docs.spring.io/spring-cloud-kubernetes/docs/current/reference/html/index.html#kubernetes-ecosystem-awareness 2 | spring: 3 | cloud: 4 | kubernetes: 5 | enabled: false 6 | config: 7 | enabled: false 8 | secrets: 9 | enabled: false -------------------------------------------------------------------------------- /service-two/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.mynotes.microservices.demo 7 | service-two 8 | service-two 9 | jar 10 | Demo project for Spring 11 | 12 | 13 | org.springframework.boot 14 | spring-boot-starter-parent 15 | 3.0.2 16 | 17 | 18 | 19 | 20 | 21 | 22 | org.springframework.cloud 23 | spring-cloud-dependencies 24 | ${spring-cloud.version} 25 | pom 26 | import 27 | 28 | 29 | 30 | 31 | 32 | UTF-8 33 | UTF-8 34 | 17 35 | 2022.0.1 36 | false 37 | false 38 | dhananjay12 39 | demo 40 | 41 | 42 | 43 | 44 | 45 | org.springframework.boot 46 | spring-boot-starter-web 47 | 48 | 49 | io.micrometer 50 | micrometer-tracing-bridge-brave 51 | 52 | 53 | io.zipkin.reporter2 54 | zipkin-reporter-brave 55 | 56 | 57 | org.springframework.boot 58 | spring-boot-starter-actuator 59 | 60 | 61 | io.micrometer 62 | micrometer-registry-prometheus 63 | runtime 64 | 65 | 66 | org.springframework.cloud 67 | spring-cloud-starter-kubernetes-fabric8-all 68 | 69 | 70 | org.springframework.cloud 71 | spring-cloud-starter-loadbalancer 72 | 73 | 74 | org.springframework.cloud 75 | spring-cloud-starter-netflix-eureka-client 76 | 77 | 78 | org.projectlombok 79 | lombok 80 | true 81 | 82 | 83 | 84 | 85 | ${project.artifactId} 86 | 87 | 88 | org.springframework.boot 89 | spring-boot-maven-plugin 90 | 91 | 92 | org.apache.maven.plugins 93 | maven-surefire-plugin 94 | 95 | 96 | ${surefireArgLine} 97 | ${skip.unit.tests} 98 | 99 | 100 | **/*IT.java 101 | **/*It.java 102 | 103 | 104 | 105 | 106 | 107 | org.apache.maven.plugins 108 | maven-failsafe-plugin 109 | 110 | 111 | ${failsafeArgLine} 112 | ${skip.integration.tests} 113 | 114 | **/*It.java 115 | **/*IT.java 116 | 117 | 118 | 119 | 120 | pl.project13.maven 121 | git-commit-id-plugin 122 | 4.0.0 123 | 124 | 125 | get-the-git-infos 126 | initialize 127 | 128 | revision 129 | 130 | 131 | 132 | 133 | false 134 | yyyy-MM-dd'T'HH:mm:ss'Z' 135 | GMT 136 | 137 | ^git.commit.time$ 138 | 139 | 140 | 141 | 142 | com.google.cloud.tools 143 | jib-maven-plugin 144 | 2.6.0 145 | 146 | 147 | eclipse-temurin:17-jre-alpine 148 | 149 | 150 | ${docker.image.url}/${docker.image.prefix}-${project.artifactId} 151 | 152 | ${project.version} 153 | 154 | 155 | 156 | ${git.commit.time} 157 | ${git.commit.time} 158 | com.mynotes.microservices.demo.servicetwo.ServiceTwoApplication 159 | 160 | 8080 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | -------------------------------------------------------------------------------- /service-two/src/main/java/com/mynotes/microservices/demo/servicetwo/ServiceTwoApplication.java: -------------------------------------------------------------------------------- 1 | package com.mynotes.microservices.demo.servicetwo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 6 | 7 | @SpringBootApplication 8 | @EnableDiscoveryClient 9 | public class ServiceTwoApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(ServiceTwoApplication.class, args); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /service-two/src/main/java/com/mynotes/microservices/demo/servicetwo/ServiceTwoController.java: -------------------------------------------------------------------------------- 1 | package com.mynotes.microservices.demo.servicetwo; 2 | 3 | import jakarta.servlet.http.HttpServletRequest; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | import java.util.stream.Collectors; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.context.ApplicationContext; 10 | import org.springframework.http.HttpStatus; 11 | import org.springframework.http.ResponseEntity; 12 | import org.springframework.util.MultiValueMap; 13 | import org.springframework.web.bind.annotation.GetMapping; 14 | import org.springframework.web.bind.annotation.PathVariable; 15 | import org.springframework.web.bind.annotation.RequestHeader; 16 | import org.springframework.web.bind.annotation.RequestMapping; 17 | import org.springframework.web.bind.annotation.RequestMethod; 18 | import org.springframework.web.bind.annotation.ResponseBody; 19 | import org.springframework.web.bind.annotation.RestController; 20 | 21 | 22 | 23 | @RestController 24 | @Slf4j 25 | public class ServiceTwoController { 26 | 27 | @Autowired 28 | ApplicationContext context; 29 | 30 | @RequestMapping(value = "/status/{status}", method = RequestMethod.GET) 31 | public ResponseEntity status(@PathVariable("status") Integer status) { 32 | return ResponseEntity.status(status).body("response from service two"); 33 | } 34 | 35 | @RequestMapping(value = "/hello", method = RequestMethod.GET) 36 | @ResponseBody 37 | public String hello() { 38 | return "Hello from service two"; 39 | } 40 | 41 | @RequestMapping(value = "/message/{text}", method = RequestMethod.GET) 42 | public ResponseEntity text(@PathVariable("text") String text) { 43 | log.info(text); 44 | log.warn(text); 45 | log.debug(text); 46 | text = text + "+++++Service two"; 47 | return ResponseEntity.status(HttpStatus.OK).body(text); 48 | 49 | } 50 | 51 | @RequestMapping(value = "/calculate/divide/{a}/{b}", method = RequestMethod.GET) 52 | @ResponseBody 53 | public String divide(@PathVariable("a") Integer a, @PathVariable("b") Integer b) { 54 | 55 | log.debug("Passed values {}/{}", a, b); 56 | String result; 57 | try{ 58 | int c = a / b; 59 | result= String.valueOf(c); 60 | }catch (Exception e){ 61 | e.printStackTrace(); 62 | result = "Error"; 63 | log.error("Error in division"); 64 | } 65 | return result; 66 | } 67 | 68 | @GetMapping("/headers") 69 | @ResponseBody 70 | public Map headers(@RequestHeader MultiValueMap headers) { 71 | 72 | Map map = new HashMap<>(); 73 | 74 | log.info("server.forward-headers-strategy = "+ context.getEnvironment().getProperty("server.forward-headers-strategy")); 75 | 76 | headers.forEach((key, value) -> { 77 | log.info(String.format("Header '%s' = %s", key, value.stream().collect(Collectors.joining("|")))); 78 | map.put(key, value.stream().collect(Collectors.joining("|"))); 79 | }); 80 | 81 | return map; 82 | } 83 | 84 | @RequestMapping(value = "/ip", method = RequestMethod.GET) 85 | @ResponseBody 86 | public Map ip(@RequestHeader MultiValueMap headers, 87 | HttpServletRequest httpServletRequest) { 88 | 89 | Map serviceOneResult = new HashMap<>(); 90 | 91 | serviceOneResult.put("server.forward-headers-strategy",context.getEnvironment().getProperty("server.forward-headers-strategy")); 92 | serviceOneResult.put("x-forwarded-For",headers.getFirst("x-forwarded-for")); 93 | serviceOneResult.put("forwarded",headers.getFirst("forwarded")); 94 | serviceOneResult.put("httpServletRequest.getRequestURI()",httpServletRequest.getRequestURI()); 95 | serviceOneResult.put("httpServletRequest.getRemoteAddr()",httpServletRequest.getRemoteAddr()); 96 | 97 | return serviceOneResult; 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /service-two/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | management: 2 | endpoints: 3 | web: 4 | exposure: 5 | include: '*' 6 | 7 | eureka: 8 | client: 9 | enabled: true 10 | serviceUrl: 11 | defaultZone: http://localhost:8761/eureka/ 12 | 13 | spring: 14 | main: 15 | banner-mode: off 16 | application: 17 | name: service-two 18 | cloud: 19 | kubernetes: 20 | enabled: false 21 | config: 22 | enabled: false 23 | reload: 24 | enabled: false 25 | zipkin: 26 | enabled: false -------------------------------------------------------------------------------- /service-two/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | #Disable kubernetes initially. https://docs.spring.io/spring-cloud-kubernetes/docs/current/reference/html/index.html#kubernetes-ecosystem-awareness 2 | spring: 3 | cloud: 4 | kubernetes: 5 | enabled: false 6 | config: 7 | enabled: false 8 | secrets: 9 | enabled: false -------------------------------------------------------------------------------- /tracing-zipkins/README.md: -------------------------------------------------------------------------------- 1 | ## Services 2 | 3 | All java images have port `8080` and exposed on different ports. 4 | 5 | Frontend - http://localhost 6 | 7 | Eureka - http://localhost:8761/ 8 | 9 | Gateway routes - http://localhost:8080/actuator/gateway/routes 10 | 11 | ## Zipkin Dashboard 12 | 13 | By default sending spans to Zipkin is disabled in service. We enabled it and added the server url in the common 14 | properties of `docker-compose.yml` file. Also, we increased the sampling probability to 1 which means it will send 15 | all the spans to Zipkins. 16 | 17 | Dashboard - http://localhost:9411/zipkin/ 18 | -------------------------------------------------------------------------------- /tracing-zipkins/config/frontend/nginx.conf: -------------------------------------------------------------------------------- 1 | events{ 2 | 3 | } 4 | http { 5 | 6 | types { 7 | module js; 8 | } 9 | 10 | include /etc/nginx/mime.types; 11 | 12 | upstream gateway { 13 | server gateway:8080; 14 | } 15 | 16 | server { 17 | 18 | location /api { 19 | proxy_pass http://gateway; 20 | } 21 | 22 | location /websocketservice { 23 | proxy_pass http://gateway; 24 | } 25 | 26 | location / { 27 | root /usr/share/nginx/html; 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /tracing-zipkins/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.7" 2 | 3 | x-common-variables: &common-variables 4 | EUREKA_CLIENT_SERVICEURL_DEFAULTZONE: http://eureka:8080/eureka 5 | EUREKA_INSTANCE_NONSECUREPORT: 8080 6 | SPRING_ZIPKIN_ENABLED: 'false' 7 | # SPRING_ZIPKIN_BASEURL: 'http://zipkin:9411' 8 | #management.tracing.sampling.probability: "1.0" 9 | #management.zipkin.tracing.endpoint: "http://zipkin:9411/api/v2/spans" 10 | #traceID and spanId are predefined MDC keys - we want the logs to include them 11 | logging.pattern.level: "%5p [%cn,%X{traceId:-},%X{spanId:-}]" 12 | services: 13 | eureka: 14 | image: "dhananjay12/demo-eureka-server:latest" 15 | environment: 16 | <<: *common-variables 17 | ports: 18 | - "8761:8080" 19 | frontend: 20 | image: "dhananjay12/demo-frontend:latest" 21 | volumes: 22 | - ./config/frontend/nginx.conf:/etc/nginx/nginx.conf 23 | ports: 24 | - "80:80" 25 | gateway: 26 | image: "dhananjay12/demo-gateway:latest" 27 | environment: 28 | <<: *common-variables 29 | #spring.cloud.kubernetes.enabled: "false" 30 | #spring.cloud.kubernetes.leader.auto-startup: "false" 31 | #spring.cloud.kubernetes.leader.enabled: "false" 32 | #spring.cloud.kubernetes.secrets.enabled: "false" 33 | #spring.cloud.kubernetes.discovery.enabled: "false" 34 | #spring.cloud.kubernetes.config.enabled: "false" 35 | #spring.cloud.kubernetes.config.enable-api: "false" 36 | #spring.cloud.bootstrap.enabled: "false" 37 | #spring.sleuth.reactor.instrumentation-type: DECORATE_QUEUES 38 | ports: 39 | - "8080:8080" 40 | service-one: 41 | image: "dhananjay12/demo-service-one:latest" 42 | environment: 43 | <<: *common-variables 44 | #Required if using old version like 2.7.7 45 | #spring.sleuth.propagation.type: w3c,b3 46 | #SPRING_ZIPKIN_BASEURL: 'http://zipkin:9411' 47 | #SPRING_ZIPKIN_ENABLED: "false" 48 | ports: 49 | - "8100:8080" 50 | service-two: 51 | image: "dhananjay12/demo-service-two:latest" 52 | environment: 53 | <<: *common-variables 54 | ports: 55 | - "8200:8080" 56 | zipkin: 57 | container_name: zipkin 58 | image: openzipkin/zipkin 59 | environment: 60 | - STORAGE_TYPE=mem 61 | ports: 62 | - "9411:9411" 63 | -------------------------------------------------------------------------------- /tracing-zipkins/old/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.7" 2 | 3 | x-common-variables: &common-variables 4 | EUREKA_CLIENT_SERVICEURL_DEFAULTZONE: http://eureka:8080/eureka 5 | EUREKA_INSTANCE_NONSECUREPORT: 8080 6 | SPRING_ZIPKIN_ENABLED: 'true' 7 | SPRING_ZIPKIN_BASEURL: 'http://zipkin:9411' 8 | spring.sleuth.sampler.percentage: "1.0" 9 | logging.pattern.level: "%5p [%cn,%X{traceId:-},%X{spanId:-}]" 10 | services: 11 | eureka: 12 | image: "dhananjay12/demo-eureka-server:2.7.7" 13 | environment: 14 | <<: *common-variables 15 | ports: 16 | - "8761:8080" 17 | gateway: 18 | image: "dhananjay12/demo-gateway:2.7.7" 19 | environment: 20 | <<: *common-variables 21 | #spring.cloud.kubernetes.enabled: "false" 22 | #spring.cloud.kubernetes.leader.auto-startup: "false" 23 | #spring.cloud.kubernetes.leader.enabled: "false" 24 | #spring.cloud.kubernetes.secrets.enabled: "false" 25 | #spring.cloud.kubernetes.discovery.enabled: "false" 26 | #spring.cloud.kubernetes.config.enabled: "false" 27 | #spring.cloud.kubernetes.config.enable-api: "false" 28 | #spring.cloud.bootstrap.enabled: "false" 29 | #spring.sleuth.reactor.instrumentation-type: DECORATE_QUEUES 30 | ports: 31 | - "8080:8080" 32 | service-one: 33 | image: "dhananjay12/demo-service-one:2.7.7" 34 | environment: 35 | <<: *common-variables 36 | ports: 37 | - "8100:8080" 38 | service-two: 39 | image: "dhananjay12/demo-service-two:2.7.7" 40 | environment: 41 | <<: *common-variables 42 | ports: 43 | - "8200:8080" 44 | zipkin: 45 | container_name: zipkin 46 | image: openzipkin/zipkin 47 | environment: 48 | - STORAGE_TYPE=mem 49 | ports: 50 | - "9411:9411" 51 | -------------------------------------------------------------------------------- /uml-diagrams/forwarded-for/ip-with-forward-headers-stratergy-empty.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | title server.forward-headers-strategy=null for Service 1/2 3 | actor "Browser/Postman" as B 4 | participant "Proxy" as P #Teal 5 | participant "Gateway" as G #SkyBlue 6 | participant "Service One" as S1 #Orange 7 | participant "Service Two" as S2 #Yellow 8 | 9 | B -> P : /api/service-one/url 10 | P -> G : /api/service-one/url 11 | note right of P #Teal 12 | X-Forwarded-For: 192.168.65.3 13 | X-Real-IP: 192.168.65.3 14 | end note 15 | G -> S1: /url 16 | note right of G #SkyBlue 17 | X-Forwarded-For: 192.168.65.3,192.168.6.3 18 | X-Real-IP: 192.1.68.65.3 19 | Forwarded: proto=http;host=kubernetes.docker.internal;for="192.168.65.3:44722" 20 | end note 21 | 22 | note over S1 #Orange 23 | In controller requestObject: 24 | "getRemoteAddr()": "192.168.65.3", 25 | "x-forwarded-For": null, 26 | "forwarded": "proto=http;host=kubernetes.docker.internal;for="192.168.65.3:44722"" 27 | end note 28 | S1 -> S2: /some-other-url 29 | note right of S1 #Orange 30 | X-Real-IP: 192.1.68.65.3 31 | Forwarded: proto=http;host=kubernetes.docker.internal;for="192.168.65.3:44722" 32 | end note 33 | note over S2 #Yellow 34 | In controller requestObject: 35 | "getRemoteAddr()": "10.1.6.246", 36 | "x-forwarded-For": null, 37 | "forwarded": "proto=http;host=kubernetes.docker.internal;for="192.168.65.3:44722"" 38 | end note 39 | @enduml -------------------------------------------------------------------------------- /uml-diagrams/forwarded-for/ip-with-forward-headers-stratergy-framework.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | title server.forward-headers-strategy=FRAMEWORK for Service 1/2 3 | actor "Browser/Postman" as B 4 | participant "Proxy" as P #Teal 5 | participant "Gateway" as G #SkyBlue 6 | participant "Service One" as S1 #Orange 7 | participant "Service Two" as S2 #Yellow 8 | 9 | B -> P : /api/service-one/url 10 | P -> G : /api/service-one/url 11 | note right of P #Teal 12 | X-Forwarded-For: 192.168.65.3 13 | X-Real-IP: 192.168.65.3 14 | end note 15 | G -> S1: /url 16 | note right of G #SkyBlue 17 | X-Forwarded-For: 192.168.65.3,192.168.6.3 18 | X-Real-IP: 192.1.68.65.3 19 | Forwarded: proto=http;host=kubernetes.docker.internal;for="192.168.65.3:56956" 20 | end note 21 | note over S1 #Orange 22 | In controller requestObject: 23 | "getRemoteAddr()": "192.168.65.3", 24 | "x-forwarded-For": null, 25 | "forwarded": null 26 | end note 27 | S1 -> S2: /some-other-url 28 | note right of S1 #Orange 29 | X-Real-IP: 192.1.68.65.3 30 | end note 31 | note over S2 #Yellow 32 | In controller requestObject: 33 | "getRemoteAddr()": "10.1.7.8", 34 | "x-forwarded-For": null, 35 | "forwarded": null 36 | end note 37 | @enduml -------------------------------------------------------------------------------- /uml-diagrams/forwarded-for/ip-with-forward-headers-stratergy-native.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | title server.forward-headers-strategy=NATIVE for Service 1/2 3 | actor "Browser/Postman" as B 4 | participant "Proxy" as P #Teal 5 | participant "Gateway" as G #SkyBlue 6 | participant "Service One" as S1 #Orange 7 | participant "Service Two" as S2 #Yellow 8 | 9 | B -> P : /api/service-one/url 10 | P -> G : /api/service-one/url 11 | note right of P #Teal 12 | X-Forwarded-For: 192.168.65.3 13 | X-Real-IP: 192.168.65.3 14 | end note 15 | G -> S1: /url 16 | note right of G #SkyBlue 17 | X-Forwarded-For: 192.168.65.3,192.168.6.3 18 | X-Real-IP: 192.1.68.65.3 19 | Forwarded: proto=http;host=kubernetes.docker.internal;for="192.168.65.3:56956" 20 | end note 21 | note over S1 #Orange 22 | In controller requestObject: 23 | "getRemoteAddr()": "192.168.65.3", 24 | "x-forwarded-For": null, 25 | "forwarded": "proto=http;host=kubernetes.docker.internal;for="192.168.65.3:56956"" 26 | end note 27 | S1 -> S2: /some-other-url 28 | note right of S1 #Orange 29 | X-Real-IP: 192.1.68.65.3 30 | Forwarded: proto=http;host=kubernetes.docker.internal;for="192.168.65.3:56956" 31 | end note 32 | note over S2 #Yellow 33 | In controller requestObject: 34 | "getRemoteAddr()": "10.1.7.4", 35 | "x-forwarded-For": null, 36 | "forwarded": "proto=http;host=kubernetes.docker.internal;for="192.168.65.3:56956"" 37 | end note 38 | @enduml -------------------------------------------------------------------------------- /uml-diagrams/forwarded-for/ip-with-forward-headers-stratergy-none.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | title server.forward-headers-strategy=NONE for Service 1/2 3 | actor "Browser/Postman" as B 4 | participant "Proxy" as P #Teal 5 | participant "Gateway" as G #SkyBlue 6 | participant "Service One" as S1 #Orange 7 | participant "Service Two" as S2 #Yellow 8 | 9 | B -> P : /api/service-one/url 10 | P -> G : /api/service-one/url 11 | note right of P #Teal 12 | X-Forwarded-For: 192.168.65.3 13 | X-Real-IP: 192.168.65.3 14 | end note 15 | G -> S1: /url 16 | note right of G #SkyBlue 17 | X-Forwarded-For: 192.168.65.3,192.168.6.3 18 | X-Real-IP: 192.1.68.65.3 19 | Forwarded: proto=http;host=kubernetes.docker.internal;for="192.168.65.3:39238" 20 | end note 21 | 22 | note over S1 #Orange 23 | In controller requestObject: 24 | "getRemoteAddr()": "10.1.7.1", 25 | "x-forwarded-For": "192.168.65.3,192.168.65.3", 26 | "forwarded": "proto=http;host=kubernetes.docker.internal;for="192.168.65.3:39238"" 27 | end note 28 | S1 -> S2: /some-other-url 29 | note right of S1 #Orange 30 | X-Real-IP: 192.1.68.65.3 31 | x-forwarded-for: "192.168.65.3,192.168.65.3" 32 | Forwarded: proto=http;host=kubernetes.docker.internal;for="192.168.65.3:39238" 33 | end note 34 | note over S2 #Yellow 35 | In controller requestObject: 36 | "getRemoteAddr()": "10.1.6.255", 37 | "x-forwarded-For": "192.168.65.3,192.168.65.3", 38 | "forwarded": "proto=http;host=kubernetes.docker.internal;for="192.168.65.3:39238"" 39 | end note 40 | @enduml -------------------------------------------------------------------------------- /websocket-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.mynotes.microservices.demo 7 | websocket-service 8 | websocket-service 9 | Demo project for Spring Boot 10 | 11 | 12 | org.springframework.boot 13 | spring-boot-starter-parent 14 | 2.3.6.RELEASE 15 | 16 | 17 | 18 | 19 | UTF-8 20 | UTF-8 21 | 11 22 | Hoxton.SR9 23 | false 24 | false 25 | dhananjay12 26 | demo 27 | 28 | 29 | 30 | 31 | 32 | org.springframework.cloud 33 | spring-cloud-dependencies 34 | ${spring-cloud.version} 35 | pom 36 | import 37 | 38 | 39 | 40 | 41 | 42 | 43 | org.springframework.boot 44 | spring-boot-starter-websocket 45 | 46 | 47 | 51 | 52 | org.springframework.boot 53 | spring-boot-starter-actuator 54 | 55 | 56 | io.micrometer 57 | micrometer-registry-prometheus 58 | 59 | 60 | org.springframework.cloud 61 | spring-cloud-starter-kubernetes 62 | 63 | 64 | org.springframework.cloud 65 | spring-cloud-starter-loadbalancer 66 | 67 | 68 | org.springframework.cloud 69 | spring-cloud-starter-netflix-eureka-client 70 | 71 | 72 | org.springframework.cloud 73 | spring-cloud-starter-netflix-archaius 74 | 75 | 76 | org.springframework.cloud 77 | spring-cloud-starter-netflix-ribbon 78 | 79 | 80 | com.netflix.ribbon 81 | ribbon-eureka 82 | 83 | 84 | 85 | 86 | org.projectlombok 87 | lombok 88 | true 89 | 90 | 91 | 92 | org.webjars 93 | webjars-locator-core 94 | 95 | 96 | org.webjars 97 | sockjs-client 98 | 1.0.2 99 | 100 | 101 | org.webjars 102 | stomp-websocket 103 | 2.3.3 104 | 105 | 106 | org.webjars 107 | bootstrap 108 | 3.3.7 109 | 110 | 111 | org.webjars 112 | jquery 113 | 3.1.1 114 | 115 | 116 | 117 | org.springframework.boot 118 | spring-boot-starter-test 119 | test 120 | 121 | 122 | 123 | 124 | ${project.artifactId} 125 | 126 | 127 | org.springframework.boot 128 | spring-boot-maven-plugin 129 | 130 | 131 | org.apache.maven.plugins 132 | maven-surefire-plugin 133 | 134 | 135 | 136 | ${surefireArgLine} 137 | ${skip.unit.tests} 138 | 139 | 140 | **/*IT.java 141 | **/*It.java 142 | 143 | 144 | 145 | 146 | 147 | org.apache.maven.plugins 148 | maven-failsafe-plugin 149 | 150 | 151 | 152 | ${failsafeArgLine} 153 | ${skip.integration.tests} 154 | 155 | **/*It.java 156 | **/*IT.java 157 | 158 | 159 | 160 | 161 | pl.project13.maven 162 | git-commit-id-plugin 163 | 4.0.0 164 | 165 | 166 | get-the-git-infos 167 | initialize 168 | 169 | revision 170 | 171 | 172 | 173 | 174 | false 175 | yyyy-MM-dd'T'HH:mm:ss'Z' 176 | GMT 177 | 178 | ^git.commit.time$ 179 | 180 | 181 | 182 | 183 | com.google.cloud.tools 184 | jib-maven-plugin 185 | 2.6.0 186 | 187 | 188 | openjdk:11.0.5-jre 189 | 190 | 191 | ${docker.image.url}/${docker.image.prefix}-${project.artifactId} 192 | 193 | ${project.version} 194 | 195 | 196 | 197 | ${git.commit.time} 198 | ${git.commit.time} 199 | com.mynotes.microservices.demo.websocket.WebsocketServiceApplication 200 | 201 | 8080 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | -------------------------------------------------------------------------------- /websocket-service/src/main/java/com/mynotes/microservices/demo/websocket/Greeting.java: -------------------------------------------------------------------------------- 1 | package com.mynotes.microservices.demo.websocket; 2 | 3 | public class Greeting { 4 | 5 | private String content; 6 | 7 | public Greeting() { 8 | } 9 | 10 | public Greeting(String content) { 11 | this.content = content; 12 | } 13 | 14 | public String getContent() { 15 | return content; 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /websocket-service/src/main/java/com/mynotes/microservices/demo/websocket/GreetingController.java: -------------------------------------------------------------------------------- 1 | package com.mynotes.microservices.demo.websocket; 2 | 3 | import org.springframework.messaging.handler.annotation.MessageMapping; 4 | import org.springframework.messaging.handler.annotation.SendTo; 5 | import org.springframework.stereotype.Controller; 6 | import org.springframework.web.util.HtmlUtils; 7 | 8 | @Controller 9 | public class GreetingController { 10 | 11 | 12 | @MessageMapping("/hello") 13 | @SendTo("/topic/greetings") 14 | public Greeting greeting(HelloMessage message) throws Exception { 15 | Thread.sleep(1000); // simulated delay 16 | return new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!"); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /websocket-service/src/main/java/com/mynotes/microservices/demo/websocket/HelloMessage.java: -------------------------------------------------------------------------------- 1 | package com.mynotes.microservices.demo.websocket; 2 | 3 | public class HelloMessage { 4 | 5 | private String name; 6 | 7 | public HelloMessage() { 8 | } 9 | 10 | public HelloMessage(String name) { 11 | this.name = name; 12 | } 13 | 14 | public String getName() { 15 | return name; 16 | } 17 | 18 | public void setName(String name) { 19 | this.name = name; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /websocket-service/src/main/java/com/mynotes/microservices/demo/websocket/WebSocketConfig.java: -------------------------------------------------------------------------------- 1 | package com.mynotes.microservices.demo.websocket; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.messaging.simp.config.MessageBrokerRegistry; 5 | import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer; 6 | import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; 7 | import org.springframework.web.socket.config.annotation.StompEndpointRegistry; 8 | import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; 9 | 10 | @Configuration 11 | @EnableWebSocketMessageBroker 12 | public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { 13 | 14 | @Override 15 | public void configureMessageBroker(MessageBrokerRegistry config) { 16 | config.enableSimpleBroker("/topic"); 17 | config.setApplicationDestinationPrefixes("/app"); 18 | } 19 | 20 | @Override 21 | public void registerStompEndpoints(StompEndpointRegistry registry) { 22 | registry.addEndpoint("/gs-guide-websocket").setAllowedOrigins("*").withSockJS(); 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /websocket-service/src/main/java/com/mynotes/microservices/demo/websocket/WebsocketServiceApplication.java: -------------------------------------------------------------------------------- 1 | package com.mynotes.microservices.demo.websocket; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class WebsocketServiceApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(WebsocketServiceApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /websocket-service/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | management: 2 | endpoints: 3 | web: 4 | exposure: 5 | include: '*' 6 | 7 | eureka: 8 | client: 9 | enabled: true 10 | serviceUrl: 11 | defaultZone: http://localhost:8080/eureka/ 12 | 13 | spring: 14 | main: 15 | banner-mode: off 16 | application: 17 | name: websocketservice 18 | sleuth: 19 | integration: 20 | websockets: 21 | enabled: false 22 | autoconfigure: 23 | exclude: org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration 24 | cloud: 25 | bootstrap: 26 | enabled: true 27 | kubernetes: 28 | enabled: false 29 | reload: 30 | enabled: false -------------------------------------------------------------------------------- /websocket-service/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: websocketservice -------------------------------------------------------------------------------- /websocket-service/src/main/resources/static/app.js: -------------------------------------------------------------------------------- 1 | var stompClient = null; 2 | 3 | function setConnected(connected) { 4 | $("#connect").prop("disabled", connected); 5 | $("#disconnect").prop("disabled", !connected); 6 | if (connected) { 7 | $("#conversation").show(); 8 | } 9 | else { 10 | $("#conversation").hide(); 11 | } 12 | $("#greetings").html(""); 13 | } 14 | 15 | function connect() { 16 | var socket = new SockJS('/gs-guide-websocket'); 17 | stompClient = Stomp.over(socket); 18 | stompClient.connect({}, function (frame) { 19 | setConnected(true); 20 | console.log('Connected: ' + frame); 21 | stompClient.subscribe('/topic/greetings', function (greeting) { 22 | showGreeting(JSON.parse(greeting.body).content); 23 | }); 24 | }); 25 | } 26 | 27 | function disconnect() { 28 | if (stompClient !== null) { 29 | stompClient.disconnect(); 30 | } 31 | setConnected(false); 32 | console.log("Disconnected"); 33 | } 34 | 35 | function sendName() { 36 | stompClient.send("/app/hello", {}, JSON.stringify({'name': $("#name").val()})); 37 | } 38 | 39 | function showGreeting(message) { 40 | $("#greetings").append("" + message + ""); 41 | } 42 | 43 | $(function () { 44 | $("form").on('submit', function (e) { 45 | e.preventDefault(); 46 | }); 47 | $( "#connect" ).click(function() { connect(); }); 48 | $( "#disconnect" ).click(function() { disconnect(); }); 49 | $( "#send" ).click(function() { sendName(); }); 50 | }); 51 | 52 | -------------------------------------------------------------------------------- /websocket-service/src/main/resources/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Hello WebSocket 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 16 |
17 |
18 |
19 |
20 |
21 | 22 | 23 | 25 |
26 |
27 |
28 |
29 |
30 |
31 | 32 | 33 |
34 | 35 |
36 |
37 |
38 |
39 |
40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
Greetings
49 |
50 |
51 |
52 | 53 | 54 | -------------------------------------------------------------------------------- /websocket-service/src/main/resources/static/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #f5f5f5; 3 | } 4 | 5 | #main-content { 6 | max-width: 940px; 7 | padding: 2em 3em; 8 | margin: 0 auto 20px; 9 | background-color: #fff; 10 | border: 1px solid #e5e5e5; 11 | -webkit-border-radius: 5px; 12 | -moz-border-radius: 5px; 13 | border-radius: 5px; 14 | } --------------------------------------------------------------------------------