SpringCloud(遗憾的未完结系列....)


写在前面,跟着狂神学了一半,不是自己不想坚持下去了,因为Netflix已经不再维护一些组件,因为我用的springboot版本为:2.7.10,springcloud版本为:2021.0.6都是些还算新的版本(两年了,也不算新,但这时候netflix好多组件已经停止维护),导致我学习途中各种版本问题给我整麻了,因为某些原因,没那么大精力去搞这些版本问题…后续会看有无机会继续学习吧!!!


文章目录

SpringCloud简介及Rest环境搭建

前言

框架:

  • MVC:(Servlet+JSP+JavaBean):代码太过繁琐;

  • spring:目的是为了简化开发,但配置太过繁琐;

  • springBoot:简化了spring的配置,目前大多公司都把SSM转为springboot;

    新一代JavaEE开发标准

模块化(all in one),代码没变化


微服务架构的4个核心问题?

  1. 当服务很多,客户端如何访问?

    服务集群,负载均衡

  2. 服务之间如何通信?

    RPC和Http

  3. 服务之间的管理?

    注册与发现机制

  4. 某个服务挂了怎么办(最难解决)?

    备份

  • SpringCloud生态可以解决上述四个问题

    ① SpringCloud NetFlix:

    一站式解决方案~已经停更停止维护

    • api网关,zuul主键
    • Feign --HttpClient —Http通信方式,同步,阻塞
    • 服务注册发现:Eureka
    • 熔断机制:Hustrix

    ② Dubbo +Zookeeper

    半自动,需要整合别人的

    • api网关:没有,第三方组件/自己实现
    • Dubbo:RPC通信框架
    • 服务注册发现:Zookeeper
    • 熔断机制:没有,第三方组件

    ③ SpringCloud Alibaba

    一站式解决方案~比起SpringCloud NetFlix更加简化

新概念:服务网格~Sever Mesh

  • 总的来说就是解决:

    ①API网关 ②Http,RPC ③注册和发现 ④熔断机制

1 微服务和微服务架构

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

  • 微服务架构强调的一个重点是“业务需要彻底的组件化和服务化”,原有的单个业务系统会拆分为多个可以独立开发、设计、运行的小应用

1.1.微服务

重点在一个"微"字上,服务并不大,指在解决某一个问题/落地对应服务的一个服务应用。

  • 对标IDEA中的一个个微服务工程或者说Model
  • 强调个体,每个个体做自己的事情

1.2 微服务架构

微服务架构强调的一个重点是“业务需要彻底的组件化和服务化”,原有的单个业务系统会拆分为多个可以独立开发、设计、运行的小应用

1.3 微服务优缺点

优点

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

缺点:

  • 分布式系统的复杂性,难以部署
  • 多服务运维,随着服务增加,运维压力增大
  • 系统部署依赖
  • 服务间通信成本
  • 数据一致性
  • 系统集成测试
  • 性能监控

1.4 微服务技术栈有哪些?

微服务 微服务架构
服务开发 spring boot、spring mvc、spring。
服务的配置与管理 netfix 公司、archaius 阿里的 diamond 等。
服务的注册与发现 spring cloud 所采用的 eureka、consul、zookeeper 等。
服务的调用 rest 、GRPC、 RPC
服务的熔断器 hystrix,envoy 等。
负载均衡 ribbon、nginx。
服务接口调用 (客户端调用服务的简化工具) Feign 等消息队列 kafa、RabbitMQ、ActiveMQ等。
服务配置中心管理 Spring Cloud Config、Chef 等服务路由(API 网关) Zuu 等。
服务监控 Zabbix、Nagios、Metrics、Spectator 等。
全链路追踪 Zipkin、Brave、Dapper等。
服务部署 Docker、Open Stack、Kubernetes等。
数据流操作开发包 Spring Could Stream(封装与 Redis、Rabbit、Kafa 等发送接收消息)。
事件消息总线 Spring Cloud Bus

