【SpringCloud】Eureka的使用

3. Eureka

3.1 Eureka 介绍

Eureka主要分为两个部分:

EurekaServer:

作为注册中心Server端,向微服务应用程序提供服务注册,发现,健康检查等能力。

EurekaClient:

服务提供者,服务启动时,会向 EurekaServer 注册自己的信息 (IP,端口,服务信息

等),Eureka Server 会存储这些信息。

3.2 搭建 Eureka 服务

Eureka 是一个单独的服务,所以咱们要手动搭建出 Eureka 服务器。

这里使用父子项目来搭建 Eureka 服务,先创建一个 spring-cloud-demo 的父项目:

后续所有的项目创建都在这个父项目中创建子项目。

直接复制下面 spring-cloud-demo 的 pom.xml 文件

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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>spring-cloud-demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>

    <modules>

    </modules>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.1.6</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <java.version>17</java.version>
        <spring-cloud.version>2022.0.3</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </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>
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>${mybatis.version}</version>
            </dependency>
            <dependency>
                <groupId>com.mysql</groupId>
                <artifactId>mysql-connector-j</artifactId>
                <version>${mysql.version}</version>
            </dependency>
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter-test</artifactId>
                <version>${mybatis.version}</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

pom.xml 关键部分解析:

  1. <modules>
    定义子模块(当前为空),用于多模块项目管理。所有子模块需在此声明,Maven 会按顺序构建。
  2. <parent>
    继承 Spring Boot 的默认配置(如插件、依赖管理),版本为 3.1.6,简化项目配置。
  3. <properties>
    统一定义变量,便于维护版本:
    • Java 版本:17
    • Spring Cloud:2022.0.3
  4. <dependencies>
    当前仅引入 Lombok(代码简化工具),<optional>true</optional> 表示不传递依赖给子模块。
  5. <dependencyManagement>
    统一管理依赖版本 ,子模块引用时无需指定版本:
    • 引入 Spring Cloud 全家桶的版本控制
    • 锁定 MyBatis 和 MySQL 驱动版本
    • 测试用 MyBatis 依赖(仅测试范围生效)

记得删除掉父工程的 src 目录,因为后续不会在父工程中写代码。

有了上述操作,咱们的父工程就创建好了,接下来开始创建子工程。

下面开始在父工程底下创建一个子模块,也就是用这个子模块去搭建 Eureka 服务:

创建子模块项目名为 eureka-server:

此时点击 Create 后,就会发现 spring-cloud-demo 目录下出现了 eureka-server 子项目,同时在父项目的 pom.xml 中的 modules 里出现了咱们子模块的名字

在子项目 eureka-server 的 pom.xml <dependencies> </dependencies> 中引入 eureka-server 依赖:

xml 复制代码
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>

在子项目 eureka-server 的 pom.xml <project> </project> 加入项目构件插件

xml 复制代码
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

记得点击右上角 maven 的刷新,或者打开 maven 面板点击 Reload ALL Maven Project 按钮重新加载下依赖。

由于创建的子项目是一个空的项目,所以需要手动创建好对应的包和子模块的启动类:

EurekaServerApplication.class 代码如下:

java 复制代码
package com.zlcode.eureka;

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

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

编写配置文件,这里需要手动在 resoureces 中创建该项目的配置文件 papplication.yml

yaml 复制代码
# Eureka相关配置
# Eureka 服务
server:
  port: 10010
spring:
  application:
    name: eureka-server
eureka:
  instance:
    hostname: localhost
  client:
    # 表示是否从Eureka Server获取注册信息,默认为true.因为这是一个单点的Eureka Server,
    # 不需要同步其他的Eureka Server节点的数据,这里设置为false
    fetch-registry: false
    # 表示是否将自己注册到Eureka Server,默认为true.由于当前应用就是Eureka Server,故而设置为false.
    register-with-eureka: false
    service-url:
      # 设置Eureka Server的地址,查询服务和注册服务都需要依赖这个地址
     defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

有了上述的操作,就能执行 eureka-server 项目中的 main 方法了。

在浏览器中输入 http://127.0.0.01:10010 就能发现此时 eureka-server 已经成功启动了。

3.3 创建cook服务和waiter服务

接下来再来创建两个子项目,分别是 厨师(cook) 和 服务员(waiter),厨师需要让服务员上菜。

创建厨师服务和服务员服务跟创建 Eureka 项目一致,创建的流程这里就省略了,也就是在 spring-cloud-demo 目录下创建厨师子模块和服务员子模块。
### 3.3.1 cook-service 模块配置代码

