一、认识微服务
1.1 微服务介绍
**单体架构:**将业务的所有功能集中在一个项目进行开发,打成一个包进行部署。
优点:
1、架构简单
2、部署成本低
3、适合小型项目
缺点:
1、耦合度高
2、不利于大型项目的开发
**分布式架构:**根据业务功能对系统进行拆分,每个业务模块作为独立项目开发,称为一个服务。
优点:
1、降低服务藕合
2、有利于服务升级拓展
微服务:一种经过良好架构设计的分布式架构方案。
特征:
1、单一职责:拆分粒度更小,每一个服务对应唯一的业务功能,做到单一职责,避免重复业务开发。
2、面向服务:对外暴露业务接口
3、自治:团队独立、技术独立、数据独立、部署独立
4、离性强:服务调用做好隔离、容错、降级,避免出现级联问题
缺点:
1、架构复杂
2、运维、监控、部署难度提高
SpringCloud作为目前国内使用最广泛的微服务架构,集成了各种微服务功能组件,并基于SpringBoot实现了这些组件的自动装配,从而提供了良好的开箱即用体验。下图中设计的技术,后面会逐一介绍。
SpringCloud和SpringBoot的版本兼容关系如下:(具体可点击此处,查看官网链接) 本学习笔记以SpringCloud Hoxton.SR10版本,对应的SpringBoot版本为2.3.x为例。
1.2 微服务小Demo
开始前,先说一下微服务的拆分注意事项:
1、不同微服务,不要重复开发相同的业务
2、微服务数据独立,不要访问其他微服务的数据库
3、微服务可以将自己的业务暴露为接口,供其他微服务调用
开始之前,可以到我的阿里云盘下载我的代码跟一些sql文件和资料。
下载链接:https://www.aliyundrive.com/s/uNC6PPQHzWB
OK,接下来我们直接进入正题,开始代码实战:
首先,我们新建一个干净的Maven项目,名字就叫demo吧。 然后再在这个干净的项目下面新建两个Moodle,用Spring Initializr或者Maven创建都行,一个叫user,一个叫order。 能选择2.3.9的SpringBoot版本的话,就选2.3.9的SpringBoot版本,IDEA太新,不能选的话,就随便选一个,到时候再到pom.xml文件进行更改就好。
然后我们修改demo父工程的pom.xml文件如下:(我的mysql使用8.x版本的,如果是用5.x版本的话,记得换驱动版本号)
<?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>
<groupId>org.example</groupId>
<artifactId>demo</artifactId>
<version>1.0-SNAPSHOT</version>
<modules>
<module>user</module>
<module>order</module>
</modules>
<packaging>pom</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.9.RELEASE</version>
<relativePath/>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR10</spring-cloud.version>
<mysql.version>8.0.26</mysql.version>
<mybatis.version>2.1.1</mybatis.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
</project>
之后,我们再修改user和order的pom.xml文件如下:
首先是user的pom.xml
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.example</groupId>
<artifactId>demo</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>com.example</groupId>
<artifactId>user</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>user</name>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
</dependencies>
<build>
<finalName>app</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
然后是order的pom.xml
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.example</groupId>
<artifactId>demo</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>com.example</groupId>
<artifactId>order</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>order</name>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
然后我们借助Navicat工具,新建两个数据库,一个叫user,一个叫order,并把前面网盘中的sql文件导入进来。user数据库导入
右键右边的空白处,选择Execute SQL File。 选择对应的文件后,点击Start即可。 导入成功后,点击Close,然后刷新一下空白处,即可看到对应的表格。 然后,我们写User的配置文件:
server:
port: 8081
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/user?useSSL=false&serverTimezone=GMT%2B8
username: root
password: root
mybatis:
type-aliases-package: com.example.user.po
configuration:
map-underscore-to-camel-case: true
logging:
level:
com.example.user: debug
pattern:
dateformat: MM-dd HH:mm:ss:SSS
接着写Order的配置文件:
server:
port: 8080
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/order?useSSL=false&serverTimezone=GMT%2B8
username: root
password: root
mybatis:
type-aliases-package: com.example.order.po
configuration:
map-underscore-to-camel-case: true
logging:
level:
com.example.order: debug
pattern:
dateformat: MM-dd HH:mm:ss:SSS
然后,我们来写User和Order的 po层,都新建下面两个文件:
User.java
package com.example.user.po;
import lombok.Data;
import java.io.Serializable;
@Data
public class User implements Serializable {
private Long id;
private String userName;
private String address;
}
Order.java
package com.example.user.po;
import lombok.Data;
import java.io.Serializable;
@Data
public class Order implements Serializable {
private Long id;
private Long price;
private String name;
private Integer num;
private Long userId;
private User user;
}
然后我们写User和Order的Mapper层:
UserMapper.java
package com.example.user.mapper;
import com.example.user.po.User;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
public interface UserMapper {
@Select("select * from tb_user where id = #{id}")
User selectById(@Param("id") Long id);
}
OrderMapper.java
package com.example.order.mapper;
import com.example.order.po.Order;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
public interface OrderMapper {
@Select("select * from tb_order where id = #{id}")
Order selectById(@Param("id") Long id);
}
然后我们写User和Order的Service层:
UserService.java
package com.example.user.service;
import com.example.user.mapper.UserMapper;
import com.example.user.po.User;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class UserService {
@Resource
UserMapper userMapper;
public User selectById(Long id){
return userMapper.selectById(id);
}
}
OrderService.java
package com.example.order.service;
import com.example.order.mapper.OrderMapper;
import com.example.order.po.Order;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class OrderService {
@Resource
OrderMapper orderMapper;
public Order selectById(Long id) {
return orderMapper.selectById(id);
}
}
然后我们写User和Order的Controller层:
UserController.java
package com.example.user.controller;
import com.example.user.po.User;
import com.example.user.service.UserService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
@Slf4j
public class UserController {
@Resource
private UserService userService;
@GetMapping("/user/{id}")
public User selectById(@PathVariable("id") Long id){
return userService.selectById(id);
}
}
OrderController.java
package com.example.order.controller;
import com.example.order.po.Order;
import com.example.order.service.OrderService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
@Slf4j
public class OrderController {
@Resource
OrderService orderService;
@GetMapping("/order/{id}")
public Order selectById(@PathVariable("id") Long id){
return orderService.selectById(id);
}
}
最后,我们还要让SpringBoot扫描我们的mapper包:
分别在UserApplication.java和OrderApplication.java文件上添加注释如下:
UserApplication.java
package com.example.user;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.example.user.mapper")
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}
}
OrderApplication.java
package com.example.order;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.example.order.mapper")
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
最后包的结构大致如下: 全部写完了,我们就运行两个微服务,然后,我们在浏览器访问一下,看运行是否正常:
http://localhost:8081/user/1
http://localhost:8080/order/101
什么情况,说好的微服务,怎么给你整了两个SpringBoot,一点调用没有??!!!别急,好戏还在后面呢。请听我细细道来。
现在,假如我们需要根据订单信息,将该订单的用户信息一起带回来,怎么玩?简单嘛,让我们的订单微服务向用户微服务发送请求,把数据拿过来,再贴到订单实体类里面不就行了。怎么写呢?请看:
步骤一、注册RestTemplate
在user和order下都新建一个config目录,并新建一个配置类叫MyConfig。
myConfig.java
package com.example.user.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class MyConfig {
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
步骤二、服务远程调用RestTemplate
然后我们修改我们的Service的逻辑代码如下:
OrderService.java
package com.example.order.service;
import com.example.order.mapper.OrderMapper;
import com.example.order.po.Order;
import com.example.order.po.User;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
@Service
public class OrderService {
@Resource
OrderMapper orderMapper;
@Resource
RestTemplate restTemplate;
public Order selectById(Long id) {
Order order = orderMapper.selectById(id);
String url = "http://localhost:8081/user/"+order.getUserId();
User user = restTemplate.getForObject(url, User.class);
order.setUser(user);
return order;
}
}
重启一下我们的order微服务,然后再次访问 http://localhost:8080/order/1
结果如下:(User用户成功请求到) 当然了,使用RestTemplate这种方式发送请求与语言无关的远程调用,只要知道对方的ip、端口号、接口路径、请求参数即可。
|