1.5 为什么使用SpringCloud作为微服务架构?

  • 学习门槛低(几个注解)
  • 发展快,有人维护
  • 天然支持SpringBoot,便于业务落地
  • 功能强大(几乎支持所有微服务,分布式架构下一站式解决方案, 俗称微服务全家桶 )

SpringCloud和Dubbo对比

功能 Dubbo SpringCloud
服务注册中心 Zookeeper Eureka(主流)、Consul、zookeeper
服务调用方式 RPC基于Dubbo协议 REST API 基于Http协议
服务监控 Dubbo-Monitor/Dubbo-admin Spring Boot Admin
熔断器 不完善 Spring Cloud Netflix Hystrix
服务网关 Spring Cloud Netflix Zuul、Gateway
分布式配置 Spring Cloud Config
服务跟踪 Spring Cloud Sleuth+Zipkin(一般)
数据流 Spring Cloud Stream
批量任务 Spring Cloud Task
信息总线 Spring Cloud Bus

2 SpringCloud入门概述

在这里插入图片描述

  • SpringCloud 基于Spring boot提供了一套微服务解决方案,包括服务注册与发现、配置中心、全链路中心、服务网关、负载均衡、熔断器等组件,除了Netflix的开源组件高度抽象封装,还有一些选型中立的开源组件。

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

2.1 SpringCloud和SpringBoot的关系

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

2.2 Dubbo和SpringCloud技术选型

  • 分布式+服务治理Dubbo

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

在这里插入图片描述

最大区别:SpringCloud抛弃了Dubbo的RPC通信,采用的是基于HTTP的REST方式。

用法:cloud开箱即用,Dubbo可以选择多种协议更灵活

维护:spring维护性比dubbo更好

定位:dubbo是一款RPC框架,SpringCloud目标是微服务架构下一站式解决方案

2.3 SpringCloud作用

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

3 Rest和Restful风格

REST (REpresentation State Transfer) 描述了一个架构样式的网络系统,比如 web 应用程序。它首次出现在 2000 年 Roy Fielding 的博士论文中,他是 HTTP 规范的主要编写者之一。**REST 指的是一组架构约束条件和原则。**满足这些约束条件和原则的应用程序或设计就是 RESTful。

  • REST是面向资源的,这个概念非常重要,而资源是通过URI进行暴露。
  • REST因为是面向资源,URL中只能由名词,不能有动词
请求方式 含义
GET(SELECT) 从服务器获取资源(一项或多项)
POST(CREATE) 在服务器新建一个资源
PUT(UPDATE) 在服务器更新资源(客户端提供改变后的完整资源)
PATCH(UPDATE) 在服务器更新资源(客户端提供改变的属性)
DELETE(DELETE) 从服务器删除资源。

3.1 传统模式和RESTful的对比

  • 查询数据

    • 传统模式url
        http://localhost/user/getUser   
    
    • Restful
    	http://localhost/user
    
    • 后端数据处理
        @GetMapping("/user")
    	public List<User> get()
    
  • 添加数据

    • 传统模式url
    	http://localhost/user/addUser?user=....    
    
    • Restful
    	http://localhost/user
    
    • 后端数据处理
         @PostMapping("/user")
        public Result add(@RequestBody User user) {
          
          }
    
  • 修改数据

    • 传统模式url
    	http://localhost/user/update
    
    • Restful
    	http://localhost/user
    
    • 后端数据处理
        @putMapping("/user")
        public Result update(@RequestBody User user)
    
  • 删除数据

    • 传统模式url
    	http://localhost/user/deleteById?userId=1
    
    • Restful
    	http://localhost/user/{userId}
    
    • 后端数据处理
    	@DeleteMapping("/user/{userId}")
        public Result getDeptById(@PathVariable("userId") int userId)
    