cook-service 的 pom.xml 文件如下:

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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.example</groupId>
        <artifactId>spring-cloud-demo</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>cook-service</artifactId>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

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

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

cook-service 的 application.yml 文件如下:

yaml 复制代码
server:
  port: 8080
spring:
  application:
    name: cook-service # 添加服务器名称
#Eureka Client
eureka:
  client:
    service-url:
      # eureka 地址
      defaultZone: http://127.0.0.1:10010/eureka/

cook-service 的 启动类 文件如下:

java 复制代码
package com.zlcode.cook;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

3.3.2 waiter-service 模块配置代码

waiter-service 的 pom.xml 文件如下:

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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.example</groupId>
        <artifactId>spring-cloud-demo</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>waiter-service</artifactId>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

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

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>

waiter-service 的 application.yml 文件如下:

yaml 复制代码
server:
  port: 9090
spring:
  application:
    name: waiter-service # 添加服务器名称
#Eureka Client
eureka:
  client:
    service-url:
      # eureka 地址
      defaultZone: http://127.0.0.1:10010/eureka/

waiter-service 的 启动类 文件如下:

java 复制代码
package com.zlcode.waiter;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

3.3.3 cook 实体类

将 CookInfo 创建在 cookie-service/*/cook/model/目录下:

java 复制代码
package com.zlcode.cook.model;

import lombok.Data;

@Data
public class CookInfo {
    private Integer cookId;
    private String cookieName;
}

3.3.4 waiter 实体类

将 WaiterInfo 创建在 waiter-service/*/waiter/model/目录下:

java 复制代码
package com.zlcode.waiter.model;

import lombok.Data;

@Data
public class WaiterInfo {
    private Integer waiterId;
    private String waiterName;
}

3.4 服务注册

启动咱们的 waiter-service 和 cook-service 项目,就能自动的进行 eureka 的服务注册了,因为咱们在 .yml 已经配置好了 eureka 的地址。

启动 waiter 和 cook 后,刷新 http://127.0.0.1:10010 就能发现:

通过上面可以看到,已经把 cook-service 和 waiter-service 注册到咱们部搭建的 eureka-server 服务中了。

上述这样的操作,咱们就称作为服务注册。

由于 cook-service 想通过 eureka-server 去发现 waiter-service 所以,也要让 cook-service 自己在 eureka 中注册,这样一来 cook-service 就可以从 eureka-server 中去发现 waiter-service 服务。

3.5 服务发现

前面咱们说,厨师想通过喊服务员名字来让服务员上菜,但是呢它并不知道当前哪些服务员是能提供服务的,于是便需要向 eureka-server 去获取可用服务列表,于是厨师就不需要关注服务员叫什么了。

咱们对 cook-service 和 waiter-service 分别创建一个 controller 包,在 cook 的 controller 包中新建 CookController类,在 waiter 的 controller 中新建 WaiterController 类,此处咱们就不新建 Service 层 和 Mapper 层了,这里只是为了学习服务发现,所以使用一层演示就足够了。

对于 waiter 服务来说,就提供一个接口,这个接口就是执行一个模拟的任务:

java 复制代码
package com.zlcode.waiter.controller;

import jakarta.websocket.server.PathParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/waiter")
@Slf4j
public class WaiterController {

    @RequestMapping("/up")
    public String up(@PathParam("content") String content) {
      log.info("正在执行: " + content);
      return "执行: " + content + "成功!";
    }
}

上述 waiter 就提供了 /waiter/up 这个接口,比如传递的 content 为 "给55桌上红烧肉",这个请求就会打印一下这个content,然后再模拟返回结果。下面咱们来使用 postman 测试这个接口的可用性:

这里可用发现 waiter 服务的 up 接口成功返回了预期的值,同时在 waiter 的控制台也能看到:

调用 waiter 的 up 方法后,成功的模拟执行了上菜操作!

接下来咱们看一下 cook 提供了哪个接口:

java 复制代码
package com.zlcode.cook.controller;

import jakarta.websocket.server.PathParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("/cook")
public class CookController {

    @Autowired
    private DiscoveryClient discoveryClient;

    @RequestMapping("/ok")
    public String ok(@PathParam("content")String content) {
        // 通过 discoveryClient 从 eureka-server 获取服务列表
        List<ServiceInstance> instances = discoveryClient.getInstances("waiter-service");
        String uri = instances.get(0).getUri().toString(); // 拿到服务列表的第一个 ip:端口号

        // ----------------------------------------------------------
        // 这里咱们拿到了 waiter 的 ip 地址和端口号
        // 也就是知道了服务员的名字,那么要如何告诉服务员你需要让他上菜呢?
        // 当然现实中可用喊一嗓子,但在代码中呢?
        // ---------------------------------------------------------

        return "";
    }
}

此时这里就可用通过 discoveryClient.getInstances 实例列表,此处是获取名字为 waiter-service 的服务列表,获取后,通过 instances.get(0).getUri().toString(); 获取这个实例的 ip 和 端口号。

为什么这里可能会有多个实例呢?别忘记了,把同一个项目换成不同的端口号,分别运行,这就是两个服务,同时这两个服务都进行服务注册,此时 eureka 上就会有两个这样的服务了,只是对应的端口号不同,如果端口号相同,但是ip不同,也就是在不同的主机上,这样也是 ok 的。

但是这里只启动了一个 waiter-service 服务,所以咱们取第 0 个就ok了。

ip 和端口号是拿到了,可是如何调用 waiter-service 服务提供的 up 接口呢?

其实也很简单,既然都拿到ip端口号了,直接使用 ip+端口号/waiter/up?content="xxx" 这个 url 给 waiter 发一个 http 请求就ok了,但是使用 js 发 http 请求相信都会,但是在 Java 中如何给发送 http 请求呢?

这里可用使用 Spring 提供的 RestTemplate 类,通过这个可用发送一个 http 请求,但是注意了!!!

想认识 RestTemplate 就得知道什么是 Rest。

REST(Representational State Transfer),表现层资源状态转移。

简单来说:REST描述的是在网络中Client和Server的⼀种交互形式,REST本身不实用,实用的是如何设计RESTfulAPI(REST风格的网络接口)接口类似于:

复制代码
GET/blog/{blogId}:查询博客
DELETE/blog/{blogId}:删除博客

而 RestTemplate 是Spring提供,封装HTTP调⽤,并强制使用RESTful风格。它会处理HTTP连接和关闭,只需要使用者提供资源的地地和参数即可。

所以要想使用这个 RestTemplate 进行 http 请求,先得把咱们 waiter 服务的 up 接口改成 Rest 风格的接口:

java 复制代码
package com.zlcode.waiter.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/waiter")
@Slf4j
public class WaiterController {

    @RequestMapping("/up/{content}")
    public String up(@PathVariable("content") String content) {
      log.info("正在执行: " + content);
      return "执行: " + content + "成功!";
    }
}

接下来在 cook-service 项目中先定义 RestTemplate,把这个对象交给 Spring 容器管理,在 cook 目录下创建 config 目录,在这个目录下创建一个 BeanConfig 类,在这个类中定义好咱们要用的 RestTemplate 就 OK 了。

java 复制代码
package com.zlcode.cook.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;

@Configuration
public class BeanConfig {

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

接下来修改 CookConroller 的 ok 接口:

java 复制代码
package com.zlcode.cook.controller;

import jakarta.websocket.server.PathParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
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
@RequestMapping("/cook")
public class CookController {

    @Autowired
    private DiscoveryClient discoveryClient;

    @Autowired
    private RestTemplate restTemplate;

