├── .circleci
└── config.yml
├── .gitignore
├── account-service-grpc
├── pom.xml
└── src
│ ├── main
│ ├── java
│ │ └── pl
│ │ │ └── piomin
│ │ │ └── services
│ │ │ └── grpc
│ │ │ └── account
│ │ │ ├── AccountApplication.java
│ │ │ ├── repository
│ │ │ └── AccountRepository.java
│ │ │ └── service
│ │ │ └── AccountsService.java
│ ├── proto-imports
│ │ ├── empty.proto
│ │ └── wrappers.proto
│ ├── proto
│ │ └── account.proto
│ └── resources
│ │ └── application.yml
│ └── test
│ └── java
│ └── pl
│ └── piomin
│ └── services
│ └── grpc
│ └── account
│ └── AccountServicesTests.java
├── account-service
├── .gitignore
├── pom.xml
└── src
│ ├── main
│ ├── .gitignore
│ ├── java
│ │ └── pl
│ │ │ └── piomin
│ │ │ └── services
│ │ │ └── protobuf
│ │ │ └── account
│ │ │ ├── Application.java
│ │ │ ├── controller
│ │ │ └── AccountController.java
│ │ │ └── data
│ │ │ └── AccountRepository.java
│ ├── proto
│ │ └── account.proto
│ └── resources
│ │ └── application.yml
│ └── test
│ └── java
│ └── pl
│ └── piomin
│ └── services
│ └── protobuf
│ └── account
│ └── AccountApplicationTest.java
├── customer-service-grpc
├── pom.xml
└── src
│ ├── main
│ ├── java
│ │ └── pl
│ │ │ └── piomin
│ │ │ └── services
│ │ │ └── grpc
│ │ │ └── customer
│ │ │ ├── CustomerApplication.java
│ │ │ ├── client
│ │ │ └── AccountClient.java
│ │ │ ├── repository
│ │ │ └── CustomerRepository.java
│ │ │ └── service
│ │ │ └── CustomersService.java
│ ├── proto
│ │ └── customer.proto
│ └── resources
│ │ └── application.yml
│ └── test
│ └── java
│ └── pl
│ └── piomin
│ └── services
│ └── grpc
│ └── customer
│ └── CustomerServicesTests.java
├── customer-service
├── .gitignore
├── pom.xml
└── src
│ ├── main
│ ├── .gitignore
│ ├── java
│ │ └── pl
│ │ │ └── piomin
│ │ │ └── services
│ │ │ └── protobuf
│ │ │ └── customer
│ │ │ ├── Application.java
│ │ │ ├── config
│ │ │ ├── LoadBalancerConfigurationProperties.java
│ │ │ └── ServiceConfig.java
│ │ │ ├── contract
│ │ │ └── AccountClient.java
│ │ │ ├── controller
│ │ │ └── CustomerController.java
│ │ │ ├── data
│ │ │ └── CustomerRepository.java
│ │ │ └── loadbalancer
│ │ │ ├── StaticServiceInstance.java
│ │ │ └── StaticServiceInstanceListSupplier.java
│ ├── proto
│ │ └── customer.proto
│ └── resources
│ │ └── application.yml
│ └── test
│ └── java
│ └── pl
│ └── piomin
│ └── services
│ └── protobuf
│ └── customer
│ └── CustomerApplicationTest.java
├── discovery-service
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── pl
│ │ └── piomin
│ │ └── services
│ │ └── discovery
│ │ └── DiscoveryService.java
│ └── resources
│ └── application.yml
├── pom.xml
├── readme.md
└── renovate.json
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2.1
2 |
3 | jobs:
4 | build:
5 | docker:
6 | - image: 'cimg/openjdk:21.0.6'
7 | steps:
8 | - checkout
9 | - run:
10 | name: Analyze on SonarCloud
11 | command: mvn verify sonar:sonar
12 |
13 | executors:
14 | jdk:
15 | docker:
16 | - image: 'cimg/openjdk:21.0.6'
17 |
18 | orbs:
19 | maven: circleci/maven@2.0.0
20 |
21 | workflows:
22 | maven_test:
23 | jobs:
24 | - maven/test:
25 | executor: jdk
26 | - build:
27 | context: SonarCloud
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /.project
2 | /.settings/
3 |
--------------------------------------------------------------------------------
/account-service-grpc/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 |
8 | pl.piomin
9 | sample-microservices-protobuf
10 | 1.0-SNAPSHOT
11 |
12 |
13 | account-service-grpc
14 | 1.1-SNAPSHOT
15 |
16 |
17 | ${project.artifactId}
18 |
19 |
20 |
21 |
22 | net.devh
23 | grpc-server-spring-boot-starter
24 | ${grpc-springboot.version}
25 |
26 |
27 | org.springframework.boot
28 | spring-boot-starter-web
29 |
30 |
31 | org.springframework.boot
32 | spring-boot-starter-actuator
33 |
34 |
35 | org.springframework.cloud
36 | spring-cloud-starter-netflix-eureka-client
37 |
38 |
39 | javax.annotation
40 | javax.annotation-api
41 | 1.3.2
42 |
43 |
44 | io.grpc
45 | grpc-testing
46 | ${grpc.version}
47 | test
48 |
49 |
50 | org.springframework.boot
51 | spring-boot-starter-test
52 | test
53 |
54 |
55 | net.devh
56 | grpc-client-spring-boot-starter
57 | ${grpc-springboot.version}
58 | test
59 |
60 |
61 |
62 |
63 |
64 |
65 | com.github.os72
66 | protoc-jar-maven-plugin
67 | 3.11.4
68 |
69 |
70 | generate-sources
71 |
72 | run
73 |
74 |
75 | all
76 | direct
77 | true
78 | src/main/generated
79 |
80 | src/main/proto
81 |
82 | true
83 |
84 |
85 | java
86 | src/main/generated
87 |
88 |
89 | grpc-java
90 | io.grpc:protoc-gen-grpc-java:1.61.1
91 | src/main/generated
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 | org.codehaus.mojo
100 | build-helper-maven-plugin
101 |
102 |
103 | add-source
104 | generate-sources
105 |
106 | add-source
107 |
108 |
109 |
110 | src/main/generated
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
--------------------------------------------------------------------------------
/account-service-grpc/src/main/java/pl/piomin/services/grpc/account/AccountApplication.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.services.grpc.account;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 | import org.springframework.context.annotation.Bean;
6 | import pl.piomin.services.grpc.account.model.AccountProto;
7 | import pl.piomin.services.grpc.account.repository.AccountRepository;
8 |
9 | import java.util.ArrayList;
10 | import java.util.List;
11 |
12 | @SpringBootApplication
13 | public class AccountApplication {
14 |
15 | public static void main(String[] args) {
16 | SpringApplication.run(AccountApplication.class, args);
17 | }
18 |
19 | @Bean
20 | AccountRepository repository() {
21 | List accounts = new ArrayList<>();
22 | accounts.add(AccountProto.Account.newBuilder().setId(1).setCustomerId(1).setNumber("111111").build());
23 | accounts.add(AccountProto.Account.newBuilder().setId(2).setCustomerId(2).setNumber("222222").build());
24 | accounts.add(AccountProto.Account.newBuilder().setId(3).setCustomerId(3).setNumber("333333").build());
25 | accounts.add(AccountProto.Account.newBuilder().setId(4).setCustomerId(4).setNumber("444444").build());
26 | accounts.add(AccountProto.Account.newBuilder().setId(5).setCustomerId(1).setNumber("555555").build());
27 | accounts.add(AccountProto.Account.newBuilder().setId(6).setCustomerId(2).setNumber("666666").build());
28 | accounts.add(AccountProto.Account.newBuilder().setId(7).setCustomerId(2).setNumber("777777").build());
29 | return new AccountRepository(accounts);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/account-service-grpc/src/main/java/pl/piomin/services/grpc/account/repository/AccountRepository.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.services.grpc.account.repository;
2 |
3 |
4 | import pl.piomin.services.grpc.account.model.AccountProto;
5 |
6 | import java.util.List;
7 | import java.util.concurrent.atomic.AtomicInteger;
8 |
9 | public class AccountRepository {
10 |
11 | List accounts;
12 | AtomicInteger id;
13 |
14 | public AccountRepository(List accounts) {
15 | this.accounts = accounts;
16 | this.id = new AtomicInteger();
17 | this.id.set(accounts.size());
18 | }
19 |
20 | public List findAll() {
21 | return accounts;
22 | }
23 |
24 | public List findByCustomer(int customerId) {
25 | return accounts.stream().filter(it -> it.getCustomerId() == customerId).toList();
26 | }
27 |
28 | public AccountProto.Account findByNumber(String number) {
29 | return accounts.stream()
30 | .filter(it -> it.getNumber().equals(number))
31 | .findFirst()
32 | .orElseThrow();
33 | }
34 |
35 | public AccountProto.Account add(int customerId, String number) {
36 | AccountProto.Account a = AccountProto.Account.newBuilder()
37 | .setId(id.incrementAndGet())
38 | .setCustomerId(customerId)
39 | .setNumber(number)
40 | .build();
41 | return a;
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/account-service-grpc/src/main/java/pl/piomin/services/grpc/account/service/AccountsService.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.services.grpc.account.service;
2 |
3 | import com.google.protobuf.Empty;
4 | import com.google.protobuf.Int32Value;
5 | import com.google.protobuf.StringValue;
6 | import io.grpc.stub.StreamObserver;
7 | import net.devh.boot.grpc.server.service.GrpcService;
8 | import org.springframework.beans.factory.annotation.Autowired;
9 | import pl.piomin.services.grpc.account.model.AccountProto;
10 | import pl.piomin.services.grpc.account.model.AccountsServiceGrpc;
11 | import pl.piomin.services.grpc.account.repository.AccountRepository;
12 |
13 | import java.util.List;
14 |
15 | @GrpcService
16 | public class AccountsService extends AccountsServiceGrpc.AccountsServiceImplBase {
17 |
18 | @Autowired
19 | AccountRepository repository;
20 |
21 | @Override
22 | public void findByNumber(StringValue request, StreamObserver responseObserver) {
23 | AccountProto.Account a = repository.findByNumber(request.getValue());
24 | responseObserver.onNext(a);
25 | responseObserver.onCompleted();
26 | }
27 |
28 | @Override
29 | public void findByCustomer(Int32Value request, StreamObserver responseObserver) {
30 | List accounts = repository.findByCustomer(request.getValue());
31 | AccountProto.Accounts a = AccountProto.Accounts.newBuilder().addAllAccount(accounts).build();
32 | responseObserver.onNext(a);
33 | responseObserver.onCompleted();
34 | }
35 |
36 | @Override
37 | public void findAll(Empty request, StreamObserver responseObserver) {
38 | List accounts = repository.findAll();
39 | AccountProto.Accounts a = AccountProto.Accounts.newBuilder().addAllAccount(accounts).build();
40 | responseObserver.onNext(a);
41 | responseObserver.onCompleted();
42 | }
43 |
44 | @Override
45 | public void addAccount(AccountProto.Account request, StreamObserver responseObserver) {
46 | AccountProto.Account a = repository.add(request.getCustomerId(), request.getNumber());
47 | responseObserver.onNext(a);
48 | responseObserver.onCompleted();
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/account-service-grpc/src/main/proto-imports/empty.proto:
--------------------------------------------------------------------------------
1 | // Protocol Buffers - Google's data interchange format
2 | // Copyright 2008 Google Inc. All rights reserved.
3 | // https://developers.google.com/protocol-buffers/
4 | //
5 | // Redistribution and use in source and binary forms, with or without
6 | // modification, are permitted provided that the following conditions are
7 | // met:
8 | //
9 | // * Redistributions of source code must retain the above copyright
10 | // notice, this list of conditions and the following disclaimer.
11 | // * Redistributions in binary form must reproduce the above
12 | // copyright notice, this list of conditions and the following disclaimer
13 | // in the documentation and/or other materials provided with the
14 | // distribution.
15 | // * Neither the name of Google Inc. nor the names of its
16 | // contributors may be used to endorse or promote products derived from
17 | // this software without specific prior written permission.
18 | //
19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
31 | syntax = "proto3";
32 |
33 | package google.protobuf;
34 |
35 | option csharp_namespace = "Google.Protobuf.WellKnownTypes";
36 | option go_package = "google.golang.org/protobuf/types/known/emptypb";
37 | option java_package = "com.google.protobuf";
38 | option java_outer_classname = "EmptyProto";
39 | option java_multiple_files = true;
40 | option objc_class_prefix = "GPB";
41 | option cc_enable_arenas = true;
42 |
43 | // A generic empty message that you can re-use to avoid defining duplicated
44 | // empty messages in your APIs. A typical example is to use it as the request
45 | // or the response type of an API method. For instance:
46 | //
47 | // service Foo {
48 | // rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty);
49 | // }
50 | //
51 | message Empty {}
52 |
--------------------------------------------------------------------------------
/account-service-grpc/src/main/proto-imports/wrappers.proto:
--------------------------------------------------------------------------------
1 | // Protocol Buffers - Google's data interchange format
2 | // Copyright 2008 Google Inc. All rights reserved.
3 | // https://developers.google.com/protocol-buffers/
4 | //
5 | // Redistribution and use in source and binary forms, with or without
6 | // modification, are permitted provided that the following conditions are
7 | // met:
8 | //
9 | // * Redistributions of source code must retain the above copyright
10 | // notice, this list of conditions and the following disclaimer.
11 | // * Redistributions in binary form must reproduce the above
12 | // copyright notice, this list of conditions and the following disclaimer
13 | // in the documentation and/or other materials provided with the
14 | // distribution.
15 | // * Neither the name of Google Inc. nor the names of its
16 | // contributors may be used to endorse or promote products derived from
17 | // this software without specific prior written permission.
18 | //
19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
31 | // Wrappers for primitive (non-message) types. These types are useful
32 | // for embedding primitives in the `google.protobuf.Any` type and for places
33 | // where we need to distinguish between the absence of a primitive
34 | // typed field and its default value.
35 | //
36 | // These wrappers have no meaningful use within repeated fields as they lack
37 | // the ability to detect presence on individual elements.
38 | // These wrappers have no meaningful use within a map or a oneof since
39 | // individual entries of a map or fields of a oneof can already detect presence.
40 |
41 | syntax = "proto3";
42 |
43 | package google.protobuf;
44 |
45 | option csharp_namespace = "Google.Protobuf.WellKnownTypes";
46 | option cc_enable_arenas = true;
47 | option go_package = "google.golang.org/protobuf/types/known/wrapperspb";
48 | option java_package = "com.google.protobuf";
49 | option java_outer_classname = "WrappersProto";
50 | option java_multiple_files = true;
51 | option objc_class_prefix = "GPB";
52 |
53 | // Wrapper message for `double`.
54 | //
55 | // The JSON representation for `DoubleValue` is JSON number.
56 | message DoubleValue {
57 | // The double value.
58 | double value = 1;
59 | }
60 |
61 | // Wrapper message for `float`.
62 | //
63 | // The JSON representation for `FloatValue` is JSON number.
64 | message FloatValue {
65 | // The float value.
66 | float value = 1;
67 | }
68 |
69 | // Wrapper message for `int64`.
70 | //
71 | // The JSON representation for `Int64Value` is JSON string.
72 | message Int64Value {
73 | // The int64 value.
74 | int64 value = 1;
75 | }
76 |
77 | // Wrapper message for `uint64`.
78 | //
79 | // The JSON representation for `UInt64Value` is JSON string.
80 | message UInt64Value {
81 | // The uint64 value.
82 | uint64 value = 1;
83 | }
84 |
85 | // Wrapper message for `int32`.
86 | //
87 | // The JSON representation for `Int32Value` is JSON number.
88 | message Int32Value {
89 | // The int32 value.
90 | int32 value = 1;
91 | }
92 |
93 | // Wrapper message for `uint32`.
94 | //
95 | // The JSON representation for `UInt32Value` is JSON number.
96 | message UInt32Value {
97 | // The uint32 value.
98 | uint32 value = 1;
99 | }
100 |
101 | // Wrapper message for `bool`.
102 | //
103 | // The JSON representation for `BoolValue` is JSON `true` and `false`.
104 | message BoolValue {
105 | // The bool value.
106 | bool value = 1;
107 | }
108 |
109 | // Wrapper message for `string`.
110 | //
111 | // The JSON representation for `StringValue` is JSON string.
112 | message StringValue {
113 | // The string value.
114 | string value = 1;
115 | }
116 |
117 | // Wrapper message for `bytes`.
118 | //
119 | // The JSON representation for `BytesValue` is JSON string.
120 | message BytesValue {
121 | // The bytes value.
122 | bytes value = 1;
123 | }
124 |
--------------------------------------------------------------------------------
/account-service-grpc/src/main/proto/account.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package model;
4 |
5 | option java_package = "pl.piomin.services.grpc.account.model";
6 | option java_outer_classname = "AccountProto";
7 |
8 | import "google/protobuf/empty.proto";
9 | import "google/protobuf/wrappers.proto";
10 |
11 | service AccountsService {
12 | rpc FindByNumber(google.protobuf.StringValue) returns (Account) {}
13 | rpc FindByCustomer(google.protobuf.Int32Value) returns (Accounts) {}
14 | rpc FindAll(google.protobuf.Empty) returns (Accounts) {}
15 | rpc AddAccount(Account) returns (Account) {}
16 | }
17 |
18 | message Accounts {
19 | repeated Account account = 1;
20 | }
21 |
22 | message Account {
23 | int32 id = 1;
24 | string number = 2;
25 | int32 customer_id = 3;
26 | }
--------------------------------------------------------------------------------
/account-service-grpc/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | spring.application.name: account-service-grpc
2 | spring.output.ansi.enabled: always
3 |
4 | management.endpoints.web.exposure.include: metrics
5 | management.endpoint.metrics.enabled: true
--------------------------------------------------------------------------------
/account-service-grpc/src/test/java/pl/piomin/services/grpc/account/AccountServicesTests.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.services.grpc.account;
2 |
3 | import com.google.protobuf.Empty;
4 | import com.google.protobuf.Int32Value;
5 | import com.google.protobuf.StringValue;
6 | import net.devh.boot.grpc.client.inject.GrpcClient;
7 | import org.junit.jupiter.api.Test;
8 | import org.springframework.boot.test.context.SpringBootTest;
9 | import org.springframework.test.annotation.DirtiesContext;
10 | import pl.piomin.services.grpc.account.model.AccountProto;
11 | import pl.piomin.services.grpc.account.model.AccountsServiceGrpc;
12 |
13 | import static org.junit.jupiter.api.Assertions.*;
14 |
15 | @SpringBootTest(properties = {
16 | "grpc.server.inProcessName=test",
17 | "grpc.server.port=-1",
18 | "grpc.client.inProcess.address=in-process:test"
19 | })
20 | @DirtiesContext
21 | public class AccountServicesTests {
22 |
23 | @GrpcClient("inProcess")
24 | AccountsServiceGrpc.AccountsServiceBlockingStub service;
25 |
26 | @Test
27 | void shouldFindAll() {
28 | AccountProto.Accounts a = service.findAll(Empty.newBuilder().build());
29 | assertNotNull(a);
30 | assertFalse(a.getAccountList().isEmpty());
31 | }
32 |
33 | @Test
34 | void shouldFindByCustomer() {
35 | AccountProto.Accounts a = service.findByCustomer(Int32Value.newBuilder().setValue(1).build());
36 | assertNotNull(a);
37 | assertFalse(a.getAccountList().isEmpty());
38 | }
39 |
40 | @Test
41 | void shouldFindByNumber() {
42 | AccountProto.Account a = service.findByNumber(StringValue.newBuilder().setValue("111111").build());
43 | assertNotNull(a);
44 | assertNotEquals(0, a.getId());
45 | }
46 |
47 | @Test
48 | void shouldAddAccount() {
49 | AccountProto.Account a = AccountProto.Account.newBuilder()
50 | .setNumber("123456")
51 | .setCustomerId(10)
52 | .build();
53 |
54 | a = service.addAccount(a);
55 | assertNotNull(a);
56 | assertNotEquals(0, a.getId());
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/account-service/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
2 | /.settings/
3 | /.classpath
4 | /.project
5 |
--------------------------------------------------------------------------------
/account-service/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 | pl.piomin
6 | sample-microservices-protobuf
7 | 1.0-SNAPSHOT
8 |
9 | account-service
10 |
11 |
12 | ${project.artifactId}
13 |
14 |
15 |
16 |
17 | org.springframework.boot
18 | spring-boot-starter-web
19 |
20 |
21 | com.fasterxml.jackson.core
22 | jackson-databind
23 |
24 |
25 |
26 |
27 | com.google.protobuf
28 | protobuf-java
29 | 4.31.1
30 |
31 |
32 | com.googlecode.protobuf-java-format
33 | protobuf-java-format
34 | 1.4
35 |
36 |
37 | org.springframework.boot
38 | spring-boot-starter-test
39 | test
40 |
41 |
42 |
43 |
44 |
45 |
46 | com.github.os72
47 | protoc-jar-maven-plugin
48 | 3.11.4
49 |
50 |
51 | generate-sources
52 |
53 | run
54 |
55 |
56 | all
57 | direct
58 | src/main/generated
59 | true
60 | 4.26.1
61 |
62 | src/main/proto
63 |
64 | true
65 |
66 |
67 | java
68 | src/main/generated
69 |
70 |
71 | grpc-java
72 | io.grpc:protoc-gen-grpc-java:1.63.0
73 | src/main/generated
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 | org.codehaus.mojo
82 | build-helper-maven-plugin
83 |
84 |
85 | add-source
86 | generate-sources
87 |
88 | add-source
89 |
90 |
91 |
92 | src/main/generated
93 |
94 |
95 |
96 |
97 |
98 |
99 | org.jacoco
100 | jacoco-maven-plugin
101 | 0.8.13
102 |
103 |
104 |
105 | prepare-agent
106 |
107 |
108 |
109 | report
110 | test
111 |
112 | report
113 |
114 |
115 |
116 |
117 |
118 |
119 |
--------------------------------------------------------------------------------
/account-service/src/main/.gitignore:
--------------------------------------------------------------------------------
1 | /generated/
2 |
--------------------------------------------------------------------------------
/account-service/src/main/java/pl/piomin/services/protobuf/account/Application.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.services.protobuf.account;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Arrays;
5 | import java.util.List;
6 |
7 | import org.springframework.boot.SpringApplication;
8 | import org.springframework.boot.autoconfigure.SpringBootApplication;
9 | import org.springframework.context.annotation.Bean;
10 | import org.springframework.context.annotation.Primary;
11 | import org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter;
12 | import org.springframework.web.client.RestTemplate;
13 |
14 | import pl.piomin.services.protobuf.account.data.AccountRepository;
15 | import pl.piomin.services.protobuf.account.model.AccountProto.Account;
16 |
17 | @SpringBootApplication
18 | public class Application {
19 |
20 | public static void main(String[] args) {
21 | SpringApplication.run(Application.class, args);
22 | }
23 |
24 | @Bean
25 | @Primary
26 | ProtobufHttpMessageConverter protobufHttpMessageConverter() {
27 | return new ProtobufHttpMessageConverter();
28 | }
29 |
30 | @Bean
31 | RestTemplate restTemplate(ProtobufHttpMessageConverter hmc) {
32 | return new RestTemplate(Arrays.asList(hmc));
33 | }
34 |
35 | @Bean
36 | AccountRepository repository() {
37 | List accounts = new ArrayList<>();
38 | accounts.add(Account.newBuilder().setId(1).setCustomerId(1).setNumber("111111").build());
39 | accounts.add(Account.newBuilder().setId(2).setCustomerId(2).setNumber("222222").build());
40 | accounts.add(Account.newBuilder().setId(3).setCustomerId(3).setNumber("333333").build());
41 | accounts.add(Account.newBuilder().setId(4).setCustomerId(4).setNumber("444444").build());
42 | accounts.add(Account.newBuilder().setId(5).setCustomerId(1).setNumber("555555").build());
43 | accounts.add(Account.newBuilder().setId(6).setCustomerId(2).setNumber("666666").build());
44 | accounts.add(Account.newBuilder().setId(7).setCustomerId(2).setNumber("777777").build());
45 | return new AccountRepository(accounts);
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/account-service/src/main/java/pl/piomin/services/protobuf/account/controller/AccountController.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.services.protobuf.account.controller;
2 |
3 | import java.util.logging.Logger;
4 |
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.web.bind.annotation.PathVariable;
7 | import org.springframework.web.bind.annotation.RequestMapping;
8 | import org.springframework.web.bind.annotation.RestController;
9 |
10 | import pl.piomin.services.protobuf.account.data.AccountRepository;
11 | import pl.piomin.services.protobuf.account.model.AccountProto.Account;
12 | import pl.piomin.services.protobuf.account.model.AccountProto.Accounts;
13 |
14 | @RestController
15 | public class AccountController {
16 |
17 | @Autowired
18 | AccountRepository repository;
19 |
20 | protected Logger logger = Logger.getLogger(AccountController.class.getName());
21 |
22 | @RequestMapping(value = "/accounts/{number}", produces = "application/x-protobuf")
23 | public Account findByNumber(@PathVariable("number") String number) {
24 | logger.info(String.format("Account.findByNumber(%s)", number));
25 | return repository.findByNumber(number);
26 | }
27 |
28 | @RequestMapping(value = "/accounts/customer/{customer}", produces = "application/x-protobuf")
29 | public Accounts findByCustomer(@PathVariable("customer") Integer customerId) {
30 | logger.info(String.format("Account.findByCustomer(%s)", customerId));
31 | return Accounts.newBuilder().addAllAccount(repository.findByCustomer(customerId)).build();
32 | }
33 |
34 | @RequestMapping(value = "/accounts", produces = "application/x-protobuf")
35 | public Accounts findAll() {
36 | logger.info("Account.findAll()");
37 | return Accounts.newBuilder().addAllAccount(repository.findAll()).build();
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/account-service/src/main/java/pl/piomin/services/protobuf/account/data/AccountRepository.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.services.protobuf.account.data;
2 |
3 | import java.util.List;
4 |
5 | import pl.piomin.services.protobuf.account.model.AccountProto.Account;
6 |
7 | public class AccountRepository {
8 |
9 | List accounts;
10 |
11 | public AccountRepository(List accounts) {
12 | this.accounts = accounts;
13 | }
14 |
15 | public List findAll() {
16 | return accounts;
17 | }
18 |
19 | public List findByCustomer(int customerId) {
20 | return accounts.stream().filter(it -> it.getCustomerId() == customerId).toList();
21 | }
22 |
23 | public Account findByNumber(String number) {
24 | return accounts.stream()
25 | .filter(it -> it.getNumber().equals(number))
26 | .findFirst()
27 | .orElseThrow();
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/account-service/src/main/proto/account.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package model;
4 |
5 | option java_package = "pl.piomin.services.protobuf.account.model";
6 | option java_outer_classname = "AccountProto";
7 |
8 | message Accounts {
9 | repeated Account account = 1;
10 | }
11 |
12 | message Account {
13 |
14 | int32 id = 1;
15 | string number = 2;
16 | int32 customer_id = 3;
17 |
18 | }
--------------------------------------------------------------------------------
/account-service/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | server:
2 | port: ${PORT:2222}
3 |
4 | spring:
5 | application:
6 | name: account-service
7 |
8 | #eureka:
9 | # client:
10 | # serviceUrl:
11 | # defaultZone: ${DISCOVERY_URL:http://localhost:8761}/eureka/
12 | # instance:
13 | # leaseRenewalIntervalInSeconds: 1
14 | # leaseExpirationDurationInSeconds: 2
15 |
16 | #ribbon:
17 | # eureka:
18 | # enabled: true
--------------------------------------------------------------------------------
/account-service/src/test/java/pl/piomin/services/protobuf/account/AccountApplicationTest.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.services.protobuf.account;
2 |
3 | import java.util.logging.Logger;
4 |
5 | import org.junit.jupiter.api.Test;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.boot.test.context.SpringBootTest;
8 | import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
9 | import org.springframework.boot.test.context.TestConfiguration;
10 | import org.springframework.boot.test.web.client.TestRestTemplate;
11 | import org.springframework.boot.web.client.RestTemplateBuilder;
12 | import org.springframework.context.annotation.Bean;
13 | import org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter;
14 |
15 | import pl.piomin.services.protobuf.account.model.AccountProto.Account;
16 | import pl.piomin.services.protobuf.account.model.AccountProto.Accounts;
17 |
18 | @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
19 | public class AccountApplicationTest {
20 |
21 | protected Logger logger = Logger.getLogger(AccountApplicationTest.class.getName());
22 |
23 | @Autowired
24 | TestRestTemplate template;
25 |
26 | @Test
27 | public void testFindByNumber() {
28 | Account a = this.template.getForObject("/accounts/{id}", Account.class, "111111");
29 | logger.info("Account[\n" + a + "]");
30 | }
31 |
32 | @Test
33 | public void testFindByCustomer() {
34 | Accounts a = this.template.getForObject("/accounts/customer/{customer}", Accounts.class, "2");
35 | logger.info("Accounts[\n" + a + "]");
36 | }
37 |
38 | @Test
39 | public void testFindAll() {
40 | Accounts a = this.template.getForObject("/accounts", Accounts.class);
41 | logger.info("Accounts[\n" + a + "]");
42 | }
43 |
44 |
45 | @TestConfiguration
46 | static class Config {
47 |
48 | @Bean
49 | public RestTemplateBuilder restTemplateBuilder() {
50 | return new RestTemplateBuilder().additionalMessageConverters(new ProtobufHttpMessageConverter());
51 | }
52 |
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/customer-service-grpc/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 |
8 | pl.piomin
9 | sample-microservices-protobuf
10 | 1.0-SNAPSHOT
11 |
12 |
13 | customer-service-grpc
14 | 1.1-SNAPSHOT
15 |
16 |
17 | ${project.artifactId}
18 |
19 |
20 |
21 |
22 | net.devh
23 | grpc-server-spring-boot-starter
24 | ${grpc-springboot.version}
25 |
26 |
27 | org.springframework.boot
28 | spring-boot-starter-web
29 |
30 |
31 | org.springframework.boot
32 | spring-boot-starter-actuator
33 |
34 |
35 | net.devh
36 | grpc-client-spring-boot-starter
37 | ${grpc-springboot.version}
38 |
39 |
40 | org.springframework.cloud
41 | spring-cloud-starter-netflix-eureka-client
42 |
43 |
44 | javax.annotation
45 | javax.annotation-api
46 | 1.3.2
47 |
48 |
49 | io.grpc
50 | grpc-testing
51 | ${grpc.version}
52 | test
53 |
54 |
55 | org.springframework.boot
56 | spring-boot-starter-test
57 | test
58 |
59 |
60 |
61 |
62 |
63 |
64 | com.github.os72
65 | protoc-jar-maven-plugin
66 | 3.11.4
67 |
68 |
69 | generate-sources
70 |
71 | run
72 |
73 |
74 | all
75 | true
76 | true
77 | direct
78 | src/main/generated
79 |
80 | src/main/proto
81 |
82 |
83 |
84 |
85 |
86 |
87 | java
88 | src/main/generated
89 |
90 |
91 | grpc-java
92 | io.grpc:protoc-gen-grpc-java:1.60.1
93 | src/main/generated
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 | org.codehaus.mojo
102 | build-helper-maven-plugin
103 |
104 |
105 | add-source
106 | generate-sources
107 |
108 | add-source
109 |
110 |
111 |
112 | src/main/generated
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
--------------------------------------------------------------------------------
/customer-service-grpc/src/main/java/pl/piomin/services/grpc/customer/CustomerApplication.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.services.grpc.customer;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
6 | import org.springframework.context.annotation.Bean;
7 | import pl.piomin.services.grpc.customer.model.CustomerProto;
8 | import pl.piomin.services.grpc.customer.repository.CustomerRepository;
9 |
10 | import java.util.ArrayList;
11 | import java.util.List;
12 |
13 | @SpringBootApplication
14 | @EnableDiscoveryClient
15 | public class CustomerApplication {
16 |
17 | public static void main(String[] args) {
18 | SpringApplication.run(CustomerApplication.class, args);
19 | }
20 |
21 | @Bean
22 | CustomerRepository repository() {
23 | List customers = new ArrayList<>();
24 | customers.add(CustomerProto.Customer.newBuilder().setId(1).setPesel("12345").setName("Adam Kowalski")
25 | .setType(CustomerProto.Customer.CustomerType.INDIVIDUAL).build());
26 | customers.add(CustomerProto.Customer.newBuilder().setId(2).setPesel("12346").setName("Anna Malinowska")
27 | .setType(CustomerProto.Customer.CustomerType.INDIVIDUAL).build());
28 | customers.add(CustomerProto.Customer.newBuilder().setId(3).setPesel("12347").setName("Paweł Michalski")
29 | .setType(CustomerProto.Customer.CustomerType.INDIVIDUAL).build());
30 | customers.add(CustomerProto.Customer.newBuilder().setId(4).setPesel("12348").setName("Karolina Lewandowska")
31 | .setType(CustomerProto.Customer.CustomerType.INDIVIDUAL).build());
32 | return new CustomerRepository(customers);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/customer-service-grpc/src/main/java/pl/piomin/services/grpc/customer/client/AccountClient.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.services.grpc.customer.client;
2 |
3 | import com.google.protobuf.Int32Value;
4 | import io.grpc.StatusRuntimeException;
5 | import net.devh.boot.grpc.client.inject.GrpcClient;
6 | import net.devh.boot.grpc.client.inject.GrpcClientBean;
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 | import org.springframework.stereotype.Service;
10 | import pl.piomin.services.grpc.customer.model.AccountsServiceGrpc;
11 | import pl.piomin.services.grpc.customer.model.CustomerProto;
12 |
13 | @Service
14 | public class AccountClient {
15 |
16 | private static final Logger LOG = LoggerFactory.getLogger(AccountClient.class);
17 |
18 | @GrpcClient("account-service-grpc")
19 | AccountsServiceGrpc.AccountsServiceBlockingStub stub;
20 |
21 | public CustomerProto.Accounts getAccountsByCustomerId(int customerId) {
22 | try {
23 | return stub.findByCustomer(Int32Value.newBuilder().setValue(customerId).build());
24 | } catch (final StatusRuntimeException e) {
25 | LOG.error("Error in communication", e);
26 | return null;
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/customer-service-grpc/src/main/java/pl/piomin/services/grpc/customer/repository/CustomerRepository.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.services.grpc.customer.repository;
2 |
3 |
4 | import pl.piomin.services.grpc.customer.model.CustomerProto;
5 |
6 | import java.util.List;
7 | import java.util.concurrent.atomic.AtomicInteger;
8 |
9 | public class CustomerRepository {
10 |
11 | private List customers;
12 | AtomicInteger id;
13 |
14 | public CustomerRepository(List customers) {
15 | this.customers = customers;
16 | this.id = new AtomicInteger();
17 | this.id.set(customers.size());
18 | }
19 |
20 | public CustomerProto.Customer findById(int id) {
21 | return customers.stream().filter(it -> it.getId() == id).findFirst().orElseThrow();
22 | }
23 |
24 | public CustomerProto.Customer findByPesel(String pesel) {
25 | return customers.stream().filter(it -> it.getPesel().equals(pesel)).findFirst().orElseThrow();
26 | }
27 |
28 | public List findAll() {
29 | return customers;
30 | }
31 |
32 | public CustomerProto.Customer add(CustomerProto.Customer.CustomerType type, String name, String pesel) {
33 | CustomerProto.Customer c = CustomerProto.Customer.newBuilder()
34 | .setId(id.incrementAndGet())
35 | .setType(type)
36 | .setName(name)
37 | .setPesel(pesel)
38 | .build();
39 | return c;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/customer-service-grpc/src/main/java/pl/piomin/services/grpc/customer/service/CustomersService.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.services.grpc.customer.service;
2 |
3 | import com.google.protobuf.Empty;
4 | import com.google.protobuf.Int32Value;
5 | import com.google.protobuf.StringValue;
6 | import io.grpc.stub.StreamObserver;
7 | import net.devh.boot.grpc.server.service.GrpcService;
8 | import org.springframework.beans.factory.annotation.Autowired;
9 | import pl.piomin.services.grpc.customer.client.AccountClient;
10 | import pl.piomin.services.grpc.customer.model.CustomerProto;
11 | import pl.piomin.services.grpc.customer.model.CustomersServiceGrpc;
12 | import pl.piomin.services.grpc.customer.repository.CustomerRepository;
13 |
14 | import java.util.List;
15 |
16 | @GrpcService
17 | public class CustomersService extends CustomersServiceGrpc.CustomersServiceImplBase {
18 |
19 | @Autowired
20 | CustomerRepository repository;
21 | @Autowired
22 | AccountClient accountClient;
23 |
24 | @Override
25 | public void findById(Int32Value request, StreamObserver responseObserver) {
26 | CustomerProto.Customer c = repository.findById(request.getValue());
27 | CustomerProto.Accounts a = accountClient.getAccountsByCustomerId(c.getId());
28 | List l = a.getAccountList();
29 | c = CustomerProto.Customer.newBuilder(c).addAllAccounts(l).build();
30 | responseObserver.onNext(c);
31 | responseObserver.onCompleted();
32 | }
33 |
34 | @Override
35 | public void findByPesel(StringValue request, StreamObserver responseObserver) {
36 | CustomerProto.Customer c = repository.findByPesel(request.getValue());
37 | responseObserver.onNext(c);
38 | responseObserver.onCompleted();
39 | }
40 |
41 | @Override
42 | public void findAll(Empty request, StreamObserver responseObserver) {
43 | List customerList = repository.findAll();
44 | CustomerProto.Customers c = CustomerProto.Customers.newBuilder().addAllCustomers(customerList).build();
45 | responseObserver.onNext(c);
46 | responseObserver.onCompleted();
47 | }
48 |
49 | @Override
50 | public void addCustomer(CustomerProto.Customer request, StreamObserver responseObserver) {
51 | CustomerProto.Customer c = repository.add(request.getType(), request.getName(), request.getPesel());
52 | responseObserver.onNext(c);
53 | responseObserver.onCompleted();
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/customer-service-grpc/src/main/proto/customer.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package model;
4 |
5 | option java_package = "pl.piomin.services.grpc.customer.model";
6 | option java_outer_classname = "CustomerProto";
7 |
8 | import "google/protobuf/empty.proto";
9 | import "google/protobuf/wrappers.proto";
10 |
11 | service CustomersService {
12 | rpc FindByPesel(google.protobuf.StringValue) returns (Customer) {}
13 | rpc FindById(google.protobuf.Int32Value) returns (Customer) {}
14 | rpc FindAll(google.protobuf.Empty) returns (Customers) {}
15 | rpc AddCustomer(Customer) returns (Customer) {}
16 | }
17 |
18 | service AccountsService {
19 | rpc FindByNumber(google.protobuf.StringValue) returns (Account) {}
20 | rpc FindByCustomer(google.protobuf.Int32Value) returns (Accounts) {}
21 | rpc FindAll(google.protobuf.Empty) returns (Accounts) {}
22 | rpc AddAccount(Account) returns (Account) {}
23 | }
24 |
25 | message Accounts {
26 | repeated Account account = 1;
27 | }
28 |
29 | message Account {
30 | int32 id = 1;
31 | string number = 2;
32 | int32 customer_id = 3;
33 | }
34 |
35 | message Customer {
36 | int32 id = 1;
37 | string pesel = 2;
38 | string name = 3;
39 | CustomerType type = 4;
40 | repeated Account accounts = 5;
41 | enum CustomerType {
42 | INDIVIDUAL = 0;
43 | COMPANY = 1;
44 | }
45 | }
46 |
47 | message Customers {
48 | repeated Customer customers = 1;
49 | }
--------------------------------------------------------------------------------
/customer-service-grpc/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | spring.application.name: customer-service-grpc
2 | spring.output.ansi.enabled: always
3 |
4 | management.endpoints.web.exposure.include: metrics
5 | management.endpoint.metrics.enabled: true
6 |
7 | server.port: 8081
8 | grpc.server.port: 9091
9 |
10 | eureka:
11 | instance:
12 | status-page-url-path: /actuator/info
13 | health-check-url-path: /actuator/health
14 | client:
15 | registerWithEureka: false
16 | fetchRegistry: true
17 | serviceUrl:
18 | defaultZone: http://localhost:8761/eureka/
19 |
20 | grpc:
21 | client:
22 | account-service-grpc:
23 | address: 'discovery:///account-service-grpc'
24 | enableKeepAlive: true
25 | keepAliveWithoutCalls: true
26 | negotiationType: plaintext
--------------------------------------------------------------------------------
/customer-service-grpc/src/test/java/pl/piomin/services/grpc/customer/CustomerServicesTests.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.services.grpc.customer;
2 |
3 | public class CustomerServicesTests {
4 | }
5 |
--------------------------------------------------------------------------------
/customer-service/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
2 | /.settings/
3 | /.classpath
4 | /.project
5 |
--------------------------------------------------------------------------------
/customer-service/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 | pl.piomin
6 | sample-microservices-protobuf
7 | 1.0-SNAPSHOT
8 |
9 | customer-service
10 |
11 |
12 | ${project.artifactId}
13 |
14 |
15 |
16 |
17 | org.springframework.cloud
18 | spring-cloud-starter-openfeign
19 |
20 |
21 | org.springframework.cloud
22 | spring-cloud-starter-loadbalancer
23 |
24 |
25 | org.springframework.boot
26 | spring-boot-starter-web
27 |
28 |
29 | com.fasterxml.jackson.core
30 | jackson-databind
31 |
32 |
33 |
34 |
35 | com.google.protobuf
36 | protobuf-java
37 | 4.31.1
38 |
39 |
40 | com.googlecode.protobuf-java-format
41 | protobuf-java-format
42 | 1.4
43 |
44 |
45 | org.springframework.boot
46 | spring-boot-starter-test
47 | test
48 |
49 |
50 |
51 |
52 |
53 |
54 | com.github.os72
55 | protoc-jar-maven-plugin
56 | 3.11.4
57 |
58 |
59 | generate-sources
60 |
61 | run
62 |
63 |
64 | all
65 | true
66 | 4.26.1
67 | direct
68 | src/main/generated
69 |
70 | src/main/proto
71 |
72 |
73 |
74 |
75 |
76 |
77 | org.codehaus.mojo
78 | build-helper-maven-plugin
79 |
80 |
81 | add-source
82 | generate-sources
83 |
84 | add-source
85 |
86 |
87 |
88 | src/main/generated
89 |
90 |
91 |
92 |
93 |
94 |
95 | org.jacoco
96 | jacoco-maven-plugin
97 | 0.8.13
98 |
99 |
100 |
101 | prepare-agent
102 |
103 |
104 |
105 | report
106 | test
107 |
108 | report
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
--------------------------------------------------------------------------------
/customer-service/src/main/.gitignore:
--------------------------------------------------------------------------------
1 | /generated/
2 |
--------------------------------------------------------------------------------
/customer-service/src/main/java/pl/piomin/services/protobuf/customer/Application.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.services.protobuf.customer;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 | import org.springframework.cloud.openfeign.EnableFeignClients;
6 | import org.springframework.context.annotation.Bean;
7 | import org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter;
8 | import org.springframework.web.client.RestTemplate;
9 | import pl.piomin.services.protobuf.customer.data.CustomerRepository;
10 | import pl.piomin.services.protobuf.customer.model.CustomerProto.Customer;
11 | import pl.piomin.services.protobuf.customer.model.CustomerProto.Customer.CustomerType;
12 |
13 | import java.util.ArrayList;
14 | import java.util.Arrays;
15 | import java.util.List;
16 |
17 | @SpringBootApplication
18 | @EnableFeignClients
19 | public class Application {
20 |
21 | public static void main(String[] args) {
22 | SpringApplication.run(Application.class, args);
23 | }
24 |
25 | @Bean
26 | ProtobufHttpMessageConverter protobufHttpMessageConverter() {
27 | return new ProtobufHttpMessageConverter();
28 | }
29 |
30 | @Bean
31 | RestTemplate restTemplate(ProtobufHttpMessageConverter hmc) {
32 | return new RestTemplate(Arrays.asList(hmc));
33 | }
34 |
35 | @Bean
36 | CustomerRepository repository() {
37 | List customers = new ArrayList<>();
38 | customers.add(Customer.newBuilder().setId(1).setPesel("12345").setName("Adam Kowalski")
39 | .setType(CustomerType.INDIVIDUAL).build());
40 | customers.add(Customer.newBuilder().setId(2).setPesel("12346").setName("Anna Malinowska")
41 | .setType(CustomerType.INDIVIDUAL).build());
42 | customers.add(Customer.newBuilder().setId(3).setPesel("12347").setName("Paweł Michalski")
43 | .setType(CustomerType.INDIVIDUAL).build());
44 | customers.add(Customer.newBuilder().setId(4).setPesel("12348").setName("Karolina Lewandowska")
45 | .setType(CustomerType.INDIVIDUAL).build());
46 | return new CustomerRepository(customers);
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/customer-service/src/main/java/pl/piomin/services/protobuf/customer/config/LoadBalancerConfigurationProperties.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.services.protobuf.customer.config;
2 |
3 | import org.springframework.boot.context.properties.ConfigurationProperties;
4 | import org.springframework.context.annotation.Configuration;
5 |
6 | import java.util.ArrayList;
7 | import java.util.List;
8 |
9 | @Configuration
10 | @ConfigurationProperties("spring.cloud.loadbalancer")
11 | public class LoadBalancerConfigurationProperties {
12 |
13 | List instances = new ArrayList<>();
14 |
15 | public List getInstances() {
16 | return instances;
17 | }
18 |
19 | public void setInstances(List instances) {
20 | this.instances = instances;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/customer-service/src/main/java/pl/piomin/services/protobuf/customer/config/ServiceConfig.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.services.protobuf.customer.config;
2 |
3 | public class ServiceConfig {
4 |
5 | private String name;
6 | private String servers;
7 |
8 | public String getName() {
9 | return name;
10 | }
11 |
12 | public void setName(String name) {
13 | this.name = name;
14 | }
15 |
16 | public String getServers() {
17 | return servers;
18 | }
19 |
20 | public void setServers(String servers) {
21 | this.servers = servers;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/customer-service/src/main/java/pl/piomin/services/protobuf/customer/contract/AccountClient.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.services.protobuf.customer.contract;
2 |
3 | import org.springframework.cloud.openfeign.FeignClient;
4 | import org.springframework.web.bind.annotation.PathVariable;
5 | import org.springframework.web.bind.annotation.RequestMapping;
6 | import org.springframework.web.bind.annotation.RequestMethod;
7 |
8 | import pl.piomin.services.protobuf.customer.model.CustomerProto.Accounts;
9 |
10 | @FeignClient(value = "account-service")
11 | public interface AccountClient {
12 |
13 | @RequestMapping(method = RequestMethod.GET, value = "/accounts/customer/{customerId}")
14 | Accounts getAccounts(@PathVariable("customerId") Integer customerId);
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/customer-service/src/main/java/pl/piomin/services/protobuf/customer/controller/CustomerController.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.services.protobuf.customer.controller;
2 |
3 | import java.util.logging.Logger;
4 |
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.web.bind.annotation.PathVariable;
7 | import org.springframework.web.bind.annotation.RequestMapping;
8 | import org.springframework.web.bind.annotation.RestController;
9 |
10 | import pl.piomin.services.protobuf.customer.contract.AccountClient;
11 | import pl.piomin.services.protobuf.customer.data.CustomerRepository;
12 | import pl.piomin.services.protobuf.customer.model.CustomerProto.Accounts;
13 | import pl.piomin.services.protobuf.customer.model.CustomerProto.Customer;
14 | import pl.piomin.services.protobuf.customer.model.CustomerProto.Customers;
15 |
16 | @RestController
17 | public class CustomerController {
18 |
19 | @Autowired
20 | CustomerRepository repository;
21 | @Autowired
22 | AccountClient accountClient;
23 |
24 | protected Logger logger = Logger.getLogger(CustomerController.class.getName());
25 |
26 | @RequestMapping(value = "/customers/pesel/{pesel}", produces = "application/x-protobuf")
27 | public Customer findByPesel(@PathVariable("pesel") String pesel) {
28 | logger.info(String.format("Customer.findByPesel(%s)", pesel));
29 | return repository.findByPesel(pesel);
30 | }
31 |
32 | @RequestMapping(value = "/customers", produces = "application/x-protobuf")
33 | public Customers findAll() {
34 | logger.info("Customer.findAll()");
35 | return Customers.newBuilder().addAllCustomers(repository.findAll()).build();
36 | }
37 |
38 | @RequestMapping(value = "/customers/{id}", produces = "application/x-protobuf")
39 | public Customer findById(@PathVariable("id") Integer id) {
40 | logger.info(String.format("Customer.findById(%s)", id));
41 | Customer customer = repository.findById(id);
42 | Accounts accounts = accountClient.getAccounts(id);
43 | customer = Customer.newBuilder(customer).addAllAccounts(accounts.getAccountList()).build();
44 | return customer;
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/customer-service/src/main/java/pl/piomin/services/protobuf/customer/data/CustomerRepository.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.services.protobuf.customer.data;
2 |
3 | import java.util.List;
4 |
5 | import pl.piomin.services.protobuf.customer.model.CustomerProto.Customer;
6 |
7 | public class CustomerRepository {
8 |
9 | private List customers;
10 |
11 | public CustomerRepository(List customers) {
12 | this.customers = customers;
13 | }
14 |
15 | public Customer findById(int id) {
16 | return customers.stream().filter(it -> it.getId() == id).findFirst().orElseThrow();
17 | }
18 |
19 | public Customer findByPesel(String pesel) {
20 | return customers.stream().filter(it -> it.getPesel().equals(pesel)).findFirst().orElseThrow();
21 | }
22 |
23 | public List findAll() {
24 | return customers;
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/customer-service/src/main/java/pl/piomin/services/protobuf/customer/loadbalancer/StaticServiceInstance.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.services.protobuf.customer.loadbalancer;
2 |
3 | import org.springframework.cloud.client.ServiceInstance;
4 |
5 | import java.net.URI;
6 | import java.util.Map;
7 |
8 | public class StaticServiceInstance implements ServiceInstance {
9 |
10 | private String serviceName;
11 | private String address;
12 |
13 | public StaticServiceInstance(String serviceName, String address) {
14 | this.serviceName = serviceName;
15 | this.address = address;
16 | }
17 |
18 | @Override
19 | public String getServiceId() {
20 | return serviceName;
21 | }
22 |
23 | @Override
24 | public String getHost() {
25 | return address.split(":", 2)[0];
26 | }
27 |
28 | @Override
29 | public int getPort() {
30 | return Integer.parseInt(address.split(":", 2)[1]);
31 | }
32 |
33 | @Override
34 | public boolean isSecure() {
35 | return false;
36 | }
37 |
38 | @Override
39 | public URI getUri() {
40 | return null;
41 | }
42 |
43 | @Override
44 | public Map getMetadata() {
45 | return Map.of();
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/customer-service/src/main/java/pl/piomin/services/protobuf/customer/loadbalancer/StaticServiceInstanceListSupplier.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.services.protobuf.customer.loadbalancer;
2 |
3 | import org.springframework.cloud.client.ServiceInstance;
4 | import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
5 | import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
6 | import org.springframework.core.env.Environment;
7 | import pl.piomin.services.protobuf.customer.config.LoadBalancerConfigurationProperties;
8 | import pl.piomin.services.protobuf.customer.config.ServiceConfig;
9 | import reactor.core.publisher.Flux;
10 |
11 | import java.util.Arrays;
12 | import java.util.List;
13 | import java.util.stream.Collectors;
14 |
15 | public class StaticServiceInstanceListSupplier implements ServiceInstanceListSupplier {
16 |
17 | private LoadBalancerConfigurationProperties properties;
18 | private Environment environment;
19 |
20 | @Override
21 | public String getServiceId() {
22 | return environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
23 | }
24 |
25 | @Override
26 | public Flux> get() {
27 | ServiceConfig config = properties.getInstances().stream()
28 | .filter(it -> it.getName().equals(getServiceId()))
29 | .findAny()
30 | .orElseThrow();
31 |
32 | List instances = Arrays.stream(config.getServers().split(",", 0))
33 | .map( it -> new StaticServiceInstance(getServiceId(), it))
34 | .collect(Collectors.toList());
35 |
36 | return Flux.just(instances);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/customer-service/src/main/proto/customer.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package model;
4 |
5 | option java_package = "pl.piomin.services.protobuf.customer.model";
6 | option java_outer_classname = "CustomerProto";
7 |
8 | message Accounts {
9 | repeated Account account = 1;
10 | }
11 |
12 | message Account {
13 |
14 | int32 id = 1;
15 | string number = 2;
16 | int32 customer_id = 3;
17 |
18 | }
19 |
20 | message Customers {
21 | repeated Customer customers = 1;
22 | }
23 |
24 | message Customer {
25 |
26 | int32 id = 1;
27 | string pesel = 2;
28 | string name = 3;
29 | CustomerType type = 4;
30 | repeated Account accounts = 5;
31 |
32 | enum CustomerType {
33 | INDIVIDUAL = 0;
34 | COMPANY = 1;
35 | }
36 |
37 | }
--------------------------------------------------------------------------------
/customer-service/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | server:
2 | port: ${PORT:3333}
3 |
4 | spring:
5 | application:
6 | name: customer-service
7 | cloud:
8 | loadbalancer:
9 | ribbon:
10 | enabled: false
11 | instances:
12 | - name: account-service
13 | servers: localhost:2222
14 |
15 | #eureka:
16 | # client:
17 | # serviceUrl:
18 | # defaultZone: ${DISCOVERY_URL:http://localhost:8761}/eureka/
19 | # instance:
20 | # leaseRenewalIntervalInSeconds: 1
21 | # leaseExpirationDurationInSeconds: 2
22 |
23 | ribbon:
24 | listOfServers: localhost:2222
25 | # eureka:
26 | # enabled: true
--------------------------------------------------------------------------------
/customer-service/src/test/java/pl/piomin/services/protobuf/customer/CustomerApplicationTest.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.services.protobuf.customer;
2 |
3 | import java.util.logging.Logger;
4 |
5 | import org.junit.jupiter.api.Test;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.boot.test.context.SpringBootTest;
8 | import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
9 | import org.springframework.boot.test.context.TestConfiguration;
10 | import org.springframework.boot.test.web.client.TestRestTemplate;
11 | import org.springframework.boot.web.client.RestTemplateBuilder;
12 | import org.springframework.context.annotation.Bean;
13 | import org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter;
14 |
15 | import pl.piomin.services.protobuf.customer.model.CustomerProto.Customer;
16 | import pl.piomin.services.protobuf.customer.model.CustomerProto.Customers;
17 |
18 | @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
19 | public class CustomerApplicationTest {
20 |
21 | protected Logger logger = Logger.getLogger(CustomerApplicationTest.class.getName());
22 |
23 | @Autowired
24 | TestRestTemplate template;
25 |
26 | // @Test
27 | public void testFindById() {
28 | Customer c = this.template.getForObject("/customers/{id}", Customer.class, 1);
29 | logger.info("Customer[\n" + c + "]");
30 | }
31 |
32 | @Test
33 | public void testFindByPesel() {
34 | Customer c = this.template.getForObject("/customers/pesel/{pesel}", Customer.class, "12346");
35 | logger.info("Customer[\n" + c + "]");
36 | }
37 |
38 | @Test
39 | public void testFindAll() {
40 | Customers c = this.template.getForObject("/customers", Customers.class);
41 | logger.info("Customers[\n" + c + "]");
42 | }
43 |
44 |
45 | @TestConfiguration
46 | static class Config {
47 |
48 | @Bean
49 | public RestTemplateBuilder restTemplateBuilder() {
50 | return new RestTemplateBuilder().additionalMessageConverters(new ProtobufHttpMessageConverter());
51 | }
52 |
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/discovery-service/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 |
8 | org.springframework.boot
9 | spring-boot-starter-parent
10 | 3.5.0
11 |
12 |
13 |
14 | pl.piomin
15 | discovery-service
16 | 1.0-SNAPSHOT
17 |
18 |
19 | 2025.0.0
20 |
21 |
22 |
23 |
24 | org.springframework.cloud
25 | spring-cloud-starter-netflix-eureka-server
26 |
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 |
--------------------------------------------------------------------------------
/discovery-service/src/main/java/pl/piomin/services/discovery/DiscoveryService.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.services.discovery;
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 DiscoveryService {
10 |
11 | public static void main(String[] args) {
12 | SpringApplication.run(DiscoveryService.class, args);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/discovery-service/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | server:
2 | port: 8761
3 |
4 | eureka:
5 | instance:
6 | hostname: localhost
7 | client:
8 | registerWithEureka: false
9 | fetchRegistry: false
10 | serviceUrl:
11 | defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
12 |
13 | spring:
14 | application:
15 | name: discovery-service
16 | output:
17 | ansi:
18 | enabled: always
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 | pl.piomin
5 | sample-microservices-protobuf
6 | 1.0-SNAPSHOT
7 | pom
8 |
9 |
10 | 21
11 | piomin_sample-microservices-protobuf
12 | piomin
13 | https://sonarcloud.io
14 | 1.63.0
15 | 3.1.0.RELEASE
16 |
17 |
18 |
19 | org.springframework.boot
20 | spring-boot-starter-parent
21 | 3.5.0
22 |
23 |
24 |
25 |
26 |
27 | org.springframework.cloud
28 | spring-cloud-dependencies
29 | 2025.0.0
30 | pom
31 | import
32 |
33 |
34 |
35 |
36 |
37 | account-service
38 | customer-service
39 | account-service-grpc
40 | customer-service-grpc
41 | discovery-service
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | ## Spring Boot with Protocol Buffers and gRPC [](https://twitter.com/piotr_minkowski)
2 |
3 | [](https://circleci.com/gh/piomin/sample-microservices-protobuf)
4 |
5 | [](https://sonarcloud.io/dashboard?id=piomin_sample-microservices-protobuf)
6 | [](https://sonarcloud.io/dashboard?id=piomin_sample-microservices-protobuf)
7 | [](https://sonarcloud.io/dashboard?id=piomin_sample-microservices-protobuf)
8 | [](https://sonarcloud.io/dashboard?id=piomin_sample-microservices-protobuf)
9 |
10 | There are two articles related to that repo:
11 | 1. How to expose Protocol Buffers over REST. Detailed description can be found here: [Exposing Microservices over REST Protocol Buffers](https://piotrminkowski.com/2017/06/05/exposing-microservices-over-rest-protocol-buffers/)
12 | 2. How to create gRPC service with Spring Boot and integrate gRPC client with Spring Cloud discovery. Detailed description can be found here: [Introduction to gRPC with Spring Boot](https://piotrminkowski.com/2023/08/29/introduction-to-grpc-with-spring-boot/)
13 |
14 | ## Application Architecture
15 |
16 | This project demonstrates a microservices architecture using Spring Boot with two distinct inter-service communication approaches: **REST with Protocol Buffers** and **gRPC**. The same business logic (Account and Customer management) is implemented in parallel for hands-on comparison.
17 |
18 | ### Microservices Overview
19 |
20 | The repository contains **5 Spring Boot applications** and one service discovery component:
21 |
22 | #### Service Discovery
23 | - **discovery-service**
24 | - **Port:** 8761
25 | - **Role:** Netflix Eureka server for service registration and discovery
26 | - **Dashboard URL:** http://localhost:8761
27 |
28 | #### Business Services (REST + Protocol Buffers)
29 | - **account-service**
30 | - **Port:** 2222 (configurable via `PORT` env var)
31 | - **Communication:** HTTP + Protobuf
32 | - **Proto file:** `account-service/src/main/proto/account.proto`
33 |
34 | - **customer-service**
35 | - **Port:** 3333 (configurable via `PORT` env var)
36 | - **Communication:** HTTP + Protobuf
37 | - **Proto file:** `customer-service/src/main/proto/customer.proto`
38 |
39 | #### Business Services (gRPC)
40 | - **account-service-grpc**
41 | - **HTTP Port:** 8081
42 | - **gRPC Port:** 9091
43 | - **Library:** `grpc-server-spring-boot-starter`
44 | - **Services:** `FindByNumber`, `FindByCustomer`, `FindAll`, `AddAccount`
45 |
46 | - **customer-service-grpc**
47 | - **HTTP Port:** 8081
48 | - **gRPC Port:** 9091
49 | - **Services:** `FindByPesel`, `FindById`, `FindAll`, `AddCustomer`
50 | - **Discovery:** Registers with Eureka
51 |
52 | ### Technology Stack
53 |
54 | - **Java 21**
55 | - **Spring Boot 3.4.5**
56 | - **Spring Cloud 2024.0.1** (Eureka, LoadBalancer, OpenFeign)
57 | - **Protocol Buffers 4.31.1**
58 | - **gRPC 1.63.0**
59 | - **Maven 3.8+**
60 | - **Docker & Docker Compose** (optional)
61 |
62 | ### Communication Patterns
63 |
64 | #### REST + Protobuf
65 |
66 | ```
67 | Client → API Gateway → Service Discovery → Customer Service (HTTP:3333, Protobuf) → Account Service (HTTP:2222, Protobuf)
68 | ```
69 |
70 | #### gRPC
71 |
72 | ```
73 | Client → gRPC Client → Service Discovery → Customer Service gRPC (9091) → Account Service gRPC (9091)
74 | ```
75 |
76 | #### Service Discovery Flow
77 |
78 | 1. Start **discovery-service** (8761).
79 | 2. Each microservice registers with Eureka.
80 | 3. Clients and services discover each other by querying Eureka.
81 | 4. Spring Cloud LoadBalancer handles service-side load balancing.
82 | 5. Health checks via Spring Boot Actuator endpoints.
83 |
84 | ## Running Applications Locally
85 |
86 | ### Prerequisites
87 |
88 | - **Java 21+** (`java --version`)
89 | - **Maven 3.8+** (`mvn --version`)
90 | - **Git 2+** (`git --version`)
91 | - Optional: **Docker & Docker Compose**
92 |
93 | ### Building the Applications
94 |
95 | 1. Clone the repo:
96 | ```bash
97 | git clone https://github.com/piomin/sample-microservices-protobuf.git
98 | cd sample-microservices-protobuf
99 | ```
100 | 2. Compile (includes Protobuf & gRPC codegen):
101 | ```bash
102 | mvn clean compile
103 | ```
104 | 3. Package JARs (skip tests for speed):
105 | ```bash
106 | mvn clean package -DskipTests
107 | ```
108 |
109 | ### Service Startup Order
110 |
111 | > **Important:** Always start in this sequence to ensure proper registration.
112 |
113 | 1. **Discovery Service**
114 | ```bash
115 | cd discovery-service
116 | mvn spring-boot:run
117 | ```
118 | 2. **REST + Protobuf Approach**
119 | ```bash
120 | # In parallel terminals:
121 | cd account-service && mvn spring-boot:run
122 | cd customer-service && mvn spring-boot:run
123 | ```
124 | **OR**
125 | **gRPC Approach**
126 | ```bash
127 | cd account-service-grpc && mvn spring-boot:run
128 | cd customer-service-grpc && mvn spring-boot:run
129 | ```
130 |
131 | ### Running with JARs
132 |
133 | ```bash
134 | # Discovery
135 | java -jar discovery-service/target/discovery-service-*.jar
136 | # Choose one approach per microservice:
137 | java -jar account-service/target/*.jar
138 | java -jar customer-service/target/*.jar
139 | # OR for gRPC:
140 | java -jar account-service-grpc/target/*.jar
141 | java -jar customer-service-grpc/target/*.jar
142 | ```
143 |
144 | ### Verification & Testing
145 |
146 | - **Eureka Dashboard:** http://localhost:8761
147 | - **Health Checks:**
148 | ```bash
149 | curl http://localhost:8761/actuator/health
150 | curl http://localhost:2222/actuator/health
151 | curl http://localhost:3333/actuator/health
152 | curl http://localhost:9091/actuator/health
153 | ```
154 | - **REST API Example:**
155 | ```bash
156 | curl -H "Accept: application/json" http://localhost:3333/customers
157 | ```
158 | - **gRPC Example (using grpcurl):**
159 | ```bash
160 | grpcurl -plaintext -d '{}' localhost:9091 model.CustomersService/FindAll
161 | ```
162 |
163 | ### Port Reference
164 |
165 | | Service | HTTP Port | gRPC Port | Env Var | Protocol |
166 | |---------------------------|-----------|-----------|---------|-----------------|
167 | | discovery-service | 8761 | – | – | HTTP |
168 | | account-service | 2222 | – | PORT | HTTP + Protobuf |
169 | | customer-service | 3333 | – | PORT | HTTP + Protobuf |
170 | | account-service-grpc | 8081 | 9091 | – | gRPC |
171 | | customer-service-grpc | 8081 | 9091 | – | gRPC |
172 |
173 | ### Troubleshooting & Tips
174 |
175 | - **Port Conflicts:**
176 | ```bash
177 | lsof -ti:8761 | xargs kill -9
178 | ```
179 | - **Protobuf Errors:**
180 | ```bash
181 | mvn clean compile -X
182 | ```
183 | - **Increase JVM Memory:**
184 | ```bash
185 | export MAVEN_OPTS="-Xmx2G -Xms1G"
186 | mvn spring-boot:run
187 | ```
188 | - **Hot Reloading:** Add DevTools dependency:
189 | ```xml
190 |
191 | org.springframework.boot
192 | spring-boot-devtools
193 | runtime
194 | true
195 |
196 | ```
197 | - **gRPC Testing:** Use `grpcurl` or BloomRPC for introspection.
198 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": [
4 | "config:base",":dependencyDashboard"
5 | ],
6 | "packageRules": [
7 | {
8 | "matchUpdateTypes": ["minor", "patch", "pin", "digest"],
9 | "automerge": true
10 | }
11 | ],
12 | "prCreation": "not-pending"
13 | }
--------------------------------------------------------------------------------