参考资料:《SpringCloud与Docker微服务架构实战》
服务提供者:服务的被调用方(即:为其他服务提供服务的服务)
服务消费者:服务的调用方(即:依赖其他服务的服务)
用户购票时,向电影微服务发起一个购票请求,在进行购票的业务操作前,电影微服务需要调用用户微服务的接口,查询当前用户的余额是多少,是不是符合购票标准等。
在这个例子中,用户微服务就是一个服务提供者,电影微服务则是一个服务消费者
围绕该场景,先编写一个用户微服务,再编写一个电影微服务
编写一个服务提供者,该服务通过主键查询用户信息
再写一个消费者,通过调用提供者来查询用户
原书中使用的是SpringDataJPA 而我手头正好有以前学习springboot的时候写的简单Demo 方便起见就把实体类和持久层复制过来了 使用的是MySQL+MyBatis
提供者项目结构:
配置文件application:
## 数据源配置
spring.datasource.url=jdbc:mysql://localhost:3306/springbootdemo?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
## Mybatis 配置
mybatis.typeAliasesPackage=com.example.springbootdemo.domain
mybatis.mapperLocations=classpath:mapper/*.xml
简单对象User只有int类型的id String类型的username和realname
UserDao:
package com.example.springbootdemo.dao;
import com.example.springbootdemo.domain.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
@Mapper
@Repository
public interface UserDao
{
//根据uid查询用户
User findByUid(@Param("uid") int uid);
}
mapper:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.example.springbootdemo.dao.UserDao">
<resultMap id="BaseResultMap" type="com.example.springbootdemo.domain.User">
<result column="uid" property="uid" />
<result column="username" property="username" />
<result column="realname" property="realname" />
</resultMap>
<select id="findByUid" resultMap="BaseResultMap" parameterType="Integer">
select
*
from user
where uid = #{uid}
</select>
</mapper>
Service和实现:
Controller:
package com.example.springbootdemo.controller;
import com.example.springbootdemo.domain.User;
import com.example.springbootdemo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController
{
@Autowired
private UserService userService;
@RequestMapping(value="/getuser",method = RequestMethod.GET)
public User findOneUser(@RequestParam(value="uid",required=true) int uid)
{
return userService.findByUid(uid);
}
}
启动类是自动生成的 由于在同包下所以不需要用Scan Dao层也加入了Mapper注解
依赖:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>springbootdemo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springbootdemo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
运行后测试一下:
接下来编写消费者
User类与提供者保持一致
controller:
package com.example.consumer.controller;
import com.example.consumer.domain.User;
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
public class controller
{
@Autowired
private RestTemplate restTemplate;
@GetMapping("/user/{id}")
public User findById(@PathVariable int id)
{
return this.restTemplate.getForObject("http://localhost:8080/getuser?uid="+id,User.class);
}
}
提供者项目的端口号使用默认8080接口 消费者项目设置为其他 我使用的是8081
server.port=8081
两个项目都运行起来之后
也可以将提供者的url配置在配置文件中
这样就可以将Controller修改为:
然而这种简单的固定接口调用的方式存在许多问题比如:
- 适用场景有局限:如果服务提供者的网络地址(IP和端口)发生了变化,会影响服务消费者,消费者需要修改代码并重新发布
- 无法动态伸缩:在生产环境中,每个微服务一般都会部署多个实例,从而实现容灾和负载均衡,还需要动态的增减节点等,硬编码的形式也无法满足这个需求