Containerized nacos deployment and implementation of service discovery (gradle)

1. How to deploy mysql in a container
2. How to deploy nacos in a container

In order not to expose my server address, this article uses localhost instead of the server address. All localhosts should be adjusted to your own server addresses.
In order not to expose my server address, this article uses localhost instead of the server address. All localhosts should be adjusted to your own server addresses.

Containerize nacos and implement service discovery

  • Technologies used in this article: springclod, springcloudAlibaba, kotlin, jpa, gradle
Create project
  • Create empty project

  • Select jdk17, language level 17

New provider module

  • Create a new module (named provider) and select spring 3.0.2
  • Depend on adding spring data jpa, spring web, lombok
  • Add mysql connection pool dependency
dependencies {<!-- -->
// other denpendencies
implementation 'mysql:mysql-connector-java:8.0.26'
implementation("com.alibaba:druid:1.2.20")
// other denpendencies
}
  • The mvn official website queries the import method of springcloud, and the github page of springcloud queries the version correspondence.
  • mvn official website queries springcloudalibaba and introduces the corresponding version of springcloudalibaba
  • Introducing spring-cloud-alibaba-nacos-discovery
ext {<!-- -->
    set('springCloudVersion', "2022.0.0")
    set('springCloudAlibabaVersion', "2022.0.0.0") // Set the version number of Spring Cloud Alibaba
}

repositories {<!-- -->
    //Alibaba source
    maven {<!-- --> url 'https://maven.aliyun.com/repository/public' }
    mavenCentral()
}

dependencies {<!-- -->
...
implementation 'com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-discovery:2022.0.0.0'
...
}

dependencyManagement {<!-- -->
    imports {<!-- -->
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:${<!-- -->springCloudVersion}"
        mavenBom "com.alibaba.cloud:spring-cloud-alibaba-dependencies:${<!-- -->springCloudAlibabaVersion}" // Import Spring Cloud Alibaba BOM
    }
}
  • Open server ports 8848 and 9848

  • Configure the application (just change the database address and account password)

spring:
  cloud:
    nacos:
      discovery:
        # nacos registration center address
        server-addr: 127.0.0.1:8848
        username: nacos
        password: nacos
  #Microservice name
  application:
    name: depart-provider
  jpa:
    # Specify whether to create the table when the spring container starts, the default is false
    generate-ddl: true
    show-sql: true
    hibernate:
      # Specify not to re-update the table when the application is restarted
      ddl-auto: none
    # Turn off the lazy loading mechanism for front-end access
    open-in-view: false

  # Configure data source
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1/rdtcloud?serverTimezone=Asia/Shanghai & amp;autoReconnect=true & amp;useUnicode=true & amp;characterEncoding=UTF-8 & amp;allowMultiQueries=true & amp;useSSL= false
    username: root
    password: 'your_password'
    druid:
      #Initial number of connections
      initialSize: 5
      #Minimum number of connection pools
      minIdle: 10
      #Maximum number of connection pools
      maxActive: 20
      #Configure the timeout to wait for the connection to be obtained
      maxWait: 60000
      # Configure how often to detect idle connections that need to be closed. The unit is milliseconds.
      timeBetweenEvictionRunsMillis: 60000
      # Configure the minimum survival time of a connection in the pool, the unit is milliseconds
      minEvictableIdleTimeMillis: 300000
      # Configure the maximum survival time of a connection in the pool, in milliseconds
      maxEvictableIdleTimeMillis: 900000
      #Configure to check whether the connection is valid
      validationQuery: SELECT 1 FROM DUAL
      testWhileIdle: true
      testOnBorrow: false
      testOnReturn: false

# log
logging:
  #Console log output format
  pattern:
    console: level-%level %msg%n
  level:
    #Control the log level displayed when springboot starts
    root: info
    # hibernate related log levels
    org.hibernate: info
    #Control the log level displayed when the code you write is run
    com.rdt:debug
    # Show-sql: true displays dynamic parameter values in sql
    org.hibernate.type.descriptor.sql.BasicBinder: trace
    # Display sql query results under the premise of show-sql: true
    org.hibernate.type.descriptor.sql.BasicExtractor: trace

