Hystrix案例准备—SpringCloud(H版)微服务学习教程(26)

Java技术 潘老师 3年前 (2021-03-26) 1398 ℃ (0) 扫码查看

本节我们主要演示我们原先的分布式系统由于某些接口被高并发访问会导致其他正常的接口也会被拖慢,甚至导致超时报错的线下。为我们引入Hystrix做准备。下面我们要准备好Hystrix案例的演示环境,这里我们将之前的项目只启用cloud-eureka-server7001,并恢复为单机版,这里就不多说了,只需要修改yml配置,即:

#单机版
defaultZone: http://eureka7001.com:7001/eureka/

接下来,我们开始新建名为cloud-provider-hystrix-payment8001模块(去除了数据库相关的配置)。

第1步:创建Module

mscloud下新建名为cloud-provider-hystrix-payment8001模块

第2步:修改pom

<?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>mscloud</artifactId>
        <groupId>com.panziye.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-provider-hystrix-payment8001</artifactId>

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

        <!--  引入自定义的api通用包,可以使用支付Payment等Entity      -->
        <dependency>
            <groupId>com.panziye.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>

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

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

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

    </dependencies>
</project>

第3步:写yml

application.yml如下:

server:
  port: 8001

spring:
  application:
    name: cloud-payment-hystrix-service

eureka:
  client:
    #表示是否将自己注册进eureka服务中心,默认true
    register-with-eureka: true
    #表示是否从EurekaServer抓取已有注册信息,默认true。单节点无所谓,集群必须设置true才能配合ribbon使用负载均衡
    fetch-registry: true
    service-url:  #指向eureka集群
      defaultZone: http://eureka7001.com:7001/eureka

第4步:主启动类

新建PaymentHystrixMain8001主启动类:

package com.panziye.springcloud;

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

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

第5步:业务类

1)新建接口PaymentService

package com.panziye.springcloud.service;

public interface PaymentService {
    String paymentOk(Long id);
    String paymentTimeout(Long id);
}

2)新建实现类:

package com.panziye.springcloud.service.impl;

import com.panziye.springcloud.service.PaymentService;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
public class PaymentServiceImpl implements PaymentService {

    //演示成功
    @Override
    public String paymentOk(Long id) {
        return "当前线程:"+Thread.currentThread().getName()+"\t paymentOk,id="+id;
    }

    //演示超时
    @Override
    public String paymentTimeout(Long id) {
        int num = 3;
        try {
            //睡眠num秒
            TimeUnit.SECONDS.sleep(num);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "当前线程:"+Thread.currentThread().getName()+"\t paymentTimeout,id="+id;
    }
}

3)新建controller
新建PaymentController

package com.panziye.springcloud.controller;

import com.panziye.springcloud.service.PaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@Slf4j
public class PaymentController {
    @Resource
    private PaymentService paymentService;

    @GetMapping(value = "/payment/ok/{id}")
    public String paymentOk(@PathVariable("id") Long id) {
        return paymentService.paymentOk(id);
    }

    @GetMapping(value = "/payment/timeout/{id}")
    public String paymentTimeout(@PathVariable("id") Long id) {
        return paymentService.paymentTimeout(id);
    }
}

第6步:测试

启动7001和8001,测试访问,我们发现:

  • 请求http://localhost:8001/payment/ok/1速度非常快(浏览器基本不转圈)
  • 请求http://localhost:8001/payment/timeout/1会等待几秒钟才有结果

1)说明我们环境搭建的没问题,接下来我们提供Jmeter测试工具对timeout请求进行压力测试,如果你没有Jmeter,请参考:

Jmeter压力测试工具下载安装与运行

文章目录 一、什么是Jmeter 二、Jmeter下载 二、Jmeter安装与运行 一、什么是Jmeter A […]

2)我们添加线程组,如下200个线程循环发送100次(即20000次并发压力测试),然后保存
Hystrix案例准备—SpringCloud(H版)微服务学习教程(26)
3)我们在该线程组下添加取样器->Http请求,修改如下:
Hystrix案例准备—SpringCloud(H版)微服务学习教程(26)
4)运行该测试后,立即再去浏览器请求之前的两个接口,发现两个请求都转圈,原先非常快的ok接口也变慢了。

原因:tomcat的默认的工作线程数被打满了, 没有多余的线程来分解压力和处理。

5)压力测式结论:上面还是服务提供者8001自己测试,假如此时外部的消费者80也来访问,那消费者只能干等,最终导致消费端80不满意,服务端8001直接被拖死。

第7步:新建消费端

我们新建名为cloud-consumer-feign-hystrix-order80消费端模块,参考cloud-consumer-feign-order80,我们的具体实现如下:
1)pom.xml,新增了hystrix依赖

<?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>mscloud</artifactId>
        <groupId>com.panziye.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-consumer-feign-hystrix-order80</artifactId>
    <dependencies>
        <!--   OpenFign     -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!--   eureka client     -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <!--  引入自定义的api通用包,可以使用支付Payment等Entity      -->
        <dependency>
            <groupId>com.panziye.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>

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

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

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

    </dependencies>

</project>

2)application.yml:

server:
  port: 80

spring:
  application:
    name: cloud-order-hystrix-service

eureka:
  client:
    #表示是否将自己注册进eureka服务中心,默认true
    register-with-eureka: false
    #表示是否从EurekaServer抓取已有注册信息,默认true。单节点无所谓,集群必须设置true才能配合ribbon使用负载均衡
    fetch-registry: true
    service-url:  #指向eureka注册中心
      defaultZone: http://eureka7001.com:7001/eureka

3)主启动OrderHystrixMain80

package com.panziye.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;

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

4)业务类
a)service接口OrderService

package com.panziye.springcloud.service;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@Component
@FeignClient(value = "CLOUD-PAYMENT-HYSTRIX-SERVICE")
public interface OrderService {

    @GetMapping(value = "/payment/ok/{id}")
    public String paymentOk(@PathVariable("id") Long id);

    @GetMapping(value = "/payment/timeout/{id}")
    public String paymentTimeout(@PathVariable("id") Long id);
}

5)新建OrderController

package com.panziye.springcloud.controller;

import com.panziye.springcloud.service.OrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@Slf4j
public class OrderController {

    @Resource
    private OrderService orderService;

    @GetMapping(value = "/consumer/payment/ok/{id}")
    public String paymentOk(@PathVariable("id") Long id) {
        String result = orderService.paymentOk(id);
        return result;
    }

    @GetMapping(value = "/consumer/payment/timeout/{id}")
    public String paymentTimeout(@PathVariable("id") Long id){
        String result = orderService.paymentTimeout(id);
        return result;
    }
}

第8步:压力测试消费端

我们在用Jmeter去压力测试timeout端口,同时浏览器访问消费端ok接口http://localhost/consumer/payment/ok/1,发现同样要么被拖慢了转圈圈,要么直接超时报错(如果你的测试没报错就加大线程数,我加到400直接超时):
Hystrix案例准备—SpringCloud(H版)微服务学习教程(26)

原因:

我们在学OpenFeign时都知道,OpenFeign默认等待响应时间为1秒,而我们的ok请求被timeout接口拖慢,导致超时报错。

结论:

正因为有上述故障或不佳表现,才有我们的降级/容错/限流等技术诞生

第9步:如何解决?

可能出现的问题:

1)超时导致服务器变慢(转圈)
超时不再等待
2)出错(宕机或程序运行出错)
出错要有兜底

解决:
  • 对方服务(8001)超时了,调用者(80)不能一直卡死等待,必须有服务降级
  • 对方服务(8001 ) 宕机了,调用者(80)不能一直卡死等待,必须有服务降级
  • 对方服务(8001 )正常,调用者(80)自己出故障或有自我要求(自己的等待时间小于服务提供者),自己处理降级

版权声明:本站文章,如无说明,均为本站原创,转载请注明文章来源。如有侵权,请联系博主删除。
本文链接:https://www.panziye.com/java/2813.html
喜欢 (1)
请潘老师喝杯Coffee吧!】
分享 (0)
用户头像
发表我的评论
取消评论
表情 贴图 签到 代码

Hi,您需要填写昵称和邮箱!

  • 昵称【必填】
  • 邮箱【必填】
  • 网址【可选】