概述
在先前的单体应用架构中,一个归档包(如 war 格式)包含了所有功能的应用程序。
例如一个电影售票系统,尽管已经进行模块化,但由于 UI 和若干业务模块最终被打包在一个 war 中,该 war 包含了整个系统所有业务功能,这样的应用称之为单体应用。
单体应用容易部署、测试、在开发初期能很好的运行,但随着需求不断增加,代码库飞速膨胀,慢慢地单体应用变得越来越臃肿,可维护性、灵活性下降,复杂性高、部署频率低、可靠性差、扩展能力受限制、阻碍技术创新等问题也随之出现。
微服务是什么?
随着单体应用架构存在的问题,微服务架构开始流行。
微服务架构是将一个单一应用程序开发为一组小型服务的方法,每个服务运行在自己的进程中,服务间通信采用轻量级通信机制(通常为 HTTP 资源 API),这些服务围绕业务能力构建并独立部署,共用一个最小型的集中式管理,服务可用不同的语言开发,使用不同的技术。
微服务的特性:
每个微服务可独立运行在自己的进程里
一系列独立运行的微服务共同构建起整个系统
每个服务为独立业务开发,一个微服务只关注某个特定点的功能,例如订单管理、用户管理等
微服务之间通过一些轻量级通信机制进行通信,例如 RESTful API
可以用不同的语言和数据库存储技术
全自动的部署机制
例如电影售票系统可以划分为:用户微服务、电影微服务
微服务的优点:
易开发和维护
单个微服务启动较快
局部修改容易部署
技术不受限
按需伸缩
微服务的挑战:
运维要求较高
分布式固有的复杂性
接口调整成本高
重复劳动
微服务设计原则:
单一职责原则:一个单元只应关注整个系统中单独的一部分
服务自治原则:每个微服务应具备独立的业务能力,依赖与运行环境,服务是独立的业务单元,应该与其它服务高度解耦
轻量级通信:微服务之间通过轻量级通信机制进行交互,例如 REST 协议
微服务粒度:并非一味的把服务做小,代码量多少不能作为微服务划分的依据,因为不同业务复杂性不同,代码量也不同
如何实现微服务
开发框架选择:Spring Cloud(具备了开箱即用的生产特性,可大大提高效率,社区活跃,资料多)
运行平台:微服务并不绑定运行平台,将微服务部署在 PC Server、阿里云、Docker等都是可以的
图中将所有服务都注册到了服务发现组件上,服务之间使用轻量级的通信机制,除了 SecviceA、ServiceB 等还有服务发现组件、服务网关、配置服务器等组件
微服务架构
Spring Cloud
Spring Cloud 是什么?
Spring Cloud 虽然带有“Cloud”字样,但不是云计算解决方案,而是在 Spring Boot 基础上构建的,用于快速构建分布式系统的工具集,它非常适合在 Docker 或者 PaaS 上部署。
Spring Cloud 特点:
约定优于配置
适用于各种环境、开发、部署
隐藏了组件的复杂性,提供声明式、无 xml 配置方式
开箱即用,快速启动
轻量级组件,Spring Cloud 整合的组件大多比较轻量级
组件丰富、功能齐全,提供了配置管理、服务发现、断路器、微服务网管等
灵活、解耦
Spring Cloud 快速入门
编写一个电影购票系统,用户向电影微服务请求购票时,电影购票系统调用用户微服务系统的接口,查询当前余额是多少,是否符合购票标准,然后在进行购票操作
graph LR A[用户] --> |购票|B[电影微服务] B--> |查询用户信息|C[用户微服务]
添加依赖库
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency><dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope></dependency><dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional></dependency>
创建数据库并插入数据
SET NAMES utf8; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for `user`-- ---------------------------- DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(40) DEFAULT NULL, `name` varchar(20) DEFAULT NULL, `age` int(3) DEFAULT NULL, `balance` decimal(10,2) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of `user`-- ---------------------------- BEGIN; INSERT INTO `user` VALUES ('1', 'account1', '张三', '20', '100.00'), ('2', 'account2', '李四', '28', '180.00'), ('3', 'account3', '王五', '32', '230.00'); COMMIT; SET FOREIGN_KEY_CHECKS = 1;
用户微服务(Demo 源码 film-user)
实体类
@Data@Entity(name = "user")public class UserEntity { @Id @GeneratedValue private int id; private String username; private String name; private int age; private BigDecimal balance; }
Repository
@Repositorypublic interface UserRepository extends JpaRepository<UserEntity,Integer>{}
Controller
@RestControllerpublic class UserController { @Autowired private UserRepository userRepository; @GetMapping("/{id}") public UserEntity findById(@PathVariable int id){ UserEntity userEntity = userRepository.findOne(id); return userEntity; } }
编写配置文件
spring: datasource: url: jdbc:mysql://localhost:3306/flim?useSSL=false username: root password: 123456ly jpa: show-sql: true database-platform: org.hibernate.dialect.MySQL5Dialect
访问 http://localhost:8080/1 ,输出
{"id":1,"username":"account1","name":"张三","age":20,"balance":100.00}
消费者微服务(Demo 源码 film-consumer)
将 RestTemplate 注入到 IOC 容器
@SpringBootApplicationpublic class ConsumerApplication { @Bean public RestTemplate restTemplate(){ return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } }
创建Controller,通过 RestTemplate 访问用户微服务 API
@RestControllerpublic class MovieController { @Autowired private RestTemplate restTemplate; @GetMapping("/user/{id}") public UserEntity findById(@PathVariable int id){ return this.restTemplate.getForObject("http://localhost:8080/"+id,UserEntity.class); } }
修改配置文件,修改端口
server: port: 8010spring: datasource: url: jdbc:mysql://localhost:3306/flim?useSSL=false username: root password: 123456ly
{"id":1,"username":"account1","name":"张三","age":20,"balance":100.00}
硬编码问题
上述例子中使用了硬编码,我们把服务提供者的网络地址、IP、端口硬编码在代码中,这样会出现以下问题:
适用场景有局限,若服务提供者网络地址发生了变化,会影响服务消费者
无法动态伸缩,在生产环境中,每个微服务一般都会部署多个实例,从而实现负载均衡,在微服务架构中,还需要系统具备自动伸缩的能力,例如动态增减节点
解决硬编码的问题是通过服务发现,如 Eureka
作者:林塬
链接:https://www.jianshu.com/p/1beb12335fda