Spring Cloud入门操作

一、spring cloud简介
spring cloud 为开发人员提供了快速构建分布式系统的一些工具,包括配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话等等。它运行环境简单,可以在开发人员的电脑上跑。另外说明spring cloud是基于springboot的,所以需要开发中对springboot有一定的了解。


第一篇: 服务的注册与发现(Eureka)
服务的注册与发现
 
关系调用说明:
•    服务生产者启动时,向服务注册中心注册自己提供的服务
•    服务消费者启动时,在服务注册中心订阅自己所需要的服务
•    注册中心返回服务提供者的地址信息给消费者
•    消费者从提供者中调用服务
Eureka简介
Eureka是Spring Cloud Netflix微服务套件中的一部分,可以与Springboot构建的微服务很容易的整合起来。
Eureka包含了服务器端和客户端组件。服务器端,也被称作是服务注册中心,用于提供服务的注册与发现。Eureka支持高可用的配置,当集群中有分片出现故障时,Eureka就会转入自动保护模式,它允许分片故障期间继续提供服务的发现和注册,当故障分片恢复正常时,集群中其他分片会把他们的状态再次同步回来。
客户端组件包含服务消费者与服务生产者。在应用程序运行时,Eureka客户端向注册中心注册自身提供的服务并周期性的发送心跳来更新它的服务租约。同时也可以从服务端查询当前注册的服务信息并把他们缓存到本地并周期性的刷新服务状态。

http://cloud.spring.io/spring-cloud-static/Finchley.RELEASE/single/spring-cloud.html
https://springcloud.cc/spring-cloud-dalston.html#_hystrix_timeouts_and_ribbon_clients

https://cloud.spring.io/spring-cloud-netflix
https://projects.spring.io/spring-cloud/
POM配置文件
Maven的用户可以通过继承spring-boot-starter-parent项目来获得一些合理的默认配置。这个parent提供了以下特性:
•    默认使用Java 8
•    使用UTF-8编码
•    一个引用管理的功能,在dependencies里的部分配置可以不用填写version信息,这些version信息会从spring-boot-dependencies里得到继承。
•    识别过来资源过滤(Sensible resource filtering.)
•    识别插件的配置(Sensible plugin configuration (exec plugin, surefire, Git commit ID, shade).)
•    能够识别application.properties和application.yml类型的文件,同时也能支持profile-specific类型的文件(如: application-foo.properties and application-foo.yml,这个功能可以更好的配置不同生产环境下的配置文件)。
maven把默认的占位符${…}改为了@..@

<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>
        <spring-cloud.version>Finchley.SR1</spring-cloud.version>
    </properties>

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

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

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
YML配置
eureka是一个高可用的组件,它没有后端缓存,每一个实例注册之后需要向注册中心发送心跳(因此可以在内存中完成),在默认情况下erureka server也是一个eureka client ,必须要指定一个 server。eureka server的配置文件appication.yml:
Yml配置:
server:
  port: 8761

eureka:
  instance:
    hostname: localhost
  client:
#是否将eureka自身作为应用注册到eureka注册中心【这里设置为false,因为eureka服务和client不可能自己监听两套端口,serviceUrl可以天其他eureka服务地址,实现高可用】
    registerWithEureka: false
#为true时,可以启动,但报异常:Cannot execute request on any known server   
 fetchRegistry: false
serviceUrl:
.    

扫描二维码关注公众号,回复: 5981903 查看本文章

#引用对应值,通过配置其他可用的serviceUrl来实现相互注册【高可用配置】,这里可以实现本服务器注册到其他服务器去,实现服务器互为注册功能,达到两个或多个注册中心【即整个系统】的服务提供者信息共享
   defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/


@EnableEurekaServer
只需要一个注解@EnableEurekaServer,这个注解需要在springboot工程的启动application类上加:
 
eureka server 界面
启动工程,打开浏览器访问: 
http://localhost:8761


创建一个服务提供者 (eureka client)

其他pom和server一样 只有这个地方不一样
<dependency> 
<groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
通过注解@EnableEurekaClient 表明自己是一个eurekaclient.
@EnableEurekaClient
 
Yml配置:
application.yml配置文件如下:
 

在controller写一个接口
 
打开eureka看下
 