3.2 Rest优缺点

  • 前后端分离,减少流量

  • 安全问题集中在接口上,由于接受json格式,防止了注入型等安全问题

  • 前端无关化,后端只负责数据处理,前端表现方式可以是任何前端语言(android,ios,html5)

  • 前端和后端人员更加专注于各自开发,只需接口文档便可完成前后端交互,无需过多相互了解

  • 服务器性能优化:由于前端是静态页面,通过nginx便可获取,服务器主要压力放在了接口上

4 REST-服务提供者

4.1 创建父项目

  • 引入对应依赖

    dependencyManagement:管理依赖

        <packaging>pom</packaging>
        <properties>
            <maven.compiler.source>8</maven.compiler.source>
            <maven.compiler.target>8</maven.compiler.target>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <junit.version>4.13.2</junit.version>
            <lombok.version>1.18.26</lombok.version>
            <log4j.version>1.2.17</log4j.version>
            <logback.version>1.2.11</logback.version>
        </properties>
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <!--springCloud依赖-->
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-dependencies</artifactId>
                    <version>2021.0.6</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
                <dependency>
                    <!--springboot依赖-->
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-dependencies</artifactId>
                    <version>2.7.10</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
                <!--数据库-->
                <dependency>
                    <groupId>mysql</groupId>
                    <artifactId>mysql-connector-java</artifactId>
                    <version>8.0.32</version>
                </dependency>
                <!--数据源-->
                <dependency>
                    <groupId>com.alibaba</groupId>
                    <artifactId>druid</artifactId>
                    <version>1.2.15</version>
                </dependency>
                <!-- springboot启动-->
                <dependency>
                    <groupId>org.mybatis.spring.boot</groupId>
                    <artifactId>mybatis-spring-boot-starter</artifactId>
                    <version>3.0.1</version>
                </dependency>
                <!-- log4j-->
                <dependency>
                    <groupId>log4j</groupId>
                    <artifactId>log4j</artifactId>
                    <version>${log4j.version}</version>
                </dependency>
                <dependency>
                    <groupId>ch.qos.logback</groupId>
                    <artifactId>logback-core</artifactId>
                    <version>${logback.version}</version>
                </dependency>
                <dependency>
                    <groupId>junit</groupId>
                    <artifactId>junit</artifactId>
                    <version>${junit.version}</version>
                    <scope>test</scope>
                </dependency>
                <dependency>
                    <groupId>org.projectlombok</groupId>
                    <artifactId>lombok</artifactId>
                    <version>${lombok.version}</version>
                </dependency>
    
            </dependencies>
        </dependencyManagement>
    </project>
    

4.2 创建第一个模块-只管Bean层

4.2.1 引入依赖

    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
    </dependencies>

4.2.2 Bean包的Dept类

  • @Accessors(chain = true):支持链式写法

    例如:

    dept.setDeptno(1).setDname("qwe").setDbSource('001')
    
@AllArgsConstructor
@NoArgsConstructor
@Data
@Accessors(chain = true)//链式写法
public class Dept implements Serializable {
    
    
    private Long deptno;
    private String dname;
    // 这些数据存放在哪个数据库的字段
    // 微服务,一个服务对应一个数据库,同一个信息可能存在不同数据库
    private String dbsource;

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

4.3 另外一个模块-dept服务提供者

4.3.1 引入依赖

    <dependencies>
        <!--api module-->
        <dependency>
            <groupId>com.jiazhong</groupId>
            <artifactId>springcloud-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <!--junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </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>
        <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>
        <!--热部署-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
    </dependencies>

4.3.2 application.yml配置

server:
  port: 8001

mybatis:
  type-aliases-package: com.jiazhong.springcloud.bean
  configuration:
    #开启二级缓存
    cache-enabled: true
spring:
  application:
    name: springcloud-provider-dept
  #数据源
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/db01?useUnicode=true&characterEncoding=utf-8
    username: root
    password: root


4.3.3 创建DeptMapper

@Mapper
@Repository
public interface DeptMapper {
    
    
    @Insert("insert into db01.dept values(default,#{dname},DATABASE())")
    void addDept(Dept dept);
    @Select("select * from db01.dept where deptno=#{deptno}")
    Dept getDeptById(int deptno);
    @Select("select * from db01.dept")
    List<Dept> getAllDept();
}

4.4.1 创建Service层

