手记

Spring Cloud(一):微服务

概述

  • 在先前的单体应用架构中,一个归档包(如 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
{"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


0人推荐
随时随地看视频
慕课网APP