作为服务注册中心,Eureka比Zookeeper好在哪里
著名的CAP理论指出,一个分布式系统不可能同时满足C(一致性)、A(可用性)和P(分区容错性)。由于分区容错性在是分布式系统中必须要保证的,因此我们只能在A和C之间进行权衡。在此Zookeeper保证的是CP, 而Eureka则是AP。
4.1 Zookeeper保证CP
当向注册中心查询服务列表时,我们可以容忍注册中心返回的是几分钟以前的注册信息,但不能接受服务直接down掉不可用。也就是说,服务注册功能对可用性的要求要高于一致性。但是zk会出现这样一种情况,当master节点因为网络故障与其他节点失去联系时,剩余节点会重新进行leader选举。问题在于,选举leader的时间太长,30 ~ 120s, 且选举期间整个zk集群都是不可用的,这就导致在选举期间注册服务瘫痪。在云部署的环境下,因网络问题使得zk集群失去master节点是较大概率会发生的事,虽然服务能够最终恢复,但是漫长的选举时间导致的注册长期不可用是不能容忍的。
4.2 Eureka保证AP
Eureka看明白了这一点,因此在设计时就优先保证可用性。Eureka各个节点都是平等的,几个节点挂掉不会影响正常节点的工作,剩余的节点依然可以提供注册和查询服务。而Eureka的客户端在向某个Eureka注册或时如果发现连接失败,则会自动切换至其它节点,只要有一台Eureka还在,就能保证注册服务可用(保证可用性),只不过查到的信息可能不是最新的(不保证强一致性)。除此之外,Eureka还有一种自我保护机制,如果在15分钟内超过85%的节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,此时会出现以下几种情况: 
1. Eureka不再从注册列表中移除因为长时间没收到心跳而应该过期的服务 
2. Eureka仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上(即保证当前节点依然可用) 
3. 当网络稳定时,当前实例新的注册信息会被同步到其它节点中
因此, Eureka可以很好的应对因网络故障导致部分节点失去联系的情况,而不会像zookeeper那样使整个注册服务瘫痪。
5. 总结
Eureka作为单纯的服务注册中心来说要比zookeeper更加“专业”,因为注册服务更重要的是可用性,我们可以接受短期内达不到一致性的状况。不过Eureka目前1.X版本的实现是基于servlet的java web应用,它的极限性能肯定会受到影响。期待正在开发之中的2.X版本能够从servlet中独立出来成为单独可部署执行的服务。

第二篇: 服务消费者(rest+ribbon-负载均衡)
在微服务架构中,业务都会被拆分成一个独立的服务,服务与服务的通讯是基于http restful的。Spring cloud有两种服务调用方式,一种是ribbon+restTemplate,另一种是feign
一、ribbon简介
ribbon是一个负载均衡客户端,可以很好的控制http和tcp的一些行为。Feign默认集成了ribbon。
ribbon 已经默认实现了这些配置bean:
什么是Ribbon
Ribbon是Netflix公司开源的一个负载均衡的项目,它属于上述的第二种,是一个客户端负载均衡器,运行在客户端上。它是一个经过了云端测试的IPC库,可以很好地控制HTTP和TCP客户端的一些行为
•    负载均衡
•    容错
•    多协议(HTTP,TCP,UDP)支持异步和反应模型
•    缓存和批处理


二:准备工作
注册2个服务到eureka中  达到集群的效果
 

三:建一个服务消费者
<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
注入restTemplate
 
Controller调用
 
入口注解
 
在使用springcloud ribbon客户端负载均衡的时候,可以给RestTemplate bean 加一个@LoadBalanced注解,就能让这个RestTemplate在请求时拥有客户端负载均衡的能力:
四:yml配置
  
总结
综上所述,Ribbon的负载均衡,主要通过LoadBalancerClient来实现的,而LoadBalancerClient具体交给了ILoadBalancer来处理,ILoadBalancer通过配置IRule、IPing等信息,并向EurekaClient获取注册列表的信息,并默认10秒一次向EurekaClient发送“ping”,进而检查是否更新服务列表,最后,得到注册列表后,ILoadBalancer根据IRule的策略进行负载均衡。
而RestTemplate 被@LoadBalance注解后,能过用负载均衡,主要是维护了一个被@LoadBalance注解的RestTemplate列表,并给列表中的RestTemplate添加拦截器,进而交给负载均衡器去处理。