  • DeptService接口
public interface DeptService {
    
    

    void addDept(Dept dept);

    Dept getDeptById(int deptno);
    List<Dept> getAllDept();
}
  • DeptServiceImpl
@Service
public class DeptServiceImpl implements DeptService {
    
    
    @Resource
    private DeptMapper deptMapper;

    @Override
    public void addDept(Dept dept) {
    
    
         deptMapper.addDept(dept);
    }

    @Override
    public Dept getDeptById(int deptno) {
    
    
        return deptMapper.getDeptById(deptno);
    }

    @Override
    public List<Dept> getAllDept() {
    
    
        return deptMapper.getAllDept();
    }
}

4.5.1 DeptController

//提供Restful服务
@RestController
@RequestMapping("/dept")
public class DeptController {
    
    
    @Resource
    private DeptService deptService;
    @PostMapping("/")
    public Result addDept(@RequestBody Dept dept){
    
    
        try {
    
    
            deptService.addDept(dept);
            return Result.success("数据添加成功");
        } catch (Exception e) {
    
    
            e.printStackTrace();
            return Result.fail(500,"数据添加失败!!!");
        }
    }
    @GetMapping("/{deptno}")
    public Dept getDeptById(@PathVariable("deptno") int deptno){
    
    
        return deptService.getDeptById(deptno);
    }
    @GetMapping("/")
    List<Dept> getAllDept(){
    
    
        return deptService.getAllDept();
    }
}

4.5.2 启动类

@SpringBootApplication
@MapperScan("com.jiazhong.springcloud.mapper")
public class Dept_provider_8001 {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(Dept_provider_8001.class,args);
    }
}

5 REST-服务消费者

5.1 引入依赖

    <dependencies>
        <dependency>
            <groupId>com.jiazhong</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>
  • application.yml
server:
  port: 9001

5.2 DeptConsumerController

RestTemplate:Rest模版供我们直接调用,注册到spring中
(url,实体:Map,Class responseType)

@RestController
@RequestMapping("/consumer/dept")
public class DeptConsumerController {
    
    
    /**
     * 消费者不能有service层
     * RestTemplate:Rest模版供我们直接调用,注册到spring中
     * (url,实体:Map,Class<T> responseType)
     */

    @Resource
    private RestTemplate restTemplate;

    private static  final String REST_URL_PREFIX ="http://localhost:8001";
    @GetMapping("/{id}")
    public Dept get(@PathVariable int id){
    
    
        return restTemplate.getForObject(REST_URL_PREFIX+"/dept/"+id, Dept.class);
    }

    @PostMapping("/")
    public Result add(@RequestBody Dept dept){
    
    
        try {
    
    
            restTemplate.postForObject(REST_URL_PREFIX+"/dept/",dept,Result.class);
            return Result.success("数据添加成功");
        } catch (RestClientException e) {
    
    
            e.printStackTrace();
            return Result.fail(500,"数据添加失败!!!");
        }
    }

    @GetMapping("/")
    public List<Dept> getAll(){
    
    
        return restTemplate.getForObject(REST_URL_PREFIX+"/dept/",List.class);
    }
}

5.3 启动类

@SpringBootApplication
public class DeptConsumer_9001 {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(DeptConsumer_9001.class);
    }
    @Bean
    RestTemplate restTemplate(){
    
    
        return new RestTemplate();
    }
}

文章目录

springCloud常用组件

1 Eureka(服务注册与发现)

1.1 什么是Eureka(对标Dubbo的Zookeeper)