server:
  port: 8081
  servlet:
    encoding:
      charset: utf-8
      # Ensure character set is always applied
      force: true

New consumer module

  • Create a new module (named provider) and select spring 3.0.2
  • Depend on adding spring web and lombok
  • Introduce the microservice dependencies introduced by provider0 (spring, springcloudalibaba, nacos-discovery)
ext {<!-- -->
    set('springCloudVersion', "2022.0.0")
    set('springCloudAlibabaVersion', "2022.0.0.0") // Set the version number of Spring Cloud Alibaba
}

repositories {<!-- -->
    //Alibaba source
    maven {<!-- --> url 'https://maven.aliyun.com/repository/public' }
    mavenCentral()
}

dependencies {<!-- -->
...
implementation 'com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-discovery:2022.0.0.0'
...
}

dependencyManagement {<!-- -->
    imports {<!-- -->
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:${<!-- -->springCloudVersion}"
        mavenBom "com.alibaba.cloud:spring-cloud-alibaba-dependencies:${<!-- -->springCloudAlibabaVersion}" // Import Spring Cloud Alibaba BOM
    }
}
  • Configure consumer yml
spring:
  cloud:
    nacos:
      discovery:
        # nacos registration center address
        server-addr: 127.0.0.1:8848
        username: nacos
        password: nacos
  #Microservice name
  application:
    name: depart-consumer
  • After the above configuration is completed, start the provider and consumer. These two services can be found in nacos during registration.
  • How to containerize configuration and start nacos is in the link at the beginning of the article

Communication between consumer and provider

provider code test

Just use the traditional three-tier architecture

  • Entity class
package com.rdt.provider8081.bean

import com.fasterxml.jackson.annotation.JsonIgnoreProperties
import jakarta.persistence.Entity
import jakarta.persistence.GeneratedValue
import jakarta.persistence.GenerationType
importjakarta.persistence.Id

@Entity
@JsonIgnoreProperties(value = ["hibernateLazyInitializer", "handler", "fieldHandler"])
class Depart {<!-- -->
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Int? = null
    var name: String? = null

}
  • service layer
package com.rdt.provider8081.service

import com.rdt.provider8081.bean.Depart
import com.rdt.provider8081.repository.DepartRepository
import lombok.RequiredArgsConstructor
import org.springframework.stereotype.Service

@Service
@RequiredArgsConstructor
class DepartService(private val departRepository: DepartRepository) {<!-- -->
    fun saveDepart(depart: Depart) = departRepository.save(depart)
    fun removeDepart(id: Int) {<!-- -->
        if (departRepository.existsById(id) != null)
            departRepository.deleteById(id)
    }

    fun getDepartById(id: Int): Depart {<!-- -->
        if (departRepository.existsById(id)) {<!-- -->
            return departRepository.getReferenceById(id)
        } else {<!-- -->
            returnDepart()
        }

    }

    fun modifyDepart(depart: Depart): Depart {<!-- -->
        if (departRepository.existsById(depart.id!!)) {<!-- -->
            return departRepository.save(depart)
        }
        returnDepart()
    }

    fun findAllDeparts() = departRepository.findAll()

}
  • persistence layer
package com.rdt.provider8081.repository
  
import com.rdt.provider8081.bean.Depart
import org.springframework.data.jpa.repository.JpaRepository
  
interface DepartRepository :JpaRepository<Depart,Int>{<!-- -->
}
  • view layer
package com.rdt.provider8081.controller

import com.rdt.provider8081.bean.Depart
import com.rdt.provider8081.service.DepartService
import lombok.RequiredArgsConstructor
import org.springframework.cloud.client.discovery.DiscoveryClient
import org.springframework.web.bind.annotation.*

