上一篇文章创建了一个 user-service 微服务,本文编写一个消费者,本文比较简单。
1 目标
创建一个 movie-service,该服务器也能够查询用户信息,但是内部是通过调用 user-service 服务调用的。可能有人会问,为什么还要有一个中转,直接调用 user-service 不就好了吗?这里由于我只是做例子业务比较简单,两者都可以,但是实际中如果我们购买电影票,第一步要先验证用户信息,就需要先去请求 user-service 服务,本文例子是模拟这种情况。
本文的最终实现欠缺一定的灵活性,文末会给出一定的解决方案。
2 编写代码
2.1 创建工程
参照第一篇文章 Spring Cloud 入门(1)– 第一个 Kotlin 微服务 创建微服务 movie-service, artifactId 为 movie-service。依赖目前只选择 web 就可以了。
2.2 User Entity
将 user-service 中的 User 类,复制到新工程下(对于公用的一些Model,可以做成 Maven 的依赖,这里为了简单就复制了)。
package io.mshare.study.movieservice.entity
import java.math.BigDecimal
data class User(val id: Long = 0,
val username: String = "",
val name: String = "",
val age: Int =0,
val balance: BigDecimal = BigDecimal.ZERO)
2.3 修改 Application 类
在 Application 类中添加 restTemplate, 这个可以理解成 Spring Cloud 封装的网络请求的工具,可以类比 HttpClient,但是与Spring Cloud 结合更加紧密(以后会看到与 Eureka 等结合使用)。
package io.mshare.study.movieservice.movieservice
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.context.annotation.Bean
import org.springframework.web.client.RestTemplate
@SpringBootApplication
class MovieServiceApplication {
// 使用 Bean 注解,表示该返回值会被 Spring 所管理,然后可以注入到对应的变量属性中去
@Bean
fun restTemplate(): RestTemplate {
return RestTemplate()
}
}
fun main(args: Array<String>) {
SpringApplication.run(MovieServiceApplication::class.java, *args)
}
2.4 添加 MovieController
在该类中,我们就会使用上面创建的 restTemplate 请求 user-service 服务中的 user 信息,具体看注释:
package io.mshare.study.movieservice.movieservice.controller
import io.mshare.study.movieservice.entity.User
import org.apache.log4j.Logger
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.client.RestTemplate
@RestController
class MovieController {
private val logger = Logger.getLogger(this::class.java)
// 使用 Autowired 注解,Spring 会自动将 Application 中使用 @Bean 注解的 restTemplate 注入(赋值)到该属性上
@Autowired
private lateinit var restTemplate: RestTemplate
@GetMapping("/user/{id}")
fun findById(@PathVariable id: Long): User {
// 这里是直接写死的地址 后续会使用 Eureka 服务发现去处理
var user = restTemplate.getForObject("http://localhost:8000/{1}", User::class.java, id)
logger.info("return user: ${user}")
return user
}
}
2.5 修改 application.yml
server:
port: 8010
spring:
application:
name: movie-service
# 设置日志打印级别
logging:
level: info
2.6 运行测试
平时我们会点击 idea 的右上角的运行按钮,如果我们有多个运行配置就有点不方便,idea 对 Spring boot 的运行提供了很好的支持,我们可以使用 Run Dashboard。
点击 Edit Configurations…
勾选 Show in Run Dashboard,然后点击 OK。
会看到如下的显示,然后就能够运行了:
运行起来后,在浏览器输入:http://localhost:8010/user/1,输入如下,说明成功。
{
"id": 1,
"username": "account1",
"name": "张三",
"age": 20,
"balance": 234
}
3 总结
本文实现了一个最简单的微服务的调用,但是在调用的时候直接写死了请求的地址,这将导致我们很多的硬编码,如果 user-service 服务地址改变了,我们需要修改这些代码,然后重新启动服务,这在线上环境中是不可以接受的。
为了解决这个问题,我们可以将地址写在配置文件中,读取配置文件中的配置项。但是如果服务一多也不方便,所以 Spring Cloud 中也提供了服务管理的功能 Eureka。我们在后续的文章中会介绍这两种实现方式。