CS架构,遵循AP原则

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

  • 基本架构

    • SpringCloud封装了NetFlix公司开发的Eureka模块来实现服务注册和发现(对比Zookeeper)

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

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

  • 三大角色

    • EurekaServer:提供服务注册与发现;
    • Service Provider:将自身服务注册到Eureka中,从而使消费方能够找到。
    • Service Consumer:服务消费方从Eureka中获取注册服务列表,从而找到消费服务。

1.2 搭建Eureka模块

1.2.1 引入依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
            <version>2.2.10.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
    </dependencies>

1.2.2 配置application.yml

server:
  port: 7001

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

1.2.3 启动类

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

1.3 服务注册

1.3.1 在服务提供者模块中加入Eureka依赖

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
            <version>2.2.10.RELEASE</version>
        </dependency>

1.3.2 添加application.yml配置

#Eureka,服务注册到哪里
eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka/
  instance:
    instance-id: springcloud-provider-dept-8001 #修改默认描述信息
  • 启动类开启Eureka
@SpringBootApplication
@MapperScan("com.jiazhong.springcloud.mapper")
@EnableEurekaClient  //开启后,在服务启动后自动注入到Eureka
public class Dept_provider_8001 {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(Dept_provider_8001.class,args);
    }
}
  • 注册成功

在这里插入图片描述

1.3.3 Eureka自我保护机制

EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY'RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE.

出现上面这种情况,Eureka进入自我保护模式

因为某些因素:某一时刻微服务掉了,eureka不会立即进行清理,将会对该微服务信息进行报存

  • 默认情况下,如果EurekaServer在一定时间内没有接收到某个微服务实例的心跳,EurekaServer将会注销该实例(默认90秒)。但是当网络分区故障发生时,微服务与Eureka之间无法正常通行,以上行为可能变得非常危险了–因为微服务本身其实是健康的,此时本不应该注销这个服务。Eureka通过自我保护机制来解决这个问题–当EurekaServer节点在短时间内丢失过多客户端时(可能发生了网络分区故障)),那么这个节点就会进入自我保护模式。一旦进入该模式,EurekaServer就会保护服务注册表中的信息,不再删除服务注册表中的数据(也就是不会注销任何微服务)。当网络故障恢复后,该EurekaServer节点会自动退出自我保护模式。

  • 在自我保护模式中,EurekaServer会保护服务注册表中的信息,不再注销任何服务实例。当它收到的心跳数重新恢复到阈值( 默认阈值为:85% )以上时,该EurekaServer节点就会自动退出自我保护模式。它的设计哲学就是宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例。

  • 综上,自我保护模式是一种应对网络异常的安全保护措施。它的架构哲学是宁可同时保留所有微服务(健康的微服务和不健康的微服务都会保留),也不盲目注销任何健康的微服务。使用自我保护模式,可以让Eureka集群更加的健壮和稳定。

  • 使用server:enable-self-preservation: false关闭自我保护机制(不推荐关闭)

1.3.4 获取注册进来的微服务信息

 	@Resource
    private DiscoveryClient client;

    @GetMapping("/discovery")
    public Object discovery(){
    
    
        //得到微服务清单
        List<String> services = client.getServices();
        System.out.println("discovery=>services:"+services);
        //得到具体微服务信息
        List<ServiceInstance> instances = client.getInstances("SPRINGCLOUD-PROVIDER-DEPT");
        for (ServiceInstance instance : instances) {
    
    
            System.out.println(instance.getHost()+"\n"+instance.getInstanceId()+"\n"+instance.getUri());
        }
        return this.client;
    }
  • 启动类还有开启DiscoveryClient:@EnableDiscoveryClient

1.4 集群环境配置

1.4.1 再搭建两个Eureka

步骤如上文1.2

  • 引入依赖
  • 配置文件
  • 启动类

1.4.2 Eureka集群

我们三个Eureka需要,形成联系

  • 关联三个Eureka
  • 老生长谈修改hosts文件
	127.0.0.1       eureka7001.com #将域名和ip绑定
	127.0.0.1       eureka7002.com #将域名和ip绑定
	127.0.0.1       eureka7003.com #将域名和ip绑定
  • 修改配置文件(其他两个同),下文是7003端口的Eureka
