SpringCloud

微服务是一种架构方式,最终肯定需要技术架构去实施。

微服务的实现方式很多,但是最火的莫过于Spring Cloud了。为什么?

  • 后台硬:作为Spring家族的一员,有整个Spring全家桶靠山,背景十分强大。
  • 技术强:Spring作为Java领域的前辈,可以说是功力深厚。有强力的技术团队支撑,一般人还真比不了
  • 群众基础好:可以说大多数程序员的成长都伴随着Spring框架,试问:现在有几家公司开发不用Spring?SpringCloud与Spring的各个框架无缝整合,对大家来说一切都是熟悉的配方,熟悉的味道。
  • 使用方便:相信大家都体会到了SpringBoot给我们开发带来的便利,而SpringCloud完全支持SpringBoot的开发,用很少的配置就能完成微服务框架的搭建

3.1.简介

SpringCloud是Spring旗下的项目之一,官网地址:http://projects.spring.io/spring-cloud/

Spring最擅长的就是集成,把世界上最好的框架拿过来,集成到自己的项目中。

SpringCloud也是一样,它将现在非常流行的一些技术整合到一起,实现了诸如:配置管理,服务发现,智能路由,负载均衡,熔断器,控制总线,集群状态等等功能。其主要涉及的组件包括:

  • Eureka:服务治理组件,包含服务注册中心,服务注册与发现机制的实现。(服务治理,服务注册/发现)
  • Zuul ( 音 : 祖儿 ):网关组件,提供智能路由,访问过滤功能
  • Ribbon:客户端负载均衡的服务调用组件(客户端负载)
  • Feign:服务调用,给予Ribbon和Hystrix的声明式服务调用组件 (声明式服务调用)
  • Hystrix:容错管理组件,实现断路器模式,帮助服务依赖中出现的延迟和为故障提供强大的容错能力。(熔断、断路器,容错)

架构图:

springcloud启动需要10秒 springcloud启动原理_maven

以上只是其中一部分。

3.2.版本

因为Spring Cloud不同其他独立项目,它拥有很多子项目的大项目。所以它的版本是版本名+版本号 (如Angel.SR6)。


版本名:是伦敦的地铁名

版本号:SR(Service Releases)是固定的 ,大概意思是稳定版本。后面会有一个递增的数字。

所以 Edgware.SR3就是Edgware的第3个Release版本。


我们在项目中,会是以Finchley的版本。

其中包含的组件,也都有各自的版本,如下表:

Component

Edgware.SR3

Finchley.RC1

Finchley.BUILD-SNAPSHOT

spring-cloud-aws

1.2.2.RELEASE

2.0.0.RC1

2.0.0.BUILD-SNAPSHOT

spring-cloud-bus

1.3.2.RELEASE

2.0.0.RC1

2.0.0.BUILD-SNAPSHOT

spring-cloud-cli

1.4.1.RELEASE

2.0.0.RC1

2.0.0.BUILD-SNAPSHOT

spring-cloud-commons

1.3.3.RELEASE

2.0.0.RC1

2.0.0.BUILD-SNAPSHOT

spring-cloud-contract

1.2.4.RELEASE

2.0.0.RC1

2.0.0.BUILD-SNAPSHOT

spring-cloud-config

1.4.3.RELEASE

2.0.0.RC1

2.0.0.BUILD-SNAPSHOT

spring-cloud-netflix

1.4.4.RELEASE

2.0.0.RC1

2.0.0.BUILD-SNAPSHOT

spring-cloud-security

1.2.2.RELEASE

2.0.0.RC1

2.0.0.BUILD-SNAPSHOT

spring-cloud-cloudfoundry

1.1.1.RELEASE

2.0.0.RC1

2.0.0.BUILD-SNAPSHOT

spring-cloud-consul

1.3.3.RELEASE

2.0.0.RC1

2.0.0.BUILD-SNAPSHOT

spring-cloud-sleuth

1.3.3.RELEASE

2.0.0.RC1

2.0.0.BUILD-SNAPSHOT

spring-cloud-stream

