Spring Cloud(一):微服务 - Go语言中文社区

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
@Repository
public interface UserRepository extends JpaRepository<UserEntity,Integer>{}
  • Controller
@RestController
public 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 容器
@SpringBootApplication
public class ConsumerApplication {
    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
    }
}

  • 创建Controller,通过 RestTemplate 访问用户微服务 API
@RestController
public 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: 8010
spring:
  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
版权声明:本文来源简书,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://www.jianshu.com/p/1beb12335fda
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2020-01-12 10:38:00
  • 阅读 ( 1009 )
  • 分类:

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