SpringCloud

2020-02-14 17:31发布

SpringCloud

入门问题

  1. 微服务概念
  2. 微服务之间如何通信
  3. SpringCloud与Dubbo的区别
  4. SpringBoot与SpringCloud的关系
  5. 服务熔断和服务降级概念
  6. 微服务的优缺点
  7. 微服务技术栈
  8. eureka和zookeeper的区别

微服务概述

微服务起源:微服务

微服务将单一应用程序划分为一组小服务,每个服务独立在及自己的进程中,通过Restful方式互相沟通、调用。每个服务提供单个业务功能,去耦合。


微服务与微服务架构

微服务:指系统中的一个服务应用。

微服务架构:架构风格,即包括微服务及微服务之间的通信。


微服务的优缺点

优点

  1. 服务高内聚,完成一个细微化的业务或功能
  2. 单个服务的开发更便捷,开发简单、开发效率高
  3. 微服务可由独立团队开发
  4. 松耦合,开发及部署都独立
  5. 可以由不同语言开发,易于集成
  6. 前后端分离、灵活分配数据库

缺点

  1. 分布式系统的复杂性
  2. 运维难度增加,系统部署依赖问题
  3. 服务间通信额外花费
  4. 数据一致性、系统集成测试难度
  5. 性能监控难

微服务技术栈

微服务 技术
开发 Spring、SpringBoot、SpringMVC
配置管理 Archaius(Netflix)、Diamond(Ali)
注册与实现 Eureka、Consul、Zookeeper
调用 Rest、RPC、gRPC
熔断器 Hystrix、Envoy
负载均衡 Ribbon、Nginx
接口调用工具 Feign
消息队列 Kaflka、RabbitMQ、ActiveMQ
配置中心管理 SpringCloudConfig、Chef
路由(API网关) Zuul
监控 Zabbix、Nagios、Metrics、Spectator
全链路追踪 ZipKin、Brave、Dapper
部署 Docker、OpenStack、Kubernates
数据流操作 SpringCloud Steam(Redis、Rabbit...)
事件消息总线 Spring Cloud Bus

SpringCloud 架构

主流选用

厂商 技术选用
阿里 Dubbo/HSF
京东 JSF
新浪微博 Motan
当当 Dubbo

框架对比

功能 Spring Cloud Motan gRPC Thrift Dubbo/DubboX
定位 完整微服务 RPC+ZK/Consul RPC RPC RPC
Rest 支持
RPC
多语言
注册/发现 (Eurka) Zookeeper/Consul
负载均衡 Zuul+Ribbon
配置服务 Archaius/sp config servier
调用链监控 Zuul API
高可用/容错 Hystrix Ribbon
其他

SpringCloud简介

SpringCloud微服务架构,涵盖了服务注册/发现、配置中心、全链路监控、服务网管、负载均衡、熔断器等,使用SpringBooot简化开发,提供快速构建分布式系统的工具,包括配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策精选、分布式会话等,都可以使用SpringBoot开发进行快速启动和部署。

SpringCloud:一站式分布式微服务解决方案


Dubbo 对比 SpringCloud

对比 Dubbo SpringCloud
注册中心 Zookeeper Eureka
调用方式 RPC REST API
监控 Dubbo-minitor spring boot admin
断路器 不完善 SC Netfilx Hystrix
网管 SC Netfilx Zull
分布式配置 SC config
跟踪 SC Sleuth
消息总线 SC Bus
数据流 SC Stream
批量任务 SC Task
...

文档

官方文档 API 文档 中国社区 Spring中文网


实战

版本

  1. Cloud: Dalston.SR1
  2. Boot: 1.5.9

创建工程

创建父工程Maven配置打包类型为Pom

在父工程中创建子工程模块

创建子工程


Eureka

服务注册与发现,注册订阅后可以依据服务标识符访问服务,不需要修改配置文件,功能和Zookeeper类似,遵循AP原则。

功能对比 Eurka Zookeeper
架构 CS架构 Server端注册服务器 RPC
稳定性 冗余备份,所有集群均可独立工作 主从备份,选举费时

Eureka简介

服务端:提供服务注册服务,存储所有可用服务节点的信息。

客户端:Java客户端,简化同服务端的交互,包含负载均衡器,启动后会向服务端周期发送心跳(默认30s),当服务器在多个周期中未受到某节点心跳(默认90s),节点将会移除。