#配置Eureka
eureka:
  instance:
    hostname: eureka7003.com #Eureka服务端实例名称
  client:
    register-with-eureka: false #是否向eureka注册中心注册自己
    fetch-registry: false #如果为false,表示自己为注册中心
    service-url:
      #单机:http://${eureka.instance.hostname}:${server.port}/eureka/
      #集群(将其他Eureka关联起来)
      defaultZone : http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
  • 将服务生产者发布到集群,修改服务提供者配置文件
#Eureka,服务注册到哪里
eureka:
  client:
    service-url:
      defaultZone: http://eureka7003.com:7003/eureka,http://eureka7002.com:7002/eureka,http://eureka7001.com:7001/eureka
  instance:
    instance-id: springcloud-provider-dept-8001 #修改默认描述信息

1.5 CAP原则,及对比Zookeeper

  • 关系型数据库:ACID
  • NOSQL:CAP

C:一致性

A:可用性

P:分区容错性

  • 分布式系统中不能同时满足以上三个要求

  • 由于分区容错性P在分布式系统中是必须要保证的,因此我们只能在A和C之间进行权衡。

    • Zookeeper保证的是CP;

    当向注册中心查询服务列表时,我们可以容忍注册中心返回的是几分钟以前的注册信息,但不能接受服务直接down掉不可用。也就是说,服务注册功能对可用性的要求要高于一致性。

    但是zk会出现这样一种情况,当master节点因为网络故障与其他节点失去联系时,剩余节点会重新进行leader选举。

    问题在于,选举leader的时间太长,30~120s,且选举期间整个zk集群都是不可用的,这就导致在选举期间注册服务瘫痪。

    在云部署的环境下,因为网络问题使得zk集群失去master节点是较大概率会发生的事件,虽然服务最终能够恢复,但是漫长的选举时间导致的注册长期不可用是不能容忍的。

    • Eureka保证的是AP;

    Eureka看明白了这一点,因此在设计时就优先保证可用性。

    Eureka各个节点都是平等的,几个节点挂掉不会影响正常节点的工作,剩余的节点依然可以提供注册和查询服务。

    而Eureka的客户端在向某个Eureka注册时,如果发现连接失败,则会自动切换至其他节点,只要有一台Eureka还在,就能保住注册服务的可用性,只不过查到的信息可能不是最新的,除此之外,Eureka还有一种自我保护机制,如果在15分钟内超过85%的节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,此时会出现以下几种情况:

    1. Eureka不再从注册列表中移除因为长时间没收到心跳而应该过期的服务

    2. Eureka仍然能够接受新服务的注册和查询请求,但是不会被同步到其他节点上(即保证当前节点依然可用)

    3. 当网络稳定时,当前实例新的注册信息会被同步到其他节点中

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

2 负载均衡及Ribbon

2.1 什么是Ribbon

  • Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。
  • 简单的说,Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将NetFlix的中间层服务连接在一起。Ribbon的客户端组件提供一系列完整的配置项如:连接超时、重试等等。简单的说,就是在配置文件中列出LoadBalancer(简称LB:负载均衡)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等等)去连接这些机器。我们也很容易使用Ribbon实现自定义的负载均衡算法!

Ribbon作用

  • LB,即负载均衡(Load Balance),在微服务或分布式集群中经常用的一种应用。

  • 负载均衡简单的说就是将用户的请求平摊的分配到多个服务上,从而达到系统的HA(高可用)。

  • 常见的负载均衡软件有Nginx,Lvs等等。

  • dubbo、SpringCloud中均给我们提供了负载均衡,SpringCloud的负载均衡算法可以自定义。

负载均衡简单分类

① 集中式LB

即在服务的消费方和提供方之间使用独立的LB设施

如Nginx:反向代理服务器! ,由该设施负责把访问请求通过某种策略转发至服务的提供方!