第三篇: 服务消费者(Feign)
一、Feign简介
Feign是一个声明式的伪Http客户端,它使得写Http客户端变得更简单。使用Feign,只需要创建一个接口并注解。它具有可插拔的注解特性,可使用Feign 注解和JAX-RS注解。Feign支持可插拔的编码器和解码器。Feign默认集成了Ribbon,并和Eureka结合,默认实现了负载均衡的效果。
简而言之:
•    Feign 采用的是基于接口的注解
•    Feign 整合了ribbon,具有负载均衡的能力
•    整合了Hystrix,具有熔断的能力
二.创建一个feign的服务
新建一个spring-boot工程,取名为serice-feign,在它的pom文件引入Feign的起步依赖spring-cloud-starter-feign、Eureka的起步依赖spring-cloud-starter-netflix-eureka-client、Web的起步依赖spring-boot-starter-web 
 
主要依赖这3个  加到配置文件里面或者用STS自己生成,生成后需要修改eureka的依赖,改为 
三:yml配置
 
在程序的启动类ServiceFeignApplication ,加上@EnableFeignClients注解开启Feign的功能:

@EnableEurekaClient  标识Eureka客户端 
@EnableDiscoveryClient   如果不是Eureka注册中心 可以使用这个注解
@EnableFeignClients注解开启Feign的功能:


四:feign接口
定义一个feign接口,通过@ FeignClient(“服务名”),来指定调用哪个服务。比如在代码中调用了liushuo服务的“/zhangsani”接口,代码如下:
1.    建个接口
2.@FeignClient(value = "liushuo")  访问哪个服务名
3、 @RequestMapping(value = "/zhangsan",method = RequestMethod.GET)
 String sayHiFromClientOne(@RequestParam(value = "name") String name);

 
4.    创建controller层
 
调用服务


 
 Feign设置超时时间 
使用Feign调用接口分两层,ribbon的调用和hystrix的调用,所以ribbon的超时时间和Hystrix的超时时间的结合就是Feign的超时时间

#hystrix的超时时间
hystrix:
    command:
        default:
            execution:
              timeout:
                enabled: true
              isolation:
                    thread:
                        timeoutInMilliseconds: 9000
#ribbon的超时时间
ribbon:
  ReadTimeout: 3000
  ConnectTimeout: 3000

一般情况下 都是 ribbon 的超时时间(<)hystrix的超时时间(因为涉及到ribbon的重试机制) 
因为ribbon的重试机制和Feign的重试机制有冲突,所以源码中默认关闭Feign的重试机制,源码如下 
   
要开启Feign的重试机制如下:(Feign默认重试五次 源码中有)

@Bean
Retryer feignRetryer() {
        return  new Retryer.Default();
}

二、ribbon的重试机制 
设置重试次数:

ribbon:
  ReadTimeout: 3000
  ConnectTimeout: 3000
  MaxAutoRetries: 1 #同一台实例最大重试次数,不包括首次调用
  MaxAutoRetriesNextServer: 1 #重试负载均衡其他的实例最大重试次数,不包括首次调用
  OkToRetryOnAllOperations: false  #是否所有操作都重试 

根据上面的参数计算重试的次数:MaxAutoRetries+MaxAutoRetriesNextServer+(MaxAutoRetries *MaxAutoRetriesNextServer) 即重试3次 则一共产生4次调用 
如果在重试期间,时间超过了hystrix的超时时间,便会立即执行熔断,fallback。所以要根据上面配置的参数计算hystrix的超时时间,使得在重试期间不能达到hystrix的超时时间,不然重

试机制就会没有意义 
hystrix超时时间的计算: (1 + MaxAutoRetries + MaxAutoRetriesNextServer) * ReadTimeout 即按照以上的配置 hystrix的超时时间应该配置为 (1+1+1)*3=9秒

当ribbon超时后且hystrix没有超时,便会采取重试机制。当OkToRetryOnAllOperations设置为false时,只会对get请求进行重试。如果设置为true,便会对所有的请求进行重试,如果是put或

post等写操作,如果服务器接口没做幂等性,会产生不好的结果,所以OkToRetryOnAllOperations慎用。