注册中心

server:
  port: 7001

eureka:
  instance:
    hostname: localhost
  client:
    register-with-eureka: false   #不注册自身
    fetch-registry: false         #服务端
    service-url:
      defalutZone: http://${eureka.instance.hostname}:${s
@SpringBootApplication
@EnableEurekaServer
public class EurekaServer7001_App {
    public static void main(String[] args) {

        SpringApplication.run(EurekaServer7001_App.class,args);
    }
}

服务提供者

eureka:
  client: #客户端注册进eureka服务列表内
    service-url:
      defaultZone: http://localhost:7001/eureka
      #defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
  instance:
    instance-id: microservicecloud-dept-8001
    prefer-ip-address: true     #访问路径可以显示IP地址
@EnableEurekaClient
@SpringBootApplication
public class DeptProvider8001_App {

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

服务消费者

server:
  port: 80
@Configuration
public class ConfigBean
{ 
    @Bean
    @LoadBalanced//Spring Cloud Ribbon 负载均衡的工具。
    public RestTemplate getRestTemplate()
    {
        return new RestTemplate();
    }
}
    private static final String REST_URL_PREFIX = "http://localhost:8001";

//private static final String REST_URL_PREFIX = "http://MICROSERVICECLOUD-DEPT";

    @Autowired
    private RestTemplate restTemplate;

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

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

    @SuppressWarnings("unchecked")
    @RequestMapping(value = "/consumer/dept/list")
    public List<Dept> list()
    {
        return restTemplate.getForObject(REST_URL_PREFIX + "/dept/list", List.class);
    }

    // 测试@EnableDiscoveryClient,消费端可以调用服务发现
    @RequestMapping(value = "/consumer/dept/discovery")
    public Object discovery()
    {
        return restTemplate.getForObject(REST_URL_PREFIX + "/dept/discovery", Object.class);
    }

负载均衡

Ribbon

客户端负载均衡工具

   <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-ribbon</artifactId>
        </dependency>
@Configuration
public class ConfigBean
{ 
    @Bean
    @LoadBalanced//Spring Cloud Ribbon 负载均衡的工具。
    public RestTemplate getRestTemplate()
    {
        return new RestTemplate();
    }
}
    @Bean
    public IRule myRule()
    {
        //return new RoundRobinRule();
        //return new RandomRule();//达到的目的,用我们重新选择的随机算法替代默认的轮询。
        return new RetryRule();
    }

算法

  1. RoundRobinRule

    轮询

  2. RandomRule

    随机

  3. AvaliabilityFilteringRule

    过滤访问导致故障处于断路器跳闸的服务、超并发阈值服务,剩余轮询

  4. WeightedResponseTimeRule

    根据平均响应时间分配权重,权重大的被选中几率大,服务启动使用轮询,后切换为WeightedResponseTimeRule。

  5. RetryRule

    按照轮询获取服务,失败时,在指定时间内重试。

  6. BestAvaliableRule

    过滤故障跳闸服务,选择并发量小的服务。

  7. ZoneAvoidanceRule

    默认规则,复合判断服务性能和选择可用服务。

自定义负载均衡策略

import com.wang.myrule.MySelfRule;
/////////////////
@RibbonClient(name="microservicecloud-dept",configration=MySelfRule.class)
/////////////////
@EnableEurekaClient
@SpringBootApplication
public class ConsumerDept80 {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerDept80.class,args);
    }
}
//自动配置类 不能载SpringBoot的自动扫描范围
//如果被扫描 所有的客户端将共享这个配置
package com.wang.myrule;//与主配置并列包

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RoundRobinRule;

@Configuration
public class MySelfRule
{
    @Bean
    public IRule myRule()
    {
        //return new RandomRule();
        //return new RoundRobinRule();
        return new RandomRule();// 自己编写的类
    }
}

自定义规则编写

自定义策略类

package com.atguigu.myrule;

import java.util.List;

import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;

public class RandomRuleUserDefined extends AbstractLoadBalancerRule
{

    // total = 0 // 当total==5以后,我们指针才能往下走,
    // index = 0 // 当前对外提供服务的服务器地址,
    // total需要重新置为零,但是已经达到过一个5次,我们的index = 1
    // 分析:我们5次,但是微服务只有8001 8002 8003 三台,OK?
    // 
    
    
    private int total = 0;          // 总共被调用的次数,目前要求每台被调用5次
    private int currentIndex = 0;   // 当前提供服务的机器号

