├── .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 [![Twitter](https://img.shields.io/twitter/follow/piotr_minkowski.svg?style=social&logo=twitter&label=Follow%20Me)](https://twitter.com/piotr_minkowski) 2 | 3 | [![CircleCI](https://circleci.com/gh/piomin/sample-microservices-protobuf.svg?style=svg)](https://circleci.com/gh/piomin/sample-microservices-protobuf) 4 | 5 | [![SonarCloud](https://sonarcloud.io/images/project_badges/sonarcloud-black.svg)](https://sonarcloud.io/dashboard?id=piomin_sample-microservices-protobuf) 6 | [![Bugs](https://sonarcloud.io/api/project_badges/measure?project=piomin_sample-microservices-protobuf&metric=bugs)](https://sonarcloud.io/dashboard?id=piomin_sample-microservices-protobuf) 7 | [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=piomin_sample-microservices-protobuf&metric=coverage)](https://sonarcloud.io/dashboard?id=piomin_sample-microservices-protobuf) 8 | [![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=piomin_sample-microservices-protobuf&metric=ncloc)](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 | } --------------------------------------------------------------------------------