{"id":1356,"date":"2022-02-27T19:56:41","date_gmt":"2022-02-28T02:56:41","guid":{"rendered":"https:\/\/bullyrooks.com\/?p=1356"},"modified":"2022-02-27T19:57:25","modified_gmt":"2022-02-28T02:57:25","slug":"kube-cloud-pt5-create-an-event-consumer","status":"publish","type":"post","link":"https:\/\/bullyrooks.com\/index.php\/2022\/02\/27\/kube-cloud-pt5-create-an-event-consumer\/","title":{"rendered":"Kube Cloud Pt5 | Create an Event Consumer"},"content":{"rendered":"\n<p>Now that we&#8217;ve got messages being published to kafka, we are going to need to build our consumer that receives those events and stores them into the mongo database.<\/p>\n\n\n\n<p>Go ahead and create a new repository called message-repository according to <a href=\"https:\/\/bullyrooks.com\/index.php\/2022\/01\/02\/cloud-kube-create-github-repo\/\">the microservice startup course here<\/a>.  <\/p>\n\n\n\n<p>This is the pom.xml that we&#8217;ll need:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt;\n&lt;project xmlns=\"http:\/\/maven.apache.org\/POM\/4.0.0\" xmlns:xsi=\"http:\/\/www.w3.org\/2001\/XMLSchema-instance\"\n\txsi:schemaLocation=\"http:\/\/maven.apache.org\/POM\/4.0.0 https:\/\/maven.apache.org\/xsd\/maven-4.0.0.xsd\"&gt;\n\t&lt;modelVersion&gt;4.0.0&lt;\/modelVersion&gt;\n\t&lt;parent&gt;\n\t\t&lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\n\t\t&lt;artifactId&gt;spring-boot-starter-parent&lt;\/artifactId&gt;\n\t\t&lt;version&gt;2.6.3&lt;\/version&gt;\n\t\t&lt;relativePath\/&gt; &lt;!-- lookup parent from repository --&gt;\n\t&lt;\/parent&gt;\n\t&lt;groupId&gt;com.bullyrooks&lt;\/groupId&gt;\n\t&lt;artifactId&gt;message-repository&lt;\/artifactId&gt;\n\t&lt;version&gt;0.0.1-SNAPSHOT&lt;\/version&gt;\n\t&lt;name&gt;message-repository&lt;\/name&gt;\n\t&lt;description&gt;Stores messages&lt;\/description&gt;\n\t&lt;properties&gt;\n\t\t&lt;java.version&gt;11&lt;\/java.version&gt;\n\t\t&lt;spring-cloud.version&gt;2021.0.1&lt;\/spring-cloud.version&gt;\n\t\t&lt;org.mapstruct.version&gt;1.4.2.Final&lt;\/org.mapstruct.version&gt;\n\t\t&lt;logstash-logback-encoder.version&gt;6.4&lt;\/logstash-logback-encoder.version&gt;\n\t\t&lt;log4j2.version&gt;2.17.1&lt;\/log4j2.version&gt;\n\t\t&lt;logzio-logback-appender.version&gt;v1.0.25&lt;\/logzio-logback-appender.version&gt;\n\t\t&lt;micrometer-registry-logzio.version&gt;1.0.2&lt;\/micrometer-registry-logzio.version&gt;\n\t\t&lt;javafaker.version&gt;1.0.2&lt;\/javafaker.version&gt;\n\t&lt;\/properties&gt;\n\t&lt;dependencies&gt;\n\t\t&lt;dependency&gt;\n\t\t\t&lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\n\t\t\t&lt;artifactId&gt;spring-boot-starter-web&lt;\/artifactId&gt;\n\t\t&lt;\/dependency&gt;\n\n\t\t&lt;dependency&gt;\n\t\t\t&lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\n\t\t\t&lt;artifactId&gt;spring-boot-starter-data-mongodb&lt;\/artifactId&gt;\n\t\t&lt;\/dependency&gt;\n\t\t&lt;!-- Streams and Kafka --&gt;\n\t\t&lt;dependency&gt;\n\t\t\t&lt;groupId&gt;org.springframework.cloud&lt;\/groupId&gt;\n\t\t\t&lt;artifactId&gt;spring-cloud-stream&lt;\/artifactId&gt;\n\t\t&lt;\/dependency&gt;\n\t\t&lt;dependency&gt;\n\t\t\t&lt;groupId&gt;org.springframework.cloud&lt;\/groupId&gt;\n\t\t\t&lt;artifactId&gt;spring-cloud-stream-binder-kafka&lt;\/artifactId&gt;\n\t\t&lt;\/dependency&gt;\n\t\t&lt;dependency&gt;\n\t\t\t&lt;groupId&gt;org.springframework.cloud&lt;\/groupId&gt;\n\t\t\t&lt;artifactId&gt;spring-cloud-stream&lt;\/artifactId&gt;\n\t\t\t&lt;type&gt;test-jar&lt;\/type&gt;\n\t\t\t&lt;classifier&gt;test-binder&lt;\/classifier&gt;\n\t\t\t&lt;scope&gt;test&lt;\/scope&gt;\n\t\t&lt;\/dependency&gt;\n\n\t\t&lt;dependency&gt;\n\t\t\t&lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\n\t\t\t&lt;artifactId&gt;spring-boot-devtools&lt;\/artifactId&gt;\n\t\t\t&lt;scope&gt;runtime&lt;\/scope&gt;\n\t\t\t&lt;optional&gt;true&lt;\/optional&gt;\n\t\t&lt;\/dependency&gt;\n\t\t&lt;dependency&gt;\n\t\t\t&lt;groupId&gt;org.projectlombok&lt;\/groupId&gt;\n\t\t\t&lt;artifactId&gt;lombok&lt;\/artifactId&gt;\n\t\t\t&lt;optional&gt;true&lt;\/optional&gt;\n\t\t&lt;\/dependency&gt;\n\t\t&lt;dependency&gt;\n\t\t\t&lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\n\t\t\t&lt;artifactId&gt;spring-boot-starter-test&lt;\/artifactId&gt;\n\t\t\t&lt;scope&gt;test&lt;\/scope&gt;\n\t\t&lt;\/dependency&gt;\n\n\n\t\t&lt;!-- Health Checks and Metrics --&gt;\n\t\t&lt;dependency&gt;\n\t\t\t&lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\n\t\t\t&lt;artifactId&gt;spring-boot-starter-actuator&lt;\/artifactId&gt;\n\t\t&lt;\/dependency&gt;\n\n\t\t&lt;!-- mapstruct --&gt;\n\t\t&lt;dependency&gt;\n\t\t\t&lt;groupId&gt;org.mapstruct&lt;\/groupId&gt;\n\t\t\t&lt;artifactId&gt;mapstruct&lt;\/artifactId&gt;\n\t\t\t&lt;version&gt;${org.mapstruct.version}&lt;\/version&gt;\n\t\t&lt;\/dependency&gt;\n\t\t&lt;dependency&gt;\n\t\t\t&lt;groupId&gt;org.mapstruct&lt;\/groupId&gt;\n\t\t\t&lt;artifactId&gt;mapstruct-processor&lt;\/artifactId&gt;\n\t\t\t&lt;version&gt;${org.mapstruct.version}&lt;\/version&gt;\n\t\t&lt;\/dependency&gt;\n\n\t\t&lt;!-- logging --&gt;\n\t\t&lt;dependency&gt;\n\t\t\t&lt;groupId&gt;net.logstash.logback&lt;\/groupId&gt;\n\t\t\t&lt;artifactId&gt;logstash-logback-encoder&lt;\/artifactId&gt;\n\t\t\t&lt;version&gt;${logstash-logback-encoder.version}&lt;\/version&gt;\n\t\t&lt;\/dependency&gt;\n\t\t&lt;dependency&gt;\n\t\t\t&lt;groupId&gt;io.logz.logback&lt;\/groupId&gt;\n\t\t\t&lt;artifactId&gt;logzio-logback-appender&lt;\/artifactId&gt;\n\t\t\t&lt;version&gt;${logzio-logback-appender.version}&lt;\/version&gt;\n\t\t&lt;\/dependency&gt;\n\t\t&lt;dependency&gt;\n\t\t\t&lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\n\t\t\t&lt;artifactId&gt;spring-boot-starter-aop&lt;\/artifactId&gt;\n\t\t&lt;\/dependency&gt;\n\t\t&lt;dependency&gt;\n\t\t\t&lt;groupId&gt;io.logz.micrometer&lt;\/groupId&gt;\n\t\t\t&lt;artifactId&gt;micrometer-registry-logzio&lt;\/artifactId&gt;\n\t\t\t&lt;version&gt;${micrometer-registry-logzio.version}&lt;\/version&gt;\n\t\t&lt;\/dependency&gt;\n\n\t\t&lt;dependency&gt;\n\t\t\t&lt;groupId&gt;com.github.javafaker&lt;\/groupId&gt;\n\t\t\t&lt;artifactId&gt;javafaker&lt;\/artifactId&gt;\n\t\t\t&lt;version&gt;${javafaker.version}&lt;\/version&gt;\n\t\t&lt;\/dependency&gt;\n\n\t\t&lt;dependency&gt;\n\t\t\t&lt;groupId&gt;de.flapdoodle.embed&lt;\/groupId&gt;\n\t\t\t&lt;artifactId&gt;de.flapdoodle.embed.mongo&lt;\/artifactId&gt;\n\t\t\t&lt;scope&gt;test&lt;\/scope&gt;\n\t\t&lt;\/dependency&gt;\n\n\t&lt;\/dependencies&gt;\n\t&lt;dependencyManagement&gt;\n\t\t&lt;dependencies&gt;\n\t\t\t&lt;dependency&gt;\n\t\t\t\t&lt;groupId&gt;org.springframework.cloud&lt;\/groupId&gt;\n\t\t\t\t&lt;artifactId&gt;spring-cloud-dependencies&lt;\/artifactId&gt;\n\t\t\t\t&lt;version&gt;${spring-cloud.version}&lt;\/version&gt;\n\t\t\t\t&lt;type&gt;pom&lt;\/type&gt;\n\t\t\t\t&lt;scope&gt;import&lt;\/scope&gt;\n\t\t\t&lt;\/dependency&gt;\n\t\t&lt;\/dependencies&gt;\n\t&lt;\/dependencyManagement&gt;\n\n\t&lt;build&gt;\n\t\t&lt;plugins&gt;\n\t\t\t&lt;plugin&gt;\n\t\t\t\t&lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\n\t\t\t\t&lt;artifactId&gt;spring-boot-maven-plugin&lt;\/artifactId&gt;\n\t\t\t\t&lt;configuration&gt;\n\t\t\t\t\t&lt;excludes&gt;\n\t\t\t\t\t\t&lt;exclude&gt;\n\t\t\t\t\t\t\t&lt;groupId&gt;org.projectlombok&lt;\/groupId&gt;\n\t\t\t\t\t\t\t&lt;artifactId&gt;lombok&lt;\/artifactId&gt;\n\t\t\t\t\t\t&lt;\/exclude&gt;\n\t\t\t\t\t&lt;\/excludes&gt;\n\t\t\t\t&lt;\/configuration&gt;\n\t\t\t&lt;\/plugin&gt;\n\t\t&lt;\/plugins&gt;\n\t&lt;\/build&gt;\n\n&lt;\/project&gt;\n\n<\/code><\/pre>\n\n\n\n<p>You can see we&#8217;re adding the spring cloud streaming libraries as well as the mongodb ones.<\/p>\n\n\n\n<p>Now make the <code>MessageEvent<\/code> class in <code>com.bullyrooks.messagerepository.event.dto<\/code><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>package com.bullyrooks.messagerepository.event.dto;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n@Data\n@Builder\n@AllArgsConstructor\n@NoArgsConstructor\npublic class MessageEvent {\n    private String messageId;\n    private String firstName;\n    private String lastName;\n    private String message;\n}<\/code><\/pre>\n\n\n\n<p>This is the object structure that our event will serialize into<\/p>\n\n\n\n<p>Now copy over the <code>MessageRepository<\/code>, <code>MessageDocument <\/code>and <code>MessageDocumentMapper <\/code>from the previous implementation (you can find them in the github history of the <code>cloud-application<\/code> project if you didn&#8217;t stash the changes anywhere).  I put them into <code>com.bullyrooks.messagerepository.repository<\/code> under <code>document <\/code>and <code>mapper <\/code>packages as needed.<\/p>\n\n\n\n<p>There is one change to callout though.  Since I don&#8217;t need a model object (the <code>MessageDocument <\/code>will work) the mapper maps from the event to the document.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@Mapper\npublic interface MessageDocumentMapper {\n    MessageDocumentMapper INSTANCE = Mappers.getMapper(MessageDocumentMapper.class);\n\n    MessageDocument eventToDocument(MessageEvent msgEvent);\n\n    MessageEvent documentToEvent(MessageDocument returnDoc);\n}<\/code><\/pre>\n\n\n\n<p>The <code>MessageService <\/code>in <code>com.bullyrooks.messagerepository.service<\/code> is very stripped down<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>package com.bullyrooks.messagerepository.service;\n\nimport com.bullyrooks.messagerepository.config.LoggingEnabled;\nimport com.bullyrooks.messagerepository.event.dto.MessageEvent;\nimport com.bullyrooks.messagerepository.repository.MessageRepository;\nimport com.bullyrooks.messagerepository.repository.document.MessageDocument;\nimport com.bullyrooks.messagerepository.repository.mapper.MessageDocumentMapper;\nimport com.bullyrooks.messagerepository.service.model.Message;\nimport io.micrometer.core.instrument.Counter;\nimport io.micrometer.core.instrument.MeterRegistry;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\n@Service\n@Slf4j\n@LoggingEnabled\npublic class MessageStorageService {\n\n    MessageRepository messageRepository;\n    MeterRegistry logzioMeterRegistry;\n\n    Counter messageSaved;\n\n    @Autowired\n    public MessageStorageService(MessageRepository messageRepository,\n                                 MeterRegistry logzioMeterRegistry){\n        this.messageRepository = messageRepository;\n        this.logzioMeterRegistry = logzioMeterRegistry;\n        initCounters();\n    }\n\n    private void initCounters() {\n        messageSaved = Counter.builder(\"message.stored.count\")\n                .description(\"Number of messages successfully stored in the repository\")\n                .register(logzioMeterRegistry);\n    }\n\n\n    public MessageDocument saveMessage(MessageDocument msgDoc){\n        log.info(\"saving document: {}\", msgDoc);\n        MessageDocument returnDoc = messageRepository.save(msgDoc);\n        messageSaved.increment();\n        return returnDoc;\n    }\n}<\/code><\/pre>\n\n\n\n<p>Its essentially a copy of the one from <code>cloud-application<\/code>, but it only writes to the repository.<\/p>\n\n\n\n<p>Now lets implement the spring cloud stream consumer.  Create a class called <code>MessageEventConsumer <\/code>in <code>com.bullyrooks.messagerepository.event<\/code> with this content<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>package com.bullyrooks.messagerepository.event;\n\nimport com.bullyrooks.messagerepository.event.dto.MessageEvent;\nimport com.bullyrooks.messagerepository.repository.mapper.MessageDocumentMapper;\nimport com.bullyrooks.messagerepository.service.MessageStorageService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.stereotype.Component;\n\nimport java.util.function.Consumer;\n\n@Component\n@Slf4j\npublic class MessageEventConsumer {\n\n    @Autowired\n    MessageStorageService storageService;\n\n    @Bean\n    public Consumer&lt;MessageEvent&gt; consumeMessageEvent(){\n        return (eventIn) -&gt; storageService\n                .saveMessage(MessageDocumentMapper.INSTANCE.eventToDocument(eventIn));\n    }\n}\n<\/code><\/pre>\n\n\n\n<p>This is all you need.  A component that injects the service.  The Bean defines the function which has a special signature.  The Consumer tells the function that it should expect a <code>MessageEvent <\/code>object, this is different than a normal signature where the input is in the parenthesis.  Then we&#8217;re saying for each <code>MessageEvent<\/code> (<code>eventIn<\/code>), call the mapper and send the result to the storage service.  That&#8217;s all we have to do! <\/p>\n\n\n\n<p>the <code>application.yaml<\/code> should look familiar we&#8217;ve seen the mongo, health and kafka configuration before<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>spring:\n  application:\n    name: message-repository\n  cloud:\n    stream:\n      bindings:\n        consumeMessageEvent-in-0:\n          destination: message.created\n  kafka:\n    properties:\n      sasl.mechanism: PLAIN\n      bootstrap.servers: pkc-2396y.us-east-1.aws.confluent.cloud:9092\n      security.protocol: SASL_SSL\n  data:\n    mongodb:\n      uri: mongodb+srv:\/\/bullyrooks:${mongodb.password}@bullyrooks.4zqpz.mongodb.net\/bullyrooks?retryWrites=true&amp;w=majority\nmanagement:\n  endpoints:\n    web:\n      exposure:\n        include: \"*\"\n  endpoint:\n    health:\n      probes:\n        enabled: true\n      show-details: always\n      group:\n        readiness:\n          include: \"readinessState,mongo,messageGenerator\"\n    metrics:\n      enabled: true\n\nlogging:\n  level:\n    root: INFO\n    com.bullyrooks: DEBUG\n    io.micrometer.logzio: WARN\nlogzio:\n  metrics:\n    url: https:\/\/listener.logz.io:8053\n    registry:\n      mock: false <\/code><\/pre>\n\n\n\n<p>The unique part here is that we&#8217;re defining an <code>in<\/code> binding for the topic and we need to use the same topic name (<code>destination<\/code>) that the <code>cloud-application<\/code> is publishing to.<\/p>\n\n\n\n<p>The <code>MessageEventConsumerTest <\/code>in <code>src\/test\/java\/com\/bullyrooks\/messagerepository\/event<\/code> is going to look a little new though<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>package com.bullyrooks.messagerepository.event;\n\nimport com.bullyrooks.messagerepository.event.dto.MessageEvent;\nimport com.bullyrooks.messagerepository.repository.MessageRepository;\nimport com.bullyrooks.messagerepository.repository.document.MessageDocument;\nimport com.github.javafaker.Faker;\nimport com.github.javafaker.service.FakeValuesService;\nimport com.github.javafaker.service.RandomService;\nimport lombok.extern.slf4j.Slf4j;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.autoconfigure.data.mongo.AutoConfigureDataMongo;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.cloud.stream.binder.test.InputDestination;\nimport org.springframework.cloud.stream.binder.test.TestChannelBinderConfiguration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.data.domain.Example;\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.support.GenericMessage;\nimport org.springframework.test.context.ActiveProfiles;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport java.util.Locale;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\n@ExtendWith(SpringExtension.class)\n@SpringBootTest\n@AutoConfigureDataMongo\n@Slf4j\n@ActiveProfiles(\"test\")\n@Import(TestChannelBinderConfiguration.class)\npublic class MessageEventConsumerTest {\n\n    @Autowired\n    MessageRepository messageRepository;\n\n\n    FakeValuesService fakesvc = new FakeValuesService(\n            new Locale(\"en-US\"), new RandomService());\n    Faker faker = new Faker();\n\n    @Autowired\n    private InputDestination inputDestination;\n\n    @Test\n    void testMessageStore() {\n\n        \/\/given\n        MessageEvent msgEventIn = MessageEvent.builder()\n                .firstName(faker.name().firstName())\n                .lastName(faker.name().lastName())\n                .message(faker.gameOfThrones().quote()).build();\n        Message&lt;MessageEvent&gt; messageIn = new GenericMessage&lt;&gt;(msgEventIn);\n\n        \/\/when\n        inputDestination.send(messageIn);\n\n        \/\/then\n        Example&lt;MessageDocument&gt; example = Example.of(\n                MessageDocument.builder()\n                        .firstName(msgEventIn.getFirstName())\n                        .lastName(msgEventIn.getLastName())\n                        .message(msgEventIn.getMessage())\n                        .build());\n        MessageDocument doc = messageRepository.findOne(example).get();\n        assertEquals(msgEventIn.getFirstName(), doc.getFirstName());\n        assertEquals(msgEventIn.getLastName(), doc.getLastName());\n        assertEquals(msgEventIn.getMessage(), doc.getMessage());\n    }\n\n}<\/code><\/pre>\n\n\n\n<p>We&#8217;re using the <code>TestChannelBinderConfiguration<\/code> to inject an <code>InputDestination <\/code>this time.  The input is a <code>MessageEvent<\/code> that we wrap into a <code>GenericMessage <\/code>object that we then post to that destination.  Additionally, since we don&#8217;t have an id to search the repo for, we use an example query object to search for the record we expect to be there.<\/p>\n\n\n\n<p>The default test for MessageRepositoryApplication needs an update as well.  It needs to know about mongo in order to actually be able to run.  Add these annotations<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@SpringBootTest\n@AutoConfigureDataMongo\n@ActiveProfiles(\"test\")\nclass MessageRepositoryApplicationTests {\n\n\t@Test\n\tvoid contextLoads() {\n\t}\n\n}\n<\/code><\/pre>\n\n\n\n<p>The <code>application-test.yaml<\/code> in the <code>src\/test\/resources<\/code> directory needs to know the mongodb version<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>spring:\n  mongodb:\n    embedded:\n      version: 2.6.10 # Version of Mongo to use.<\/code><\/pre>\n\n\n\n<p>Finally, add the same environment configuration to the helm <code>deployment.yaml<\/code> file as we did for<code> cloud-application<\/code>, make sure to get the mongo config copied over as well.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>          - name: SPRING_DATA_MONGODB_URI\n            valueFrom:\n              secretKeyRef:\n                name: mongo-secrets\n                key: SPRING_DATA_MONGODB_URI\n          - name: SPRING_KAFKA_PROPERTIES_SASL_JAAS_CONFIG\n            valueFrom:\n              secretKeyRef:\n                name: confluent-secrets\n                key: SPRING_KAFKA_PROPERTIES_SASL_JAAS_CONFIG<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Verifying<\/h2>\n\n\n\n<p>We could run this locally, but we won&#8217;t be able to test it directly unless we have a way to push messages onto the kafka topic.  One way to test is to run both cloud-application and message-repository locally.  You should see message requests generate events that are consumed by message-repository in the logs<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"960\" height=\"657\" src=\"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-49-1024x701.png?resize=960%2C657&#038;ssl=1\" alt=\"\" class=\"wp-image-1357\" srcset=\"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-49.png?resize=1024%2C701&amp;ssl=1 1024w, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-49.png?resize=300%2C205&amp;ssl=1 300w, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-49.png?resize=768%2C526&amp;ssl=1 768w, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-49.png?w=1027&amp;ssl=1 1027w\" sizes=\"auto, (max-width: 960px) 100vw, 960px\" data-recalc-dims=\"1\" \/><\/figure>\n\n\n\n<pre class=\"wp-block-code\"><code>2022-02-27 15:33:23,588 DEBUG &#91;,] c.b.m.c.LoggingAspect: -&gt; MessageStorageService.saveMessage invocation.  params: {\"msgDoc\":{\"messageId\":null,\"firstName\":\"Bully\",\"lastName\":\"Rooks\",\"message\":\"local test 123\"}}\n2022-02-27 15:33:23,592 INFO  &#91;,] c.b.m.s.MessageStorageService: saving document: MessageDocument(messageId=null, firstName=Bully, lastName=Rooks, message=local test 123)\n2022-02-27 15:33:23,728 DEBUG &#91;,] c.b.m.c.LoggingAspect: &lt;- MessageStorageService.saveMessage returns:{\"messageId\":\"621bfc3369057f23a56b7d2e\",\"firstName\":\"Bully\",\"lastName\":\"Rooks\",\"message\":\"local test 123\"}.  Execution time: 138ms\n<\/code><\/pre>\n\n\n\n<p>Go ahead and push up the changes to your feature branch and then merge to main when the build succeeds.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Verifying in the Cloud<\/h2>\n\n\n\n<p>Turn off all of your local instances.  They&#8217;re currently in the same consumer group as the okteto versions and could be the recipient of the message to process.  Hit the cloud-application endpoint for the okteto instance in postman<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"960\" height=\"598\" src=\"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-53-1024x638.png?resize=960%2C598&#038;ssl=1\" alt=\"\" class=\"wp-image-1365\" srcset=\"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-53.png?resize=1024%2C638&amp;ssl=1 1024w, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-53.png?resize=300%2C187&amp;ssl=1 300w, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-53.png?resize=768%2C479&amp;ssl=1 768w, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-53.png?w=1048&amp;ssl=1 1048w\" sizes=\"auto, (max-width: 960px) 100vw, 960px\" data-recalc-dims=\"1\" \/><\/figure>\n\n\n\n<p>We should see the message in confluent<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"960\" height=\"434\" src=\"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-54-1024x463.png?resize=960%2C434&#038;ssl=1\" alt=\"\" class=\"wp-image-1366\" srcset=\"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-54.png?resize=1024%2C463&amp;ssl=1 1024w, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-54.png?resize=300%2C136&amp;ssl=1 300w, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-54.png?resize=768%2C347&amp;ssl=1 768w, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-54.png?w=1256&amp;ssl=1 1256w\" sizes=\"auto, (max-width: 960px) 100vw, 960px\" data-recalc-dims=\"1\" \/><\/figure>\n\n\n\n<p>You can see the logs in Kibana<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"960\" height=\"478\" src=\"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-55-1024x510.png?resize=960%2C478&#038;ssl=1\" alt=\"\" class=\"wp-image-1367\" srcset=\"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-55.png?resize=1024%2C510&amp;ssl=1 1024w, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-55.png?resize=300%2C149&amp;ssl=1 300w, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-55.png?resize=768%2C383&amp;ssl=1 768w, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-55.png?w=1044&amp;ssl=1 1044w\" sizes=\"auto, (max-width: 960px) 100vw, 960px\" data-recalc-dims=\"1\" \/><\/figure>\n\n\n\n<p>And you can see the full trace in Jaeger<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"960\" height=\"286\" src=\"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-56-1024x305.png?resize=960%2C286&#038;ssl=1\" alt=\"\" class=\"wp-image-1368\" srcset=\"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-56.png?resize=1024%2C305&amp;ssl=1 1024w, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-56.png?resize=300%2C89&amp;ssl=1 300w, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-56.png?resize=768%2C229&amp;ssl=1 768w, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-56.png?resize=1536%2C458&amp;ssl=1 1536w, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-56.png?w=1635&amp;ssl=1 1635w\" sizes=\"auto, (max-width: 960px) 100vw, 960px\" data-recalc-dims=\"1\" \/><\/figure>\n\n\n\n<p>In particular, you can see that the calls through the controller respond faster because the longer call to mongodb is not on the original request path now.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\"><p>Note:  there is an issue with <a href=\"https:\/\/github.com\/open-telemetry\/opentelemetry-java-instrumentation\/issues\/5382\">OTEL and kafka tracing<\/a>.  I did have to make a change to my dockerfile in order to get it working:<\/p><p><\/p><\/blockquote>\n\n\n\n<pre class=\"wp-block-code\"><code>ENTRYPOINT &#91;\"java\",\"-Dotel.instrumentation.common.experimental.suppress-messaging-receive-spans=true\", \"-jar\", \"app.jar\"]\n<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<div class=\"entry-summary\">\nNow that we&#8217;ve got messages being published to kafka, we are going&hellip;\n<\/div>\n<div class=\"link-more\"><a href=\"https:\/\/bullyrooks.com\/index.php\/2022\/02\/27\/kube-cloud-pt5-create-an-event-consumer\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &ldquo;Kube Cloud Pt5 | Create an Event Consumer&rdquo;<\/span>&hellip;<\/a><\/div>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[41],"tags":[90,190,80,195,166,43,194,65],"course":[188],"class_list":["post-1356","post","type-post","status-publish","format-standard","hentry","category-software-development","tag-event-driven-architecture","tag-kafka","tag-kubernetes","tag-microservices","tag-observability","tag-spring-boot","tag-spring-cloud-stream","tag-tracing","course-kube-cloud-pt5-kafka-events","entry"],"jetpack_featured_media_url":"","jetpack-related-posts":[{"id":1339,"url":"https:\/\/bullyrooks.com\/index.php\/2022\/02\/27\/kube-cloud-pt5-create-an-event-publisher\/","url_meta":{"origin":1356,"position":0},"title":"Kube Cloud Pt5 | Create an Event Publisher","author":"Bullyrook","date":"February 27, 2022","format":false,"excerpt":"We're going to be updating cloud-application to remove the mongodb repository logic and replace it with an event publisher. Create a new branch from main git checkout -b migrate Update the pom.xml first, remove the mongodb dependencies <dependency> <groupId>org.springframework.boot<\/groupId> <artifactId>spring-boot-starter-data-mongodb<\/artifactId> <\/dependency> ... <dependency> <groupId>org.testcontainers<\/groupId> <artifactId>mongodb<\/artifactId> <version>1.16.3<\/version> <scope>test<\/scope> <\/dependency> and add\u2026","rel":"","context":"In &quot;Software Development&quot;","block_context":{"text":"Software Development","link":"https:\/\/bullyrooks.com\/index.php\/category\/software-development\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-39.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-39.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-39.png?resize=700%2C400&ssl=1 2x"},"classes":[]},{"id":1069,"url":"https:\/\/bullyrooks.com\/index.php\/2021\/07\/23\/spring-boot-lambda-implementation\/","url_meta":{"origin":1356,"position":1},"title":"Spring Boot Lambda Implementation","author":"Bullyrook","date":"July 23, 2021","format":false,"excerpt":"Now we're going to add some code. I'm going to follow my ports and adapters method of building a DTO and value object that I've used previously. Yes, its a bit of overkill for this project (especially a hello world example), but if you're using this course as a springboard\u2026","rel":"","context":"In &quot;Software Development&quot;","block_context":{"text":"Software Development","link":"https:\/\/bullyrooks.com\/index.php\/category\/software-development\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2021\/07\/image-4.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2021\/07\/image-4.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2021\/07\/image-4.png?resize=700%2C400&ssl=1 2x"},"classes":[]},{"id":1258,"url":"https:\/\/bullyrooks.com\/index.php\/2022\/02\/13\/kube-cloud-pt3-rest-interaction\/","url_meta":{"origin":1356,"position":2},"title":"Kube Cloud Pt3 | REST Interaction","author":"Bullyrook","date":"February 13, 2022","format":false,"excerpt":"Now that we've got a new service, we're going to make it discoverable via kubernetes and call it from the cloud application service. Enable Kubernetes Features Let's start a new branch in our cloud_application project $ git checkout -b kube Switched to a new branch 'kube' Edit the pom.xml and\u2026","rel":"","context":"In &quot;Software Development&quot;","block_context":{"text":"Software Development","link":"https:\/\/bullyrooks.com\/index.php\/category\/software-development\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image.png?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image.png?resize=1050%2C600&ssl=1 3x"},"classes":[]},{"id":1242,"url":"https:\/\/bullyrooks.com\/index.php\/2022\/02\/13\/kube-cloud-pt3-synchronous-service-interaction\/","url_meta":{"origin":1356,"position":3},"title":"Kube Cloud Pt3 | Synchronous Service Interaction","author":"Bullyrook","date":"February 13, 2022","format":false,"excerpt":"In this course I'm going to show you how to make another spring boot microservice (message-generator), deploy it with our first service (cloud-application). I'll show how cloud-application service can discover message-generator via kubernetes services, call an endpoint in message-generator with a feign based REST client as well as how to\u2026","rel":"","context":"In &quot;Software Development&quot;","block_context":{"text":"Software Development","link":"https:\/\/bullyrooks.com\/index.php\/category\/software-development\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/01\/image-45.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/01\/image-45.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/01\/image-45.png?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/01\/image-45.png?resize=1050%2C600&ssl=1 3x, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/01\/image-45.png?resize=1400%2C800&ssl=1 4x"},"classes":[]},{"id":827,"url":"https:\/\/bullyrooks.com\/index.php\/2020\/03\/30\/simple-spring-boot-service-to-kubernetes-application-step-1-d67f80487848\/","url_meta":{"origin":1356,"position":4},"title":"Setup: IDE and New Project","author":"Bullyrook","date":"March 30, 2020","format":false,"excerpt":"\u00a0Its very easy to get a spring boot microservice up and running. You can read any variety of articles on medium (or other service) on how to do that. However, there\u2019s little about how to do some of the more complicated things that you need in order to support a\u2026","rel":"","context":"In &quot;Software Development&quot;","block_context":{"text":"Software Development","link":"https:\/\/bullyrooks.com\/index.php\/category\/software-development\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":829,"url":"https:\/\/bullyrooks.com\/index.php\/2020\/03\/30\/simple-spring-boot-service-to-kubernetes-application-step-15-34e1bba8351b\/","url_meta":{"origin":1356,"position":5},"title":"Messaging and Event Driven Design","author":"Bullyrook","date":"March 30, 2020","format":false,"excerpt":"In order publish messages, we need a message broker and add some logic to use the message broker. We\u2019re going to use a cloud based SaaS service as a message broker and use spring cloud stream to interact with it. Setup the Message\u00a0Broker We\u2019ll need a message broker in order\u2026","rel":"","context":"In &quot;Software Development&quot;","block_context":{"text":"Software Development","link":"https:\/\/bullyrooks.com\/index.php\/category\/software-development\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"_links":{"self":[{"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/posts\/1356","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/comments?post=1356"}],"version-history":[{"count":8,"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/posts\/1356\/revisions"}],"predecessor-version":[{"id":1372,"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/posts\/1356\/revisions\/1372"}],"wp:attachment":[{"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/media?parent=1356"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/categories?post=1356"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/tags?post=1356"},{"taxonomy":"course","embeddable":true,"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/course?post=1356"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}