admin管理员组

文章数量:1650821

文章目录

    • 1、常见面试题
    • 2、微服务概述
      • 2.1、什么是微服务
      • 2.2、微服务与微服务架构
      • 2.3、微服务的优缺点
      • 2.4、微服务的技术栈
      • 2.5、为甚选择SpringCloud作为微服务架构
        • 1、选择依据
        • 2、当前各大IT公司的微服务架构有哪些?
        • 3、各个微服务框架对比
    • 3、SpringCloud入门概念
      • 3.1、SpringCloud是什么
      • 3.2、SpringBoot和SpringCloud关系
      • 3.3、Dubbo和SpringCloud技术选型
        • 1、分布式+服务治理Dubbo
        • 2、Dubbo和SpringCloud对比
      • 3.4、SpringCloud用途
      • 3.5、SpringCloud下载
    • 4、基于Rest学习的环境搭建案例
      • 4.1、搭建父项目
      • 4.2、搭建实体类项目
      • 4.3、搭建提供者项目
      • 4.4、搭建消费者项目
      • 4.5、总结
    • 5、Eureka服务注册与发现
      • 5.1、什么是Eureka
      • 5.2、原理部分
        • 1、Eureka的基本结构
        • 2、三大角色
      • 5.3、构建步骤
        • 1、Eureka-server
        • 2、Service Provider
        • 3、Eureka的自我保护机制
        • 4、8001服务发现Discovery机制
      • 5.4、构建三台计算机的集群测试
      • 5.5、对比ZooKeeper

学习SpringCloud知识储备要求:

  • 数据库
  • Mybatis
  • Spring
  • SpringMVC
  • SpringBoot
  • Dubbo、ZooKeeper、分布式基础
  • Maven、Git
  • AJAX、JSON

对于SpringCloud的部分简介,可以参考之前的一篇关于微服务的简介

传送门


本实验中所涉及的所有代码的码云链接:
传送门


1、常见面试题

  1. 什么是微服务?
  2. 微服务之间是如何独立通信的?
  3. SpringCloud和Dubbo有哪些区别?
  4. SoringBoot和SpringCloud,请你谈谈对他们的理解。
  5. 什么是服务熔断?什么是服务降级?
  6. 微服务的优缺点分别是什么?说下你再实际的项目开发中遇到过的问题。
  7. 你所知道的微服务技术栈有哪些?请举例说明
  8. Eureka和ZooKeeper都可以提供服务的注册与发现功能,请说说两者的区别。

2、微服务概述

2.1、什么是微服务

微服务(Microservice Architecture)是近几年流行的一种架构思想。

就目前而言,对于微服务,也就没有一个统一的标准定义。但通常而言,微服务架构是一种架构模式。或者说是一种架构风格,它提倡将单一的应用程序划分成一组小的服务,每个服务运行在其独立的自己的进程内,服务之间互相协调,互相配置,为用户提供最终价值,服务之间采用轻量级的通信机制互相沟通,每个服务都围绕着具体的业务进行构建,并且能够被独立的部署到生产环境中,另外,应该尽量避免统一的、集中的服务管理机制,对具体的一个服务而言,应根据业务上下文,选择合适的语言,工具对其进行构建,可以有一个非常轻量级的集中式管理来协调这些服务,可以使用不同的语言来编写服务,也可以使用不同的数据存储。

微服务化的核心就是将传统的一站式应用,根据业务拆分成一个一个的服务,彻底的去解耦,每一个微服务提供单一业务功能的服务,一个服务做一件事情,从技术角度看,就是一种小而独立的处理过程,类似进程的概念,能够自行单独启动或者销毁,拥有自己独立的数据库。

2.2、微服务与微服务架构

微服务:

强调的是服务的大小,关注的是 某一个点,是具体解决某一个问题,提供落地对应服务的一个服务应用,下一的看,可以看作是IDEA中的一个微服务工程,或者model

微服务架构:

一种新得结构模式,2014年Martin Fowler提出

微服务架构是一种架构模式,它提倡将单一应用程序划分成一组小得服务,服务之间互相协调、互相配合,为用户提供最终价值,每一个服务运行在其独立的进程中,服务与服务之间采用轻量级的通信机制互相协作,每个服务都围绕着具体的业务进行构建,并且能够被独立的部署到生产环境中,另外,应尽量不免统一的、集中式的服务管理机制,对具体的一个服务而言,应根据业务上下文,选择合适的语言、工具对其进行构建。

2.3、微服务的优缺点

