Netflix Hystrix 准备工作

点击下载教程项目代码:netflix_hystrix_demo.zip

为了后续更好的验证 Netflix Hystrix,我们需要提前准备注册中心 Eureka Server、用户(user)服务和 Hystrix 示例。

整体项目结构如下图:

dbfa15b90114378370a37951432f3278_1731481479370-115f27b8-ed57-481e-8e11-7dbc5078c02c_x-oss-process=image%2Fformat%2Cwebp.png

Eureka 服务

Eureka 是 Netflix 开源的一款基于 REST 的服务发现组件,主要用于在 AWS 云中实现服务的注册与发现,它在微服务架构中扮演着重要的角色,点击学习Netflix Eureka 教程

项目结构如下图:

939ebf9d87edf7282ad02a4f04e07733_1731481558687-1078ad51-e869-4b3e-940b-e033175cad0a_x-oss-process=image%2Fformat%2Cwebp.png

pom.xml 依赖

在 pom.xml 添加如下 maven 依赖:

<?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-eureka-server</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>hystrix-eureka-server</name>
    <description>hystrix-eureka-server</description>

    <properties>
        <java.version>8</java.version>
        <spring-cloud.version>Hoxton.SR8</spring-cloud.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</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>
    </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>netflix-eureka-server</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 配置

配置 Eureka Server 注册中心,配置文件内容如下:

server:
  port: 8761

spring:
  application:
    name: eureka-server

eureka:
  instance:
    hostname: localhost
  client:
    # 向注册中心注册自己,false 表示禁用eureka客户端的注册功能
    register-with-eureka: false
    # 取消检索服务
    fetch-registry: false
  server:
    # 开启注册中心的保护机制,默认是开启
    enable-self-preservation: true
    # 设置保护机制的阈值,默认是0.85
    renewal-percent-threshold: 0.5

启动类

编写 Eureka Server 启动类,需要在启动类上添加 @EnableEurekaServer 注解,完整代码如下:

package com.hxstrive.netflixeurekaserver;

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

/**
 * 启动 Eureka Server 注册中心
 * @author hxstrive
 */
@SpringBootApplication
@EnableEurekaServer
public class HystrixEurekaServerApplication {

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

}

启动注册中心,访问 http://localhost:8761,效果如下图:

a78fef30a9e34cb9600c08f02b341212_1731414869414-d6de5b1d-c799-4584-a1cd-50bed6aca948.png

提供 User 服务

User 服务是一个简单的 Spring Boot 服务,该服务提供了对用户信息简单 CRUD 操作。

项目结构如下图:

c0848ae933d94f123a931aeb36800480_1731481657866-9d5b25e4-0b02-4b5f-92ff-a174930ee3d7.png

pom.xml 依赖

由于 User 服务需要将服务注册到 Eureka Server 注册中心,因此需要在 pom.xml 中添加 Eureka Clinet 的 maven 依赖。如下:

<?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-service-user</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>hystrix-service-user</name>
    <description>hystrix-service-user</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>

        <!-- 文件上传 -->
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.4</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>
        <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 配置

在该配置文件中配置 Eureka Server 的注册中心信息,配置如下:

server:
  port: 8090
  compression:
    # 启用压缩功能
    enabled: true
    # 配置要压缩的 mime-type 列表
    mime-types: application/json,text/html,text/xml,text/plain
    # 设置最小响应大小,低于该大小则不开启压缩,单位字节
    min-response-size: 1

spring:
  application:
    name: service-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

控制器类

该控制器通过静态代码块初始化用户信息,使用 List 存放这些用户信息,为了演示方便,不使用数据库。具体代码如下:

package com.hxstrive.service_demo.controller;

import com.hxstrive.service_demo.dto.CommonReturn;
import com.hxstrive.service_demo.entity.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;

/**
 * 用户控制器
 * @author hxstrive.com
 */
