[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-4025":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":16,"stars7d":17,"stars30d":18,"stars90d":16,"forks30d":16,"starsTrendScore":19,"compositeScore":20,"rankGlobal":10,"rankLanguage":10,"license":21,"archived":22,"fork":22,"defaultBranch":23,"hasWiki":22,"hasPages":22,"topics":24,"createdAt":10,"pushedAt":10,"updatedAt":30,"readmeContent":31,"aiSummary":32,"trendingCount":16,"starSnapshotCount":16,"syncStatus":33,"lastSyncTime":34,"discoverSource":35},4025,"piggymetrics","sqshq\u002Fpiggymetrics","sqshq","Microservice Architecture with Spring Boot, Spring Cloud and Docker","",null,"Java",13935,6391,805,27,0,8,35,3,45,"MIT License",false,"master",[25,26,27,28,29],"docker","microservice","microservices","spring-boot","spring-cloud","2026-06-12 02:00:57","[![Build Status](https:\u002F\u002Ftravis-ci.org\u002Fsqshq\u002FPiggyMetrics.svg?branch=master)](https:\u002F\u002Ftravis-ci.org\u002Fsqshq\u002FPiggyMetrics)\n[![codecov.io](https:\u002F\u002Fcodecov.io\u002Fgithub\u002Fsqshq\u002FPiggyMetrics\u002Fcoverage.svg?branch=master)](https:\u002F\u002Fcodecov.io\u002Fgithub\u002Fsqshq\u002FPiggyMetrics?branch=master)\n[![GitHub license](https:\u002F\u002Fimg.shields.io\u002Fgithub\u002Flicense\u002Fmashape\u002Fapistatus.svg)](https:\u002F\u002Fgithub.com\u002Fsqshq\u002FPiggyMetrics\u002Fblob\u002Fmaster\u002FLICENCE)\n[![Join the chat at https:\u002F\u002Fgitter.im\u002Fsqshq\u002FPiggyMetrics](https:\u002F\u002Fbadges.gitter.im\u002Fsqshq\u002FPiggyMetrics.svg)](https:\u002F\u002Fgitter.im\u002Fsqshq\u002FPiggyMetrics?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)\n\n# Piggy Metrics\n\nPiggy Metrics is a simple financial advisor app built to demonstrate the [Microservice Architecture Pattern](http:\u002F\u002Fmartinfowler.com\u002Fmicroservices\u002F) using Spring Boot, Spring Cloud and Docker. The project is intended as a tutorial, but you are welcome to fork it and turn it into something else!\n\n\u003Cbr>\n\n![](https:\u002F\u002Fcloud.githubusercontent.com\u002Fassets\u002F6069066\u002F13864234\u002F442d6faa-ecb9-11e5-9929-34a9539acde0.png)\n![Piggy Metrics](https:\u002F\u002Fcloud.githubusercontent.com\u002Fassets\u002F6069066\u002F13830155\u002F572e7552-ebe4-11e5-918f-637a49dff9a2.gif)\n\n## Functional services\n\nPiggy Metrics is decomposed into three core microservices. All of them are independently deployable applications organized around certain business domains.\n\n\u003Cimg width=\"880\" alt=\"Functional services\" src=\"https:\u002F\u002Fcloud.githubusercontent.com\u002Fassets\u002F6069066\u002F13900465\u002F730f2922-ee20-11e5-8df0-e7b51c668847.png\">\n\n#### Account service\nContains general input logic and validation: incomes\u002Fexpenses items, savings and account settings.\n\nMethod\t| Path\t| Description\t| User authenticated\t| Available from UI\n------------- | ------------------------- | ------------- |:-------------:|:----------------:|\nGET\t| \u002Faccounts\u002F{account}\t| Get specified account data\t|  | \t\nGET\t| \u002Faccounts\u002Fcurrent\t| Get current account data\t| × | ×\nGET\t| \u002Faccounts\u002Fdemo\t| Get demo account data (pre-filled incomes\u002Fexpenses items, etc)\t|   | \t×\nPUT\t| \u002Faccounts\u002Fcurrent\t| Save current account data\t| × | ×\nPOST\t| \u002Faccounts\u002F\t| Register new account\t|   | ×\n\n\n#### Statistics service\nPerforms calculations on major statistics parameters and captures time series for each account. Datapoint contains values normalized to base currency and time period. This data is used to track cash flow dynamics during the account lifetime.\n\nMethod\t| Path\t| Description\t| User authenticated\t| Available from UI\n------------- | ------------------------- | ------------- |:-------------:|:----------------:|\nGET\t| \u002Fstatistics\u002F{account}\t| Get specified account statistics\t          |  | \t\nGET\t| \u002Fstatistics\u002Fcurrent\t| Get current account statistics\t| × | × \nGET\t| \u002Fstatistics\u002Fdemo\t| Get demo account statistics\t|   | × \nPUT\t| \u002Fstatistics\u002F{account}\t| Create or update time series datapoint for specified account\t|   | \n\n\n#### Notification service\nStores user contact information and notification settings (reminders, backup frequency etc). Scheduled worker collects required information from other services and sends e-mail messages to subscribed customers.\n\nMethod\t| Path\t| Description\t| User authenticated\t| Available from UI\n------------- | ------------------------- | ------------- |:-------------:|:----------------:|\nGET\t| \u002Fnotifications\u002Fsettings\u002Fcurrent\t| Get current account notification settings\t| × | ×\t\nPUT\t| \u002Fnotifications\u002Fsettings\u002Fcurrent\t| Save current account notification settings\t| × | ×\n\n#### Notes\n- Each microservice has its own database, so there is no way to bypass API and access persistence data directly.\n- MongoDB is used as a primary database for each of the services.\n- All services are talking to each other via the Rest API\n\n## Infrastructure\n[Spring cloud](https:\u002F\u002Fspring.io\u002Fprojects\u002Fspring-cloud) provides powerful tools for developers to quickly implement common distributed systems patterns -\n\u003Cimg width=\"880\" alt=\"Infrastructure services\" src=\"https:\u002F\u002Fcloud.githubusercontent.com\u002Fassets\u002F6069066\u002F13906840\u002F365c0d94-eefa-11e5-90ad-9d74804ca412.png\">\n### Config service\n[Spring Cloud Config](http:\u002F\u002Fcloud.spring.io\u002Fspring-cloud-config\u002Fspring-cloud-config.html) is horizontally scalable centralized configuration service for the distributed systems. It uses a pluggable repository layer that currently supports local storage, Git, and Subversion.\n\nIn this project, we are going to use `native profile`, which simply loads config files from the local classpath. You can see `shared` directory in [Config service resources](https:\u002F\u002Fgithub.com\u002Fsqshq\u002FPiggyMetrics\u002Ftree\u002Fmaster\u002Fconfig\u002Fsrc\u002Fmain\u002Fresources). Now, when Notification-service requests its configuration, Config service responses with `shared\u002Fnotification-service.yml` and `shared\u002Fapplication.yml` (which is shared between all client applications).\n\n##### Client side usage\nJust build Spring Boot application with `spring-cloud-starter-config` dependency, autoconfiguration will do the rest.\n\nNow you don't need any embedded properties in your application. Just provide `bootstrap.yml` with application name and Config service url:\n```yml\nspring:\n  application:\n    name: notification-service\n  cloud:\n    config:\n      uri: http:\u002F\u002Fconfig:8888\n      fail-fast: true\n```\n\n##### With Spring Cloud Config, you can change application config dynamically. \nFor example, [EmailService bean](https:\u002F\u002Fgithub.com\u002Fsqshq\u002FPiggyMetrics\u002Fblob\u002Fmaster\u002Fnotification-service\u002Fsrc\u002Fmain\u002Fjava\u002Fcom\u002Fpiggymetrics\u002Fnotification\u002Fservice\u002FEmailServiceImpl.java) is annotated with `@RefreshScope`. That means you can change e-mail text and subject without rebuild and restart the Notification service.\n\nFirst, change required properties in Config server. Then make a refresh call to the Notification service:\n`curl -H \"Authorization: Bearer #token#\" -XPOST http:\u002F\u002F127.0.0.1:8000\u002Fnotifications\u002Frefresh`\n\nYou could also use Repository [webhooks to automate this process](http:\u002F\u002Fcloud.spring.io\u002Fspring-cloud-config\u002Fspring-cloud-config.html#_push_notifications_and_spring_cloud_bus)\n\n##### Notes\n- `@RefreshScope` doesn't work with `@Configuration` classes and doesn't ignores `@Scheduled` methods\n- `fail-fast` property means that Spring Boot application will fail startup immediately, if it cannot connect to the Config Service.\n\n### Auth service\nAuthorization responsibilities are extracted to a separate server, which grants [OAuth2 tokens](https:\u002F\u002Ftools.ietf.org\u002Fhtml\u002Frfc6749) for the backend resource services. Auth Server is used for user authorization as well as for secure machine-to-machine communication inside the perimeter.\n\nIn this project, I use [`Password credentials`](https:\u002F\u002Ftools.ietf.org\u002Fhtml\u002Frfc6749#section-4.3) grant type for users authorization (since it's used only by the UI) and [`Client Credentials`](https:\u002F\u002Ftools.ietf.org\u002Fhtml\u002Frfc6749#section-4.4) grant for service-to-service communciation.\n\nSpring Cloud Security provides convenient annotations and autoconfiguration to make this really easy to implement on both server and client side. You can learn more about that in [documentation](http:\u002F\u002Fcloud.spring.io\u002Fspring-cloud-security\u002Fspring-cloud-security.html).\n\nOn the client side, everything works exactly the same as with traditional session-based authorization. You can retrieve `Principal` object from the request, check user roles using the expression-based access control and `@PreAuthorize` annotation.\n\nEach PiggyMetrics client has a scope: `server` for backend services and `ui` - for the browser. We can use `@PreAuthorize` annotation to protect controllers from  an external access:\n\n``` java\n@PreAuthorize(\"#oauth2.hasScope('server')\")\n@RequestMapping(value = \"accounts\u002F{name}\", method = RequestMethod.GET)\npublic List\u003CDataPoint> getStatisticsByAccountName(@PathVariable String name) {\n\treturn statisticsService.findByAccountName(name);\n}\n```\n\n### API Gateway\nAPI Gateway is a single entry point into the system, used to handle requests and routing them to the appropriate backend service or by [aggregating results from a scatter-gather call](http:\u002F\u002Ftechblog.netflix.com\u002F2013\u002F01\u002Foptimizing-netflix-api.html). Also, it can be used for authentication, insights, stress and canary testing, service migration, static response handling and active traffic management.\n\nNetflix opensourced [such an edge service](http:\u002F\u002Ftechblog.netflix.com\u002F2013\u002F06\u002Fannouncing-zuul-edge-service-in-cloud.html) and Spring Cloud allows to use it with a single `@EnableZuulProxy` annotation. In this project, we use Zuul to store some static content (the UI application) and to route requests to appropriate the microservices. Here's a simple prefix-based routing configuration for the Notification service:\n\n```yml\nzuul:\n  routes:\n    notification-service:\n        path: \u002Fnotifications\u002F**\n        serviceId: notification-service\n        stripPrefix: false\n\n```\n\nThat means all requests starting with `\u002Fnotifications` will be routed to the Notification service. There is no hardcoded addresses, as you can see. Zuul uses [Service discovery](https:\u002F\u002Fgithub.com\u002Fsqshq\u002FPiggyMetrics\u002Fblob\u002Fmaster\u002FREADME.md#service-discovery) mechanism to locate Notification service instances and also [Circuit Breaker and Load Balancer](https:\u002F\u002Fgithub.com\u002Fsqshq\u002FPiggyMetrics\u002Fblob\u002Fmaster\u002FREADME.md#http-client-load-balancer-and-circuit-breaker), described below.\n\n### Service Discovery\n\nService Discovery allows automatic detection of the network locations for all registered services. These locations might have dynamically assigned addresses due to auto-scaling, failures or upgrades.\n\nThe key part of Service discovery is the Registry. In this project, we use Netflix Eureka. Eureka is a good example of the client-side discovery pattern, where client is responsible for looking up the locations of available service instances and load balancing between them.\n\nWith Spring Boot, you can easily build Eureka Registry using the `spring-cloud-starter-eureka-server` dependency, `@EnableEurekaServer` annotation and simple configuration properties.\n\nClient support enabled with `@EnableDiscoveryClient` annotation a `bootstrap.yml` with application name:\n``` yml\nspring:\n  application:\n    name: notification-service\n```\n\nThis service will be registered with the Eureka Server and provided with metadata such as host, port, health indicator URL, home page etc. Eureka receives heartbeat messages from each instance belonging to the service. If the heartbeat fails over a configurable timetable, the instance will be removed from the registry.\n\nAlso, Eureka provides a simple interface where you can track running services and a number of available instances: `http:\u002F\u002Flocalhost:8761`\n\n### Load balancer, Circuit breaker and Http client\n\n#### Ribbon\nRibbon is a client side load balancer which gives you a lot of control over the behaviour of HTTP and TCP clients. Compared to a traditional load balancer, there is no need in additional network hop - you can contact desired service directly.\n\nOut of the box, it natively integrates with Spring Cloud and Service Discovery. [Eureka Client](https:\u002F\u002Fgithub.com\u002Fsqshq\u002FPiggyMetrics#service-discovery) provides a dynamic list of available servers so Ribbon could balance between them.\n\n#### Hystrix\nHystrix is the implementation of [Circuit Breaker Pattern](http:\u002F\u002Fmartinfowler.com\u002Fbliki\u002FCircuitBreaker.html), which gives us a control over latency and network failures while communicating with other services. The main idea is to stop cascading failures in the distributed environment - that helps to fail fast and recover as soon as possible - important aspects of a fault-tolerant system that can self-heal.\n\nMoreover, Hystrix generates metrics on execution outcomes and latency for each command, that we can use to [monitor system's behavior](https:\u002F\u002Fgithub.com\u002Fsqshq\u002FPiggyMetrics#monitor-dashboard).\n\n#### Feign\nFeign is a declarative Http client which seamlessly integrates with Ribbon and Hystrix. Actually, a single `spring-cloud-starter-feign` dependency and `@EnableFeignClients` annotation gives us a full set of tools, including Load balancer, Circuit Breaker and Http client with reasonable default configuration.\n\nHere is an example from the Account Service:\n\n``` java\n@FeignClient(name = \"statistics-service\")\npublic interface StatisticsServiceClient {\n\n\t@RequestMapping(method = RequestMethod.PUT, value = \"\u002Fstatistics\u002F{accountName}\", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)\n\tvoid updateStatistics(@PathVariable(\"accountName\") String accountName, Account account);\n\n}\n```\n\n- Everything you need is just an interface\n- You can share `@RequestMapping` part between Spring MVC controller and Feign methods\n- Above example specifies just a desired service id - `statistics-service`, thanks to auto-discovery through Eureka\n\n### Monitor dashboard\n\nIn this project configuration, each microservice with Hystrix on board pushes metrics to Turbine via Spring Cloud Bus (with AMQP broker). The Monitoring project is just a small Spring boot application with the [Turbine](https:\u002F\u002Fgithub.com\u002FNetflix\u002FTurbine) and [Hystrix Dashboard](https:\u002F\u002Fgithub.com\u002FNetflix-Skunkworks\u002Fhystrix-dashboard).\n\nLet's see observe the behavior of our system under load: Statistics Service imitates a delay during the request processing. The response timeout is set to 1 second:\n\n\u003Cimg width=\"880\" src=\"https:\u002F\u002Fcloud.githubusercontent.com\u002Fassets\u002F6069066\u002F14194375\u002Fd9a2dd80-f7be-11e5-8bcc-9a2fce753cfe.png\">\n\n\u003Cimg width=\"212\" src=\"https:\u002F\u002Fcloud.githubusercontent.com\u002Fassets\u002F6069066\u002F14127349\u002F21e90026-f628-11e5-83f1-60108cb33490.gif\">\t| \u003Cimg width=\"212\" src=\"https:\u002F\u002Fcloud.githubusercontent.com\u002Fassets\u002F6069066\u002F14127348\u002F21e6ed40-f628-11e5-9fa4-ed527bf35129.gif\"> | \u003Cimg width=\"212\" src=\"https:\u002F\u002Fcloud.githubusercontent.com\u002Fassets\u002F6069066\u002F14127346\u002F21b9aaa6-f628-11e5-9bba-aaccab60fd69.gif\"> | \u003Cimg width=\"212\" src=\"https:\u002F\u002Fcloud.githubusercontent.com\u002Fassets\u002F6069066\u002F14127350\u002F21eafe1c-f628-11e5-8ccd-a6b6873c046a.gif\">\n--- |--- |--- |--- |\n| `0 ms delay` | `500 ms delay` | `800 ms delay` | `1100 ms delay`\n| Well behaving system. Throughput is about 22 rps. Small number of active threads in the Statistics service. Median service time is about 50 ms. | The number of active threads is growing. We can see purple number of thread-pool rejections and therefore about 40% of errors, but the circuit is still closed. | Half-open state: the ratio of failed commands is higher than 50%, so the circuit breaker kicks in. After sleep window amount of time, the next request goes through. | 100 percent of the requests fail. The circuit is now permanently open. Retry after sleep time won't close the circuit again because a single request is too slow.\n\n### Log analysis\n\nCentralized logging can be very useful while attempting to identify problems in a distributed environment. Elasticsearch, Logstash and Kibana stack lets you search and analyze your logs, utilization and network activity data with ease.\n\n### Distributed tracing\n\nAnalyzing problems in distributed systems can be difficult, especially trying to trace requests that propagate from one microservice to another.\n\n[Spring Cloud Sleuth](https:\u002F\u002Fcloud.spring.io\u002Fspring-cloud-sleuth\u002F) solves this problem by providing support for the distributed tracing. It adds two types of IDs to the logging: `traceId` and `spanId`. `spanId` represents a basic unit of work, for example sending an HTTP request. The traceId contains a set of spans forming a tree-like structure. For example, with a distributed big-data store, a trace might be formed by a PUT request. Using `traceId` and `spanId` for each operation we know when and where our application is as it processes a request, making reading logs much easier. \n\nThe logs are as follows, notice the `[appname,traceId,spanId,exportable]` entries from the Slf4J MDC:\n\n```text\n2018-07-26 23:13:49.381  WARN [gateway,3216d0de1384bb4f,3216d0de1384bb4f,false] 2999 --- [nio-4000-exec-1] o.s.c.n.z.f.r.s.AbstractRibbonCommand    : The Hystrix timeout of 20000ms for the command account-service is set lower than the combination of the Ribbon read and connect timeout, 80000ms.\n2018-07-26 23:13:49.562  INFO [account-service,3216d0de1384bb4f,404ff09c5cf91d2e,false] 3079 --- [nio-6000-exec-1] c.p.account.service.AccountServiceImpl   : new account has been created: test\n```\n\n- *`appname`*: The name of the application that logged the span from the property `spring.application.name`\n- *`traceId`*: This is an ID that is assigned to a single request, job, or action\n- *`spanId`*: The ID of a specific operation that took place\n- *`exportable`*: Whether the log should be exported to [Zipkin](https:\u002F\u002Fzipkin.io\u002F)\n\n## Infrastructure automation\n\nDeploying microservices, with their interdependence, is much more complex process than deploying a monolithic application. It is really important to have a fully automated infrastructure. We can achieve following benefits with Continuous Delivery approach:\n\n- The ability to release software anytime\n- Any build could end up being a release\n- Build artifacts once - deploy as needed\n\nHere is a simple Continuous Delivery workflow, implemented in this project:\n\n\u003Cimg width=\"880\" src=\"https:\u002F\u002Fcloud.githubusercontent.com\u002Fassets\u002F6069066\u002F14159789\u002F0dd7a7ce-f6e9-11e5-9fbb-a7fe0f4431e3.png\">\n\nIn this [configuration](https:\u002F\u002Fgithub.com\u002Fsqshq\u002FPiggyMetrics\u002Fblob\u002Fmaster\u002F.travis.yml), Travis CI builds tagged images for each successful git push. So, there are always the `latest` images for each microservice on [Docker Hub](https:\u002F\u002Fhub.docker.com\u002Fr\u002Fsqshq\u002F) and older images, tagged with git commit hash. It's easy to deploy any of them and quickly rollback, if needed.\n\n## Let's try it out\n\nNote that starting 8 Spring Boot applications, 4 MongoDB instances and a RabbitMq requires at least 4Gb of RAM.\n\n#### Before you start\n- Install Docker and Docker Compose.\n- Change environment variable values in `.env` file for more security or leave it as it is.\n- Build the project: `mvn package [-DskipTests]`\n\n#### Production mode\nIn this mode, all latest images will be pulled from Docker Hub.\nJust copy `docker-compose.yml` and hit `docker-compose up`\n\n#### Development mode\nIf you'd like to build images yourself, you have to clone the repository and build artifacts using maven. After that, run `docker-compose -f docker-compose.yml -f docker-compose.dev.yml up`\n\n`docker-compose.dev.yml` inherits `docker-compose.yml` with additional possibility to build images locally and expose all containers ports for convenient development.\n\nIf you'd like to start applications in Intellij Idea you need to either use [EnvFile plugin](https:\u002F\u002Fplugins.jetbrains.com\u002Fplugin\u002F7861-envfile) or manually export environment variables listed in `.env` file (make sure they were exported: `printenv`)\n\n#### Important endpoints\n- http:\u002F\u002Flocalhost:80 - Gateway\n- http:\u002F\u002Flocalhost:8761 - Eureka Dashboard\n- http:\u002F\u002Flocalhost:9000\u002Fhystrix - Hystrix Dashboard (Turbine stream link: `http:\u002F\u002Fturbine-stream-service:8080\u002Fturbine\u002Fturbine.stream`)\n- http:\u002F\u002Flocalhost:15672 - RabbitMq management (default login\u002Fpassword: guest\u002Fguest)\n\n## Contributions are welcome!\n\nPiggyMetrics is open source, and would greatly appreciate your help. Feel free to suggest and implement any improvements.\n","Piggy Metrics 是一个基于微服务架构的简单财务管理应用示例，使用了Spring Boot、Spring Cloud 和 Docker 技术。该项目由三个核心微服务组成：账户服务、统计服务和通知服务，每个服务都围绕特定业务领域构建并独立部署，能够实现用户账户管理、财务数据分析及用户通知等功能。Piggy Metrics 适用于需要学习或实践微服务架构设计模式的开发者，尤其是那些希望深入了解如何利用Spring生态系统与Docker容器化技术来搭建分布式应用程序的人士。此外，它也可以作为基础框架被进一步开发成更复杂的应用程序。",2,"2026-06-11 02:58:00","top_language"]