优点:

  • 每个服务足够的内聚,足够小,代码容易被理解,这样能聚焦以恶搞指定的业务功能或业务需求
  • 开发简单,开发效率提高,一个服务可能就只干一件事情,单一职责
  • 微服务能够被小团队单独开发,一个小团队是一般为2-5人
  • 微服务是松耦合的,是有功能意义的服务,无论是在开发阶段或部署阶段都是独立的
  • 微服务能使用不同的语言开发
  • 易于和第三方集成,微服务允许容易且灵活的方式集成自动部署,通过持续集成工具,如jenkins、Hudson、bamboo
  • 微服务易于被一个开发人员理解、修改、为何,这样小团队能够更关注自己的工作成果,无需通过合作才能体现价值
  • 微服务允许你利用融合最新的技术
  • 微服务知识业务逻辑代码,不会和HTML、CSS或其他界面混合
  • 每一个微服务都有自己的存储能力,可以有自己的数据库,也可以有统一数据库

缺点:

  • 开发人员要处理分布式系统的复杂性
  • 多服务运维难度,对着服务的增加,运维压力也逐步增大
  • 系统部署依赖
  • 服务间通信成本高
  • 数据一致性难
  • 系统集成测试复杂
  • 性能监控

2.4、微服务的技术栈

微服务条目落地技术
服务开发Spring、SpringMVC、SpringBoot
服务配置与管理Netflix公司的Archaius、阿里的Diamond等
服务注册与发现Eureka、Consul、ZooKeeper
服务调用Rest、RPC、gRPC
服务熔断器Hystrix、Envoy等
负载均衡Ribbon、Niginx等
服务接口调用(客户端调用服务的简化工具)Feign等
消息队列Kafka、RabbitMQ、ActiveMQ等
服务配置中心管理SpringCloudConfig、Chef等
服务路由(API网关)Zuul等
服务监控Zabbix、Nagios、Metrics、Specatator等
全链路追踪Zipkin、Brave、Dapper等
服务部署Docker、OpenStack、Kubernetes等
数据流操作开发包SpringCloud Stream(封装Redis,Rabbit,Kafka等发送接收消息)
事件消息总线SpringCloud Bus

2.5、为甚选择SpringCloud作为微服务架构

1、选择依据
  • 整体解决方案和框架成熟度
  • 社区活跃度
  • 可维护性强
  • 学习曲线低
2、当前各大IT公司的微服务架构有哪些?
  • 阿里:dubbo+HFS
  • 京东:JSF
  • 新浪:Motan
  • 当当网:Dubbox

3、各个微服务框架对比
功能点/服务框架Netflix/SpringCloudMontangRPCThriftDubbo/Dubbox
功能定位完整的微服务框架RPC框架,但整合了Zookeeper或Consul,实现集群环境的基本服务注册/发现RPC框架RPC框架服务框架
支持Rest是,Ribbon支持多种可插拔的序列化选择
支持RPC是(Hession2)
支持多语言是(Rest形式)
负载均衡是(服务端zuul+客户端Ribbon),zuul服务,动态路由,云端负载均衡Eureka(针对中间层服务器)是(客户端)是(客户端)
配置服务NetfixArchaius,SpringCloud Config Server集中配置是(ZooKeeper提出)
服务调用链监控是(zuul),zuul提供边缘服务,API网关
高可用/容错是(服务端Hystri+客户端Ribbon)是(客户端)是(客户端)
典型应用案例NetflixSinaGoogleFacebook
社区活跃程度一般一般2017年后重新开始维护,之前中断了5年
学习难度中断
文档丰富程度一般一般一般
其他Spring Cloud Bus为我们的应用程序带来了更多管理端点支持降级Netflix内部在开发集成RPCIDL定义实践的公司比较多

3、SpringCloud入门概念

3.1、SpringCloud是什么

SpringCloud是基于SpringBoot提供了一套微服务解决方案,包括服务注册与发现,配置中心,全链路监控,服务网关,负载均衡,容断器等组件,除了基于NetFlix的开源组件做高度的抽象封装外,它还有一些选型中立的开源组件

SpringCloud利用SpringBoot的开发便利性,巧妙地简化了分布式系统基础设施地开发,SpringCloud为开发人员提供了快速构建分布式系统地一些工具,包括配置管理,服务发现,熔断器,路由,为代理,事件总线,全局锁,决策竞选,分布式会话等,它们都可以用SpringBoot地开发风格做到一键启动和部署。

SpringBoot并没有重复造轮子,它只是将目前各家公司开发地比较成熟的、经得起实际考研的服务框架组合起来,通过SpringBoot风格进行再封装,屏蔽了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署、易维护的分布式系统开发工具包

SpringCloud是分布式微服务架构下的一站式解决方案,是各个微服务架构落地技术的集合体,俗称微服务全家桶。

