SpringCloud+Eureka初识+Ribbon+Feign+Hystrix(服务熔断,服务降级)+hashbroad

Posted MyBlogs-joyiyii

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringCloud+Eureka初识+Ribbon+Feign+Hystrix(服务熔断,服务降级)+hashbroad相关的知识,希望对你有一定的参考价值。

​Eureka注册中心

 1.导包

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-eureka-server -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
            <version>2.1.2.RELEASE</version>
            <exclusions>
                <exclusion>
                    <groupId>javax.servlet</groupId>
                    <artifactId>servlet-api</artifactId>
                </exclusion>
            </exclusions>

        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka -->

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

 

2.配置文件

server:
  port: 7001
eureka:
  instance:
    hostname: localhost  #eureka服务器的实例
  client:
    register-with-eureka: false
    fetch-registry: false

    service-url:
      defaultZone: http://$eureka.instance.hostname:$server.port/eureka/

 

3.主启动类: 

package com.lian.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

//启动之后,访问http://localhost:7001/
@SpringBootApplication
@EnableEurekaServer//服务端的启动类
public class EurekaServer_7001 
    public static void main(String[] args) 
        SpringApplication.run(EurekaServer_7001.class,args);
    

 

报错1:Caused by: java.lang.ClassNotFoundException: org.springframework.boot.actuate.health.CompositeHealthContributor

 

解决方法:原因是eureka依赖包的版本和我的spring-boot-starter-web冲突了,讲eureka版本降成2.1.2.RELEASE

 报错2:集成eureka报错 main] org.apache.catalina.core.ContainerBase : A child container failed duri

2023-05-20 10:39:31.626 ERROR 22292 --- [           main] org.apache.catalina.core.ContainerBase   : A child container failed during start

 解决方法:原因是jar包冲突,排除依赖的包servlet-api

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            <version>3.1.0</version>
            <exclusions>
                <exclusion>
                    <groupId>javax.servlet</groupId>
                    <artifactId>servlet-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

 

 

访问成功:

 

 

 

 

 

在provider配置注册中心

上面我们已经成功连接上了eureka的服务中心,下面我们需要在启动provider模块时使其自动注册进eureka的服务中心

1.导包:

      <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            <version>2.1.2.RELEASE</version>
        </dependency>
<!--        #监视器-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

 

2.配置类

这里info要导入依赖spring-boot-starter-actuator才能使用

 

3.主启动类开启注解

 

 

测试访问:先启动eureka-7001,再启动provider-8001

成功拿到

 

 


Eureka的自我保护机制:

当某个服务断电了等原因不能用了,eruka不会立即清理数据,依旧会对微服务的信息进行保存

 

 

 扩展:在公司团队协作中:添加微服务列表清单

 测试结果:成功访问

 

 

Eureka集群配置

 

CAP原则:C一致性 A可用性 P容错性

 

 

zookeeper保证一致性和容错性

 eureka保证可用性和容错性

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

 

客户端负载均衡及Ribbon

 

 

 

 

 测试1.保证消费者模块成功注册到eureka注册中心

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">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.example</groupId>
        <artifactId>springcloud</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>springcloud-consumer-dept-80</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            <version>2.1.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>springcloud-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

</project>

 

 

2.配置文件 application.yaml

 

server:
  port: 80
  #配置 ure
eureka:
  client:
    register-with-eureka: false
    service-url:
      defaultZone: http://eureka7003.com:7003/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7001.com:7001/eureka/

 

3.配置类添加注解开启Ribbon

 3.controller类请求

package com.lian.springcloud.controller;

import com.lian.springcloud.pojo.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.List;

@RestController
public class DeptConsumerController 
    //消费层,不应该有service,消费者只和前端打交道
    //所以要调用方法,通过RestTemplate..供我们调用
    @Autowired
    private RestTemplate restTemplate;
    //通过ribbon实现,我们这里应该是一个变量
    private static final String REST_URL_PREFIX="http://SPRINGCLOUD-PROVIDER-DEPT";
//    private static final String REST_URL_PREFIX="http://localhost:8080";
    @RequestMapping("/consumer/dept/add")
    //
    public Boolean add(Dept dept)
        return restTemplate.postForObject(REST_URL_PREFIX+"/dept/add",dept,Boolean.class);
    
    ////通过id获取用户
    @RequestMapping("/consumer/dept/get/id")
    public Dept get(@PathVariable("id")int id)
        return restTemplate.getForObject(REST_URL_PREFIX+"/dept/get/"+id,Dept.class);
    

    //查询全部用户
    @RequestMapping("/consumer/dept/list")
    public List<Dept> list()
        return restTemplate.getForObject(REST_URL_PREFIX+"/dept/list",List.class);
    

4.主启动类开启euraka注解

  5.测试顺序:

开启eurka的三个注册中心,开启provider,开启consumer

报错1:

 

springcloud报错Caused by: java.lang.NoClassDefFoundError: org/hibernate/validator/internal/engine/DefaultClockProvider

原因是忘记在主启动类开启注解

 

报错2:

2023-05-20 14:29:39.083 ERROR 9264 --- [p-nio-80-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.web.client.HttpClientErrorException$MethodNotAllowed: 405 null] with root cause

 

org.springframework.web.client.HttpClientErrorException$MethodNotAllowed: 405 null

 原因是,我在前端访问的是http://localhost/consumer/dept/list,但是这个方法我在模块provider的dao底层用的是post请求,解决方法是把底层的post改成get

测试2:Ribbon轮循算法

Ribbon默认的是轮循算法,即假设我们有多个provider服务器,消费者通过80端口访问时候,根据轮循算法去访问一个provider服务器,如消费者相同的url请求,第一次访问到的是1号provider,消费者刷新一次页面,访问到的是2号provider,消费者再刷新一次页面,访问到的又是1号provider,如此循环即为轮循。轮询算法如果一个provider崩了,那么只会对其他provider进行轮循

实现步骤:

1.首先我们建了三个不同数据库,三个库都有一张dept表,这三张表只有字段db_source不同

 

2.接着我们创建了2号provider和3号provider,同时更改配置的yaml文件分别连接2号数据库和3号数据库,

 

启动测试:

启动eruka7001注册中心-->启动provider1号,2号,3号-->启动consumer模块-->访问“http://localhost/consumer/dept/get/1”

开始我并没有看到轮循效果,因为我的DeptMapper.xml文件中,把要查询的数据库定死了,如下把first删掉即可

 

 再刷新:可以看到现在是second数据库了

 

测试3:实现Ribbon自定义算法

负载均衡默认算法是轮循,但是我们可以自定义我们自己的算法,官方文档明确指出我们不应该在主启动类的同一个包下自定义ribbon算法,应该另开一个包

 

 步骤:

1.创建配置类LianRule,思想是实现接口IRule,记得注解

@Configuration
public class LianRule 
    @Bean
    public IRule myRule()//我们要自定义自己的ribbon算法,只需要实现官方的IRule接口
        //LianRandomRule()对应官方的RandomRule()
        return new LianRandomRule();
    

 

2.具体实现类LianRandomRule,

LianRandomRule()对应官方的RandomRule()
package com.lian.myrule;

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

import java.util.List;
import java.util.concurrent.ThreadLocalRandom;


public class LianRandomRule extends AbstractLoadBalancerRule 

    /**
     * Randomly choose from all living servers
     */
//    @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE")
    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 = chooseRandomInt(serverCount); //生成随机数
            server = upList.get(index);

            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;
    
    protected int chooseRandomInt(int serverCount) 
        return ThreadLocalRandom.current().nextInt(serverCount);
    

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

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

    

3.参考官方文档在主启动类添加注解

 4.启动测试:启动eruka7001注册中心-->启动provider1号,2号,3号-->启动consumer模块-->访问“http://localhost/consumer/dept/list”

不断刷新,随机连接不同的provider,使用的数据库随机变化

 Feign介绍:

feign是面向接口的编程架构,而前面的resttemplate是restful风格架构;OpenFeign 底层内置了 Ribbon 框架,因此Feign本质还是调用了Ribbon 负载均衡。

 

 

Feign原理

例子演示:我们靠Frign的方式,实现前端获取数据库用户记录 

步骤:

1.给springcloud-api导依赖

 

 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
            <version>2.1.2.RELEASE</version>
        </dependency>

 

 

 

开始的报错:

Caused by: java.io.FileNotFoundException: class path resource [org/springframework/boot/autoconfigure/web/ServerPropertiesAutoConfiguration.class] cannot be opened because it does not exist

原因是:导入的feign依赖和原本springboot的依赖版本冲突,换成下面这个就可以了

 

 2.在springcloud-api创建服务接口

 3.为了方便对比,新建一个consumer-dept-feign模块,给consumer-dept-feign模块导入依赖

<?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">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.example</groupId>
        <artifactId>springcloud</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <groupId>com.lian</groupId>
    <artifactId>springcloud-consumer-dept-feign</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-feign -->
<!--        替代ribbon-->
        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-openfeign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
            <version>2.1.2.RELEASE</version>
        </dependency>


        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            <version>2.1.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>springcloud-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

</project>

 

4.controller类方法:

 4.主启动类

 

5.测试访问成功

 服务降级:

客户端从整体网站请求负载考虑,当某个服务熔断或者关闭之后,服务将不再被调用,此时在客户端,我们可以准备一个FallbackFactory,返回一个默认的值,整体的服务水平下降了,但是系统还能正常运行

 1.springcloud-api写一个降级类

 

2.在springcloud-api模块的service目录下的接口要绑定我们写的降级类

 3.在消费者模块的配置文件中增加服务降级的配置

 

 

4.启动测试

未关闭后端的provider时若前端访问空白的用户id,则网页没有报错只是空白

关闭后端的provider后的页面提示

 

 小总结:这几天学了:

eureka是注册中心,服务提供者和消费者都需要去注册中心注册,关于分布式系统的CAP理论告诉我们:一个分布式系统只能同时满足其中两个。eurka保证的是AP,就是可用性和容错性,我们可以有多个注册中心,防止单个注册中心崩溃;我们可以搭建集群,多个注册中心,多个provider提供者,但是消费者只能一个;

erueka还有自我保护机制,这个机制使得加入有某个服务器断电或者故障了,eureka还是能够保留原来的数据不丢失。

Ribbon和负载均衡,我们通过Ribbon将用户的访问交给到不同的provider处理,类似于分流,防止服务器压力过大,Ribbon本身默认的是轮循算法,我们可以通过实现接口IRule来自定义我们的算法,但是需要注意的是官网提到了不能把自定义的轮循算法放在和主启动类同一个包下,因此我们需要自己建一个包编写我们自定义算法同时把它注册到spring容器里

Feign:开发人员习惯面向接口编程而来,是为了我们在消费者的controller更方便调用,而不使用RestTemplate模板类,实现过程:导包(注意版本)我们需要在api内定义服务接口,同时在接口上开启注解@FeignClient,在消费者模块也要导入feign依赖包,编写更简便的controller方法然后在主启动类开启支持feign注解

服务熔断:服务器之间,依赖包之间等可能存在相互调用的关系,即provider1需要调用provider2,provider2需要调用provider3,当provider2出现故障后,任务无法正常进行,后面用户的请求全部阻塞在了provider1,同时容易级联其他的问题,在高并发情况下非常容易导致整个系统雪崩,因此我们通过Hystrix技术栈有效处理服务熔断的情况。

服务降级:有时候单个服务器收到非常非常多用户的访问,而另些服务器基本无人问津,那么我们就关闭这些无人问津的服务器,把更多资源分配给火爆的服务器,来降低受很多人访问的服务器的压力,我们需要在springcloud-api的service目录写降级类实现接口FallbackFactory,我使用的是feign进行的测试,所以在我原本写的接口DeptClientService需要通过注解绑定降级类,测试:开启eruaka7001->开启provider8001->开启 FeignDeptConsumer_80  当我们关闭provider8001查看页面是否有打印出提示信息。

 

feign心得

Feign是一个声明式的WebService客户端。使用Feign能让编写WebService客户端更加简单,它的使用方法是定义一个接口,然后在接口上添加注解,同时也支持JAX-RS标准的注解。Feign也支持可插拔式的编码器和解码器。SpringCloud对Feign进行了封装,使其支持SpringMVC标准注解和HttpMessageConverters。Feign可以与Eureka和Ribbon组合使用以支持负载均衡。

如何使用Feign?

添加Feign的依赖

<dependency>

<groupId>org.springframework.cloud</groupId>

<artifactId>spring-cloud-starter-feign</artifactId>

</dependency>


      <dependency>

          <groupId>org.springframework.cloud</groupId>

          <artifactId>spring-cloud-starter-eureka</artifactId>

      </dependency>

启动入口加入:

@SpringBootApplication

@EnableEurekaClient

@EnableFeignClients


最后别忘了最关键的这个.这个总是没想到。千万别忘了。hehe 

            <dependency>

                <groupId>org.springframework.cloud</groupId>

                <artifactId>spring-cloud-dependencies</artifactId>

                <version>Dalston.SR3</version>

                <type>pom</type>

                <scope>import</scope>

            </dependency>


定义的接口中 加入注解和请求服务:@FeignClient(name="service的名称")

接口方法加入请求方法:@RequestMapping(method = RequestMethod.GET, value = "/hello")

至于service基本没什么特别的。最要是client端


service 只要放入@RestController 以及@RequestMapping 及可




@FeignClient注解中的stores属性可以是一个任意字符串,如果与Eureka组合使用,则stores应为Eureka中的服务名,Feign用它来创建一个Ribbon负载均衡器。也可以通过url属性来指定一个地址,可以是完整的URL,也可以是一个主机名。标注了@FeignClient注解的接口,在ApplicationContext中的Bean实例名是这个接口的全限定名,同时这个Bean还有一个别名,为Bean名+FeignClient。

覆盖Feign的默认配置

SpringCloud对Feign的封装中一个核心的概念就是客户端要有一个名字。每个客户端随时可以向远程服务发起请求,并且每个服务都可以像使用@FeignClient注解一样指定一个名字。SpringCloud会将所有的@FeignClient组合在一起创建一个新的ApplicationContext,并使用FeignClinetsConfiguration对Clients进行配置。配置中包括编码器、解码器和一个feign.Contract。

SpringCloud允许你通过configuration属性完全控制Feign的配置信息,这些配置比FeignClientsConfiguration优先级要高


这样就完成了一个基本的feign项目搭建了





本文出自 “13341806” 博客,转载请与作者联系!

以上是关于SpringCloud+Eureka初识+Ribbon+Feign+Hystrix(服务熔断,服务降级)+hashbroad的主要内容,如果未能解决你的问题,请参考以下文章

初识SpringCloud

feign心得

SpringCloud学习笔记-p1(服务拆分&远程调用&Eureka注册中心&Ribbon负载均衡)

Spring Cloud——什么是微服务?

springcloud费话之Eureka接口调用(feign)

springcloud费话之Eureka集群