Ditmars.SR3

Elmhurst.RELEASE

Elmhurst.BUILD-SNAPSHOT

spring-cloud-zookeeper

1.2.1.RELEASE

2.0.0.RC1

2.0.0.BUILD-SNAPSHOT

spring-boot

1.5.10.RELEASE

2.0.1.RELEASE

2.0.0.BUILD-SNAPSHOT

spring-cloud-task

1.2.2.RELEASE

2.0.0.RC1

2.0.0.RELEASE

spring-cloud-vault

1.1.0.RELEASE

2.0.0.RC1

2.0.0.BUILD-SNAPSHOT

spring-cloud-gateway

1.0.1.RELEASE

2.0.0.RC1

2.0.0.BUILD-SNAPSHOT

spring-cloud-openfeign

2.0.0.RC1

2.0.0.BUILD-SNAPSHOT

springcloud启动需要10秒 springcloud启动原理_maven_02

说Finchley 版本工作在 2.0.x 版本的 SpringBoot( 不支持 2.1.x ), 且不支持 SpringBoot 1.5.xß

接下来,我们就一一学习SpringCloud中的重要组件。

4.微服务场景模拟

首先,我们需要模拟一个服务调用的场景,搭建两个工程:yydcyy-service-provider(服务提供方)和yydcyy-service-consumer(服务调用方)。方便后面学习微服务架构

服务提供方:使用mybatis操作数据库,实现对数据的增删改查;并对外提供rest接口服务。

服务消费方:使用restTemplate远程调用服务提供方的rest接口服务,获取数据。

4.1.服务提供者

我们新建一个项目:yydcyy-service-provider,对外提供根据id查询用户的服务。

4.1.1.Spring脚手架创建工程

借助于Spring提供的快速搭建工具:

springcloud启动需要10秒 springcloud启动原理_maven_03

next–>填写项目信息:

springcloud启动需要10秒 springcloud启动原理_springcloud启动需要10秒_04

next --> 添加web依赖:

springcloud启动需要10秒 springcloud启动原理_maven_05

添加mybatis依赖:

springcloud启动需要10秒 springcloud启动原理_Cloud_06

Next --> 填写项目位置:

springcloud启动需要10秒 springcloud启动原理_spring_07

生成的项目结构,已经包含了引导类(itcastServiceProviderApplication):

springcloud启动需要10秒 springcloud启动原理_spring_08

springcloud启动需要10秒 springcloud启动原理_maven_09

依赖也已经全部自动引入:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>cn.yydcyy.service</groupId>
    <artifactId>itcast-service-provider</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>itcast-service-provider</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- 需要手动引入通用mapper的启动器,spring没有收录该依赖 -->
        <dependency>
            <groupId>tk.mybatis</groupId>
            <artifactId>mapper-spring-boot-starter</artifactId>
            <version>2.0.4</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

当然,因为要使用通用mapper,所以我们需要手动加一条依赖

<dependency>
    <groupId>tk.mybatis</groupId>
    <artifactId>mapper-spring-boot-starter</artifactId>
    <version>2.0.4</version>
</dependency>

非常快捷啊!

注 : spring.io 可能需要代理 VPN 才可以登上, 此处也可以采取 Maven 的 webapp 结果, 自行导入 pom.xml 坐标.

4.1.2.编写代码

springcloud启动需要10秒 springcloud启动原理_maven_10

springcloud启动需要10秒 springcloud启动原理_Cloud_11

4.1.2.1.配置

属性文件,这里我们采用了yaml语法,而不是properties:

server:
  port: 8081
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mybatis #你学习mybatis时,使用的数据库地址
    username: root
    password: root
mybatis:
  type-aliases-package: cn.yydcyy.service.pojo
4.1.2.2.实体类
@Table(name = "tb_user")
public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    // 用户名
    private String userName;

    // 密码
    private String password;

    // 姓名
    private String name;

    // 年龄
    private Integer age;

    // 性别,1男性,2女性
    private Integer sex;

    // 出生日期
    private Date birthday;

    // 创建时间
    private Date created;

    // 更新时间
    private Date updated;
     // 省略对应 setter() / getter()