3.2、SpringBoot和SpringCloud关系

  • SpringBoot专注于快速方便的开发单个个体微服务。 -jar
  • SpringCloud是关注全局的微服务协调整理治理框架,它将SpringBoot开发的一个个单体微服务整合并管理起来,为各个微服务之间提供:配置管理,服务发现,熔断器,路由,为代理,事件总线,全局锁,决策竞选,分布式会话等集成服务
  • SpringBoot可以离开SpringCloud独立使用,开发项目,但是SpringCloud离不开SpringBoot,属于依赖关系
  • SpringBoot专注于快速、方便的开发单个个体微服务,SpringCloud关注全局的服务治理框架

3.3、Dubbo和SpringCloud技术选型

1、分布式+服务治理Dubbo

目前成熟的互联网架构:应用服务化拆分+消息中间件

2、Dubbo和SpringCloud对比
DubboSringle
服务注册中心ZooKeeperSpring Cloud Netfilx Eureka
服务调用方式RPCREST API
服务监控Dubbo-monitorSpring Boot Admin
熔断器不完善Spring Cloud Netfilx Hystrix
服务网关Spring Cloud Netfilx Zuul
分布式配置Spring Cloud Config
服务跟踪Spring Cloud Sleuth
消息总线Spring Cloud Bus
数据流Spring Cloud Stream
批量任务Spring Cloud Task

最大区别

SpringCloud抛弃了Dubbo的RPC通信,采用的是基于HTTP的REST方式

严格来说,这两种方式各有优势,虽然从一定程度上来说,后者牺牲服务调用的性能,但也避免了原生RPC带来的问题。而且REST相比RPC更为灵活,服务提供方和调用方的依赖只依靠一纸契约,不存在代码级别的强依赖,这在强调快速演化的微服务环境下,显得更加合适。

品牌机与组装机的区别

显然,SpringCloud的功能比Dubbo更加强大,涵盖面更广,而且作为Spring的明星项目,它也能够与Spring、SpringBoot、SpringData 、Spring Batch等其他的Spring项目完美融合,这些对于微服务而言是至关重要的。使用Dubbo构建的微服务架构就像组装电脑,各个环节我们的选择自由度很高,但是最终结果很有可能是因为其中一条内存质量不行,就无法开机。但是如果是一个高手,这些也都不是什么问题。Spring Cloud就像一个品牌机,再Spring Source的整合下,做了大量的兼容性测试,保证了机器拥有更高的稳定性,但是如果想要在使用非原装组件外的东西,就需要对其他的基础有足够多的了解

社区支持与跟新力度

最为重要的是,Dubbo停止了五年左右的跟新,对然在2017年7月重启,但是对于技术发展的新需求,需要由开发者自行拓展升级(比如当当网的Dubbox),这对于很多想要采用微服务架构的中小软件组织,显然是不太适合的,中小公司没有这么强大的技术能力去修改Dubbo的源码以及开发一整套的解决方案,并不是每一个公司都有阿里的人才,以及其真实的先生生产环境测试。

总结

Dubbo重启维护开发的主要负责人,刘军。其是阿里巴巴中间件高级研发工程师,主导了Dubbo重启维护以后的几个发版计划,专注于高性能RPC框架和微服务相关领域。曾负责网易考拉RPC框架的研发以及指导在内部使用,参与了服务治理平台、分布式跟踪系统、分布式一致性框架等从无到有的设计与开发过程。

解决的问题领域不一样:Dubbo的定位是一款RPC框架,Spring Cloud的目标是微服务架构下的一站式解决方案。

3.4、SpringCloud用途

  • Distributed/versioned configuration(分布式/版本控制配置)
  • Service registration and discovery(服务注册与发现)
  • Routing(路由)
  • Service-to-service calls(服务到服务的调用)
  • Load balancing(负载均衡)
  • Circuit Breakers(熔断器)
  • Distributed messaging(分布式消息管理)

3.5、SpringCloud下载

官网:https://spring.io/projects/spring-cloud

版本的命名规则是采用伦敦地铁站的名称,同时shunxu根据字母表的顺序来对应版本的时间顺序。由A开始,往后

中文网:https://www.springcloud/spring-cloud-dalston.html


4、基于Rest学习的环境搭建案例

由于版本跟新太快,建议所有的版本都使用稍微新一点的,否则极易出现版本不兼容的问题。

4.1、搭建父项目

1、新建一个普通的maven项目,删除src目录

