一、微服务
1.1、简介
微服务是一种架构风格,按照业务板块来划分应用代码,使单个应用的职责更清晰,相互之间可以做到独立升级迭代。
1.2、微服务框架中所包含的技术栈
1.3、架构工作原理
以电子商城为例,一个商城应用拆分成了多个微服务,如用户服务、交易服务和商品服务,相互之间协作支持整个商城的应用。
1.4、架构特点
- 单一职责:微服务拆分粒度更小,每一个服务都对应唯一的业务能力,做到单一职责,避免重复业务开发
- 面向服务:微服务对外暴露业务接口
- 自治:团队独立、技术独立、数据独立、部署独立
- 隔离性强:服务调用做好隔离、容错、降级,避免出现级联问题
1.5、简单了解 SpringCloud
SpringCloud是目前国内使用最广泛的微服务框架。官网地址:Spring Cloud。
SpringCloud集成了各种微服务功能组件,并基于SpringBoot实现了这些组件的自动装配,从而提供了良好的开箱即用体验:
值得注意的是 SpringCloud 与 SpringBoot 的版本兼容关系如下:
1.6、微服务中的常用组件(面试)
我们脑海中因该出现一幅图:
- 首先,我们肯定有无数个小的微服务.
- 这无数个微服务之间是不是要进行一个相互调用,那么就会用到 OpenFeign 这样的组件.
- 这么多服务要相互调用,怎么去管理呢,这就需要用到 nacos 组件去做注册中心,那么所有的服务就会去找注册中心去注册自己的服务.
- 那么我拉取到的服务可能是一个列表,那么将来在远程调用的时候就需要做负载均衡,就需要使用 LoadBalancer 这个组件.
- 这么多服务将来要做统一配置的管理怎么办,就需要引入 nacos 作为配置中心.
- 这时候微服务集群就形成了,将来对外提供服务,是不是随便什么人都能访问呢?显然不行,所有在微服务群前面就需要有 gateway 网关 作为入口.
- 那么就算你可以访问了,万一流量激增,引起微服务雪崩,给我整个服务搞崩了,肯定不行,因此就需要 sentinel 来做限流、熔断降级保护.
- 还有一个问题,在分布式系统下,就会引发分布式事务问题,如何解决呢,这就需要 Seata 上场了.
实际上,微服务的组件远不止于此,还有很多的组件,但是以上呢,就是我们最常用的几个组件啦~
二、服务拆分和远程调用
2.1、服务拆分
前面我们了解到微服务需要对一块大的服务,拆分成多个小的微服务,那么这个拆分的粒度我们应该如何去把控呢?需要我们注意一下几点:
例如:我们将一个大的服务拆分成了订单模块和用户模块,并且每一个微服务有自己的数据库(订单数据库和用户数据库)。由于在不同的数据库中,因此只能通过订单 id 查询订单数据,或者通过用户 id 查询用户数据,不能交叉访问,如下图
那么如果需求是:“根据订单id查询订单的同时,把订单所属的用户信息一起返回”,我们该如何跨服务访问呢?接着往下走~
2.2、微服务远程调用
2.2.1、远程调用分析
需求是:“根据订单id查询订单的同时,把订单所属的用户信息一起返回”。
相同的服务,我们可以直接通过 mapper 调用其数据库,不同的服务如何相互调用呢?办法很简单,就如上图,用户模块提供公开的访问接口("/user/{id}"),订单模块主动向用户模块发送 http 请求获取数据,这样就做到了跨服务之间的调用~
2.2.2、具体调用步骤
根据刚刚所说的案例,我们只需要以下四步:
1)注册RestTemplate
在订单服务(order-service)的OrderApplication中(SpringBoot 启动类)注册RestTemplate,如下:
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
//@MapperScan 注解的作用相当于在指定包下的所有 mapper 接口上都加上了 @mapper 注解
@MapperScan("cn.itcast.order.mapper")
@SpringBootApplication
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
/**
* 将 RestTemplate 对象注入到 spring 容器中
* @return
*/
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
2)服务远程调用RestTemplate
在订单服务中,通过 RestTemplate 的 getForObject / postForObject 方法来构造get / post 请求(此处显然使用 get 请求获取用户数据),请求中携带的参数便是订单表中的 userId 数据,当用户微服务接收到请求后,就可以根据 userId 获取用户数据,最后返回到订单服务中,这样就实现了跨服务调用~
import cn.itcast.order.mapper.OrderMapper;
import cn.itcast.order.pojo.Order;
import cn.itcast.order.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private RestTemplate restTemplate;
public Order queryOrderById(Long orderId) {
// 1.查询订单
Order order = orderMapper.findById(orderId);
// 2.利用 RestTemplate 发送 http 请求,查询用户
String url
//get请求:getForObject
//post请求:postForObject
//第一个参数是 url, 第二个参数是请求后响应的参数类型(自动的反序列化)
User user = restTemplate.getForObject(url, User.class);
// 3.封装 User 到 Order
order.setUser(user);
// 4.返回
return order;
}
}
跨服务调用前的执行效果:
跨服务调用后的执行效果: