SpringCloud

SpringCloud

一.单体应用和微服务

1.单体应用

1.1.什么的单体应用

简单来说我们以前传统的应用的就是单体架构,即所有的模块,组件等都在一个应用中应用最终打成一个(war,jar)包使用一个容器(Tomcat)进行部署,通常一个应用享用一个数据库。

在单体应用中我们通常把应用分为三个组成部分:持久层,业务层,表现层,这样的应用结构在项目初期业务较少的情况下没有任何问题,但是随着业务需求不断的增加要求单体应用中的业务逻辑,业务组件等日益扩张,应用将会变得越来越臃肿,往后的开发和维护就会变得特别麻烦,再加上越来越大访的问量,并发越来越高,面对海量的用户无论从应用性能还是从数据库方面都有吃不消的时候。所以单体应用在数据量,并发量到一定程度的时候一定会遇到瓶颈。如下图:

在这里插入图片描述

1.2.单体应用的问题
  • 对于高并发、大数据量 ,处理不占优势

  • 开发时间越长,代码越多,项目越臃肿,杂乱无章

  • 模块与模块,业务与业务耦合高 :比如一个模块挂了,其他模块也挂,一个模块升级,其他模块也要重启

  • 技术选型单一,数据库选型单一

  • 项目体积庞大的时候,会造成,编译慢,项目打包等也慢。

  • 二次开发,维护难

  • 新的程序员对项目需要花很长时间去熟悉,成本高。

  • 不方便局部拓展,只能整体做集群

1.3.单体应用的优点
  • 项目初期,项目的搭建,开发都比较快

  • 技术成本低,对程序员的要求相对低 , 开发成本较低

  • 项目的部署相对简单 - 就一个tomcat

2.微服务架构

2.1.什么是微服务架构

微服务就是把一个单一的应用,按照业务/模块/api等方式进行拆分成多个小的服务(应用),这些小的服务相对独立,有自己的进程,自己的Tomcat,甚至可以有自己的数据库, 微服务之间使用网络通信协议进行数据交互(通常是基于HTTP的RESTful API)。,最终组成一个完整的系统,缺一不可。如下图:

2.2.微服务的优点
  • 服务之间相对解耦合:用户的重启(宕机),不影响其他服务
  • 单看某个微服务,业务简单,方便开发维护
  • 技术选型多样化,数据库选型多样化
  • 扩展性强:方便局部扩展,可以针对某个微服务做扩容(集群),或缩容
  • 一个程序员(一个组)只需要关注一个微服务即可 ,简单。特别是新的程序员上手快。
  • 方便持续集成,持续交互,敏捷开发。
2.3.微服务缺点
  • 技术要求高,对程序员要求比较高
  • 项目的构建相对复杂,开发成本高,资金成本高
  • 部署麻烦,需要借助一些自动化部署工具
  • 微服务之间的通讯会有延迟,一定程度上来说因影响性能

3.SpringCloud简介

3.1.什么是SpringCloud

Spring Cloud是一个基于Spring Boot实现的服务治理工具包,提供了一系列组件可以很方便的实现微服架构的基础搭建,和微服务的管理与协调工作,是一个成熟的微服务架构落地解决方案。

3.2.服务通信方式
  • RPC: Remote Produce Call远程过程调用,类似的还有RMI。自定义数据格式,基于原生TCP通信,速度快,效率高。早期的webservice,现在热门的dubbo,都是RPC的典型
  • Http:http其实是一种网络传输协议,基于TCP,规定了数据传输的格式。现在客户端浏览器与服务端通信基本都是采用Http协议。也可以用来进行远程服务调用。缺点是消息封装臃肿。
    现在热门的Rest风格,就可以通过http协议来实现, SpringCloud基于Http。
3.3.SpringCloud和Dubbo的区别
  • Dubbo是RPC框架 : 通信方式基于原生的TCP , 得配合其他的框架一起使用,如使用Zookeeper做注册中心
  • Dubbo只支持Java
  • SpringCloud是一系列微服务解决方案
  • Dubbo基于tcp通信
  • SpringCloud基于Http
  • Dubbo在通信方面的性能高于SpringCloud
  • SpringCloud使用简单
  • Dubbo相对麻烦一点

二.SpringCloud入门

1.注册中心(EurekaServer)原理

1.1.什么是Eureka

Eureka是一个服务注册与发现组件,简单说就是用来统一管理微服务的通信地址,它包含了EurekaServer服务端(也叫注册中心)和EurekaClient客户端两部分组成,EureakServer是独立的服务,而EurekaClient需要继承到每个微服务中。

微服务(EurekaClient)在启动的时候会向EureakServer提交自己的通信地址清单如:服务名,ip,端口,在EurekaServer会形成一个微服务的通信地址列表 — 这叫服务注册

微服务(EurekaClient)会定期的从EureakServer拉取一份微服务通信地址列表缓存到本地。一个微服务在向另一个微服务发起调用的时候会根据目标服务的服务名找到其通信地址清单,然后基于HTTP协议向目标服务发起请求。—这叫服务发现

另外,微服务(EurekaClient)采用“心跳”机制向EureakServer发请求进行服务续约,其实就是定时向EureakServer发请求报告自己的健康状况,告诉EureakServer自己还活着,不要把自己从服务地址清单中掉,那么当微服务(EurekaClient)宕机未向EureakServer续约,或者续约请求超时,注册中心机会从地址清单中剔除该续约失败的服务

20200317195053166

1.2.Eureka工作流程
  • 微服务在启动时,会自动向注册中心提交自己的通信地址 ,注册中心形成一个微服务通信地址列表

  • 微服务会定时向注册中心拉取一次服务通信地址列表

  • 服务之间发起调用时,会先从服务地址清单中获取到目标服务的通信地址(ip:port),然后发起Http调用

  • 微服务会使用心跳机制向注册中心续约:定时向注册中心发请求,告诉注册中心,我还活着,不要 把我删除了,一旦微服务没有发心跳请求给注册中心,或心跳请求超时,那么注册中心会剔除掉续约失败的服务。

2.搭建基础项目结构

2.1.创建多模块工程
springcloud-parent
  springcloud-eureka-server-1010 //注册中心EurekaServer 
  springcloud-user-server-1020   //用户服务EurekaClient ,提供者
  springcloud-order-server-1030  //订单服务EurekaClient ,消费者
2.2.springcloud-parent管理依赖

搭建基础项目结构 , 多模块方式,父工程管理jar包

参照文档:https://cloud.spring.io/spring-cloud-static/Greenwich.SR5/multi/multi__quick_start.html#_client_side_usage

 <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.5.RELEASE</version>
    </parent>

    <groupId>cn.chenxin.springcloud</groupId>
    <artifactId>springcloud-parent</artifactId>
    <version>1.0-SNAPSHOT</version>

    <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>
    <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>

3.搭建注册中心

3.1.导入依赖

修改 springcloud-eureka-server-1010 工程

参照文档:https://cloud.spring.io/spring-cloud-static/Greenwich.SR5/multi/multi_spring-cloud-eureka-server.html#netflix-eureka-server-starter

<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-web</artifactId>
        </dependency>
    </dependencies>
3.2.主配置类
/**
 * @EnableEurekaServer :开启注册中心
 */
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication1010 {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication1010.class);
    }
}
3.3.配置文件application.yml
server:
  port: 1010  #端口
eureka:
  instance:
    hostname: localhost #主机
  client: #客户端配置
    registerWithEureka: false  #EurekaServer自己不要注册到EurekaServer自己
    fetchRegistry: false  #不要拉取服务的通信地址列表
    serviceUrl:  #注册中心的注册地址
      defaultZone: http://localhost:1010/eureka/  ##http://${eureka.instance.hostname}:${server.port}/eureka/
  server:
    enable-self-preservation: false #关闭自我保护警告

4.搭建提供者服务

修改 springcloud-user-server-1020集成EurekaClient客户端注册到EurekaServer

参照文档 : https://cloud.spring.io/spring-cloud-static/Greenwich.SR5/multi/multi__service_discovery_eureka_clients.html#netflix-eureka-client-starter

4.1.导入依赖
 <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
4.2.创建配置类
//@EnableDiscoveryClient:开启注册中心客户端 EurekaClient
@SpringBootApplication
@EnableEurekaClient
public class UserServerApplication1020 {
    public static void main(String[] args) {
        SpringApplication.run(UserServerApplication1020.class);
    }
}
4.3.配置文件
#注册到EurekaServer
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:1010/eureka/
  #使用ip地址进行注册
  instance:
    prefer-ip-address: true
    #要指定服务的实例ID
    instance-id:  user-server:1020
server:
  port: 1020
spring:
  application:
    name: user-server  #服务名

5.搭建消费者服务

修改服务:springcloud-order-server-1030 ,集成EurekaClient注册到EureakServer注册中心,步骤同提供者服务一样,只是配置文件中要做少许修改,如:端口,实例ID,服务名

application.yml

#注册到EurekaServer
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:1010/eureka/
  #使用ip地址进行注册
  instance:
    prefer-ip-address: true
    #要指定服务的实例ID
    instance-id:  order-server:1030
server:
  port: 1030
spring:
  application:
    name: order-server  #服务名

三.服务通信

我们先使用RestTemplate来简单演示一下服务之间的通信 ,需求是:用户微服务编写controller接口返回User对象,订单微服务通过RestTemplate向用户服务器发起请求获取User对象,浏览器访问订单为服务,拿到User。
在这里插入图片描述

1.搭建用户模块

1.1.创建工程

订单服务调用用户服务获取User对象实现服务之间的通信 ,用户服务需要提供User,订单服务获取到User,我们发现两个服务都需要用到User对象,那么我们考虑吧User对象抽取成一个公共的模块 springcloud-user-common

用户微服务和订单微服务都去依赖该模块即可。创建模块后项目结构如下:

springcloud-parent
	springcloud-eureka-server-1010
	springcloud-order-server-1030
	springcloud-user-common	//公共User模块
	springcloud-user-server-1020
1.2.建立User
public class User {
    private Long id;
    private String username;
    private String desc;

    public User() {
    }

    public User(Long id, String username, String desc) {
        this.id = id;
        this.username = username;
        this.desc = desc;
    }
//...省略getter,setter...

2.User-server提供者服务编写接口

修改springcloud-user-server-1020工程

2.1.依赖公共模块

用户服务需要依赖User模块 ,然后暴露接口返回User,提供给订单服务访问

<dependency>
    <groupId>cn.chenxin.springcloud</groupId>
    <artifactId>springcloud-user-common</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>
2.2.创建controller
//用户服务:暴露接口给订单访问
@RestController
public class UserController {


    //订单服务来调用这个方法      http://localhost:1020/user/10
    // @GetMapping(value = "/user/{id}" )
    @RequestMapping(value = "/user/{id}",method = RequestMethod.GET)
    public User getById(@PathVariable("id")Long id){
        //根据id去数据库查询User
        return  new User(id,"zs:"+id,"我是zs");
    }
}

3.Order-server消费者服务

修改springcloud-order-server-1030工程

3.1.依赖公共模块
<dependency>
    <groupId>cn.chenxin.springcloud</groupId>
    <artifactId>springcloud-user-common</artifactId>
     <version>1.0-SNAPSHOT</version>
</dependency>
3.2.定义RestTemplate的bean

RestTemplate是Spring封装的一个基于Restful风格的http客户端 工具,用来向用户服务发起http请求

/**
 * 订单的启动类
 */
@SpringBootApplication
@EnableEurekaClient
public class OrderServerApplication1030
{

    //配置一个RestTemplate ,Spring封装的一个Restful风格的http客户端 工具
    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

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

3.3.定义消费者的Controller
//订单服务
@RestController
public class OrderController {

    //需要配置成Bean
    @Autowired
    private RestTemplate  restTemplate ;

    //浏览器调用该方法
    @RequestMapping(value = "/order/{id}",method = RequestMethod.GET)
    public User getById(@PathVariable("id")Long id){
        //发送http请求调用 user的服务,获取user对象 : RestTemplate
        //user的ip,user的端口,user的Controller路径
        String url = "http://localhost:1020/user/"+id;

        //发送http请求
        return restTemplate.getForObject(url, User.class);
    }
}

4.测试

浏览器访问 http://localhost:1030/order/11 , 返回结果 {id:1,”username”:”zs”}

四.EurekaServer高可用集群

1.为什么要做高可用集群

1.1.EurekaServer单节点故障

如果只有一个EurekaSever,如果EurekaSever挂了那么整个微服务都不可用,解决方案:EurekaServer高可用集群。

2.EurekaServer集群

2.1.EurekaServer集群方案

文档地址:https://cloud.spring.io/spring-cloud-static/Greenwich.SR5/single/spring-cloud.html#spring-cloud-eureka-server-peer-awareness

准备三个EurekaServer 相互注册,也就是说每个EurekaServer都需要向所有的EureakServer注册,包括自己 ,每个EurekaServer即充当了服务端,也充当了客户端。咱们的其他微服务(order,user)只需要把注册地址指向所有的EurekaServer就可以了。

20200318114451707

2.2.搭建EurekaServer集群

1.创建两个本地域名 C:\Windows\System32\drivers\etc\hosts

127.0.0.1 peer1
127.0.0.1 peer2
127.0.0.1 peer3

2.修改注册中心eureka-server-1010实现高可用配置

注意配置规则:

  • 采用SpringBoot单配置文件多环境配置

  • 因为三个EurekaServer要相互注册 ,那么三个EurekaServer的 serviceUrl都是一样的,我们就把serviceUrl抽取到最上面,其中包括 使用ip地址注册 (prefer-ip-address),EureakServer自我保护机制(enable-self-preservation) ,以及服务名(application.name)都是相同点的,都可以抽到最上面。

  • 每个EurekaServer不一样的配置:端口(port) ,环境名字(profiles),主机名(hostname),实例id(instance-id:)

eureka:
  client:
    serviceUrl:
      defaultZone: http://peer1:1010/eureka/,http://peer2:1011/eureka/,http://peer3:1012/eureka/
  instance:
      prefer-ip-address: true
  server:
    enable-self-preservation: false #关闭自我保护警告o
spring:
  profiles:
    active: peer1
  application:
    name: eureka-peer
---
#peer1第一个Eureka
spring:
  profiles: peer1
server:
  port: 1010  #端口
eureka:
  instance:
    hostname: peer1  #主机
    instance-id: eureka-peer1:1010
---
#peer2第二个Eureka
spring:
  profiles: peer2
server:
  port: 1011  #端口
eureka:
  instance:
    hostname: peer2  #主机
    instance-id: eureka-peer2:1011
---
#peer3第三个Eureka
spring:
  profiles: peer3
server:
  port: 1012  #端口
eureka:
  instance:
    hostname: peer3 #主机
    instance-id: eureka-peer3:1012
2.3.启动EurekaServer集群

1.需要指定IDEA的多实例启动配置
20200318120952088

2.启动三次EurekaServerApplication1010,每次启动的时候需要修改激活的环境

#...省略....
pring:
  profiles:
    active: peer1	#这里启动一次需要修改一次 ,每次启动使用不同的环境配置

3.微服务注册到Eureka集群

用户和订单的微服务的配置中,注册中心地址修改为所有的EurekaServer注册地址即可,即user-server和order-server都这样配,如下

eureka:
  client:
    serviceUrl:
      defaultZone: http://peer1:1010/eureka/,http://peer2:1011/eureka/,http://peer3:1012/eureka/ #注册中心地址
      ...

五.Ribbon负载均衡

1.基本概念

1.1.为什么要Ribbon

我们知道,为了防止应用出现单节点故障问题,同时为了提高应用的作业能力,我们需要对应用做集群 ,如果我们对user-server做了集群 ,那么这个时候回衍生出一些问题:现在有两个user-server意味着有两个通信地址,我的order-server在向user-server发起调用的时候该访问哪个?如何访问?这个时候就需要有一个组件帮我们做请求的分发,即:负载均衡器,而Ribbon就是一个客户端负载均衡器。

1.2.什么是Ribbon

Ribbon是Netflix发布的云中间层服务开源项目,主要功能是提供客户端负载均衡算法。Ribbon客户端组件提供一系列完善的配置项,如,连接超时,重试等。简单的说,Ribbon是一个客户端负载均衡器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器,我们也很容易使用Ribbon实现自定义的负载均衡算法。

1.3.Ribbon的工作机制

如下图,我们将user-server增加到2个节点(两个user-server的服务名要一样,ip和端口不一样),在注册中心的服务通信地址清单中user-server这个服务下面会挂载两个通信地址 , 而订单服务会定时把服务通信地址清单拉取到本地进行缓存, 那么当order-server在向用户服务发起调用时,需要指定服务名为 user-server;那么这个时候,ribbon会根据user-server这个服务名找到两个用户服务的通信地址 , 然后ribbon会按照负载均衡算法(默认轮询)选择其中的某一个通信地址,发起http请求实现服务的调用。

20200318151113059

2.提供者user-server集群

2.1.服务集群方案

使用SpringBoot多环境配置方式集群,一个配置文件配置多个用户服务环境 ,需要注意的是集群中的多个服务名(spring.application.name)应该一样,我们把相同的东西提取到最上面,不同的东西配置在各自的环境中

2.2.用户服务集群配置

修改 application.yml 如下:

#注册到EurekaServer
eureka:
  client:
    serviceUrl:
      defaultZone: http://peer1:1010/eureka/,http://peer2:1011/eureka/,http://peer3:1012/eureka/
  #使用ip地址进行注册
  instance:
    prefer-ip-address: true
spring:
  application:
    name: user-server  #服务名都叫user-server
  profiles:
    active: user-server1
---
server:
  port: 1020
eureka:
  instance:
    instance-id: user-server:1020
spring:
  profiles: user-server1
---
server:
  port: 1021
eureka:
  instance:
    instance-id: user-server:1021
spring:
  profiles: user-server2


3.消费者Order-server集成Ribbon

官方文档:https://cloud.spring.io/spring-cloud-static/Greenwich.SR5/multi/multi_spring-cloud-ribbon.html#netflix-ribbon-starter

修改springcloud-order-server-1030服务

3.1.导入依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-symltarter-netflix-ribbon</artifactId>
</dependency>
3.2.修改RestTemplate的Bean的定义

@LoadBalanced :ribbon的负载均衡标签,赋予RestTemplate有负债均衡的能力

/**
 * 订单的启动类
 */
@SpringBootApplication
@EnableEurekaClient
public class OrderServerApplication1030
{

    //配置一个RestTemplate ,Spring封装的一个机遇Restful风格的http客户端 工具
    //@LoadBalanced :让RestTemplate有负载均衡的功能
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
    //省略...
3.3.修改Controller调用方式
@RestController
public class OrderController {

    //需要配置成Bean
    @Autowired
    private RestTemplate  restTemplate ;

    //浏览器调用该方法
    @RequestMapping(value = "/order/{id}",method = RequestMethod.GET)
    public User getById(@PathVariable("id")Long id){
        //发送http请求调用 user的服务,获取user对象 : RestTemplate
        //user的ip,user的端口,user的Controller路径
        //String url = "http://localhost:1020/user/"+id;
        String url = "http://user-server/user/"+id;

        //发送http请求
        return restTemplate.getForObject(url, User.class);

    }
}
3.4.配置负载均衡算法

把负载均衡算法类配置成Bean即可

/**
 * 订单的启动类
 */
@SpringBootApplication
@EnableEurekaClient
public class OrderServerApplication1030
{

    //配置一个RestTemplate ,Spring封装的一个机遇Restful风格的http客户端 工具
    //@LoadBalanced :让RestTemplate有负载均衡的功能
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

    //负载均衡算法
    @Bean
    public RandomRule randomRule(){
        return new RandomRule();
    }
    //省略...

Ribbon默认使用的轮询策略

3.5.Ribbon源码跟踪
 1.@LoadBalanced 开启负载均衡 
 2.RibbonLoadBalancerClient.execute : 根据服务名选择一个服务, 发起调用
 	2.1.loadBalancer.chooseServer选择服务  : BaseLoadBalancer.chooseServer选择服务
	2.2.IRule.choose : RandomRule.choose  :按照具体的算法选择一个服务返回
 3.向服务发起Http调用
3.6.做个步骤小结
1.用户服务的集群
2.订单服务集成Ribbon
2.1.导入Ribbon的依赖
2.2.在RestTemplate的Bean定义方法上加上:@LoadBalanced注解
2.3.把订单的Controller向用户发起调用的restTemplate的url使用服务名调用,
	如:"http://user-server/user/"+id;
3.负载均衡算法:主配置类配置负载均衡算法类的Bean即可

六.Feign负载均衡

1.什么是Feign

1.1.Ribbon有什么问题

前面的可以发现当我们通过RestTemplate调用其它服务的API时,所需要的参数须在请求的URL中进行拼接,如果参数少的话或许我们还可以忍受,一旦有多个参数的话,这时拼接请求字符串就会效率低下,并且显得好傻

1.2.什么是Feign

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

2.搭建新支付服务注册到Eureka

2.1.创建工程

1.创建工程 springcloud-pay-server-1040,用来集成Feign

springcloud-parent
	springcloud-eureka-server-1010
	springcloud-order-server-1030
	springcloud-pay-server-1040	//支付服务用来集成Feign
	springcloud-user-common
	springcloud-user-server-1020
pom.xml
2.2.导入依赖

我们先让支付服务注册到注册中心后再考虑集成Feign , pom.xml如下

 <dependencies>
        <!--1.导入EurekaClient的包-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
		
        
        <!--web包-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--user-common-->
        <dependency>
            <groupId>cn.chenxin.springboot</groupId>
            <artifactId>springcloud-user-common</artifactId>
        </dependency>
    </dependencies>
2.3.配置类如下
/**
 * 支付的启动类
 * @EnableFeignClients :开启Feign支持
 */
@SpringBootApplication
@EnableEurekaClient
public class PayServerApplication1040
{

    public static void main( String[] args )
    {
        SpringApplication.run(PayServerApplication1040.class);
    }
}
2.4.配置文件application.yml
#注册到EurekaServer
eureka:
  client:
    serviceUrl:
      defaultZone: http://peer1:1010/eureka/,http://peer2:1011/eureka/,http://peer3:1012/eureka/
  #使用ip地址进行注册
  instance:
    prefer-ip-address: true
    #要指定服务的实例ID
    instance-id:  pay-server:1040
server:
  port: 1040
spring:
  application:
    name: pay-server  #服务名

3.集成Feign

查考文档:https://cloud.spring.io/spring-cloud-static/Greenwich.SR5/multi/multi_spring-cloud-feign.html#netflix-feign-starter

3.1.加入依赖
 <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
3.2.开启Feign

主配置类增加@EnableFeignClients标签


/**
 * 支付的启动类
 * @EnableFeignClients :开启Feign支持
 */
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients("cn.chenxin.springboot.feignclient")
public class PayServerApplication1040
{


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

3.3.编写Feign的客户端接口
@FeignClient("user-server")
public interface UserFeignClient {

    //订单服务来调用这个方法      http://localhost:1020/user/10
    // @GetMapping(value = "/user/{id}" )
    @RequestMapping(value = "/user/{id}",method = RequestMethod.GET)
    User getById(@PathVariable("id")Long id);
}
  • 解释

UserFeignClient : 这个接口就是用来调用 user-server这个服务的Feign的客户端接口
@FeignClient(“user-server”) : user-server是服务的名字,我们要掉用户服务
feign根据服务名能够找到 目标服务 ,根据方法上的 RequestMapping的值可以找到目标服务的controller的方法

  • 千万注意:

1.服务名一定不要写错
2.@RequestMapping 一定要和目标服务的controller的方法的requestMapping一样
3.方法的参数一定要和目标服务的controller的方法的参数一样
4.方法的返回值一定要和目标服务的controller的方法的返回值一样

  • 建议

服务名直接去目标服务配置中拷贝

方法直接从目标服务controller中拷贝

3.4.编写Controller

通过注入UserFeignClient ,直接发起调用

//支付服务的controller
@RestController
public class PayController{

    @Autowired
    private UserFeignClient userFeignClient;

    //浏览器来掉
    @RequestMapping("/pay/{id}")
    public User getById(@PathVariable("id")Long id){
        //使用Feign调用用户服务获取User
        return userFeignClient.getById(id);
    }
}

3.5.测试

通过浏览器访问pay-server的controller

4.做个总结

1.搭建pay-server工程
2.导入相关依赖,注册到EurekaServer  (拷贝order-server相关代码进行修改)
3.导入Feign的依赖
4.pay-server的主配置类开启Feign : @EnableFeignClients
5.pay-server编写Fiegn的客户端接口:UserFeignClient
6.pay-server编写controller注入UserFeignClient调用服务

七.Hystrix

1.理解Hystrix

1.1.雪崩效应

在电影里面经常出现的场景,在冰山雪地不要大声呼喊,应该声音的震动会导致小雪球的滑落,随着雪球的滑落可能有连锁反应导致整个雪山的崩塌,这就是生活中的雪崩,在微服务里面也是一样,微服务的调用非常复杂的 ,一个请求往往需要很多的微服务通过完成,在整个微服务调用链中,如果某个服务发生故障,会导致整个调用链调用异常,甚至可能导致整个微服务瘫痪 , — 这就是雪崩效应。

1.2.Hystrix介绍

Hystrix是国外知名的视频网站Netflix所开源的非常流行的高可用架构框架。Hystrix能够完美的解决分布式系统架构中打造高可用服务面临的一系列技术难题。

Hystrix的英文是豪猪,中文翻译为 熔断器,其思想来源于我们家里的保险开关,当家里出现短路,保险开关及时切掉电路,保证家里人员的安全,其目的就是起保护作用。

简单理解Hystrix可以通过熔断和降级等手段解决雪崩问题,它就是一个微服务保护组件。

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

20200320094253309

  • 熔断 :当请求次数达到规定的阀值都出现服务故障(超时),Hystrix就把服务标记为短路状态.

    正常情况下,断路器处于关闭状态(Closed),如果调用持续出错或者超时,电路被打开进入熔断状态(Open),后续一段时间内的所有调用都会被拒绝(Fail Fast),一段时间以后,保护器会尝试进入半熔断状态(Half-Open),允许少量请求进来尝试,如果调用仍然失败,则回到熔断状态,如果调用成功,则回到电路闭合状态;

  • 降级 :高并发情况下 ,为了保证一些主要的服务有足够的资源不出问题 ,会认为的关掉一些无关紧要的服务,然后返回一些托底的数据,给用户一个友好的提示。

    如在淘宝双十一,淘宝为了让主要的业务有充分的资源,会选择关闭一些不重要的业务,如双十一期间不能退款,不能评价等,当你去退款是会返回暂时不能退款的提示信息给你,这个就是服务的降级机制。

  • 缓存 :Hystrix内部会把请求做缓存和请求做合并

2.订单服务集成Hystrix

修改 springcloud-order-server-1030 集成Hystrix ,该工程已经集成了Ribbon。

2.1.导入依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
2.2.配置类开启Hystrix

通过 @EnableCircuitBreaker 标签开启熔断功能


/**
 * 订单的启动类
 */
@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker       //开启Hystrix熔断
public class OrderServerApplication1030
{
    //省略...
2.3.方法熔断

通过 @HystrixCommand 标签标记方法熔断,标签的fallbackMethod属性指定拖地方法。

//浏览器调用该方法
@RequestMapping(value = "/order/{id}",method = RequestMethod.GET)
@HystrixCommand(fallbackMethod = "fallbackMethod")   //方法熔断
public User getById(@PathVariable("id")Long id){
    //发送http请求调用 user的服务,获取user对象 : RestTemplate
    //user的ip,user的端口,user的Controller路径
    //String url = "http://localhost:1020/user/"+id;
    String url = "http://user-server/user/"+id;

    //发送http请求
    return restTemplate.getForObject(url, User.class);
}
2.4.托底方法
//托底方法
public User fallbackMethod(@PathVariable("id")Long id){
    //返回友好的提示信息
    return new User(-1L,"无此用户","用户服务不可用");
}

注意:托底方法的参数和返回结果要和原方法一致

2.4.测试熔断

浏览器访问 http://localhost:1030/order/1 ,当用户服务 springcloud-user-server-1020 正常启动的时候,订单服务是可以访问,浏览器也可以收到结果 , 当关闭掉用户服务 ,订单服务会触发熔断,返回拖地数据
20200320111137830

3.支付服务集成Hystrix

支付服务 springcloud-pay-server-1040 之前集成了Feign,其实Feign已经集成了Hystrix,只是默认是关闭的,我们需要开启Feign的Hystrix支持,并制定拖地。

3.1.开启Hystrix
feign:
  hystrix:
    enabled: true #开启熔断支持
  client:
    config:
      remote-service:           #服务名,填写default为所有服务
        connectTimeout: 10300
        readTimeout: 10300
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 10300
3.2.Fiegn接口熔断-方式一
@FeignClient(value = "user-server",fallback = UserFeignClientFallback.class)
public interface UserFeignClient {

    //订单服务来调用这个方法      http://localhost:1020/user/10
    // @GetMapping(value = "/user/{id}" )
    @RequestMapping(value = "/user/{id}",method = RequestMethod.GET)
    User getById(@PathVariable("id")Long id);
}

提示: fallback = UserFeignClientFallback.class : 该类是当前接口的实现类 ,也是托底数据所在的处理类

3.3.托底实现
//让Spring扫描到该托底类
@Component
public class UserFeignClientFallback implements UserFeignClient {

    //日志打印器
    private Logger log = LoggerFactory.getLogger(UserFeignClientFallback.class);

    @Override
    public User getById(Long id) {
        log.info("用户服务不可用");
        //托底数据
        return new User(-1l,"无此用户","用户服务不可用");
    }
}

提示:注意,这里托底类需要交给Spirng管理,类上需要打 @Component 注解 , 拖地类需要实现 Feign接口,复写其中方法,返回拖地数据。当Fiegn调用失败就会以拖地方法返回的结果返回给用户

3.4.启动测试

启动支付服务,访问:http://localhost:1040/pay/1 ,测试效果同订单一样。

3.5.Fiegn接口熔断-方式二
1.也要开启Hystrix
2.修改Feign接口

使用fallbackFactory属性,使用工厂方式指定托底

//@FeignClient(value = "user-server",fallback = UserFeignClientFallback.class)
@FeignClient(value = "user-server",fallbackFactory = UserFeignClientFallbackFactory.class)
public interface UserFeignClient {

    //订单服务来调用这个方法      http://localhost:1020/user/10
    // @GetMapping(value = "/user/{id}" )
    @RequestMapping(value = "/user/{id}",method = RequestMethod.GET)
    User getById(@PathVariable("id")Long id);
}

3.编写拖地类
//工厂方式的 , 托底类
@Component
public class UserFeignClientFallbackFactory implements FallbackFactory<UserFeignClient>{

    //拖地方法 : throwable,异常
    @Override
    public UserFeignClient create(Throwable throwable) {
        return new UserFeignClient() {
            @Override
            public User getById(Long id) {
                //把异常信息打印到控制台
                throwable.printStackTrace();

                //真正拖地方法
                return new User(-1L,"无此用户","用户服务不可用");
            }
        };
    }
}
3.6.启动测试

省略…

八.SpirngCloud ZUUL

1.理解zuul

1.1.为什么要zuul

试想一下如果我们有很多的微服务,他们都需要登录之后才能访问,那么我需要在每个微服务都去做一套登录检查逻辑,这样是不是会存在大量重复的代码和工作量,我们希望的是把登录检查这种公共的逻辑进行统一的抽取,只需要做一套检查逻辑即可,而zuul就可以用来干这类事情,我们可以把zuul看做是微服务的大门,所有的请求都需要通过zuul将请求分发到其他微服务,根据这一特性我们就可以在zuul做统一的登录检查,下游的微服务不再处理登录检查逻辑。

1.2.什么是zuul

Zuul 是netflix开源的一个API Gateway 服务器, 本质上是一个web servlet(filter)应用。

Zuul 在云平台上提供动态路由(请求分发),监控,弹性,安全等边缘服务的框架。Zuul 相当于是设备和 Netflix 流应用的 Web 网站后端所有请求的前门,也要注册入Eureka,用一张图来理解zuul在架构中的的工作流程:

20200320121239172

需要注意的是,zuul本身是一个独立的服务,zuul是通过Ribbon实现请求的分发,默认集成了Ribbon,同时也集成了Hystrix,我们需要建立独立的工程区搭建Zuul服务,同时需要把Zuul注册到EurekaServer,因为当请求过来时,zuul需要通过EurekaServer获取下游的微服务通信地址,使用Ribbon发起调用。

2.zuul的搭建

搭建zuul工程springcloud-zuul-server-1050 ,搭建好的工程模块如下:

springcloud-parent
pom.xml
	springcloud-eureka-server-1010
	springcloud-order-server-1030
	springcloud-pay-server-1040
	springcloud-user-common
	springcloud-user-server-1020
	springcloud-zuul-server-1050	//网关服务

修改 springcloud-zuul-server-1050 ,集成EurekaClient和zuul

2.1.导入依赖
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>springcloud-parent</artifactId>
        <groupId>cn.chenxin.springboot</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>springcloud-zuul-server-1050</artifactId>

    <name>springcloud-zuul-server-1050</name>

    <dependencies>
        <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>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
</project>

2.2.配置开开启Zuul

配置类通过 @EnableZuulProxy 注解开启zuul服务功能,同时该注解让zuul拥有了代理下游微服务的能力。

/**
 * 用户的启动类
 * @EnableEurekaClient: 标记该应用是 Eureka客户端
 * @EnableZuulProxy : 开启zuul , 改标签让zuul成为后端微服务的代理服务。
 *       @EnableZuulServer :也是开启zuul但是他没有代理的功能 ,但是我们都用  @EnableZuulProxy
 */
@SpringBootApplication
@EnableEurekaClient
@EnableZuulProxy
public class ZuulServerApplication1050
{

    public static void main( String[] args )
    {
        SpringApplication.run(ZuulServerApplication1050.class);
    }
}
2.3.配置文件配置zuul

这里配置两个东西,一个是EurekaClien的配置,让zuul注册到EurekaServer,二个就是zuul的配置了

#注册到EurekaServer
eureka:
  client:
    serviceUrl:
      defaultZone: http://peer1:1010/eureka/,http://peer2:1011/eureka/,http://peer3:1012/eureka/
  #使用ip地址进行注册
  instance:
    prefer-ip-address: true
    #要指定服务的实例ID
    instance-id:  zuul-server:1050
server:
  port: 1050
spring:
  application:
    name: zuul-server  #服务名

zuul:
  prefix: "/servers"  #统一访问前缀
  ignoredServices: "*"  #禁用掉使用浏览器通过服务名的方式访问服务
  routes:
    pay-server: "/pay/**"   #指定pay-server这个服务使用 /pay路径来访问  - 别名
    order-server: "/order/**"   #指定order-server这个服务使用 /order路径来访问

提示: 我们对zuul主要做了三个配置

  • zuul.prefix : 作为统一的前缀,在浏览器访问的时候需要加上该前缀

  • zuul.ignoredServices : 忽略使用服务名方式访问服务,而是通过routes指定的路径进行访问

  • zuul.routes : 配置服务的访问路径

注意:在么有使用zuul之前我们是通过 http://localhost:1040/pay/1 来直接访问支付服务,现在需要通过zuul来访问,格式如下:http:// zuul的ip : zuul的port /zuul前缀 / 服务路径 /服务的controller路径 ,即:

http://localhost:1050/servers/pay/pay/1

特别说明:其实这里我们直接浏览器也能访问到目标服务,即可以通过: http://localhost:1040/pay/1 绕过zuul,但是这种情况不用担心,因为在产品上线的时候我们都是内网部署,只有zuul我们部署成外网,也就是说直接访问目标微服务的方式是访问不到的,所以我们只需要通过zuul访问即可。

2.4.测试zuul

浏览器访问:http://localhost:1050/servers/pay/pay/1 ,看到如下信息:

20200320151121576

2.5.zuul的超时配置

Zuul集成了hystrix,如果服务的调用链过长,或者ribbon调用事件过长,可能会触发Hystrix的熔断机制,导致请求拿不到正常的结果,我们通常会对Ribbon和Hystrix的超时时间配置。如下配置对所有消费者微服务都有用:

zuul配置文件加上如下配置:

ribbon:
  MaxAutoRetries: 1 #最大重试次数,当Eureka中可以找到服务,但是服务连不上时将会重试
  MaxAutoRetriesNextServer: 1 #切换实例的重试次数
  OkToRetryOnAllOperations: false # 对所有的操作请求都进行重试,如果是get则可以,如果是post,put等操作没有实现幂等的情况下是很危险的,所以设置为false
  ConnectTimeout: 3000 #请求连接的超时时间
  ReadTimeout: 5000 #请求处理的超时时间
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 10000
            #如果配置ribbon的重试,hystrix的超时时间要大于ribbon的超时时间,ribbon才会重试
            #hystrix的超时时间=(1 + MaxAutoRetries + MaxAutoRetriesNextServer) * ReadTimeout 比较好,具体看需求

3.自定义zuul的Filter

3.1.zuul的执行流程

zuul的底层是通过各种Filter来实现的,zuul中的filter按照执行顺序分为了“pre”前置,”custom”自定义,“routing”路由,“post”后置,以及“error”异常Filter组成,当各种Filter出现了异常,请求会跳转到“error filter”,然后再经过“post filter” 最后返回结果,下面是Filter的执行流程图:
1568950536385
提示:

  • 正常流程:
    • 请求到达首先会经过pre类型过滤器,而后到达routing类型,进行路由,请求就到达真正的服务提供者,执行请求,返回结果后,会到达post过滤器。而后返回响应。
  • 异常流程:
    • 整个过程中,pre或者routing过滤器出现异常,都会直接进入error过滤器,再error处理完毕后,会将请求交给POST过滤器,最后返回给用户。
    • 如果是error过滤器自己出现异常,最终也会进入POST过滤器,而后返回。
    • 如果是POST过滤器出现异常,会跳转到error过滤器,但是与pre和routing不同的时,请求不会再到达POST过滤器了。
3.2.ZuulFilter

Zuul提供了一个抽象的Filter:ZuulFilter我们可以通过该抽象类来自定义Filter,该Filter有四个核心方法,如下:

public abstract class ZuulFilter implements IZuulFilter{
      
    abstract public String filterType();

    abstract public int filterOrder();
   

    //下面两个方法是 IZuulFilter 提供的 
    
    boolean shouldFilter();

    Object run() throws ZuulException;

}

提示:

  • filterType :是用来指定filter的类型的(类型见常量类:FilterConstants)
  • filterOrder :是filter的执行顺序,越小越先执行
  • shouldFilter :是其父接口IZuulFilter的方法,用来决定run方法是否要被执行
  • run :是其父接口IZuulFilter的方法,该方法是Filter的核心业务方法
3.2.自定义Filter

我们来演示一个案例,在Zuul层实现统一的登录检查:如果请求头中有“token”属性,我们就认为已经登录成功,可以继续往下游的服务执行,否则就视为请求未登录,直接返回错误信息,这一需求需要自定义Filter继承ZuulFilter类来实现,具体代码如下

package cn.chenxin.springboot.filter;

import com.alibaba.fastjson.JSON;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

@Component
public class LoginCheckFilter extends ZuulFilter {

    private static final Logger log = LoggerFactory.getLogger(LoginCheckFilter.class);

    //执行顺序
    private static final int ORDER = 1;

    //filter类型 : "pre"前置
    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;	//pre
    }

    //执行顺序
    @Override
    public int filterOrder() {
        return ORDER;
    }

    //返回结果决定 是否要执行run方法
    @Override
    public boolean shouldFilter() {
        // /static/**  ,/login , /register 不需要做登录检查,返回false
        //1.获取request对象 , 获取请求中的url
        HttpServletRequest request = RequestContext.getCurrentContext().getRequest();
        String url = request.getRequestURI();
        log.info("请求地址:"+url);
        //2.判断是否包含在: static/**  ,/login , /register
        if(url.endsWith("/login ") || url.endsWith("/register ") || url.contains("/static/") ){
            return false;
        }
        //要做登录检查的返回true
        return true;
    }

    //核心业务方法 : 登录检查 , 如果请求头中有token,就是登录成功
    @Override
    public Object run() {
        //1.获取请求对象
        HttpServletRequest request = RequestContext.getCurrentContext().getRequest();

        //响应对象
        HttpServletResponse response = RequestContext.getCurrentContext().getResponse();


        //2.获取请求头中的 token
        String token = request.getHeader("token");

        //3.如果没有token,登录检查失败 ,
        if(!StringUtils.hasLength(token)){
            //3.1.返回登录检查失败的错误信息 :{ success:false, message:"登录检查失败,请重新登录"}
            Map<String,Object> resultMap = new HashMap<>();
            resultMap.put("success" , false);
            resultMap.put("message" , "登录检查失败,请重新登录");
            //中文编码
            response.setContentType("application/json;charset=utf-8");
            //把map转成json字符串,写到浏览器
            String resultJsonString = JSON.toJSONString(resultMap);

            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);

            try {
                response.getWriter().print(resultJsonString);
            } catch (IOException e) {
                e.printStackTrace();
            }

            //3.2.阻止filter继续往后执行
            RequestContext.getCurrentContext().setSendZuulResponse(false);

        }

        //这里的返回结果没有任何意义,不用管
        return null;
    }
}

提示:

  • 在 filterType方法中我们返回“pre”前置filter的常量,让他成为前置filter(登录检查需要在请求的最前面来做)
  • 在filterOrder方法中返回的顺序值是 1 ,执行顺序越小越先执行
  • 在shouldFilter方法中通过判断请求的url来决定是否需要做登录检查,返回true就是要做然后才会执行run方法
  • 在run方法中我们通过获取请求头中的token判断是否登录,如果没登录就返回错误信息,阻止继续执行。
  • RequestContext.getCurrentContext() 是一个Zuul提供的请求上下文对象

注意:在返回JSON格式的错误信息时我用到了fastjson,需要在zuul工程中导入依赖

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.50</version>
</dependency>

九.配置中心

1.理解配置中心

1.1.什么是配置中心

在分布式系统中,由于服务数量巨多,为了方便服务配置文件统一管理,实时更新,所以需要分布式配置中心组件。在Spring Cloud中,有分布式配置中心组件spring cloud config ,它支持配置文件放在配置服务的本地,也支持放在远程Git仓库中。在spring cloud config 组件中,分两个角色,一是config server。

1.2.配置中心工作流程

我们需要把微服务的配置文件上传到Git仓库 , 然后搭建独立的ConfigServer服务,ConfigServer除了要从Git仓库拉取配置之外,还要注册到EurekaServer中,因为ConfigServer也是一个微服务,至于ConfigClient这需要集成到具体的微服务中,如支付服务,订单服务等。其工作流程是,微服务向配中心获取配置,配置中心从GIT仓库获取配置,然后再一路返回给微服务。

需要注意是,EurekaServer的配置不能交给ConfigServer管理,因为必须要先启动EurekaServer才能启动ConfigServer,试问EurekaServer都没配置怎么启动?

20200320171529803

2.Git仓库管理配置

2.1.使用码云创建远程仓库

我们用码云作为git仓库 ,创建码云仓库

创建zuul配置 在仓库中创建文件 ,我们先将zuul的配置文件赋值到码云上

注意复制好仓库地址,在克隆中取拷贝 , 待会搭建配置中心的时候会用到

3.搭建配置中心

3.1.搭建项目

搭建工程 springcloud-config-server-1060 ,搭建好项目结构如下

springcloud-parent
pom.xml
	springcloud-config-server-1060	//配置中心服务
	springcloud-eureka-server-1010
	springcloud-order-server-1030
	springcloud-pay-server-1040
	springcloud-user-common
	springcloud-user-server-1020
	springcloud-zuul-server-1050

3.2.导入依赖
 <dependencies>
     <dependency>
         <groupId>org.springframework.cloud</groupId>
         <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
     </dependency>
     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-web</artifactId>
     </dependency>
     <dependency>
         <groupId>org.springframework.cloud</groupId>
         <artifactId>spring-cloud-config-server</artifactId>
     </dependency>
</dependencies>
3.3.开启配置中心
package cn.chenxin.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

/**
 * 配置中心的启动类
 * @EnableConfigServer:开启配置中心
 */
@SpringBootApplication
@EnableEurekaClient
@EnableConfigServer
public class ConfigServerApplication1060
{

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

3.4.配置文件

ConfigServer的配置文件做两个事情,1.注册到EurekaServer,2.配置码云地址

eureka:
  client:
    serviceUrl:
      defaultZone: http://peer1:1010/eureka/,http://peer2:1001/eureka/ #注册中心地址
  instance:
    prefer-ip-address: true #使用ip地址注册
    instance-id: config-server  #指定服务的id
server:
  port: 6001
spring:
  application:
    name: config-server
  cloud:
    config:
      server:
        git:
          uri: https://gitee.com/little_wolf/springcloud-config-1010.git #配置远程仓库地址
          username: 1462163787@qq.com	#仓库是私有的需要账号
          password: xxxxx

3.5.测试配置中心

浏览器访问:http://localhost:1060/application-zuul-dev.yml 你可以看到ConfigServer从Git仓库获取到的zuul的配置文件内容如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-35EsyUeX-1584718288293)(images/image-20200320173644094.png)]

4.Zuul集成ConfigClient

上一步骤我们已经可以通过ConfigServer从码云上获取到配置文件,我们的最终目的是能够让我们的微服务,zuul-server,order-server等都可以从ConfigServer拉取到配置文件,这里功能我们需要给微服务集成ConfigClient来实现。修改“springcloud-zuul-server-1050” zuul工程如下:

4.1.导入依赖
 <dependency>
     <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-config-client</artifactId>
</dependency>
4.2.创建bootstrap.yml配置文件

需要特别说明一下,虽然我们把zuul的配置文件搬到了码云上面,但是zuul本地依然需要一些基础的配置 ,在ConfigClient需要使用bootstrap.yml来作为基础配置文件,这是因为bootstrap.yml的优先级高于application.yml或者其他配置来源,我们得让微服务优先从码云上拉取配置文件后才能做其他的事情。

创建配置文件 resources/bootstrap.yml,添加如下内容

#配置中心的地址
spring:
  cloud:
    config:
      uri: http://localhost:1060 #zuul要从配置中心拉取文件
      #你要拉取具体的哪个配置文件
      name: application-zuul  #配置文件名字   dev是环境
      profile: dev #环境  组成完整的文件名:application-zuul-dev.yml
      label: master #主分支

提示:我们的Zuul需要从ConfigServer拉取配置文件,所以这里通过…config.uri指向配置中心地址,name属性值得是配置文件的名字 ,profile是环境名字 , name的值和profile的值合在一起组成完整的配置文件名,好处是可以修改profile的值来切换配置环境,label是git的分支,默认是master。

4.3.方式二:使用服务名方式发现配置中心

上面的配置文件中通过uri指向配置中心地址获取配置文件,我们也可以通过服务发现的方式指向配置中心,该方式需要屏蔽掉 uri ,使用 discovery.service-id 来指定配置中心的服务名 ,使用discovery.enabled=true来开启服务发现。
需要注意的是:如果使用服务发现的方式来指向配置中心,那么EurekaServer的注册地址需要在本地配置文件中指定。具体配置如下:

spring:
  cloud:
    config:
      #uri: http://localhost:1030
      name: application-dept
      profile: dev
      label: master
      discovery:
        service-id: config-server #配置中心服务名
        enabled: true #使用服务名访问配置中心
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:1010/eureka/ #注册中心地址,需要从注册中心使用服务名发现配置中心

提示:如果ConfigServer配置中心做了集群,那么建议配置中心客户端使用服务名的方式发现配置中心

5.配置中心出错的问题解决

如果发现你的微服务启动不成功,有哪些问题造成:

1.配置文件拉取不到

2.配置文件拉取到了,但是配置内容有错

3.项目其他地方错误 - 不是配置文件的问题

5.1.配置拉取不到

观察启动的端口是不是以8080启动的,如果是,一定是配置文件拉取不到(除非你真的就配置的是8080)排查方式:

检查配置中心服务端是否有问题

  • 检查ConfigServer启动没有,端口,地址对不对

  • 打开浏览器访问一下配置文件看能不能拉取到配置文件,如下:

    http://localhost:1060/application-zuul-dev.yml
    

    如果配置中心能获取到配置文件,那么错误一定在ConfigClient ,或者是内容有问题

  • 如果配置中心拉取不到配置,有两种情况,1是git地址配置错误,2.内容编码错误,格式错误导致拉取不到

  • 检查配置中心的git地址,用户名,密码是否有问题

  • 打开码云确认一下内容是否有问题,格式,编码

检查配置中心客户端-就是的拉取配置的微服务

  • 对比你的 spring.cloud.config.name 和 profile是否和码云上的配置文件一样,以及 label分支是否一致
  • 检查你的 spring.cloud.config.uri 写对了没有,是不是你的配置中心服务的地址
  • 检查你的依赖:spring-cloud-config-client导入没有,导入成功么有,按ctrl去点击一下你的依赖(或者删除本地厂库中的依赖重新下载)
5.2.配置文件拉取到了

如果配置文件拉取到了,服务启动的端口应该就是你配置文件的端口,但是如果启动日志还是有异常,可以把配文件配置成本地方式来排除错误:

  • 注释掉 spring-cloud-config-client包

  • 恢复本地配置文件

    如果本地配置还是启动不能,那就是其他问题,可能是配置文件内容的问题,也可能是代码问题,根据错误日志排查

十.总结

1.重点

了解每个组件的工作原理

20200320191152368

2.面试题

3.1.SpirnCloud常用组件及其作用
3.2.Eureka服务注册与发现的工作流程
3.3.Ribbon的工作流程
3.4.如何集成Feign
3.5.集成了Feign的微服务如何集成Hystrix
3.6.你们项目中用ZUUL做了什么事情
3.7.zuul如何定义一个Filter
3.8.Hystrix如何限流,有几种方式
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值