@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {
    private final static List<User> USERS = new ArrayList<>();

    static {
        USERS.add(User.builder().id(1L).name("Tom").age(20).build());
        USERS.add(User.builder().id(2L).name("Helen").age(30).build());
        USERS.add(User.builder().id(3L).name("Bill").age(40).build());
    }

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

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


    // GET 请求,获取所有用户信息
    @GetMapping("/getAllUsers")
    public CommonReturn<List<User>> getAllUsers() {
        log.info("getAllUsers()");
        return CommonReturn.success(USERS).ext(appName, appPort);
    }

    // GET 请求,根据用户 ID 获取用户信息
    @GetMapping("/getUserById")
    public CommonReturn<User> getUserById(@RequestParam("id") Long id) {
        log.info("getUserById() id={}", id);
        return USERS.stream()
                .filter(user -> user.getId().equals(id))
                .findFirst()
                .map(u -> CommonReturn.success(u).ext(appName, appPort))
                .orElse(CommonReturn.fail("用户不存在"));
    }

    // POST 请求,创建新用户
    @PostMapping("/createUser")
    public CommonReturn<User> createUser(@RequestBody User user) {
        log.info("createUser() user={}", user);
        USERS.add(user);
        return CommonReturn.success(user).ext(appName, appPort);
    }

    // PUT 请求,更新用户信息
    @PutMapping("/updateUser")
    public CommonReturn<User> updateUser(@RequestParam("id") Long id, @RequestBody User updatedUser) {
        log.info("updateUser() id={}, updateUser={}", id, updatedUser);
        for (int i = 0; i < USERS.size(); i++) {
            if (USERS.get(i).getId().equals(id)) {
                USERS.set(i, updatedUser);
                return CommonReturn.success(updatedUser).ext(appName, appPort);
            }
        }
        return CommonReturn.fail("更新用户信息失败");
    }

    // DELETE 请求,删除用户
    @DeleteMapping("/deleteUser")
    public CommonReturn<String> deleteUser(@RequestParam("id") Long id) {
        log.info("deleteUser() id={}", id);
        USERS.removeIf(user -> user.getId().equals(id));
        return CommonReturn.success("删除成功").ext(appName, appPort);
    }
}

其他类

CommonReturn.java

通用返回对象:

package com.hxstrive.service_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.java

用户实体类:

package com.hxstrive.service_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;
}

BusinessException.java

业务异常类:

package com.hxstrive.service_demo.exception;

/**
 * 业务异常
 * @author hxstrive.com
 */
public class BusinessException extends RuntimeException {

    public BusinessException() {
    }

    public BusinessException(String message) {
        super(message);
    }

    public BusinessException(String message, Throwable cause) {
        super(message, cause);
    }

    public BusinessException(Throwable cause) {
        super(cause);
    }

}

GlobalExceptionHandler.java

全局异常处理类:

package com.hxstrive.service_demo.handler;

import com.hxstrive.service_demo.dto.CommonReturn;
import com.hxstrive.service_demo.exception.BusinessException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * 全局异常处理
 * @author hxstrive.com
 * @since 1.0.0  2024/10/23 9:28
 */
@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(BusinessException.class)
    @ResponseBody
    public CommonReturn<?> handleBusinessException(BusinessException e) {
        return CommonReturn.fail(e.getMessage());
    }

    @ExceptionHandler(Exception.class)
    @ResponseBody
    public CommonReturn<?> handleException(Exception e) {
        return CommonReturn.fail("服务异常,稍后再试");
    }

}

启动类

编写 User 服务的启动类,在该类上添加 @EnableEurekaClient 注解开启 Eureka Client 功能。代码如下:

package com.hxstrive.service_demo;

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

/**
 * 入口
 * @author Administrator
 */
@RestController
@SpringBootApplication
public class HystrixServiceUserApplication {

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

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


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


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

}

在 HystrixServiceUserApplication 类中,仅仅提供了一个 / 服务。该服务将返回当前服务的名称和端口。

启动 User 服务,访问 Eureka 服务,如下图:

d28f482b9e9ac035b62a76647e623791_1731416256634-6175a390-00ac-4db2-ad25-1a8d51dbd51c.png

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