    public Server choose(ILoadBalancer lb, Object key)
    {
        if (lb == null) {
            return null;
        }
        Server server = null;

        while (server == null) {
            if (Thread.interrupted()) {
                return null;
            }
            List<Server> upList = lb.getReachableServers();
            List<Server> allList = lb.getAllServers();

            int serverCount = allList.size();
            if (serverCount == 0) {
     /*
        * No servers. End regardless of pass, because subsequent passes only get more
      * restrictive.
      */
                return null;
            }

//          int index = rand.nextInt(serverCount);// java.util.Random().nextInt(3);
//          server = upList.get(index);

            
//          private int total = 0;          // 总共被调用的次数,目前要求每台被调用5次
//          private int currentIndex = 0;   // 当前提供服务的机器号
            if(total < 5)
            {
                server = upList.get(currentIndex);
                total++;
            }else {
                total = 0;
                currentIndex++;
                if(currentIndex >= upList.size())
                {
                  currentIndex = 0;
                }
            }           
            
            
            if (server == null) {
        /*
         * The only time this should happen is if the server list were somehow trimmed.
         * This is a transient condition. Retry after yielding.
       */
                Thread.yield();
                continue;
            }

            if (server.isAlive()) {
                return (server);
            }

            // Shouldn't actually happen.. but must be transient or a bug.
            server = null;
            Thread.yield();
        }

        return server;

    }

    @Override
    public Server choose(Object key)
    {
        return choose(getLoadBalancer(), key);
    }

    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig)
    {
        // TODO Auto-generated method stub

    }

}

策略类继承关系

Feign

声明式Rest客户端,使得编写Web服务客户端更简单,可以与Eurek、Rebbon组合使用。

定义一个接口添加注解,即可使用。

Ribbon+RestTemplate:调用

接口+注解:调用

依赖

    <dependency>
      <groupId>cn.springcloud.feign</groupId>
      <artifactId>venus-cloud-starter-feign</artifactId>
    </dependency>

接口+注解

//@FeignClient(value = "MICROSERVICECLOUD-DEPT",fallbackFactory=DeptClientServiceFallbackFactory.class) //熔断器类

@FeignClient(value = "MICROSERVICECLOUD-DEPT")
public interface DeptClientService
{
    @RequestMapping(value = "/dept/get/{id}", method = RequestMethod.GET)
    public Dept get(@PathVariable("id") long id);

    @RequestMapping(value = "/dept/list", method = RequestMethod.GET)
    public List<Dept> list();

    @RequestMapping(value = "/dept/add", method = RequestMethod.POST)
    public boolean add(Dept dept);
}

Controller

@RestController
public class DeptController_Consumer
{
    @Autowired
    private DeptClientService service;

    @RequestMapping(value = "/consumer/dept/get/{id}")
    public Dept get(@PathVariable("id") Long id)
    {
        return this.service.get(id);
    }

    @RequestMapping(value = "/consumer/dept/list")
    public List<Dept> list()
    {
        return this.service.list();
    }

    @RequestMapping(value = "/consumer/dept/add")
    public Object add(Dept dept)
    {
        return this.service.add(dept);
    }
}

集成了Rabbon,Ribbon+RestTemplate调用微服务 ==》接口+注解,调用接口的方式调用服务,即是在调用某个微服务的方式从使用RestTemplate的方式转为使用接口方式调用,Fegin还是可以使用Rabbon的负载均衡。


Hystrix

处理分布式系统的延迟和容错的开源库,分布式系统中,服务调用不可避免会有调用失败、超时、异常出现,Hystrix保证在服务出现为题时,不会导致整体服务失败,避免级联故障,提高分布式系统的弹性。当某个服务出现故障,通关断路器的故障监控,向调用方法返回一个符合预期的、可处理的备选响应,而不是进行等待或抛出异常,保证调用线程不会长时间等待、返回无用信息,避免故障蔓延。

使用方法

依赖

    <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix</artifactId>
        </dependency>

启用

@SpringBootApplication
@EnableEurekaClient 
@EnableDiscoveryClient 

@EnableCircuitBreaker       //对hystrix熔断机制的支持
public class DeptProviderHystrix
{
    public static void main(String[] args)
    {
        SpringApplication.run(DeptProviderHystrixp.class, args);
    }
}

