随着互联网行业的发展,对服务的要求也越来越高,服务架构也从单体架构逐渐演变为现在流行的微服务架构。这些架构之间有怎样的差别呢?
了解微服务架构的优缺点
单体架构:将业务的所有功能集中在一个项目中开发,打成一个包部署。
单体架构的优缺点如下:
优点:
缺点:
分布式架构:根据业务功能对系统做拆分,每个业务功能模块作为独立项目开发,称为一个服务。
分布式架构的优缺点:
优点:
缺点:
分布式架构虽然降低了服务耦合,但是服务拆分时也有很多问题需要思考:
人们需要制定一套行之有效的标准来约束分布式架构。
微服务的架构特征:
微服务的上述特性其实是在给分布式架构制定一个标准,进一步降低服务之间的耦合度,提供服务的独立性和灵活性。做到高内聚,低耦合。
因此,可以认为微服务是一种经过良好架构设计的分布式架构方案 。
但方案该怎么落地?选用什么样的技术栈?全球的互联网公司都在积极尝试自己的微服务落地方案。
其中在Java领域最引人注目的就是SpringCloud提供的方案了。
SpringCloud是目前国内使用最广泛的微服务框架。官网地址:https://spring.io/projects/spring-cloud
。
SpringCloud集成了各种微服务功能组件,并基于SpringBoot实现了这些组件的自动装配,从而提供了良好的开箱即用体验。
其中常见的组件包括:
另外,SpringCloud底层是依赖于SpringBoot的,并且有版本的兼容关系,如下:
我们课堂学习的版本是 Hoxton.SR10,因此对应的SpringBoot版本是2.3.x版本。
单体架构:简单方便,高度耦合,扩展性差,适合小型项目。例如:学生管理系统
分布式架构:松耦合,扩展性好,但架构复杂,难度大。适合大型互联网项目,例如:京东、淘宝
微服务:一种良好的分布式架构方案
①优点:拆分粒度更小、服务更独立、耦合度更低
②缺点:架构非常复杂,运维、监控、部署难度提高
SpringCloud是微服务架构的一站式解决方案,集成了各种优秀微服务功能组件
任何分布式架构都离不开服务的拆分,微服务也是一样。
这里我总结了微服务拆分时的几个原则:
以课前资料中的微服务cloud-demo为例,其结构如下:
cloud-demo:父工程,管理依赖
要求:
首先,将课前资料提供的cloud-order.sql
和cloud-user.sql
导入到mysql中:
先创建对应的两个数据库,因为要实现微服务功能,这部分就需要多个数据库。
cloud-user表中初始数据如下:
cloud-order表中初始数据如下:
cloud-order表中持有cloud-user表中的id字段。
用IDEA导入课前资料提供的Demo:
该案例的功能是查询订单详细信息以及对应用户的详细信息,因为订单Id记录了用户Id,所以可以根据这两个Id获取到相应的详细信息,由于我们要实现微服务架构,所以各个模块之间都是相互独立的,所以订单和用户模块相当于是部署到两个服务器上的项目,数据库也是分开的,这就是为什么我们之前要使用两个数据库存放两个表。那我们如何实现跨服务器调用呢?我们可以通过Ajax或者JS直接发起请求,但前端并不能满足我们的需求特别是安全性方面无法得到保障,我们需要在后台即Java代码中实现。那么我们这里是如何Java进行处理的呢?通过
RestTemplate
进行网络请求的发送。其实我们这两种方式都有缺点就是,就是提供者的服务地址是通过编码的形式编写的,不利于修改。后面在进行Eureka注册中心学习将会解决这个问题。
项目结构如下:
导入后,会在IDEA右下角出现弹窗:
点击弹窗,然后按下图选择:
会出现这样的菜单:
配置下项目使用的JDK:
实现远程调用案例
在order-service服务中,有一个根据id查询订单的接口:
根据id查询订单,返回值是Order对象,如图:
其中的user为null
在user-service中有一个根据id查询用户的接口:
查询的结果如图:
修改order-service中的根据id查询订单业务,要求在查询订单的同时,根据订单中包含的userId查询出用户信息,一起返回。
因此,我们需要在order-service中 向user-service发起一个http的请求,调用http://localhost:8081/user/{userId}这个接口。
大概的步骤是这样的:
首先,我们在order-service服务中的OrderApplication启动类中,注册RestTemplate实例:
package cn.itcast.order;
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("cn.itcast.order.mapper")
@SpringBootApplication
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
知识扩展
RestTemplate是如何注册到Spring容器中的,将类注册到Spring中我们已经很熟悉了,可以使用
@Component
注解或者其派生注解@Controller
、@Service
、@Mapper
【用在持久层上,是MyBatis中的注解】,@Repository
【用在持久层上,是Spring中的注解】,但是RestTemplate不是我们自定义的类,就使用不了@Conponent注解了,但是我们可以使用@Bean
注解,@Bean
注解是用在具有返回值的方法上,并且要求该方法需要放在@Component注解标识的类中,表示将该方法的返回值注册到Spring容器中。要使用注册到Spring容器中的RestTemplate,就可以用@Autowried
注解。
修改order-service服务中的cn.itcast.order.service包下的OrderService类中的queryOrderById方法:
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
//注入RestTemplate
@Autowired
private RestTemplate restTemplate;
public Order queryOrderById(Long orderId) {
// 1.查询订单
Order order = orderMapper.findById(orderId);
//2,远程查user
//2.1.ur1地址
String url "http://localhost:8081/user/" + order.getUserId();
//2.2.发起调用,发起get请求
User user = restTemplate.getForobject(url,User.class);
//3.存入order
order.setUser(user);
// 4.返回
return order;
}
}
总结:
我们通过RestTemplate进行请求的发送,也就是调用其他服务,这种请求是Restful风格的,与语言无关。在使用了注册中心后,我们的请求地址可以写成 http://{服务提供者应用名名称}/{具体的controller}
,即String url="http://provider-user/user/" + order.getUserId();
,同个服务有多个实例,在进行远程调用时指定服务即可,而不需要指定具体的服务实例。
在服务调用关系中,会有两个不同的角色:
服务提供者:一次业务中,被其它微服务调用的服务。(提供接口给其它微服务)
服务消费者:一次业务中,调用其它微服务的服务。(调用其它微服务提供的接口)
但是,服务提供者与服务消费者的角色并不是绝对的,而是相对于业务而言。
如果服务A调用了服务B,而服务B又调用了服务C,服务B的角色是什么?
因此,服务B既可以是服务提供者,也可以是服务消费者。