Calling a Client Via Spring @schedule Cron Job
In this article, a cron job will be set up that will run parametrically in a certain period using Spring's scheduling feature.
Join the DZone community and get the full member experience.
Join For FreeIn this article, a cron job will be set up that will run parametrically in a certain period using Spring's scheduling feature. Period information will be adjustable parametrically from the application.properties file.
Overview
In the example, parameter information is given to run every minute.
schedule.minute.param:1
The schedule
method will be run in a minute time period. It will trigger the method we want to call within the service class. This method will call a client jar from integration_microservice
via feign client
and get the response information.
For this architecture, we will have two microservices on Spring Boot. The first of these will be job_microservice
and the second will be integration_microservice
.
The schedule
method in job_microservice
will be run periodically and the service impl
will be called.
The jobIntegrationClientFacades
service class will be called from the ServiceImpl
class. JobIntegrationFeignClient
class will be called from jobIntegrationClientFacades
to go to integration_microservice
.
Access to integration_microservice
will be provided through the @FeignClient
annotation within the JobIntegrationFeignClient
class.
The response received via the relevant client.jar
controller class in integration_microservice
will be returned to job_microservice
.
Various validations can be made on the response received in job_microservice
service_impl
.
Logging can be done according to the response. Error situations can be separated and logged according to the incoming response code. On successful response, the flow will be terminated. If desired, the successful response can be logged, but it may cause the log size to increase.
The following flow should be followed for two microservices:
1. job_microservice
:
- A new Spring Boot Maven module named
job_microservice
should be created. spring-cloud-starter-openfeign
should be added as a dependency.
2. integration_microservice
:
- A new Spring Boot Maven module named
integration_microservice
should be created. spring-cloud-starter-web
should be added as a dependency.
Note: Creating a client in integration_microservice
and using client features and methods are not covered in this article, as they are the subject of another article. Client creation stages are out of scope in this article.
job_microservice
- In order to run the Spring
@schedule
feature in theJobApplication.kt
class, the@EnableScheduling
annotation must be added. - In order to communicate between two microservices via
feignclient
, the@EnableFeignClients
annotation must be added.
JobApplication.kt
:
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.cloud.openfeign.EnableFeignClients
import org.springframework.scheduling.annotation.EnableScheduling
@SpringBootApplication
@EnableFeignClients
@EnableScheduling
class JobmicroserviceApplication
fun main(args: Array<String>) {
runApplication<JobmicroserviceApplication>(*args)
}
CronScheduler
- A class named
CronScheduler
is created with@component
annotation. - With
@Value("\${schedule.minute.param:}")
, the "schedule.minute.param
" value in the application properties file is read parametrically. - It is run on a minute basis with the "
schedule.minute.param
" value taken from the parametric application properties file with@Scheduled(cron = "* */\${schedule.minute.param} * * * ?")
.- First
*
: Seconds of operation - Second
*
: Minute-based operation - Third
*
: Clockwise operation - Fourth
*
: Day of month operation - Fifth
*
: Month-based operation - Sixth
*
: Day of the week run
- First
The @schedule
annotation is added right above the relevant method. As can be seen below, the prepareClientCall
function is defined as a unit return type to perform the operation without waiting for any response.
By including jobService
, prepareClientCall
in the jobService
interface is called.
CronScheduler.kt
:
@Component
class CronScheduler(val jobService: JobService) {
@Value("\${schedule.minute.param:}")
@Scheduled(cron = "* */\${schedule.minute.param} * * * ?")
fun prepareClientCall(): Unit {
jobService.prepareClientCall()
}
}
JobService.kt
interface JobService {
fun prepareClientCall(): Unit
}
- The
prepareClientCall
method in theJobServiceImpl
class is called by the@schedule
function. Inside, theprepareClientCall()
method injobIntegrationClientFacades
is called. - Client response is set to the response object.
ResponseCode
in the response is controlled withResponseControl
.- The response object here may vary depending on the client you will call. The
responseCode
here is given as an example. - "
0
" appeared as a success inResponsecontrol
and was returned. In the success case, the flow is terminated. - It is logged as a "
-2
" system exception and a "-1
" business exception. Theelse
part has been thrown away. - The exception is logged in the main function.
Note: The response object here is given through the sample client.
JobServiceImpl.kt
:
@Service
class JobServiceImpl(
private val jobIntegrationClientFacades: JobIntegrationClientFacades,
) : JobService {
private val log = logger()
fun responseControl(response: ResponseEntity<ClientResult>) {
if (result.body?.resultCode ?: -3 == 0) {
log.info("resultCode - " + result.body?.resultCode + System.currentTimeMillis() / 1000 )
return; // success case
} else if (result.body?.resultCode ?: -3 == -2) {
log.info("resultCode - " + result.body?.resultCode + System.currentTimeMillis() / 1000 ) // -2 system exception
} else if (result.body?.resultCode ?: -3 == -1) {
log.info("resultCode - " + result.body?.resultCode + System.currentTimeMillis() / 1000 ) // -1 business exception
} else if (result.body?.resultCode ?: -3 == -3) {
log.info("resultCode - " + result.body?.resultCode + System.currentTimeMillis() / 1000 ) // -3 null pointer exception
} else {
throw e
}
}
override
fun prepareClientCall(): Unit {
try {
val response = jobIntegrationClientFacades.prepareClientCall()
responseControl(response)
} catch (exp: Exception) {
log.info("exception - " + exp + System.currentTimeMillis() / 1000 ) // exception
}
}
}
Facades
- Below,
JobIntegrationClient
is called fromJobIntegrationClientFacades
. JobIntegrationFeignClient
is called fromJobIntegrationClient
.- A
postMapping
request was provided tointegration_microservice
withfeignClient
to callclient.jar
inJobIntegrationFeignClient
.
JobIntegrationClientFacades.kt
:
@Service
class JobIntegrationClientFacades(val jobIntegrationClient: JobIntegrationClient) {
fun prepareClientCall(): ResponseEntity<ClientResult> {
return jobIntegrationClient.prepareClientCall()
}
}
JobIntegrationClient.kt
:
@Service
class JobIntegrationClient(
val jobIntegrationFeignClient: JobIntegrationFeignClient
) {
fun prepareClientCall(): ResponseEntity<ClientResult> {
return jobIntegrationFeignClient.prepareClientCall()
}
}
JobIntegrationFeignClient.kt
:
@FeignClient("integration_microservice", url = "http://localhost:10000")
interface JobIntegrationFeignClient {
@PostMapping("/jobIntegrationClient/prepareClientCall")
fun prepareClientCall(): ResponseEntity<ClientResult>
}
integration_microservice
Controller
- The
@RequestMapping("/jobIntegrationClient")
request that comes with theRestController
annotation is checked. - The correctness of matching
"/prepareClientCall"
with@PostMapping("/prepareClientCall")
is checked. prepareClientCall()
injobIntegrationClientService
is called.- The
prepareClientCall()
function in theJobIntegrationClientServiceImpl
is called from theJobIntegrationClientService
interface. - The
prepareClientCall()
function is called from the client that you want to call fromJobIntegrationClientServiceImpl
.
JobController.kt
:
@RestController
@RequestMapping("/jobIntegrationClient")
class JobController(
val jobIntegrationClientService: jobIntegrationClientService
) {
@PostMapping("/prepareClientCall")
fun prepareClientCall(): ResponseEntity<ClientResult> {
return jobIntegrationClientService.prepareClientCall()
}
}
JobIntegrationClientService.kt
:
interface JobIntegrationClientService {
fun prepareClientCall(): ResponseEntity<ClientResult>
}
JobIntegrationClientServiceImpl.kt
:
@Service
class JobIntegrationClientServiceImpl(
private val xClient: XClient
) : JobIntegrationClientService {
override
fun prepareClientCall(): ResponseEntity<ClientResult> {
return xClient.prepareClientCall()
}
}
- The
Response
object is returned to theJobIntegrationFeignClient prepareClientCall()
function injob_microservice
via theJobController
asResponseEntity<ClientResult>
. - A response is received in
JobServiceImpl
injob_microservice
.
JobIntegrationFeignClient.kt
:
@FeignClient("integration_microservice", url = "http://localhost:10000")
interface JobIntegrationFeignClient {
@PostMapping("/jobIntegrationClient/prepareClientCall")
fun prepareClientCall(): ResponseEntity<ClientResult>
}
Opinions expressed by DZone contributors are their own.
Comments