.......
4.1.2.3.UserMapper
@Mapper
public interface UserMapper extends tk.mybatis.mapper.common.Mapper<User>{
}
4.1.2.4.UserService
@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    public User queryById(Long id) {
        return this.userMapper.selectByPrimaryKey(id);
    }
}
4.1.2.5.UserController

添加一个对外查询的接口:

@RestController
@RequestMapping("user")
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("{id}")
    public User queryById(@PathVariable("id") Long id) {
        return this.userService.queryById(id);
    }
}

4.1.3.启动并测试

启动项目,访问接口:http://localhost:8081/user/1

springcloud启动需要10秒 springcloud启动原理_maven_12

问题

springcloud启动需要10秒 springcloud启动原理_springcloud启动需要10秒_13

4.2.服务调用者

搭建yydcyy-service-consumer服务消费方工程。

4.2.1.创建工程

与上面类似,这里不再赘述,需要注意的是,我们调用yydcyy-service-provider的解耦获取数据,因此不需要mybatis相关依赖了。

springcloud启动需要10秒 springcloud启动原理_maven_14

springcloud启动需要10秒 springcloud启动原理_maven_15

springcloud启动需要10秒 springcloud启动原理_Cloud_16

pom:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>cn.yydcyy.service</groupId>
    <artifactId>itcast-service-consumer</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>itcast-service-consumer</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>

4.2.2.编写代码

springcloud启动需要10秒 springcloud启动原理_maven_17

!

springcloud启动需要10秒 springcloud启动原理_spring_18

首先在引导类中注册RestTemplate

@SpringBootApplication
public class ItcastServiceConsumerApplication {

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    public static void main(String[] args) {
        SpringApplication.run(ItcastServiceConsumerApplication.class, args);
    }
}

编写配置(application.yml):

server:
  port: 80

编写UserController:

@Controller
@RequestMapping("consumer/user")
public class UserController {

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping
    @ResponseBody
    public User queryUserById(@RequestParam("id") Long id){
        User user = this.restTemplate.getForObject("http://localhost:8081/user/" + id, User.class);
        return user;
    }

}

pojo对象(User):

public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long id;

    // 用户名
    private String userName;

    // 密码
    private String password;

    // 姓名
    private String name;

    // 年龄
    private Integer age;

    // 性别,1男性,2女性
    private Integer sex;

    // 出生日期
    private Date birthday;

    // 创建时间
    private Date created;

    // 更新时间
    private Date updated;
    // 省略 setter() / getter()

4.2.3.启动测试

因为我们没有配置端口,那么默认就是8080,我们访问:http://localhost/consumer/user?id=1

springcloud启动需要10秒 springcloud启动原理_spring_19

一个简单的远程服务调用案例就实现了。

原理图 :

springcloud启动需要10秒 springcloud启动原理_maven_20

注意 : 需要 service 和 customer 两个 "服务器"都跑起来. (要不怎么响应? )

注 :应该是响应的 , 数据没处理好, 算了

4.3.有没有问题?

简单回顾一下,刚才我们写了什么:

  • yydcyy-service-provider:一个提供根据id查询用户的微服务。
  • yydcyy-service-consumer:一个服务调用者,通过RestTemplate远程调用yydcyy-service-provider。

存在什么问题?

  • 在consumer中,我们把url地址硬编码到了代码中,不方便后期维护
  • consumer需要记忆provider的地址,如果出现变更,可能得不到通知,地址将失效
  • consumer不清楚provider的状态,服务宕机也不知道
  • provider只有1台服务,不具备高可用性
  • 即便provider形成集群,consumer还需自己实现负载均衡

其实上面说的问题,概括一下就是分布式服务必然要面临的问题:

  • 服务管理
  • 如何自动注册和发现
  • 如何实现状态监管
  • 如何实现动态路由
  • 服务如何实现负载均衡
  • 服务如何解决容灾问题
  • 服务如何实现统一配置

以上的问题,我们都将在SpringCloud中得到答案。