Netflix Hystrix 入门实例

本章我们将通过一个简单的实例来介绍 Hystrix 的具体用法,该实例基于“Netflix Hystrix 准备工作”章节提供的服务。

添加 Maven 依赖

在使用 Hystrix 之前,我们需要先添加 maven 依赖。项目完整 pom.xml 内容如下:

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.11.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.hxstrive</groupId>
    <artifactId>hystrix-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>hystrix-demo</name>
    <description>hystrix-demo</description>

    <properties>
        <java.version>8</java.version>
        <spring-cloud.version>Hoxton.SR8</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

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

        <!-- hystrix依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.83</version>
        </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>
        <finalName>service-demo</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

application.yml 配置

添加 application.yml 配置文件,在该配置文件中配置注册中心地址。内容如下:

server:
  port: 8080

spring:
  application:
    name: hystrix-demo

## 服务地址
eureka:
  client:
    enabled: true
    service-url:
      # 注册中心路径,表示我们向这个注册中心注册服务,如果向多个注册中心注册,用“,”进行分隔
      defaultZone: http://localhost:8761/eureka
  instance:
    hostname: localhost
    # 心跳间隔5s,默认30s。每一个服务配置后,心跳间隔和心跳超时时间会被保存在server端,
    # 不同服务的心跳频率可能不同,server端会根据保存的配置来分别探活
    lease-renewal-interval-in-seconds: 5
    # 心跳超时时间10s,默认90s。从client端最后一次发出心跳后,
    # 达到这个时间没有再次发出心跳,表示服务不可用,将它的实例从注册中心移除
    lease-expiration-duration-in-seconds: 10

logging:
  level:
    root: info
    # 设置 RestTemplate 日志级别为 DEBUG
    org.springframework.web.client.RestTemplate: DEBUG

RestTemplateConfig 配置

添加 RestTemplate 配置,并且在该 restTemplate() 上面添加 @LoadBalanced 注解。开启负载均衡策略功能,代码如下:

package com.hxstrive.hystrix_demo.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

/**
 * RestTemplate 配置类
 * @author hxstrive.com
 */
@Configuration
public class RestTemplateConfig {

    // 配置一个 RestTemplate
    @Bean
    // 添加该注解后,可以使得 RestTemplate 拥有负载均衡能力
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

}

HystrixDemoApplication 启动类

启动类是一个简单的 Spring Boot 启动类,但是该类使用 @EnableCircuitBreaker 注解开启断路器功能。代码如下:

package com.hxstrive.hystrix_demo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 入库类
 * @author hxstrive.com
 */
@RestController
@SpringBootApplication
// 启用断路器
@EnableCircuitBreaker
public class HystrixDemoApplication {

    @Value("${spring.application.name}")
    private String appName;

    @Value("${server.port}")
    private String appPort;

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

    @GetMapping("/")
    public String index() {
        return "appName=" + appName + ", appPort=" + appPort;
    }

}

Demo1Controller 控制器

一个简单的控制器,提供 /demo1/getAllUsers 方法,该方法调用 UserService 服务的服务,然后调用远程服务。代码如下:

package com.hxstrive.hystrix_demo.controller;

import com.hxstrive.hystrix_demo.dto.CommonReturn;
import com.hxstrive.hystrix_demo.entity.User;
import com.hxstrive.hystrix_demo.service.UserService;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * 简单控制器
 * @author hxstrive.com
 */
@RestController
@RequestMapping("/demo1")
public class Demo1Controller {

    @Autowired
    private UserService userService;

    @GetMapping("/getAllUsers")
    @HystrixCommand(fallbackMethod = "fallback")
    public CommonReturn<List<User>> getAllUsers() {
        return userService.getAllUsers();
    }

    /**
     * 当服务调用失败时,调用此方法
     */
    private CommonReturn<List<User>> fallback() {
        return CommonReturn.fail("网络出现问题,调用 fallback 方法");
    }

}

UserService 服务类

用户服务类,通过服务名(见 Eureka)调用实际服务,代码如下:

package com.hxstrive.hystrix_demo.service;

import com.alibaba.fastjson.JSONObject;
import com.hxstrive.hystrix_demo.dto.CommonReturn;
import com.hxstrive.hystrix_demo.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.List;

/**
 * 用户服务
 * @author hxstrive.com
 * @since 1.0.0  2024/11/13 15:20
 */
@Service
public class UserService {

    @Autowired
    private RestTemplate restTemplate;

    public CommonReturn<List<User>> getAllUsers() {
        // 如果你要实现负载均衡,调用地址不能写具体主机地址
        // 需要将主机地址替换成对于服务的服务名,即 spring.application.name 指定的值
        // 你也可以到 Eureka server 中去查看
        String url = "http://SERVICE-DEMO/user/getAllUsers";
        // 使用 ParameterizedTypeReference 来保留泛型信息
        ParameterizedTypeReference<CommonReturn<List<User>>> typeRef = new ParameterizedTypeReference<CommonReturn<List<User>>>() {};
        ResponseEntity<CommonReturn<List<User>>> responseEntity = restTemplate.exchange(url, HttpMethod.GET, null, typeRef);
        return responseEntity.getBody();
    }