    // 咱们的 cook 里面的接口可以不遵循 Rest 规范,
    // 只要保证使用 restTemplate 调用的接口遵守 Rest 规范就ok了
    @RequestMapping("/ok")
    public String ok(@PathParam("content")String content) {
        // 通过 discoveryClient 从 eureka-server 获取服务列表
        List<ServiceInstance> instances = discoveryClient.getInstances("waiter-service");
        String uri = instances.get(0).getUri().toString(); // 拿到服务列表的第一个 ip:端口号
        // 通过 restTemplate 给 waiter-service 发送 http 请求
        String url = uri + "/waiter/up/{content}";
        // 第一个参数是请求 url, 
        // 第二个参数是这个请求的返回值类型的class对象, 
        // 第三个参数是占位符对应的值
        String resp = restTemplate.getForObject(url, String.class, content);
        return "调用成功, 已收到 waiter 的响应: " + resp;
    }
}

此时咱们此处的代码,就是 cook 厨师,通过 eureka 注册中心获取到服务列表,拿到第一个服务的 ip和端口,通过这个 ip 和 端口 拼接上完整的接口路由,带上参数,就实现了远程方法调用了。

此处如果服务员离职了,来了个新的服务员,对于厨师来说,没有任何影响,厨师只需要关注注册中心有哪些服务列表就行了。

3.6 服务注册和发现的注意点

java 复制代码
List<ServiceInstance> instances = discoveryClient.getInstances("waiter-service");
String uri = instances.get(0).getUri().toString(); // 拿到服务列表的第一个 ip:端口号
String url = uri + "/waiter/up/{content}";
String resp = restTemplate.getForObject(url, String.class, content);

上面代码是服务发现和远程调用的重要代码。

上面的 discoveryClient.getInstances("waiter-service") 这里方法的形参,waiter-service 就像是给这个服务取了一个名字,比如传菜的都叫做服务员,只要名字叫 waiter-service 的服务,都能获取到,那么注册中心是如何知道每个服务的名字呢?

观察 waiter-service 的 spring.application.name 的值是 waiter-server,注册中心就是通过这个 name 来区分的,所以也就是有可能出现如下的情况:

服务A 的名字:waiter-service 这个服务只提供了 test 接口

服务B 的名字:waiter-service 这个服务只提供了 hello 接口

由于服务 AB 启动时都会进行服务注册,那么问题来了,通过discoveryClient.getInstances("waiter-service") 会拿到两个实例,也就是可以获取两个服务的 ip 和 端口号,假设 服务C 想调用 test 接口,而服务C 只知道 name 为 waiter-service 的服务提供了 test 接口,但是服务C不知道的是:AB都叫 test-service,但是服务B没有提供 test 接口。

如果此时 C 通过 instances.get(0).getUri().toString();,拿到的是服务B的ip和端口,此时再通过 restTemplate 进行调用 test 接口,那问题就大了!!

也就是说,如果统称为服务员,那么必须都得具备传菜的接口服务。

所以对于这里的 spring.application.name 的值,要保证相同的 name 它所提供的服务(接口)要是一致的!

现在咱们就来复现上面这种情况,先创建 B/hello 和 A/test 接口,并且这两个服务都叫做 waiter-service。

AController:

java 复制代码
package com.zlcode.waiter.controller;


import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class AController {

    @RequestMapping("test")
    public String test () {
        return "成功调用了 服务A 的 test 接口";
    }
}

BController:

java 复制代码
package com.zlcode.waiter.controller;


import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class BController {

    @RequestMapping("/hello")
    public String hello() {
        return "成功调用了 服务B 的 hello 接口";
    }
}

启动这两个服务A和服务B,服务A的端口为9091,服务B的端口为9092,此时可以发现 服务A 只提供了 test 接口,服务B 只提供了 hello 接口。

此时启动服务A和服务B,都发现已经成功使用 waiter-service 这个名字进行服务注册了。

然后使用服务C去获取 waiter-service 对应的服务列表,调用下这两个服务的 test 接口:

java 复制代码
package com.zlcode.cook.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;

@Slf4j
@RestController
@RequestMapping("/C")
public class CController {

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private DiscoveryClient discoveryClient;

    @RequestMapping("/run")
    public void run() {
        List<ServiceInstance> instances = discoveryClient.getInstances("waiter-service");
        for (ServiceInstance instance : instances) {
            String uri = instance.getUri().toString();
            log.info("获取到服务器地址:" + uri);
            String url = uri + "/test";
            try {
                String resp = restTemplate.getForObject(url, String.class);
                log.info(uri + " 调用服务器 test 接口成功! resp: " + resp);
            } catch (Exception e) {
                e.printStackTrace();
                log.error("服务器: " + uri + "没有提供 test 接口.");
            }
        }
    }
}

上述就是服务C,咱们给它运行在 8080 端口上,然后调用 127.0.0.1/C/run 这个接口后,他就会从注册中心获取 waiter-service 服务信息,此时能拿到两个,9091 端口是提供了 test 接口服务的,而 9092 则没有提供 test 接口服务。

在 postman 调用 127.0.0.1/C/run 观察项目打印日志:

果然不出所料,没有提供 test 接口的服务B就会报 404 错误。所以现在你理解了服务名的重要性了吗?

相关推荐
Exclusive_Cat2 小时前
SpringMVC参数接收与数据返回详解
spring·mvc
ChinaRainbowSea3 小时前
补充:问题:CORS ,前后端访问跨域问题
java·spring boot·后端·spring
呆萌的代Ma5 小时前
解决Mac上的老版本docker desktop 无法启动/启动后一直转圈/无法登陆账号的问题
macos·docker·eureka
hqxstudying5 小时前
java依赖注入方法
java·spring·log4j·ioc·依赖
春生野草5 小时前
关于SpringMVC的整理
spring
Bug退退退1236 小时前
RabbitMQ 高级特性之重试机制
java·分布式·spring·rabbitmq
guojl7 小时前
Ribbon原理和源码分析
spring cloud·微服务
hello早上好7 小时前
CGLIB代理核心原理
java·spring
先睡13 小时前
Redis的缓存击穿和缓存雪崩
redis·spring·缓存
Bug退退退12318 小时前
RabbitMQ 高级特性之死信队列
java·分布式·spring·rabbitmq