如果不配置ribbon的重试次数,默认会重试一次 
注意: 
默认情况下,GET方式请求无论是连接异常还是读取异常,都会进行重试 
非GET方式请求,只有连接异常时,才会进行重试
第四篇:断路器(Hystrix)
在微服务架构中,根据业务来拆分成一个个的服务,服务与服务之间可以相互调用(RPC),在Spring Cloud可以用RestTemplate+Ribbon和Feign来调用。为了保证其高可用,单个服务通常会集群部署。由于网络原因或者自身的原因,服务并不能保证100%可用,如果单个服务出现问题,调用这个服务就会出现线程阻塞,此时若有大量的请求涌入,Servlet容器的线程资源会被消耗完毕,导致服务瘫痪。服务与服务之间的依赖性,故障会传播,会对整个微服务系统造成灾难性的严重后果,这就是服务故障的“雪崩”效应。
在分布式环境下,特别是微服务结构的分布式系统中, 一个软件系统调用另外一个远程系统是非常普遍的。这种远程调用的被调用方可能是另外一个进程,或者是跨网路的另外一台主机, 这种远程的调用和进程的内部调用最大的区别是,远程调用可能会失败,或者挂起而没有任何回应,直到超时。更坏的情况是, 如果有多个调用者对同一个挂起的服务进行调用,那么就很有可能的是一个服务的超时等待迅速蔓延到整个分布式系统,引起连锁反应, 从而消耗掉整个分布式系统大量资源。最终可能导致系统瘫痪。
断路器(Circuit Breaker)模式就是为了防止在分布式系统中出现这种瀑布似的连锁反应导致的灾难。
一旦某个电器出问题,为了防止灾难,电路的保险丝就会熔断。断路器类似于电路的保险丝, 实现思路非常简单,可以将需要保护的远程服务用封装起来,在内部监听失败次数, 一旦失败次数达到某阀值后,所有后续对该服务的调用,断路器截获后都直接返回错误到调用方,而不会继续调用已经出问题的服务, 从而达到保护调用方的目的, 整个系统也就不会出现因为超时而产生的瀑布式连锁反应

一、断路器简介
Netflix开源了Hystrix组件,实现了断路器模式,SpringCloud对这一组件进行了整合。 在微服务架构中,一个请求需要调用多个服务是非常常见的,如下图:
 
较底层的服务如果出现故障,会导致连锁故障。当对特定的服务的调用的不可用达到一个阀值(Hystric 是5秒20次) 断路器将会被打开。
一个微服务的超时失败可能导致瀑布式连锁反映,下图中,Hystrix通过自主反馈实现的断路器, 防止了这种情况发生


 
断路打开后,可用避免连锁故障,fallback方法可以直接返回一个固定值。
图中的服务B因为某些原因失败,变得不可用,所有对服务B的调用都会超时。当对B的调用失败达到一个特定的阀值(5秒之内发生20次失败是Hystrix定义的缺省值), 链路就会被处于open状态, 之后所有所有对服务B的调用都不会被执行, 取而代之的是由断路器提供的一个表示链路open的Fallback消息.  Hystrix提供了相应机制,可以让开发者定义这个Fallbak消息.
open的链路阻断了瀑布式错误, 可以让被淹没或者错误的服务有时间进行修复。这个fallback可以是另外一个Hystrix保护的调用, 静态数据,或者合法的空值. Fallbacks可以组成链式结构,所以,最底层调用其它业务服务的第一个Fallback返回静态数据.

二:准备工作
在消费者:service-feign 基础上增加断路器
 
三:加入依赖
<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
四:Feign中使用断路器
1) Feign内部已经支持了断路器,所以不需要想Ribbon方式一样,在Spring Boot启动类上加额外注解
2). 用@FeignClient注解添加fallback类, 该类必须实现@FeignClient修饰的接口。

Feign是自带断路器的,要在配置文件application.yml中配置打开它,在配置文件加以下代码:
 
基于service-feign工程进行改造,只需要在FeignClient的ITestService接口的注解中加上fallback的指定类就行了:
 
建一个实现类
SchedualServiceHystricImpl需要实现SchedualLiushuo接口,并注入到Ioc容器中,代码如下:
 
@component (把普通pojo实例化到spring容器中,相当于配置文件中的 
<bean id="" class=""/>)
五:测试时注意
1.    先启动注册中心
2.    在启动服务提供者
3.    启动消费者
4.    断开服务提供者
原因:在测试时,
先启动服务注册中心和消费者,但是不启动生产者的服务。发送GET请求到,你会发现服务降级并没有生效,所以需要先启动服务提供者,然后在断开服务提供者,这样服务器降级后 断路器熔断

断路器开启或者关闭的条件:
较底层的服务中的服务故障可能导致级联故障,当对特定的服务的调用达到一个阀值(Hystric 是5秒20次) 断路器将会被打开,故障百分比大于circuitBreaker.errorThresholdPercentage(默认值:> 50%)时metrics.rollingStats.timeInMilliseconds(默认10秒),断路打开后,开发人员可以回退机制。
4、  当开启的时候,所有请求都不会进行转发
5、  一段时间之后(默认是5秒),这个时候断路器是半开状态,会让其中一个请求进行转发。如果成功,断路器会关闭,若失败,继续开启。重复4 

第五篇: 路由网关(zuul)
一:zuul简介
Zuul的主要功能是路由转发和过滤器。路由功能是微服务的一部分,比如/api/user转发到到user服务,/api/shop转发到到shop服务。zuul默认和Ribbon结合实现了负载均衡的功能。
可以实现反向代理的功能
 
二:准备工作
在原有的工程上,创建一个新的工程。

三:创建demo-service-zuul工程


 

修改Pom
<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
新增依赖
<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>
@EnableZuulProxy
@EnableEurekaClient
package com.example.demo.myFilter;

import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
@Component
public class MyFilter extends ZuulFilter {
    private static Logger log = LoggerFactory.getLogger(MyFilter.class);
    @Override
    //控制过滤器是否生效  true为生效
    public boolean shouldFilter() {
        // TODO Auto-generated method stub
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        log.info(String.format("%s >>> %s", request.getMethod(), request.getRequestURL().toString()));
        Object accessToken = request.getParameter("token");
        if(accessToken == null) {
            log.warn("token is empty");
            ctx.setSendZuulResponse(false);
            ctx.setResponseStatusCode(401);
            try {
                ctx.getResponse().getWriter().write("token is empty");
            }catch (Exception e){}

            return null;
        }
        log.info("ok");
        return null;
    }
    

    @Override
    public String filterType() {
        // TODO Auto-generated method stub
        return "pre";
    }

    @Override
    public int filterOrder() {
        // TODO Auto-generated method stub
        return 0;
    }

}


四:Yml配置
 
五:测试
启动之前的所有服务 6个
  
路由功能已经配置完毕:
五:服务过滤
zuul不仅只是路由,并且还能过滤,做一些安全验证。继续改造工程;
Zuul 可以做什么?
•    身份认证
•    审查与监控
•    压力测试
•    金丝雀测试
•    动态路由
•    服务迁移
•    负载分配
•    安全
•    静态响应处理
•    主动/主动流量管理


•    filterType:该函数需要返回一个字符串来代表过滤器的类型,而这个类型就是在HTTP请求过程中定义的各个阶段。在Zuul中默认定义了四种不同生命周期的过滤器类型,具体如下:
•    pre:可以在请求被路由之前调用。
•    routing:在路由请求时候被调用。
•    post:在routing和error过滤器之后被调用。
•    error:处理请求时发生错误时被调用。
•    filterOrder:通过int值来定义过滤器的执行顺序,数值越小优先级越高。
•    shouldFilter:返回一个boolean类型来判断该过滤器是否要执行。我们可以通过此方法来指定过滤器的有效范围。
•    run:过滤器的具体逻辑。在该函数中,我们可以实现自定义的过滤逻辑,来确定是否要拦截当前的请求,不对其进行后续的路由,或是在请求路由返回结果之后,对处理结果做一些加工等。
 
从上图中,我们可以看到,当外部HTTP请求到达API网关服务的时候,首先它会进入第一个阶段pre,在这里它会被pre类型的过滤器进行处理,该类型的过滤器主要目的是在进行请求路由之前做一些前置加工,比如请求的校验等。
在完成了pre类型的过滤器处理之后,请求进入第二个阶段routing,也就是之前说的路由请求转发阶段,请求将会被routing类型过滤器处理,这里的具体处理内容就是将外部请求转发到具体服务实例上去的过程,当服务实例将请求结果都返回之后,routing阶段完成,请求进入第三个阶段post,此时请求将会被post类型的过滤器进行处理,这些过滤器在处理的时候不仅可以获取到请求信息,还能获取到服务实例的返回信息,所以在post类型的过滤器中,我们可以对处理结果进行一些加工或转换等内容。
另外,还有一个特殊的阶段error,该阶段只有在上述三个阶段中发生异常的时候才会触发,但是它的最后流向还是post类型的过滤器,因为它需要通过post过滤器将最终结果返回给请求客户端(实际实现上还有一些差别)

猜你喜欢

转载自blog.csdn.net/King_1768/article/details/89466744