    // 调用根据用户ID获取用户信息的接口
    public CommonReturn<User> getUserById(Long id) {
        String url = "http://SERVICE-DEMO/user/getUserById?id=" + id;
        // 使用 ParameterizedTypeReference 来保留泛型信息
        ParameterizedTypeReference<CommonReturn<User>> typeRef = new ParameterizedTypeReference<CommonReturn<User>>() {};
        ResponseEntity<CommonReturn<User>> responseEntity = restTemplate.exchange(url, HttpMethod.GET, null, typeRef);
        return responseEntity.getBody();
    }

    // 调用创建新用户的接口
    public CommonReturn<User> createUser(User user) {
        String url = "http://SERVICE-DEMO/user/createUser";
        HttpEntity<User> requestEntity = new HttpEntity<>(user);
        // 使用 ParameterizedTypeReference 来保留泛型信息
        ParameterizedTypeReference<CommonReturn<User>> typeRef = new ParameterizedTypeReference<CommonReturn<User>>() {};
        ResponseEntity<CommonReturn<User>> responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, typeRef);
        return responseEntity.getBody();
    }

    // 调用更新用户信息的接口
    public CommonReturn<User> updateUser(Long id, User updatedUser) {
        String url = "http://SERVICE-DEMO/user/updateUser?id=" + id;
        HttpEntity<User> requestEntity = new HttpEntity<>(updatedUser);
        // 使用 ParameterizedTypeReference 来保留泛型信息
        ParameterizedTypeReference<CommonReturn<User>> typeRef = new ParameterizedTypeReference<CommonReturn<User>>() {};
        ResponseEntity<CommonReturn<User>> responseEntity = restTemplate.exchange(url, HttpMethod.PUT, requestEntity, typeRef);
        return responseEntity.getBody();
    }

    // 调用删除用户的接口
    public CommonReturn<String> deleteUser(Long id) {
        String url = "http://SERVICE-DEMO/user/deleteUser?id=" + id;
        // 使用 ParameterizedTypeReference 来保留泛型信息
        ParameterizedTypeReference<CommonReturn<String>> typeRef = new ParameterizedTypeReference<CommonReturn<String>>() {};
        ResponseEntity<CommonReturn<String>> responseEntity = restTemplate.exchange(url, HttpMethod.DELETE, null, typeRef);
        return responseEntity.getBody();
    }

}

其他类

CommonReturn

通用返回对象实体,代码如下:

package com.hxstrive.hystrix_demo.dto;

import lombok.Data;

/**
 * 通用返回对象
 * @author hxstrive.com
 */
@Data
public class CommonReturn<T> {
    private int code;
    private String message;
    private T data;
    private String appName;
    private String port;

    public static <T> CommonReturn<T> success(T data) {
        CommonReturn<T> commonReturn = new CommonReturn<>();
        commonReturn.setCode(1);
        commonReturn.setData(data);
        return commonReturn;
    }

    public static <T> CommonReturn<T> success() {
        return success(null);
    }

    public static <T> CommonReturn<T> fail(String message) {
        CommonReturn<T> commonReturn = new CommonReturn<>();
        commonReturn.setCode(0);
        commonReturn.setMessage(message);
        return commonReturn;
    }

    public CommonReturn<T> ext(String appName, String port) {
        this.setAppName(appName);
        this.setPort(port);
        return this;
    }

}

User

用户实体类,代码如下:

package com.hxstrive.hystrix_demo.entity;

import lombok.Builder;
import lombok.Data;
import lombok.ToString;

/**
 * 用户实体
 * @author hxstrive.com
 */
@Data
@Builder
@ToString
public class User {
    private Long id;
    private String name;
    private Integer age;

    public User() {}

    public User(Long id, String name, Integer age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }
}

启动&验证

此时,启动 HystrixEurekaServerApplication、HystrixServiceUserApplication 和 HystrixDemoApplication 服务,成功启动后服务状态如下图:

image.png

此时,浏览器访问 http://localhost:8080/demo1/getAllUsers 地址,效果如下图:

image.png

如果我们尝试将“HystrixServiceUserApplication”服务停止,再次访问 http://localhost:8080/demo1/getAllUsers 地址,效果如下图:

image.png

通过上图,已经触发了 @HystrixCommand(fallbackMethod = "fallback") 注解 fallbackMethod 方法配置的 fallback 方法。fallback 方法代码如下:

/**
 * 当服务调用失败时,调用此方法
 */
private CommonReturn<List<User>> fallback() {
    return CommonReturn.fail("网络出现问题,调用 fallback 方法");
}


说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
公众号