微服务实战

本项目示例是一个图书借阅系统,本教程会更加注重于架构设计上的讲解,弱化业务功能和实现原理方面的研究。

服务的拆分

  • 单一职责:不同微服务开发不同的业务
  • 数据独立:每个微服务拥有自己的数据库
  • 面向服务:将业务暴露为接口,供其他微服务使用

微服务项目搭建

构建父项目

image-20220528192605703

image-20220528192547239

image-20220528193009882

image-20220528193030549

image-20220528193137238

父项目只负责依赖管理,删除无用文件

image-20220528193644465

导入通用依赖

image-20220528204437918

新建子模块

image-20220528201545755

image-20220528202339977

image-20220528201719876

image-20220528202407207

创建启动类

image-20220528202711560

image-20220528202911977

创建配置文件-application.yml

image-20220528203410113

添加服务端口号

image-20220528203852132

导入spring-boot-starter-web依赖

image-20220528204039657

1
2
3
4
5
6
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>

数据库设计

在实际的应用场景中,每个微服务都有自己的数据库服务器,按照单一职责的原则,每个微服务只会操作自己的数据库。

image-20220528234437570

image-20220528235133900

image-20220528235150408

image-20220528235202429

image-20220528235214185

添加索引

image-20220529000109085

在父项目引入MyBatis依赖进行版本管理

image-20220529001036883

1
2
3
4
5
6
7
8
9
10
<!--依赖版本号管理-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
</dependencies>
</dependencyManagement>

在子项目中引入Mybatis依赖

image-20220529001154257

1
2
3
4
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>

数据源信息配置

image-20220529001617549

1
2
3
4
5
6
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/cloudstudy_user
username: root
password: 13851176590++

公共项目创建和引入

将服务需要用到的实体类单独放入一个项目模块中,将需要实体类的项目模块引用存放共用实体的项目,以保证每个微服务的实体类信息共用。

image-20220529003014986

在需要共用实体类的项目模块中引入

image-20220529003513439

1
2
3
4
5
<dependency>
<groupId>com.example</groupId>
<artifactId>common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>

书籍信息查询业务

image-20220529005821372

创建Dao:

1
2
3
4
5
@Mapper
public interface BookMapper {
@Select("select * from book_info where id = #{id}")
Book getBookById(Long id);
}

创建Service:

1
2
3
public interface BookService {
Book getBookById(Long id);
}
1
2
3
4
5
6
7
8
9
10
@Service
public class BookServiceImpl implements BookService {
@Resource
BookMapper bookMapper;

@Override
public Book getBookById(Long id) {
return bookMapper.getBookById(id);
}
}

创建Controller:

1
2
3
4
5
6
7
8
9
10
@RestController
public class BookController {
@Resource
BookService bookService;

@RequestMapping("/book/{id}")
public Book getBookById(@PathVariable("id") Long id) {
return bookService.getBookById(id);
}
}

image-20220529011059566

用户信息查询业务同理

服务提供和消费

借阅业务查询业务

微服务构建的是一个分布式系统,微服务之间通过网络进行通信,可以使用服务提供者与服务消费者来描述微服务之间的调用关系。

image-20220529150533296

借阅服务是一个关联性比较强的服务,需要在查询出借阅信息的基础上,获取借阅用户和借阅书籍的详细信息。根据单一职责和数据独立的原则,我们可以让一个服务作为消费者调用另一个服务即提供者来获取信息。

image-20220323140322502

RestTemplate-HTTP请求工具实现远程调用

这里先使用RestTemplate作为HTTP请求工具,它提供了常见的REST请求方案的模版,例如 GET 请求、POST 请求、PUT 请求、DELETE 请求以及一些通用的请求执行方法 exchange 以及 execute。

注册RestTemplate

由于启动类也是配置类,我们可以直接在启动类实现RestTemplate对象的创建和注册。

1
2
3
4
5
6
7
8
9
10
11
12
13
@SpringBootApplication
@MapperScan("com.test.dao")
public class BorrowApplication {
public static void main(String[] args) {
SpringApplication.run(BorrowApplication.class, args);
}

/*注册RestTemplate对象*/
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}

这里以查询用户借阅信息为例

image-20220529142333093

配置文件配置mybatis

1
2
3
mybatis:
mapper-locations:
- classpath:mapper/*.xml

在启动类添加@MapperScan

image-20220529142636511

业务处理

创建服务实体类:
1
2
3
4
5
6
@Data
@AllArgsConstructor
public class UserBorrowDetail {
User user;
List<Book> books;
}
创建Dao:
1
2
3
4
@Mapper
public interface BorrowMapper {
List<Borrow> getBorrow(Long uid, Long bid);
}
创建mapper.xml:
1
2
3
4
5
6
7
8
9
<mapper namespace="com.test.dao.BorrowMapper">
<select id="getBorrow" resultType="com.test.entity.Borrow">
SELECT * FROM borrow_info
<where>
<if test="uid != null"> uid = #{uid}</if>
<if test="bid != null"> and bid = #{bid}</if>
</where>
</select>
</mapper>
创建Service:
1
2
3
public interface BorrowService {
UserBorrowDetail getBorrowByUser(Long uid);
}
使用RestTemplate服务远程调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Service
public class BorrowServiceImpl implements BorrowService {
@Resource
BorrowMapper borrowMapper;
@Resource
RestTemplate restTemplate;

@Override
public UserBorrowDetail getBorrowByUser(Long uid) {
List<Borrow> borrows = borrowMapper.getBorrow(uid,null);
User user = restTemplate.getForObject("http://localhost:8101/user/" + uid, User.class);
List<Book> books = borrows.stream().map(borrow -> restTemplate.getForObject("http://localhost:8201/book/" + borrow.getBid(), Book.class)).collect(Collectors.toList());
return new UserBorrowDetail(user, books);
}
}
创建Controller:
1
2
3
4
5
6
7
8
9
10
@RestController
public class BorrowController {
@Resource
BorrowService borrowService;

@RequestMapping("/borrow/user/{uid}")
public UserBorrowDetail getBorrowByUser(@PathVariable("uid") Long uid){
return borrowService.getBorrowByUser(uid);
}
}

image-20220529143045741