Controller

@RestController
public class DeptController
{
    @Autowired
    private DeptService service = null;
    //调用服务方法失败并抛出了错误信息
  //自动调用@HystrixCommand标注好的fallbackMethod调用类中的指定方法
    @HystrixCommand(fallbackMethod = "processHystrix_Get")
  @RequestMapping(value = "/dept/get/{id}", method = RequestMethod.GET)
    public Dept get(@PathVariable("id") Long id)
    {

        Dept dept = this.service.get(id);
        
        if (null == dept) {
            throw new RuntimeException("该ID:" + id + "没有没有对应的信息");
        }
        
        return dept;
    }

  //类似异常通知
    public Dept processHystrix_Get(@PathVariable("id") Long id)
    {
        return new Dept().setDeptno(id).setDname("该ID:" +id + "没有没有对应的信息,null--@HystrixCommand")
                .setDb_source("no this database in MySQL");
    }
}

熔断机制,当微服务在某个服务处发生了超时、异常时,由Hystrix返回一个结果,结果符合调用服务者的要求。举例实际使用,需要重写FallbackFactory接口并在Feign注解中配置fallbackFactory,即可实现异常处理与业务模块分离,解耦和。

服务降级

服务降级在客户端完成,暂时关闭服务,为其他服务节省资源,当服务关闭后,Hystrix依旧会为接口调用时返回一个信息。

//实现FallbackFactory接口
@Component 
public class DeptClientServiceFallbackFactory implements FallbackFactory<DeptClientService>
{
    @Override
    public DeptClientService create(Throwable throwable)
    {
        return new DeptClientService() {
            @Override
            public Dept get(long id)
            {
                return new Dept().setDeptno(id).setDname("该ID:" + id + "没有没有对应的信息")
                        .setDb_source("no this database in MySQL");
                        //,Consumer客户端提供的降级信息,此刻服务Provider已经关闭
            }

            @Override
            public List<Dept> list()
            {
                return null;
            }

            @Override
            public boolean add(Dept dept)
            {
                return false;
            }
        };
    }
}

HystrixDashboard

服务监控,持续记录通过Hystrix发起的请求的执行信息,并以统计报表和图形的形式展现给用户,即微服务的图形化监控。

依赖

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
        </dependency>

主类

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

服务

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

监控指标

  1. Delay:轮询监控信息的延迟事件,默认2000毫秒
  2. Title:监控标题

  1. 失败
  2. 超时
  3. 成功
  4. 错误请求
  5. 拒绝
  6. 短路
  7. 错误

Zuul路由网关

路由功能,对外部请求进行转发,即外部访问与服务的路由网关,提供过滤、路由功能,将以一个服务注册到Eureka中,同时实现网关功能,可以配置其他服务的路由访问规则。

依赖

    <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zuul</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>

配置

server: 
  port: 9527 //server_port
                     //服务将通过路由进行访问
 
spring: 
  application:
    name: microservicecloud-zuul-gateway
 
eureka: 
  client: 
    service-url: 
      defaultZone: eureka-ip
  instance:
    instance-id: intance_id
    prefer-ip-address: true 
 
 
zuul: //服务地址映射定义
  #ignored-services: microservicecloud-dept //过滤服务地址访问
  prefix: /prefix               //公共前缀
  ignored-services: "*" //过滤全部服务地址
  routes: 
    mydept.serviceId: microservicecloud-dept
    mydept.path: /myurl/**
 
 info:

分布式配置中心

针对当出现大量的微服务时,提供一个配置中心,对微服务的配置进行集中管理。

服务端

  1. 在github中创建一个库并保存地址

  2. 在本地创建库并克隆创建的库
  3. 创建文件配置文件application.yml(UTF-8)
  4. 提交文件

server: 
  port: 3344 
  
spring:
  application:
    name:  microservicecloud-config
  cloud:
    config:
      server:
        git:
          uri: *:*.git #GitHub上面的git仓库名字

客户端

bootstrap.xml
spring:
  cloud:
    config:
      name: microservicecloud-config-client #需要从github上读取的资源名称,
                                                                                #注意没有yml后缀名
      profile: test   #本次访问的配置项
      label: master   
      uri: ip/url:3344  #本微服务启动后先去找3344号服务,
                                        #通过SpringCloudConfig获取GitHub的服务地址
依赖
    <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>

微服务框架图

标签: