SpringCloud学习day01

这是一篇关于Spring Cloud微服务架构的学习

1.微服务架构的认识

1.1 单体应用架构:

将所有的代码及功能都包含在一个WAR包中的项目组织方式被称为Monolith(单体应用)

特点:
①编译难,部署难,测试难
②技术选择难
③扩展难

在中小型项目的情况下使用单体应用比较简单比如:
crm 物流 库存管理等

单体应用中多个模块的负载不均衡,我们扩容高负载的时候,也把低负载的模块也扩容,极大浪费了资源.

1.2 微服务架构

把一个单体项目,拆分为多个微服务,每个微服务可以独立技术选型,独立开发,独立部署,独立运维.并且多个服务相互协调,相互配合,最终完成用户的价值就是微服务

特点:
①复杂度可控
②每个微服务也可以独立部署
③技术选型灵活
④微服务架构下,故障会被隔离在单个服务中
⑤每个服务可以根据实际需求独立进行扩展

一般大型项目(功能比较多) 商城 erp等使用微服务比较多

2.What is Spring Cloud?

2.1概念

首先Spring cloud是一个基于Spring Boot实现的服务治理工具包,在微服务架构中用于管理和协调服务的。并且是一系列框架的有序集合

2.2 Spring Cloud的五大神兽

服务发现——Netflix Eureka
客服端负载均衡——Netflix Ribbon/Feign
服务网关——Netflix Zuul
断路器——Netflix Hystrix
分布式配置——Spring Cloud Config

2.3 SpringCloud的组成部分

Spring Cloud共集成了19个子项目,里面都包含一个或者多个第三方的组件或者框架!

Spring Cloud 工具框架如下:


1、Spring Cloud Config 配置中心,利用git集中管理程序的配置。 
2、Spring Cloud Netflix 集成众多Netflix的开源软件
3、Spring Cloud Bus 消息总线,利用分布式消息将服务和服务实例连接在一起,用于在一个集群中传播状态的变化 
4、Spring Cloud for Cloud Foundry 利用Pivotal Cloudfoundry集成你的应用程序
5、Spring Cloud Cloud Foundry Service Broker 为建立管理云托管服务的服务代理提供了一个起点。
6、Spring Cloud Cluster 基于Zookeeper, Redis, Hazelcast, Consul实现的领导选举和平民状态模式的抽象和实现。
7、Spring Cloud Consul 基于Hashicorp Consul实现的服务发现和配置管理。
8、Spring Cloud Security 在Zuul代理中为OAuth2 rest客户端和认证头转发提供负载均衡
9、Spring Cloud Sleuth SpringCloud应用的分布式追踪系统,和Zipkin,HTrace,ELK兼容。
10、Spring Cloud Data Flow 一个云本地程序和操作模型,组成数据微服务在一个结构化的平台上。
11、Spring Cloud Stream 基于Redis,Rabbit,Kafka实现的消息微服务,简单声明模型用以在Spring Cloud应用中收发消息。
12、Spring Cloud Stream App Starters 基于Spring Boot为外部系统提供spring的集成
13、Spring Cloud Task 短生命周期的微服务,为SpringBooot应用简单声明添加功能和非功能特性。
14、Spring Cloud Task App Starters
15、Spring Cloud Zookeeper 服务发现和配置管理基于Apache Zookeeper。
16、Spring Cloud for Amazon Web Services 快速和亚马逊网络服务集成。
17、Spring Cloud Connectors 便于PaaS应用在各种平台上连接到后端像数据库和消息经纪服务。
18、Spring Cloud Starters (项目已经终止并且在Angel.SR2后的版本和其他项目合并)
19、Spring Cloud CLI 插件用Groovy快速的创建Spring Cloud组件应用。

3.搭建Spring Cloud环境(入门)

3.1首先我们来了解SpringCloud服务治理的流程

SpringCloud学习day01

3.2 注册中心的代码实现(支持集群)

如果只有一个注册中心服务器,会存在单点故障所以要集群。

3.2.1首先创建一个多模块项目

需要引入其依赖的是:

 <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>
        <springboot.version>2.0.5.RELEASE</springboot.version>
    </properties>

    <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>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${springboot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

3.2.2创建一个注册中心子模块
①pom.xml的依赖如下:

 <dependencies>
        <!--springboot支持-->
        <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>
        </dependency>

        <!--Eureka服务端支持-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
    </dependencies>

②APP入口

@SpringBootApplication  //程序入口
@EnableEurekaServer     //注册中心需要对服务的支持
public class EurekaApp2 {
    public static void main(String[] args) {
        SpringApplication.run(EurekaApp2.class);
    }
}

③配置application.yml

server:
  port: 7002
eureka:
  instance:
    hostname: eureka-7002.com
  client:
    registerWithEureka: false #是否要注册到eureka
    fetchRegistry: false #表示是否从Eureka Server获取注册信息
    serviceUrl:
      #defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #单机配置
      defaultZone: http://eureka-7001.com:7001/eureka/ #集群配置,如果有多个,有逗号分割,不要包含自己

3.3 服务提供者的搭建

3.3.1第一步引入依赖

 <dependencies>
        <!--公共代码依赖-->
        <dependency>
            <groupId>cn.itsource</groupId>
            <artifactId>common_interface</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <!--springboot支持-->
        <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>
        </dependency>

        <!--eureka客户端支持 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>

3.3.2 APP入口的代码

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

3.3.3 application.yml配置

注意:
defaultZone配置了服务提供方代码可以配置到多个注册中心;完成集群操作

server:
  port: 8002
spring:
  application:
     name: USER-PROVIDER #不要使用下划线
eureka:
  client:
    service-url:
      #defaultZone: http://localhost:7001/eureka #告诉服务提供者要把服务注册到哪儿
       defaultZone: http://eureka-7001.com:7001/eureka,http://eureka-7002.com:7002/eureka
        instance:
          prefer-ip-address: true #显示客户端真实ip

3.3.4 controller层展示数据

@RestController
@RequestMapping("/provider")
public class ProviderController {

    @RequestMapping("/user/{id}")
    public User getUser(@PathVariable("id") Long id){
        return  new User(id,"熊瞎子8002");
    }
}

3.4 服务消费者的搭建(并用feign实现其负载均衡)

3.4.1首先引入pom.xml

<dependencies>
        <dependency>
            <groupId>cn.itsource</groupId>
            <artifactId>common_interface</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!--springboot支持-->
        <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>
        </dependency>

        <!--eureka客户端,服务消费者也要从注册中心获取可用服务列表-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <!--feign的支持-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
    </dependencies>

3.4.2 APP入口的代码

@SpringBootApplication
@EnableFeignClients(basePackages = "cn.itsource.springcloud.consumer")
public class ConsumerApp2 {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApp2.class);
    }
}

3.4.3 接口的代码(实现可跳转到服务提供者的服务)

//此处value的值就是我们在服务提供者中的配置的name
@FeignClient(value = "USER-PROVIDER")
@RequestMapping("/provider")
public interface UserInterface {

    @RequestMapping("/user/{id}")
    public User getUser(@PathVariable("id") Long id);
}

3.4.4 Controller层的跳转路径的代码

@RestController
@RequestMapping("/consumer")
public class ConsumerController {
    @Autowired
    private UserInterface userInterface;

    @RequestMapping("/user/{id}")
    public User getUser(@PathVariable("id") Long id){
        return userInterface.getUser(id);
    }
}

注意:
(需要注意在入口APP中的注册中心注解@EnableEurekaService
而提供者和消费者都是@EnableEurekaClient)

3.5上诉的操作使用了集群和负载均衡,集群很简单下面我们说一下负载均衡

4.对Spring Cloud的负载均衡的学习

4.1 什么是负载均衡

为了提供并发量,有时同一个服务提供者可以部署多个。这个客户端在调用时要根据一定的负责均衡策略完成负载调用。
SpringCloud中常见负载均衡实现技术有两个
一个是Ribbon还要一个是Feign

4.2 Ribbon的使用

4.2.1 什么是Ribbon
Ribbon是Netflix发布的云中间层服务开源项目,主要功能是提供客户端负载均衡算法。
4.2.2 单服务提供者操作
①依赖的pom.xml

<dependencies>
       <!--公共代码依赖-->
       <dependency>
           <groupId>cn.itsource</groupId>
           <artifactId>common_interface</artifactId>
           <version>1.0-SNAPSHOT</version>
       </dependency>
       <!--springboot支持-->
       <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>
       </dependency>
       <!--eureka客户端,服务消费者也要从注册中心获取可用服务列表-->
       <dependency>
           <groupId>org.springframework.cloud</groupId>
           <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
       </dependency>

       <!--客户端负载均衡实现 ribbon-->
       <dependency>
           <groupId>org.springframework.cloud</groupId>
           <artifactId>spring-cloud-starter-ribbon</artifactId>
       </dependency>
       <dependency>
           <groupId>org.springframework.cloud</groupId>
           <artifactId>spring-cloud-starter-config</artifactId>
       </dependency>
   </dependencies>

②定义一个获取bean的类来开启负载均衡

@Configuration
public class CfgBean {
    @Bean
    //这里代表开启负载均衡
    @LoadBalanced
    public RestTemplate getTemplate(){
        return new RestTemplate();
    }
    
    //修改负载均衡的策略用随机策略代替轮训
    @Bean
    public IRule getRule(){
        return  new RandomRule();
    }

}

③controller层的代码

@RestController
@RequestMapping("/consumer")
public class ConsumerController {

    private RestTemplate restTemplate;

    @RequestMapping("/user/{id}")
    public User getUser(@PathVariable("id") Long id){
        String url = "http://USER-PROVIDER/provider/user/"+id;
        return restTemplate.getForObject(url, User.class);
    }
}

④入口APP和application.yml都是我们上面搭建的Feign负载均衡类似

4.3Feign负载均衡(推荐使用)

4.3.1 Feign的认识
是一个声明式的Web Service客户端,它的目的就是让Web Service调用更加简单。Feign提供了HTTP请求的模板,通过编写简单的接口和插入注解,就可以定义好HTTP请求的参数、格式、地址等信息。而Feign则会完全代理HTTP请求,我们只需要像调用方法一样调用它就可以完成服务请求及相关处理。Feign整合了Ribbon和Hystrix(关于Hystrix我们后面再讲),可以让我们不再需要显式地使用这两个组件。

其最大的特点是:
Feign是以接口方式进行调用,而不是通过RestTemplate来调用,feign底层还是ribbo,它进行了封装,让我们调用起来更加happy.

4.3.2其中的操作见上面我们的搭建的项目即使用的Feign负载均衡哦

5 Hystrix断路器

5.1为什么要学Hystrix

*在理想状态下,一个应用依赖的服务都是健康可用的,我们可以正常的处理所有的请求。 
但当某一个服务出现延迟时,所有的请求都阻塞在依赖的服务A
当依赖A阻塞时,大多数服务器的线程池就出现阻塞(BLOCK),影响整个线上服务的稳定性
所以呢?就会出现雪崩现象*
而Hystrix就是处理依赖隔离的框架,同时也是可以帮我们做依赖服务的治理和监控


一句话简单解释:
Hystrix是保证微服务群健壮框架,做了隔离,熔断,降级等操作.
最终达到不会由于某一个服务出问题而导致雪崩现象,让整体群死掉

5.2 Hystrix可通过以下的方式解决雪崩

资源隔离(限流):
包括线程池隔离和信号量隔离,限制调用分布式服务的资源使用,某一个调用的服务出现问题不会影响其他服务调用。

融断:
当失败率达到阀值自动触发降级(如因网络故障/超时造成的失败率高),熔断器触发的快速失败会进行快速恢复。

降级机制:
超时降级、资源不足时(线程或信号量)降级,降级后可以配合降级接口返回托底数据。

缓存
提供了请求缓存、请求合并实现。

5.3 服务熔断的理解

①正常情况下,断路器处于关闭状态(Closed),

②如果调用持续出错或者超时,电路被打开进入熔断状态(Open),后续一段时间内的所有调用都会被拒绝(Fail Fast),

③一段时间以后,保护器会尝试进入半熔断状态(Half-Open),允许少量请求进来尝试,
如果调用仍然失败,则回到熔断状态

④ 如果调用成功,则回到电路闭合状态;

5.4 服务降级的理解

有了熔断,就得有降级。
所谓降级,就是当某个服务熔断之后,服务器将不再被调用,此时客户端可以自己准备一个本地的fallback回调,返回一个缺省值。
这样做,虽然服务水平下降,但好歹可用,比直接挂掉要强,当然这也要看适合的业务场景。

5.5 Hystrix实现(服务提供者实现)

5.5.1 引入pom.xml的依赖

 <dependencies>
        <!--公共代码依赖-->
        <dependency>
            <groupId>cn.itsource</groupId>
            <artifactId>common_interface</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <!--springboot支持-->
        <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>
        </dependency>

        <!--eureka客户端支持 -->
        <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-hystrix</artifactId>
    </dependency>
    </dependencies>

5.5.2 APP入口


@SpringBootApplication  //开启springboot的入口
@EnableEurekaClient //服务器提供者需要开启springcloud的客户端
@EnableHystrix      //开启熔断操作
public class ProviderApp3 {
    public static void main(String[] args) {
        SpringApplication.run(ProviderApp3.class);
    }
}

5.5.3 application.yml的配置

server:
  port: 8003
spring:
  application:
     name: USER-PROVIDER #不要使用下划线
eureka:
  client:
    service-url:
      #defaultZone: http://localhost:7001/eureka #告诉服务提供者要把服务注册到哪儿
       defaultZone: http://eureka-7001.com:7001/eureka,http://eureka-7002.com:7002/eureka
        instance:
          prefer-ip-address: true #显示客户端真实ip

5.5.4 controller层代码

@RestController
@RequestMapping("/provider")
public class ProviderController {

    @RequestMapping("/user/{id}")
    @HystrixCommand(fallbackMethod = "failGet")
    public User getUser(@PathVariable("id") Long id){

        if(id==1){
            throw new RuntimeException("运行时错误");
        }
        return  new User(id,"熊瞎子8002");
    }

    //此类就是用来返回拖地数据的
    public User failGet(Long id){
        return new User(id,"恭喜您中奖1分人民币");
    }
}

5.6 Hystrix实现(服务消费者实现)

5.6.1 pom.xml的代码

    <dependencies>
        <dependency>
            <groupId>cn.itsource</groupId>
            <artifactId>common_interface</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!--springboot支持-->
        <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>
        </dependency>

        <!--eureka客户端,服务消费者也要从注册中心获取可用服务列表-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <!--feign的支持-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
    </dependencies>

5.6.2其中yml的配置

server:
  port: 9003
eureka:
  client:
    registerWithEureka: false #不注册到Eureka,不在注册中心显示
service-url:
      defaultZone: http://localhost:7001/eureka
feign:
   hystrix:
       enabled: true #开启熔断支持
client:
    config:
      remote-service:           #服务名,填写default为所有服务
connectTimeout: 3000
readTimeout: 3000

hystrix:
  command:
      default:
        execution:
          isolation:
            thread:
              timeoutInMilliseconds: 3000

5.6.3 入口APP

@SpringBootApplication
@EnableFeignClients(basePackages = "cn.itsource.springcloud.consumer")
public class ConsumerApp3 {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApp3.class);
    }
}

5.6.4 接口的代码


//value中的值是提供者yml配置中的name,fallback值当前接口的实现类
@FeignClient(value = "USER-PROVIDER",fallback = UserClientFall.class)
//规则类上面不能写RequestMapping不能识别
public interface UserInterface {
    @RequestMapping("/provider/user/{id}")
    public User getUser(@PathVariable("id") Long id);
}

5.6.5 其接口的实现类

@Component
public class UserClientFall implements UserInterface {
    @Override
    public User getUser(Long id) {
        return new User(id,"好像你中了1元人民币");
    }
}

5.6.6 然后就是Controlle层的代码

@RestController
@RequestMapping("/consumer")
public class ConsumerController {

    @Autowired
    private UserInterface userInterface;

    @RequestMapping("/user/{id}")
    public User getUser(@PathVariable("id") Long id){
        return userInterface.getUser(id);
    }
}

第一天结束

6.zuul路由网关

6.1 什么是路由网关

Zuul 是netflix开源的一个API Gateway 服务器, 本质上是一个web servlet应用。
Zuul 在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架。Zuul 相当于是设备和 Netflix 流应用的 Web 网站后端所有请求的前门,也要注册入Eureka.

6.2为什么需要路由网关

通常一个业务系统会有很多的微服务.,为了让调用更简单,一般会在这些服务前端再封装一层,这一层俗称为“网关层”,其存在意义在于,将"1对N"问题 转换成了"1对1”问题(路由),同时在请求到达真正的微服务之前,可以做一些预处理(过滤),比如:来源合法性检测,权限校验,反爬虫之类…
让我们调用更加简单