2、引入总体项目所需要的依赖

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

    <groupId>pers.mobian</groupId>
    <artifactId>springcloud</artifactId>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>springcloud-api</module>
        <module>springcloud-provider-dept-8001</module>
        <module>springcloud-consumer-dept-80</module>
    </modules>

    <!--将打包方式设置为pom-->
    <packaging>pom</packaging>
    <!--可以使用该方式,对所有依赖的版本进行集中的管理-->
    <properties>
        <lombok.version>1.18.12</lombok.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <!--SpringCloud依赖-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Greenwich.SR3</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.2.5.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.47</version>
            </dependency>

            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>1.1.20</version>
            </dependency>
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>1.3.2</version>
            </dependency>

            <!--主要用于日志和测试-->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
            </dependency>

            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>${lombok.version}</version>
            </dependency>
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>1.2.17</version>
            </dependency>
            <dependency>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-core</artifactId>
                <version>1.2.3</version>
            </dependency>

        </dependencies>

    </dependencyManagement>

    <!--资源过滤器-->
    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.yml</include>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.yml</include>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>
</project>

4.2、搭建实体类项目

1、新建一个module,取名为springcloud-api

2、新建数据库,取名为db01;再新建张表,取名为dept;设置三个字段(deptno为自增的不为空的主键id,dname为varche(60)的部分名字字段,db_source为varche(60)的数据库的名字字段)。并且使用IDEA进行连接

3、引入Lombok依赖,方便后期代码编写

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache/POM/4.0.0"
         xmlns:xsi="http://www.w3/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache/POM/4.0.0 http://maven.apache/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>springcloud</artifactId>
        <groupId>pers.mobian</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>springcloud-api</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>
</project>

3、在指定的路径下:pers.mobian.springcloud.pojo,新建一个Dept实体类

package pers.mobian.springcloud.pojo;

import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.io.Serializable;


@Data
@NoArgsConstructor
//开启后支持链式写法
@Accessors(chain = true) //开启链式编程,如:dept.setDeptNo(1).setDname("mobian").setDbSource("001")
public class Dept implements Serializable {//ORM类表关系映射
    private Long deptno;
    private String dname;
    //查看数据是来自哪一个数据库的字段,同一个信息可能存在不同的数据库
    private String db_source;

    public Dept(String dname) {
        this.dname = dname;
    }
}

4.3、搭建提供者项目

1、新建一个提供者项目:springcloud-provider-dept-8001

2、引入相应的pom依赖(由于是子项目,可以不用写出具体的版本,直接引入父项目的版本信息)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache/POM/4.0.0"
         xmlns:xsi="http://www.w3/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache/POM/4.0.0 http://maven.apache/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>springcloud</artifactId>
        <groupId>pers.mobian</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>springcloud-provider-dept-8001</artifactId>
    <dependencies>
        <!--需要拿到我们之前的实体类,所以要配置api module-->
        <dependency>
            <groupId>pers.mobian</groupId>
            <artifactId>springcloud-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <!--junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>


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

        <!--jetty服务器-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jetty</artifactId>
        </dependency>

        <!--热部署工具-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
    </dependencies>

    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.yml</include>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.yml</include>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>

</project>

3、按照图示,在对应的位置新建对应的文件

4、配置对应的核心配置文件

server:
  port: 8001

#Mybatis配置
mybatis:
  type-aliases-package: pers.mobian.springcloud.pojo
  mapper-locations: classpath:mybatis/mapper/*.xml
  config-location: classpath:mybatis/mybatis-config.xml


#Spring配置
spring:
  application:
    name: springcloud-provider-dept
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: org.gjt.mm.mysql.Driver
    url: jdbc:mysql://localhost:3306/db01?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    username: root
    password:

5、编写连接数据库的mybatis的配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis//DTD Config 3.0//EN"
        "http://mybatis/dtd/mybatis-3-config.dtd">

<configuration>

    <settings>
        <setting name="cacheEnabled" value="true"></setting>
    </settings>
    <typeAliases>
        <typeAlias alias="Dept" type="pers.mobian.springcloud.pojo.Dept"></typeAlias>
    </typeAliases>

</configuration>

6、按照日常的步骤,先设置dao层的接口

package pers.mobian.springcloud.dao;

import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
import pers.mobian.springcloud.pojo.Dept;

import java.util.List;

@Mapper
@Component
public interface DeptDao {

    public boolean addDept(Dept dept);

    public Dept queryById(Long id);

    public List<Dept> queryAll();
}

7、编写dao层接口类的mapper文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis//DTD Mapper 3.0//EN"
        "http://mybatis/dtd/mybatis-3-mapper.dtd">


<!--namespace:绑定一个对应的Dao/Mapper接口-->
<mapper namespace="pers.mobian.springcloud.dao.DeptDao">


    <insert id="addDept" parameterType="Dept">
        insert into dept (dname, db_source) values (#{dname},DATABASE());
    </insert>


    <select id="queryById" resultType="Dept" parameterType="Long">
        select * from dept where deptno = #{deptno};
    </select>

    <select id="queryAll" resultType="Dept">
        select * from dept;
    </select>

</mapper>

8、编写service层的接口类和接口类的实现类

package pers.mobian.springcloud.service;

import pers.mobian.springcloud.pojo.Dept;

import java.util.List;

public interface DeptService {
    public boolean addDept(Dept dept);

    public Dept queryById(Long id);

    public List<Dept> queryAll();
}
package pers.mobian.springcloud.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import pers.mobian.springcloud.dao.DeptDao;
import pers.mobian.springcloud.pojo.Dept;

import java.util.List;

@Service
public class DeptServiceImpl implements DeptService {

    @Autowired(required = true)
    private DeptDao deptDao;


    public boolean addDept(Dept dept) {
        return deptDao.addDept(dept);
    }

    public Dept queryById(Long id) {
        return deptDao.queryById(id);
    }

    public List<Dept> queryAll() {
        return deptDao.queryAll();
    }
}

9、编写相应的Controller层类

package pers.mobian.springcloud.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import pers.mobian.springcloud.pojo.Dept;
import pers.mobian.springcloud.service.DeptService;

import java.util.List;

@RestController
public class DeptController {

    @Autowired
    private DeptService deptService;

    @PostMapping("/dept/add")
    public boolean addDept(Dept dept) {
        return deptService.addDept(dept);
    }

    @GetMapping("/dept/get/{id}")
    public Dept get(@PathVariable("id")Long id){
        return deptService.queryById(id);
    }

    @GetMapping("/dept/list")
    public List<Dept> queryAll(){
        return deptService.queryAll();
    }

}

10、编写SpringBoot的启动类

package pers.mobian.springcloud;


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;

//启动类
@SpringBootApplication
public class DeptProvider_8001 {
    public static void main(String[] args) {
        //SpringApplication.run(new Class[] { MusicApp.class,CommonApp.class }, args);
        SpringApplication.run(DeptProvider_8001.class,args);
    }
}

11、启动测试,访问对应的端口

一定进行访问测试,只有这样,出现了错误,才能及时确定出错误的位置

4.4、搭建消费者项目

1、新建一个消费者项目:springcloud-consumer-dept-80

2、引入对应的pom依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache/POM/4.0.0"
         xmlns:xsi="http://www.w3/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache/POM/4.0.0 http://maven.apache/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>springcloud</artifactId>
        <groupId>pers.mobian</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>springcloud-consumer-dept-80</artifactId>

    <dependencies>
        <dependency>
            <groupId>pers.mobian</groupId>
            <artifactId>springcloud-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
    </dependencies>

</project>

3、按照对应的文件位置,新建对应的文件

4、配置该项目的核心配置类

server:
  port: 80

5、由于后期的容器需要,需要先编写一个Bean的配置类

package pers.mobian.springcloud.config;


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration //等价于spring的applicationContext.xml
public class ConfigBean {

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

6、编写对应的Controller类

package pers.mobian.springcloud.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import pers.mobian.springcloud.pojo.Dept;

import java.util.List;

@RestController
public class DeptConsumerController {

    //次控制器类来自消费者,访问对应的端口,就能够访问到指定的提供者的信息
    //RestTemplate  注册到Spring容器中,供我们调用对应的方法即可
    @Autowired
    private RestTemplate restTemplate;

    private static final String REST_URL_PREFIX = "http://localhost:8001";

    @RequestMapping("/consumer/dept/add")
    public boolean add(Dept dept) {
        return restTemplate.postForObject(REST_URL_PREFIX + "/dept/add", dept, boolean.class);
    }

    @RequestMapping("/consumer/dept/get/{id}")
    public Dept get(@PathVariable("id") Long id) {
        return restTemplate.getForObject(REST_URL_PREFIX + "/dept/get/" + id, Dept.class);
    }


    @RequestMapping("/consumer/dept/list")
    public List<Dept> list() {
        //getForObject:表示对应的访问方式为get
        //参数列表:访问的提供者的路径,以及返回的类型
        return restTemplate.getForObject(REST_URL_PREFIX + "/dept/list", List.class);
    }

}

7、编写对应的SpringBoot的启动类

package pers.mobian.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

8、启动测试

至此,项目基本搭建完成

4.5、总结

自己编写过程遇到的问题:

  • 如果是由于缺少确定的配置而无法启动程序,可以去编译的项目中查看,是否缺少某些文件
  • 按道理yaml格式与yml格式的核心配置都可以,但是自己添加的资源过滤器可能只设置了其中一种,继而导致新建了一个没有配置的格式,导致出现资源无法过滤的情况。
  • 注意编写时的字母问题,特别是无法排错的常量

次项目的搭建,可以和之前的一个Dubbo+ZooKeeper+SpringBoot的项目形成一个对比,传送门。

前者的形式,基于PRC通信,由于在消费者中需要引入提供者的对应的类,继而有一定耦合性;后者基于Rest通信,只需要提供对应的url路径即可,避免了程序之间的耦合性。


5、Eureka服务注册与发现

5.1、什么是Eureka

  • Netflix在设计Eureka时,遵循的是AP原则
  • Eureka是Netflix的一个子模块,也是核心模块之一。Eureka是一个基于REST的服务,用于定位服务,以实现云端中间层服务发现和故障转移,服务注册与发现对于微服务来说非常重要,有了服务发现与注册,只需要使用服务的标识符,就可以访问到服务,而不需要修改服务调用的配置文件了,功能类似于Dubbo的注册中心,比如ZooKeeper

5.2、原理部分

1、Eureka的基本结构
  • SpringCloud封装了Netflix公司开发的Eureka模块来实现服务注册和发现(对比ZooKeeper)

  • Eureka采用了C-S架构的设计,EurekaServer作为服务注册功能的服务器,他是服务注册中心(之前的是Dubbo-admin)

  • 系统中的其他微服务。使用Eureka的客户端连接到了EurekServer并维持心跳连接。这样系统的维护人员就可以通过EurekaServer来监控系统中各个微服务是否正常运行,SpringCloud的一些其他模块(比如Zuul)就可以通过EurekaServer来发现系统中的其他微服务,并执行相关的逻辑

  • 与Dubbo架构进行对比

  • Eureka包含两个组件:Eureka Server和Eureka Client

  • Eureka Server提供服务注册服务,各个节点启动后,会在Eureka Server中注册,这样Eureka Server中的服务注册表将会出现所有可能服务节点的信息,服务节点的信息可以在界面中直观的看到

  • Eureka Client是一个Java客户端,用于简化Eureka Server的交互,客户端同时也具备一个内置的,使用轮询负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳(默认周期为30秒)。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,Eureka Server将从服务注册表中把这个服务节点移除(默认周期为90秒)

2、三大角色
  • Eureka Server:提供服务注册与发现
  • Server Provider:将自身服务注册到Eureka中,从而使得消费方能够找到
  • Server Consumer:服务消费方从Eureka中获取注册服务列表,从而找到消费服务

5.3、构建步骤

1、Eureka-server

1、新建一个项目

2、导入对应的依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache/POM/4.0.0"
         xmlns:xsi="http://www.w3/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache/POM/4.0.0 http://maven.apache/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>springcloud</artifactId>
        <groupId>pers.mobian</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>springcloud-eureka-7001</artifactId>
    <dependencies>
        <!-- https://mvnrepository/artifact/org.springframework.cloud/spring-cloud-starter-eureka-server -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka-server</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>
        <!--热部署工具-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
    </dependencies>

</project>

3、对应的文件位置

4、编写核心配置文件

server:
  port: 7001

#Eureka配置
eureka:
  instance:
    hostname: localhost #eureka服务端的实例名称
  client:
    register-with-eureka: false #表示是否eureka注册中心注册自己
    fetch-registry: false #fetch-registry如果设置为false,则表示自己为注册中心
    service-url:  #监控页面
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

5、编写对应的启动类

package pers.mobian.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer //EnableEurekaServer 服务端的启动类,可以让别人注册进来
public class EurekaService_7001 {
    public static void main(String[] args) {
        SpringApplication.run(EurekaService_7001.class,args);
    }
}

6、访问对应的地址:http://localhost:7001/

由于版本冲突,我后来将SpringBoot的依赖的版本进行了回调,变成了2.1.6

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.1.6.RELEASE</version>
    <type>pom</type>
    <scope>import</scope>
</dependency>
2、Service Provider

将8001的服务入驻到7001的eureka中

1、修改80001服务的pom文件,增加eureka的支持

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
    <version>1.4.6.RELEASE</version>
</dependency>
<!--actuator-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

2、修改yml核心配置文件

server:
  port: 8001

#Mybatis配置
mybatis:
  type-aliases-package: pers.mobian.springcloud.pojo
  mapper-locations: classpath:mybatis/mapper/*.xml
  config-location: classpath:mybatis/mybatis-config.xml


#Spring配置
spring:
  application:
    name: springcloud-provider-dept
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: org.gjt.mm.mysql.Driver
    url: jdbc:mysql://localhost:3306/db01?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    username: root
    password:



#Eureka的配置,服务注册到的目的地
eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka/
    instance:
      instance-id: springcloud-provider-dept8001

#设置对应连接后跳转的信息,actuator与注册微服务信息完善所需
info:
  app.name: mobian-springcloud
  company.name: mobian.springcloud 

3、修改8001端口类的启动类的注解支持

package pers.mobian.springcloud;


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

//启动类
@SpringBootApplication
@EnableEurekaClient //在服务启动后,自动注册到Eureka
public class DeptProvider_8001 {
    public static void main(String[] args) {
        //SpringApplication.run(new Class[] { MusicApp.class,CommonApp.class }, args);
        SpringApplication.run(DeptProvider_8001.class,args);
    }
}

4、再次访问



3、Eureka的自我保护机制

自我保护机制:好死不如赖活着

某时刻某一个微服务不可以用了,eureka不会立即清理,依旧会对该微服务的信息进行保存

  • 默认情况下,如果EurakeServer在一定时间内没有接收到某个微服务实例的心跳,EurekaServer将会注销该实例(默认90秒)。但是当网络分区故障发生时,微服务与Eureka之间无法正常通行,以上行为可能变得非常危险,因为微服务本身其实是健康的,此时不应该注销这个服务。Eureka-Server节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。一旦进入该模式,EurekaServer就会保护服务注册表中的信息,不再删除服务注册表中的数据(也就是不会注销任何微服务)。当网络故障恢复后,该EurekaServer节点会自动退出自我保护模式。
  • 在自我保护模式中,EurekaServer会保护服务注册表中的信息,不再注销任何服务实例。当它收到的心跳数重新恢复到阈值以上时,该EurekaServer节点就会自动退出自我保护模式。它的设计哲学就是宁可保留错误的服务注册信息,也不盲目注销任何可能是健康的服务实例。
  • 综上,自我保护是一种应对网络异常的安全保护措施。它的架构哲学是宁可保留所有微服务(健康的微服务和不健康的微服务都是会被保留的),也不盲目注销任何健康微服务,使用自我保护模式,可以让Eureka集群更加的健壮稳定
  • 在SpringCloud中,可以使用eureka.server.enable-self-preservation = false,禁用自我保护模式(不推荐关闭自我保护机制)
4、8001服务发现Discovery机制

在团队开发中,可以通过discovery进行查询

1、在8001端口的项目的Controller类中,添加一个映射

//注册进来的微服务,获取一些信息
@RequestMapping("/dept/discovery")
public Object discovery() {
    //获取微服务列表的清单
    List<String> services = discoveryClient.getServices();
    System.out.println("discovery=>services:" + services);
    List<ServiceInstance> instances = discoveryClient.getInstances("SPRINGCLOUD-PROVIDER-DEPT");
    for (ServiceInstance instance : instances) {
        System.out.println(
                instance.getHost() + "\t" +
                instance.getPort() + "\t" +
                instance.getUri() + "\t" +
                instance.getServiceId()
        );
    }
    return this.discoveryClient;
}

2、在项目的启动类中添加对应的服务发现注解

package pers.mobian.springcloud;


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

//启动类
@SpringBootApplication
@EnableEurekaClient //在服务启动后,自动注册到Eureka
@EnableDiscoveryClient
public class DeptProvider_8001 {
    public static void main(String[] args) {
        //SpringApplication.run(new Class[] { MusicApp.class,CommonApp.class }, args);
        SpringApplication.run(DeptProvider_8001.class,args);
    }
}

3、访问端口:http://localhost:8001/dept/discovery

5.4、构建三台计算机的集群测试

目的:集群的计算机之间可以互相访问,并且其中一台断开连接,不影响其他的计算机的正常运行

1、为了方便我们的观察,我们在我们电脑的访问域名进行修改,在C:\Windows\System32\drivers\etc路径下修改我们的hosts文件

关于不懂本地修改主机名的含义的,可以参考我之前的JavaWeb(二)中的3.3第二小节的面试题,希望对你有帮助

2、新建两个项目,项目名分别为:springcloud-eureka-7002和springcloud-eureka-7003

3、在项目中复制对应的文件夹及内容

4、修改7001、7002、7003的配置文件(绑定想要集群的电脑的端口及域名的信息)

server:
  port: 7001

#Eureka配置
eureka:
  instance:
    hostname: eureka7001 #eureka服务端的实例名称
  client:
    register-with-eureka: false #表示是否eureka注册中心注册自己
    fetch-registry: false #fetch-registry如果设置为false,则表示自己为注册中心
    service-url:  #监控页面
      defaultZone: http://eureka7002:7001/eureka/,http://eureka7003:7003/eureka/
server:
  port: 7002

#Eureka配置
eureka:
  instance:
    hostname: eureka7002 #eureka服务端的实例名称
  client:
    register-with-eureka: false #表示是否eureka注册中心注册自己
    fetch-registry: false #fetch-registry如果设置为false,则表示自己为注册中心
    service-url:  #监控页面
      defaultZone: http://eureka7001:7001/eureka/,http://eureka7003:7003/eureka/
server:
  port: 7003

#Eureka配置
eureka:
  instance:
    hostname: eureka7003 #eureka服务端的实例名称
  client:
    register-with-eureka: false #表示是否eureka注册中心注册自己
    fetch-registry: false #fetch-registry如果设置为false,则表示自己为注册中心
    service-url:  #监控页面
      defaultZone: http://eureka7001:7001/eureka/,http://eureka7002:7002/eureka/

由于我们修改了本机的名字,所以这三个程序可以看成是三个不同的计算机上的电脑程序

5、开启三个项目服务,以及对应的提供者项目

我的电脑只有8G,内存占用92%上下,异常卡

开启以后访问其中一个程序的端口,都能看到集群的其他计算机的信息

6、当我关闭了其中的一个项目后,访问其他的域名加端口,依然能够看到被关闭的项目

5.5、对比ZooKeeper

RDBMS(Mysql、Oracle、sqlServer)===> ACID

NoSQL(redis、mongdb)====> CAP

ACID是什么?

  • A(Atomicity)原子性
  • C(Consistency)一致性
  • I(Isolation)隔离性
  • D(Durability)持久性

CAP原则是什么?

  • C(Consistency)强一致性
  • A(Availability)可用性
  • P(Partition tolerance)分区容错性

CAP的三进二:CA、AP、CP

CAP理论的核心

  • 一个分布式系统不可能同时很好的满足一致性、可用性和分区容错性这三个需求
  • 根据CAP原理,将NoSQL数据库分成了满足CA原则,满足CP原则和满足AP原则三大类:
    • CA:单点集群,满足一致性,可用性的系统,通常可扩展性较差
    • CP:满足一致性,分区容错性的系统,通常性能不是特别高
    • AP:满足可用性,分区容错性的系统,通常可能对一致性要求低一些

作为服务注册中心,Eureka比ZooKeeper好在哪里?

著名的CAP理论之初,一个分布式系统不可能同时满足一致性、可用性、容错性。 由于分区容错性P在分布式系统中是必须要保证的,因此我们只能在A和C之间进行权衡

  • ZooKeeper保证的是CP
  • Eureka保证的是AP

1、ZooKeeper保证的是CP

当向注册中心车讯服务列表时,我们可以容忍注册中心返回的是几分钟以前的注册信息,但不能接受服务直接down掉不可用。即服务注册功能对可用性的要求高于一致性。但是ZooKeeper会出现这样的一个情形,当master节点因为网络故障与其他节点失去联系时,剩余节点会重新进行leader选举。问题在于,选举leader的时间太长一般需要30s-120s,且选举期间整个ZooKeeper集群都是不可用的,这就会导致在选举期间注册的服务瘫痪。在云部署的环境下,因为网络问题使得ZooKeeper事务master节点时比较大概率会发生的事件,虽然服务最终能够回复,但是漫长的选举事件导致的注册长期不可用是不能够容忍的

2、Eureka保证的是AP

Eureka看明白了这一点,因此在设计时就优先保证可用性。Eureka的各个节点都是平等的,几个节点挂掉不会影响正常节点的工作,剩余的节点依然可以提供注册和查询服务。而Eureka的客户端向某个Eureka注册时,如果发现连接失败,则会自动切换至其他节点,只要有一台Eureka还在,就能保住注册服务的可用性,只不过查到的信息可能不是最新的,除此之外,Eureka还有一种自我保护机制,如果在15分钟内超过85%的节点都没有正常的心跳,那么Eureka就认为客户端与注册中心之间出现了网络故障,此时会出现以下集中情况:

  1. Eureka不再向从注册列表中移除因为长时间没有收到心跳而应该过期的服务
  2. Eureka仍然能够接受新服务的注册和查询请求,凡是不会被同步到其他节点上(即保证当前节点依然可用)
  3. 当网络稳定时,当前实例新的注册信息会被同步到其他节点中

因此,Eureka可以很好的应对因网络故障导致部分节点失去联系的情况,而不像ZooKeeper那样使整个注册服务瘫痪

本文标签: SpringCloud