② 进程式LB

将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选出一个合适的服务器。
Ribbon就属于进程内LB,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址!

2.2 服务消费者集成Ribbon

2.2.1 引入依赖

<!--        <dependency>-->
<!--            <groupId>org.springframework.cloud</groupId>-->
<!--            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>-->
<!--            <version>2.2.10.RELEASE</version>-->
<!--        </dependency>-->

        <!--eureka client存在ribbon依赖上述ribbon不需要重复导入-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            <version>2.2.10.RELEASE</version>
        </dependency>

2.2.2 添加配置

eureka:
  client:
    register-with-eureka: false #不向eureka中注册自己
    service-url:
      defaultZone: http://eureka7003.com:7003/eureka,http://eureka7002.com:7002/eureka,http://eureka7001.com:7001/eureka
    fetch-registry: true #为true时的意思是表示检索注册中心的所有服务。如果为false则无法发现服务

2.2.3 启动类配置

@LoadBalanced:负载均衡

@SpringBootApplication
@EnableEurekaClient
public class DeptConsumer_9001 {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(DeptConsumer_9001.class);
    }
    @Bean
    @LoadBalanced //配置负载均衡
    RestTemplate restTemplate(){
    
    
        return new RestTemplate();
    }
}

2.2.4 DeptConsumerController

Ribbon和Eureka整合后,可直接根据服务名来访问

因为Ribbon的作用是负载均衡,那么你直接使用ip地址,那么就无法起到负载均衡的作用,因为每次都是调用同一个服务,当你使用的是服务名称的时候,他会根据自己的算法去选择具有该服务名称的服务。

@RestController
@RequestMapping("/consumer/dept")
public class DeptConsumerController {
    
    
    /**
     * 消费者不能有service层
     * RestTemplate:Rest模版供我们直接调用,注册到spring中
     * (url,实体:Map,Class<T> responseType)
     */
    @Resource
    private RestTemplate restTemplate;
    //ribbon:我们这里的地址,应该是一个变量,通过服务名来访问
    //private static  final String REST_URL_PREFIX ="http://localhost:8001";
    private static  final String REST_URL_PREFIX ="http://SPRINGCLOUD-PROVIDER-DEPT";
    @GetMapping("/{id}")
    public Dept get(@PathVariable int id){
    
    
        return restTemplate.getForObject(REST_URL_PREFIX+"/dept/"+id, Dept.class);
    }
    @PostMapping("/")
    public Result add(@RequestBody Dept dept){
    
    
        try {
    
    
            restTemplate.postForObject(REST_URL_PREFIX+"/dept/",dept,Result.class);
            return Result.success("数据添加成功");
        } catch (RestClientException e) {
    
    
            e.printStackTrace();
            return Result.fail(500,"数据添加失败!!!");
        }
    }
    @GetMapping("/")
    public List<Dept> getAll(){
    
    
        return restTemplate.getForObject(REST_URL_PREFIX+"/dept/",List.class);
    }
}

2.3 Ribbon实现负载均衡

服务提供者集群将服务注册到Eureka中;

消费者通过Eureka来查询提供者列表;

通过负载均衡算法使用服务提供者集群中的某一服务;

2.3.1 新建两个服务提供者和两个对应两个数据库

步骤如上文SpringCloud简介及Rest环境搭建的第四节

  • 创建数据库

  • 引入依赖

  • 配置文件

  • Mapper层

  • Service层

  • Controller

  • 启动类

2.3.2 启动注册中心和三个服务提供者

三个服务成功注册进来

在这里插入图片描述

2.3.3 获取服务提供者列表通过负载均衡算法使用服务提供者某一服务

启动服务消费者

浏览器访问:http://localhost:9001/consumer/dept/

可以看到三个服务提供者被轮询访问

2.3.4 自定义负载均衡算法