@RestController
@RequiredArgsConstructor
@RequestMapping("/provider/depart")
class DepartController(
    private val departService: DepartService, private val discoveryClient: DiscoveryClient
) {<!-- -->

    @PostMapping("/")
    fun saveHandle(@RequestBody depart: Depart) {<!-- -->
        println(depart.name)
        departService.saveDepart(depart)

    }

    @DeleteMapping("/{id}")
    fun deleteHandle(@PathVariable("id") id: Int) {<!-- -->
        return departService.removeDepart(id)
    }

    @PutMapping("/")
    fun updateHandle(@RequestBody depart: Depart) = departService

    @GetMapping("/get/{id}")
    fun getHandle(@PathVariable id: Int) = departService.getDepartById(id)

    @GetMapping("/list")
    fun listHandle() = departService.findAllDeparts()

    @GetMapping("/discovery")
    fun discoveryHandle(): List<String> {<!-- -->
        //Get all service names
        val services: List<String> = discoveryClient.services
        for (service in services) {<!-- -->
            // Get all microservice instances with the specified microservice name
            var instances = discoveryClient.getInstances(service)
            for (instance in instances) {<!-- -->
// val map = HashMap<String, Any>()
                val map = mutableMapOf<String, Any>()
                map["serviceName"] = service
                map["serviceId"] = instance.serviceId
                map["serviceHost"] = instance.host
                map["servicePort"] = instance.port
                map["uri"] = instance.uri
                println(map)
            }

        }
        return services
    }

}

Consumer code testing

  • Note that the current new version of springcloudAlibaba has deprecated ribbon, so load balancing needs to introduce dependencies.
  • The consumer does not need a persistence layer. The consumer calls the provider and does not interact with the database.
dependencies{<!-- -->
    //nacos-discover introduction
    implementation 'org.springframework.cloud:spring-cloud-starter-loadbalancer:4.0.4'
}
  • entity layer
package com.rdt.consumer8080.bean

class Depart {<!-- -->
     var id:Int?=null
     var name:String?=null
}
  • Configuration class
  • Consumer needs configuration class, which needs to be used in the view layer
package com.rdt.consumer8080.config
  
import org.springframework.cloud.client.loadbalancer.LoadBalanced
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.web.client.RestTemplate
  
@Configuration
class DepartConfig {<!-- -->
  
@LoadBalanced //Called in a load balancing manner
@Bean
public fun restTemplate():RestTemplate{<!-- -->
return RestTemplate()
}
  
}
  • view layer
package com.rdt.consumer8080.controller

import com.google.gson.GsonBuilder
import com.rdt.consumer8080.bean.Depart
import com.rdt.consumer8080.consts.ConstTime
import lombok.RequiredArgsConstructor
import org.springframework.core.ParameterizedTypeReference
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.PutMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.client.RestTemplate
import org.springframework.web.client.getForObject

@RestController
@RequestMapping("/consumer/depart")
@RequiredArgsConstructor
class DepartController(private val template:RestTemplate) {<!-- -->
    //Direct connection mode
// final val SERVICE_PROVIDER_DEPART="http://localhost:8081/provider/depart/"

    //Microservice approach
    final val SERVICE_PROVIDER_DEPART="http://depart-provider/provider/depart/"
    //increase
    @PostMapping("/")
    fun saveHandle(@RequestBody depart:Depart):Boolean?{<!-- -->
        return template.postForObject(SERVICE_PROVIDER_DEPART, depart, Boolean::class.java)
    }

    //delete
    @DeleteMapping("/del/{id}")
    fun deleteHandle(@PathVariable("id") id:Int){<!-- -->
        val url=SERVICE_PROVIDER_DEPART + "del"
        template.delete(url + id)
    }

    //change
    @PutMapping("/")
    public fun updateHandle(@RequestBody depart: Depart){<!-- -->
        template.put(SERVICE_PROVIDER_DEPART,depart)
    }

    //Check a single
    @GetMapping("/get/{id}")
    fun getHandle(@PathVariable("id") id:Int):Depart?{<!-- -->
        val url:String= "$SERVICE_PROVIDER_DEPART/get/$id"
        return template.getForObject(url,Depart::class.java)
    }

    //Check all
    @GetMapping("/list")
    fun listHandle():List<Depart>?{<!-- -->
        val url="$SERVICE_PROVIDER_DEPART/list"
        return template.getForObject(url,Array<Depart>::class.java)?.toList()
    }

}

After these are completed, you can start the two services for testing.
If you have any questions, please feel free to message me privately, or add my contact information, I will be happy to communicate!