负载均衡策略
策略 解释
RoundRobinRule 轮询策略
RandomRule 随机策略
AvailabilityFilteringRule 先过滤到处问题的服务,对剩下的轮询访问
RetryRule 如果服务获取失败则会在指定时间内重试
BestAvailableRule 过滤出故障服务器后,选择一个并发量最小的
ZoneAvoidanceRule 从最佳区域实例集合中选择一个最优性能的服务实例
WeightedResponseTimeRule 针对响应时间加权轮询
  • @RibbonClient(name = "",configuration = MyRule.class)

    参数1:服务名,参数2:配置自定义负载均衡类

  • 自定义负载均衡类

/**
 * 自定义策略,里面可以自定义自己的策略
 */
@Configuration
public class MyRule {
    
    
    //随机策略
    @Bean
    public IRule myRule() {
    
    
        return new RandomRule();
    }
}
  • 启动类
@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name = "SPRINGCLOUD-PROVIDER-DEPT",configuration = MyRule.class)
public class DeptConsumer_9001 {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(DeptConsumer_9001.class);
    }

    @Bean
    @LoadBalanced //配置负载均衡
    RestTemplate restTemplate(){
    
    
        return new RestTemplate();
    }
}

3 Feign(使用接口方式调用服务)

feign是声明式的web service客户端,它让微服务之间的调用变得更简单了,类似controller调用service。SpringCloud集成了Ribbon和Eureka,可在使用Feign时提供负载均衡的http客户端。

  • 只需要创建一个接口,然后添加注解即可!

feign,主要是社区,大家都习惯面向接口编程。这个是很多开发人员的规范。调用微服务访问两种方法

1.微服务名字【ribbon】

2.接口和注解【feign】


在Feign的实现下,我们只需要创建一个接口并使用注解的方式来配置它(类似于以前Dao接口上标注Mapper注解,现在是一个微服务接口上面标注一个Feign注解即可。)

3.1 Fegin的简单使用

3.3.1 新建一个fegin模块

步骤如上文SpringCloud简介及Rest环境搭建的第五节

  • 引入依赖及配置
  • DeptConsumerController
  • 启动类

3.3.2 在springcloud-api里写一个服务类

  • 导入依赖
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
            <version>2.2.10.RELEASE</version>
        </dependency>


        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            <version>2.2.10.RELEASE</version>    
        </dependency>
  • 服务类
/**
 * value:服务名
 */
@FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT")//写了该注解后可被服务直接调用
public interface DeptClientService {
    
    
    @PostMapping("/fegin/dept/")
    void addDept(Dept dept);
    @GetMapping("/fegin/dept/{id}")
    Dept getDeptById(int deptno);
    @PostMapping("/fegin/dept/")
    List<Dept> getAllDept();
}

3.3.3 在fegin模块里调用fegin服务

@RestController
@RequestMapping("/fegin/dept")
public class DeptConsumerController {
    
    
    @Resource
    private DeptClientService deptClientService;
    //路径要与api路径一致
    @GetMapping("/{id}")
    public Dept get(@PathVariable int id) {
    
    
        return deptClientService.getDeptById(id);
    }
    @PostMapping("/")
    public Result add(@RequestBody Dept dept) {
    
    
        try {
    
    
            deptClientService.addDept(dept);
            return Result.success("数据添加成功");
        } catch (RestClientException e) {
    
    
            e.printStackTrace();
            return Result.fail(500, "数据添加失败!!!");
        }
    }
    @GetMapping("/")
    public List<Dept> getAll() {
    
    
        return deptClientService.getAllDept();
    }
}

3.3.4 启动类

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients(basePackages = "com.jiazhong.springcloud")
public class DeptConsumer_fegin_9002 {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(DeptConsumer_fegin_9002.class);
    }
    @Bean
    @LoadBalanced //配置负载均衡
    RestTemplate restTemplate(){
    
    
        return new RestTemplate();
    }
}


猜你喜欢

转载自blog.csdn.net/woschengxuyuan/article/details/130165977