SpringCloud 服务的注册与发现

news2025/3/3 5:37:51

一、前言

        接下来是开展一系列的 SpringCloud 的学习之旅,从传统的模块之间调用,一步步的升级为 SpringCloud 模块之间的调用,此篇文章为第二篇,即使用服务注册和发现的组件,此篇文章会介绍 EurekaZookeeper Consul 分别作为注册中心的使用场景。

二、Eureka 服务注册与发现

2.1 Eureka 基础知识

2.1.1 什么是服务治理

        SpringCloud 封装了 Netflix 公司开发的 Eureka 模块来实现服务治理。

        在传统的 rpc 远程调用框架中,管理每个服务与服务之间依赖关系比较复杂,管理比较复杂,所以需要使用服务治理,管理服务于服务之间依赖关系,可以实现服务调用、负载均衡、容错等,实现服务发现与注册。

2.1.2 什么是服务注册

         Eureka 采用了 CS 的设计架构,Eureka Server 作为服务注册功能的服务器,它是服务注册中心。而系统中的其他微服务,使用 Eureka 的客户端连接到 Eureka Server 并维持心跳连接。这样系统的维护人员就可以通过 Eureka Server 来监控系统中各个微服务是否正常运行。

        在服务注册与发现中,有一个注册中心。当服务器启动的时候,会把当前自己服务器的信息 比如服务地址通讯地址等以别名方式注册到注册中心上。另一方(消费者|服务提供者),以该别名的方式去注册中心上获取到实际的服务通讯地址,然后再实现本地 RPC 调用 RPC 远程调用。

        框架核心设计思想在于注册中心,因为使用注册中心管理每个服务与服务之间的一个依赖关系(服务治理概念)。在任何 rpc 远程框架中,都会有一个注册中心(存放服务地址相关信息(接口地址))。

2.1.3 Eureka 两组件

        Eureka 包含两个组件:Eureka Server Eureka Client

        EurekaServer 提供服务注册服务各个微服务节点通过配置启动后,会在 EurekaServer 中进行注册,这样 EurekaServer 中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到。

        EurekaClient 通过注册中心进行访问是一个 Java 客户端,用于简化 EurekaServer 的交互,客户端同时也具备一个内置的、使用轮询 (round-robin) 负载算法的负载均衡器。在应用启动后,将会向 Eureka Server 发送心跳(默认周期为 30 秒)。如果 Eureka Server 在多个心跳周期内没有接收到某个节点的心跳,EurekaServer 将会从服务注册表中把这个服务节点移除(默认 90 秒)。

2.2 单机 Eureka 构建

2.2.1 修改支付模块

        修改 cloud-provider-payment8001 支付模块,使其成为 EurekaClient 端,并将其注册到 EurekaServer 端中,在 pom.xml 中,添加 eureka 客户端的依赖,如下:

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

        在 application.yml 中添加  eureka 的配置信息,如下:

eureka:
  client:
    # 表示是否将自己注册进EurekaServer默认为true。
    register-with-eureka: true
    #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetchRegistry: true
    service-url:
      defaultZone: http://localhost:7001/eureka

        在启动类上添加 eureka 客户端的注解类,如下:

@SpringBootApplication
@EnableEurekaClient
public class PaymentMain8081 {
    public static void main(String[] args) {

            SpringApplication.run(PaymentMain8081.class,args);
    }
}

2.2.2 修改订单模块

        修改 cloud-consumer-order80 订单模块,使其成为 EurekaClient 端,并将其注册到 EurekaServer 端中,在 pom.xml 中,添加 eureka 客户端的依赖,如下:

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

        在 application.yml 中添加  eureka 的配置信息,如下:

server:
  port: 80

spring:
  application:
    name: cloud-order-service

eureka:
  client:
    #表示是否将自己注册进EurekaServer默认为true。
    register-with-eureka: true
    #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetchRegistry: true
    service-url:
      defaultZone: http://localhost:7001/eureka

        在启动类上添加 eureka 客户端的注解类,如下:

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

2.2.3 创建 Eureka7001 模块

        创建 cloud-eureka-server7001 模块,使其成为 eurekaServer 端服务注册中心,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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.springcloud</groupId>
        <artifactId>SpringCloud</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>cloud-eureka-server7001</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>
        <!--eureka-server-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
        <dependency>
            <groupId>com.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!--boot web actuator-->
        <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>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
    </dependencies>
</project>

        application.yml 的内容如下所示:

server:
  port: 7001

eureka:
  instance:
    hostname: localhost # eureka服务端的实例名称
  client:
    # false 表示不向注册中心注册自己。
    register-with-eureka: false
    # false 表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
    fetch-registry: false
    service-url:
      # 设置与 Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址。
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

         启动类的代码如下所示,

package com.springcloud;

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

@SpringBootApplication
// eureka 服务端的注解
@EnableEurekaServer
public class EurekaMain7001 {
    public static void main(String[] args)
    {
        SpringApplication.run(EurekaMain7001.class,args);
    }
}

2.2.4 测试

        分别启动 cloud-provider-payment8001、cloud-consumer-order80  cloud-eureka-server7001 模块,在浏览器输入 http://localhost:7001,如下图,可以看到,注册成功了。

2.3 集群 Eureka 构建

2.3.1 原理说明

        服务注册:将服务信息注册进注册中心。

        服务发现:从注册中心上获取服务信息。实质上是存储的是 key valuekey 为服务名称,value 为服务地址。

2.3.2 微服务远程调用核心

        微服务 RPC 远程服务调用最核心的是什么?答案为高可用,假设你的注册中心只有一个,如果它发生故障,就会导致整个为服务环境不可用。解决办法是搭建 Eureka 注册中心集群,实现负载均衡 + 故障容错

2.3.3 创建 Eureka7002 模块

         创建 cloud-eureka-server7002 模块,使其成为 eurekaServer 端服务的第二个注册中心,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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.springcloud</groupId>
        <artifactId>SpringCloud</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>cloud-eureka-server7002</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>
        <!--eureka-server-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
        <dependency>
            <groupId>com.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!--boot web actuator-->
        <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>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
    </dependencies>
</project>

        启动类的代码如下所示,

package com.springcloud;

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

@SpringBootApplication
// eureka 服务端的注解
@EnableEurekaServer
public class EurekaMain7002 {
    public static void main(String[] args)
    {
        SpringApplication.run(EurekaMain7002.class,args);
    }
}

2.3.4 修改配置映射

        找到 C:\Windows\System32\drivers\etc 目录下的 hosts 文件,添加映射配置到 hosts 文件中,如下图:

2.3.5 修改 yml 文件

        修改 cloud-eureka-server7001 模块的 application.yml 文件,使其注册到 7002 服务端上,内容如下:

server:
  port: 7001

eureka:
  instance:
    hostname: eureka7001.com # eureka服务端的实例名称
  client:
    # false 表示不向注册中心注册自己。
    register-with-eureka: false
    # false 表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
    fetch-registry: false
    service-url:
      # 三台节点的写法
      # defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
      # 设置与 Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址。
      defaultZone: http://eureka7002.com:7002/eureka/
     

        修改 cloud-eureka-server7002 模块的 application.yml 文件,使其注册到 7001 服务端上,内容如下:

server:
  port: 7002

eureka:
  instance:
    hostname: eureka7002.com # eureka服务端的实例名称
  client:
    # false 表示不向注册中心注册自己。
    register-with-eureka: false
    # false 表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
    fetch-registry: false
    service-url:
      # 三台节点的写法
      # defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7003.com:7003/eureka/
      # 设置与 Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址。
      defaultZone: http://eureka7001.com:7001/eureka/

2.3.6 测试

        启动 cloud-eureka-server7001 和 cloud-eureka-server7002 模块在浏览器访问下,如下图,可以看到,互相注册成功了。

2.3.7 将支付模块注册到集群

        将支付模块 cloud-provider-payment8001 发布到上面 2Eureka 集群配置中,修改该模块的 application.yml,内容如下所示:

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

2.3.8 将订单模块注册到集群

        将支付模块 cloud-consumer-order80 发布到上面 2Eureka 集群配置中,修改该模块的 application.yml,内容如下所示:

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

2.3.9 测试

        先启动 EurekaServer 7001 7002 服务,然后再启动服务提供者 payment8001 模块,最后再启动消费者 order80 模块,然后访问 EurekaServer 的管理界面,如下所示,可以看到两个模块均注册成功了。

        并且服务可以正常的提供调用服务,如下图:

2.3.10 构建支付模块集群

        现在的支付模块只有一个 cloud-provider-payment8001,如果它要是宕机了,整个系统也就无法提供服务了,所以我们需要构建支付模块的集群,新建一个子模块 cloud-provider-payment8002

        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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.springcloud</groupId>
        <artifactId>SpringCloud</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>cloud-provider-payment8002</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>
        <!--eureka-client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>com.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.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
        <!--mysql-connector-java-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--jdbc-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</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>

        application.yml 的内容如下所示:

server:
  port: 8001

spring:
  application:
    name: cloud-payment-service
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource            
    driver-class-name: org.gjt.mm.mysql.Driver             
    url: jdbc:mysql://localhost:3306/db2019?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: 123456
eureka:
  client:
    # 表示是否将自己注册进EurekaServer默认为true。
    register-with-eureka: true
    #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetchRegistry: true
    service-url:
      # defaultZone: http://localhost:7001/eureka
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka  # 集群版

mybatis:
  mapperLocations: classpath:mapper/*.xml
  type-aliases-package: com.springcloud.entities    

        主启动类的内容如下所示:

package com.springcloud;

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

@SpringBootApplication
@EnableEurekaClient
public class PaymentMain8082 {
    public static void main(String[] args) {

        SpringApplication.run(PaymentMain8082.class,args);
    }
}

        其他的业务类都从 cloud-provider-payment8001 模块粘贴即可,都是一样的代码。

        修改 cloud-provider-payment8001 模块的 PaymentController 代码,添加一些可以标识出调用信息出自哪个端口的代码,如下:

@RestController
@Slf4j
public class PaymentController {

    @Value("${server.port}")
    private String serverPort;
    @Resource
    PaymentService paymentService;

    @PostMapping("/payment/create")
    public CommonResult create(@RequestBody Payment payment){

        int result = paymentService.create(payment);
        log.info("*****插入操作返回结果:" + result);
        if(result >0){

            return new CommonResult(200,"插入成功,返回结果"+result+"服务端口:"+serverPort,payment);
        }else{
            return new CommonResult(404,"插入失败",null);
        }
    }
    @GetMapping("/payment/get/{id}")
    public CommonResult<Payment> selectPaymentById(@PathVariable("id") Long id){

        Payment payment = paymentService.selectPaymentById(id);
        log.info("*****查询操作返回结果:" + payment);
        if(payment != null){
            int a =2;
            return new CommonResult(200,"查询成功"+"服务端口:"+serverPort,payment);
        }else{
            return new CommonResult(404,"未查询到数据"+id,null);
        }
    }
}

         修改 cloud-provider-payment8002 模块的 PaymentController 代码,添加一些可以标识出调用信息出自哪个端口的代码,如下:

@RestController
@Slf4j
public class PaymentController {

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

    @Resource
    PaymentService paymentService;

    @PostMapping("/payment/create")
    public CommonResult create(@RequestBody Payment payment){

        int result = paymentService.create(payment);
        log.info("*****插入操作返回结果:" + result);
        if(result >0){
            return new CommonResult(200,"插入成功,返回结果"+result+"服务端口:"+serverPort,payment);
        }else{
            return new CommonResult(404,"插入失败",null);
        }
    }
    @GetMapping("/payment/get/{id}")
    public CommonResult<Payment> selectPaymentById(@PathVariable("id") Long id){

        Payment payment = paymentService.selectPaymentById(id);
        log.info("*****查询操作返回结果:" + payment);
        if(payment != null){
            int a =2;
            return new CommonResult(200,"查询成功"+"服务端口:"+serverPort,payment);
        }else{
            return new CommonResult(404,"未查询到数据"+id,null);
        }
    }
}

        修改 cloud-consumer-order80 订单模块的 OrderController 类,因为以前这里面服务调用的地址是写死的,现在需要写上如下的地址

@RestController
@Slf4j
public class OrderController {

    @Resource
    private RestTemplate restTemplate;

    // public static final String PaymentSrv_URL = "http://localhost:8001";
    public static final String PAYMENT_SRV = "http://CLOUD-PAYMENT-SERVICE";
    @GetMapping("/consumer/payment/create")
    public CommonResult create(Payment payment){
        // 客户端用浏览器是get请求,但是底层实质发送post调用服务端8001
        return restTemplate.postForObject(PAYMENT_SRV+"/payment/create",payment,CommonResult.class);
    }

    @GetMapping("/consumer/payment/get/{id}")
    public CommonResult getPayment(@PathVariable Long id)
    {
        return restTemplate.getForObject(PAYMENT_SRV + "/payment/get/"+id, CommonResult.class, id);
    }
}

        修改 cloud-consumer-order80 订单模块的 ApplicationContextConfig 类,给 RestTemplate 添加负载均衡的能力,如下图:

@Configuration
public class ApplicationContextConfig {

    @Bean
    //使用@LoadBalanced注解赋予RestTemplate负载均衡的能力
    @LoadBalanced
    public RestTemplate restTemplate(){
        return  new RestTemplate();
    }
}

        先启动 EurekaServer 7001 7002 服务,然后再启动服务提供者 payment8001 和 payment8002 模块,最后再启动消费者 order80 模块,然后调用方法进行测试,如下图:

2.4 actuator 微服务信息完善

2.4.1 服务名称修改

        访问 Eureka 的管理界面,可以看到里面包含了主机的名称,这个是我们不想要的,如下图:

        那么如何才能不显示我们的主机名称呢?只需要配置一些信息即可。修改 cloud-provider-payment8001 模块的 application.yml,内容如下所示:

eureka:
  client:
    # 表示是否将自己注册进EurekaServer默认为true。
    register-with-eureka: true
    #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetchRegistry: true
    service-url:
      # defaultZone: http://localhost:7001/eureka
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka  # 集群版
  instance:
    # 设置名称
    instance-id: payment8001

        修改 cloud-provider-payment8002 模块的 application.yml,内容如下所示:

eureka:
  client:
    # 表示是否将自己注册进EurekaServer默认为true。
    register-with-eureka: true
    #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetchRegistry: true
    service-url:
      # defaultZone: http://localhost:7001/eureka
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka  # 集群版
  instance:
    # 设置名称
    instance-id: payment8002

        重启这两个模块,然后刷新 Eureka 的管理界面,如下图,可以看到,不再显示我们的主机名称。

2.4.2 显示 ip 信息

        我们想实现当鼠标放到服务的名称上时显示 ip 信息,现在的效果如下所示:

         那么如何才能显示我们的 ip 信息呢?只需要配置一些信息即可。修改 cloud-provider-payment8001 模块的 application.yml,内容如下所示:

eureka:
  client:
    # 表示是否将自己注册进EurekaServer默认为true。
    register-with-eureka: true
    #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetchRegistry: true
    service-url:
      # defaultZone: http://localhost:7001/eureka
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka  # 集群版
  instance:
    # 设置名称
    instance-id: payment8001
    # 访问路径可以显示IP地址
    prefer-ip-address: true     

        修改 cloud-provider-payment8002 模块的 application.yml,内容如下所示:

eureka:
  client:
    # 表示是否将自己注册进EurekaServer默认为true。
    register-with-eureka: true
    #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetchRegistry: true
    service-url:
      # defaultZone: http://localhost:7001/eureka
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka  # 集群版
  instance:
    # 设置名称
    instance-id: payment8002
    # 访问路径可以显示IP地址
    prefer-ip-address: true   

        重启这两个模块,然后刷新 Eureka 的管理界面,如下图,可以看到,当鼠标放上去的时候会显示我们的 ip 地址信息。

2.5 服务发现 Discovery

        如果我们想获取在 Eureka 上注册的微服务的对外暴露信息,我们该如何获取呢?比如说主机名称、端口号等。其实对于注册进 Eureka 里面的微服务,可以通过服务发现来获得该服务的信息。

        修改 cloud-provider-payment8001 模块的 PaymentController 代码,内容如下所示:

@RestController
@Slf4j
public class PaymentController {

    @Value("${server.port}")
    private String serverPort;
    @Resource
    PaymentService paymentService;
    @Resource
    private DiscoveryClient discoveryClient;
    @PostMapping("/payment/create")
    public CommonResult create(@RequestBody Payment payment){

        int result = paymentService.create(payment);
        log.info("*****插入操作返回结果:" + result);
        if(result >0){

            return new CommonResult(200,"插入成功,返回结果"+result+"服务端口:"+serverPort,payment);
        }else{
            return new CommonResult(404,"插入失败",null);
        }
    }
    @GetMapping("/payment/get/{id}")
    public CommonResult<Payment> selectPaymentById(@PathVariable("id") Long id){

        Payment payment = paymentService.selectPaymentById(id);
        log.info("*****查询操作返回结果:" + payment);
        if(payment != null){
            int a =2;
            return new CommonResult(200,"查询成功"+"服务端口:"+serverPort,payment);
        }else{
            return new CommonResult(404,"未查询到数据"+id,null);
        }
    }
    // 对外提供一个获取服务的所有方法
    @GetMapping(value = "/payment/discovery")
    public Object discovery()
    {
        List<String> services = discoveryClient.getServices();
        for (String element : services) {
            System.out.println(element);
        }

        List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
        for (ServiceInstance element : instances) {
            System.out.println(element.getServiceId() + "\t" + element.getHost() + "\t" + element.getPort() + "\t"
                    + element.getUri());
        }
        return this.discoveryClient;
    }
}

         修改 cloud-provider-payment8002 模块的 PaymentController 代码,内容如下所示:

@RestController
@Slf4j
public class PaymentController {

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

    @Resource
    PaymentService paymentService;
    @Resource
    private DiscoveryClient discoveryClient;
    @PostMapping("/payment/create")
    public CommonResult create(@RequestBody Payment payment){

        int result = paymentService.create(payment);
        log.info("*****插入操作返回结果:" + result);
        if(result >0){
            return new CommonResult(200,"插入成功,返回结果"+result+"服务端口:"+serverPort,payment);
        }else{
            return new CommonResult(404,"插入失败",null);
        }
    }
    @GetMapping("/payment/get/{id}")
    public CommonResult<Payment> selectPaymentById(@PathVariable("id") Long id){

        Payment payment = paymentService.selectPaymentById(id);
        log.info("*****查询操作返回结果:" + payment);
        if(payment != null){
            int a =2;
            return new CommonResult(200,"查询成功"+"服务端口:"+serverPort,payment);
        }else{
            return new CommonResult(404,"未查询到数据"+id,null);
        }
    }
    // 对外提供一个获取服务的所有方法
    @GetMapping(value = "/payment/discovery")
    public Object discovery()
    {
        List<String> services = discoveryClient.getServices();
        for (String element : services) {
            System.out.println(element);
        }

        List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
        for (ServiceInstance element : instances) {
            System.out.println(element.getServiceId() + "\t" + element.getHost() + "\t" + element.getPort() + "\t"
                    + element.getUri());
        }
        return this.discoveryClient;
    }
}

        修改 cloud-provider-payment8001 和 cloud-provider-payment8002 的启动类,分别加上注解,如下:

@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
public class PaymentMain8081 {
    public static void main(String[] args) {

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

        SpringApplication.run(PaymentMain8082.class,args);
    }
}

        重启所有的服务,然后输入 http://localhost:8001/payment/discovery ,进行测试,如下图:

2.6 Eureka 自我保护

2.6.1 故障现象

        保护模式主要用于一组客户端和 Eureka Server 之间存在网络分区场景下的保护。一旦进入保护模式,Eureka Server 将会尝试保护其服务注册表中的信息,不再删除服务注册表中的数据,也就是不会注销任何微服务。

        如果在 Eureka Server 的首页看到以下这段提示,则说明 Eureka 进入了保护模式:

2.6.2 导致原因

        为了防止 EurekaClient 可以正常运行,但是与 EurekaServer 网络不通情况下,EurekaServer 不会立刻将 EurekaClient 服务剔除。 

        默认情况下,如果 EurekaServer 在一定时间内没有接收到某个微服务实例的心跳,EurekaServer 将会注销该实例(默认 90 秒)。但是当网络分区故障发生(延时、卡顿、拥挤)时,微服务与 EurekaServer 之间无法正常通信,以上行为可能变得非常危险了——因为微服务本身其实是健康的,此时本不应该注销这个微服务。Eureka 通过“自我保护模式”来解决这个问题——当 EurekaServer 节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。

        在自我保护模式中,Eureka Server 会保护服务注册表中的信息,不再注销任何服务实例。它的设计哲学就是宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例。一句话讲解:好死不如赖活着。

        综上,自我保护模式是一种应对网络异常的安全保护措施。它的架构哲学是宁可同时保留所有微服务(健康的微服务和不健康的微服务都会保留)也不盲目注销任何健康的微服务。使用自我保护模式,可以让 Eureka 集群更加的健壮、稳定。

2.6.3 禁止自我保护

        首先自我保护机制是默认开启的,可以通过配置来关闭这个机制,修改 cloud-eureka-server7001application.yml 配置文件,如下:

eureka:
  instance:
    hostname: eureka7001.com # eureka服务端的实例名称
  client:
    # false 表示不向注册中心注册自己。
    register-with-eureka: false
    # false 表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
    fetch-registry: false
    service-url:
      # 设置与 Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址。
      defaultZone: http://eureka7002.com:7002/eureka/
  server:
    # 关闭自我保护机制,保证不可用服务被及时踢除
    enable-self-preservation: false
    # 时间间隔调整为 2s
    eviction-interval-timer-in-ms: 2000

        cloud-eureka-server7002 也是如此配置,这里不再赘述。接下来修改 cloud-provider-payment8001 application.yml 配置文件,如下:

eureka:
  client:
    # 表示是否将自己注册进EurekaServer默认为true。
    register-with-eureka: true
    #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetchRegistry: true
    service-url:
      # defaultZone: http://localhost:7001/eureka
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka  # 集群版
  # 心跳检测与续约时间
  # 开发时设置小些,保证服务关闭后注册中心能即使剔除服务
  instance:
    # 设置名称
    instance-id: payment8001
    # 访问路径可以显示IP地址
    prefer-ip-address: true
    # Eureka客户端向服务端发送心跳的时间间隔,单位为秒(默认是30秒)
    lease-renewal-interval-in-seconds: 1
    # Eureka服务端在收到最后一次心跳后等待时间上限,单位为秒(默认是90秒),超时将剔除服务
    lease-expiration-duration-in-seconds: 2

        cloud-provider-payment8002 也是如此配置,这里不再赘述。 

2.6.4 测试

        分别重启各个服务,然后手动停止 cloud-provider-payment8001 服务,如下图:

三、Zookeeper 服务注册与发现

3.1 Eureka 停止更新

        自从 2018 年后,Eureka 就停止了更新,如下图:

3.2 Zookeeper 代替 Eureka

3.2.1 zookeeper 简介

        zookeeper 是一个分布式协调工具,可以实现注册中心功能。可以使用 zookeeper 服务器取代 Eureka 服务器,zk 作为服务注册中心。

        使用的前提是需要在 linux 服务器上安装 zookeeper,具体按照的步骤,可以参考我的这篇文章,这里不再赘述,记得把 linux 服务器的防火墙先关掉。

3.2.2 创建服务提供者

        创建 cloud-provider-payment8004 模块作为服务的提供者,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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.springcloud</groupId>
        <artifactId>SpringCloud</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>cloud-provider-payment8004</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>
        <!-- SpringBoot整合Web组件 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
            <groupId>com.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!-- SpringBoot整合zookeeper客户端 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zookeeper-discovery</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>

        application.yml 的内容如下所示:

# 8004表示注册到zookeeper服务器的支付服务提供者端口号
server:
  port: 8004
#服务别名----注册zookeeper到注册中心名称
spring:
  application:
    name: cloud-provider-payment
  cloud:
    zookeeper:
      connect-string: 192.168.229.167:2181

        主启动类的内容如下所示:

@SpringBootApplication
// 该注解用于向使用consul或者zookeeper作为注册中心时注册服务
@EnableDiscoveryClient 
public class PaymentMain8004 {
    public static void main(String[] args)
    {
        SpringApplication.run(PaymentMain8004.class,args);
    }

}

        创建一个 Controller 类,内容如下所示:

package com.springcloud.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.beans.factory.annotation.Value;

import java.util.UUID;


@RestController
public class PaymentController
{
    @Value("${server.port}")
    private String serverPort;

    @RequestMapping(value = "/payment/zk")
    public String paymentzk()
    {
        return "springcloud with zookeeper: "+serverPort+"\t"+ UUID.randomUUID().toString();
    }
}

        启动工程进行测试,如下:

3.2.3 注意

        此时方式创建的 zookeeper 节点为临时节点,当关闭微服务模块后,节点一会就自动消失了

3.2.4 创建服务消费者

        创建 cloud-consumerzk-order80 模块作为服务的提供者,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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.springcloud</groupId>
        <artifactId>SpringCloud</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>cloud-consumer-order80</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>
        <!--eureka-client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>com.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>

        application.yml 内容如下所示:

server:
  port: 80

spring:
  application:
    name: cloud-consumer-order
  cloud:
    # 注册到zookeeper地址
    zookeeper:
      connect-string: 192.168.229.167:2181

        主启动类的内容如下所示:

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

        配置类的代码如下所示:

@Configuration
public class ApplicationContextConfig {

    @Bean
    // 使用@LoadBalanced注解赋予RestTemplate负载均衡的能力
    @LoadBalanced
    public RestTemplate restTemplate(){
        return  new RestTemplate();
    }
}

        Controller 的代码如下所示:

@RestController
public class OrderZKController
{
    public static final String INVOKE_URL = "http://cloud-provider-payment";

    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping(value = "/consumer/payment/zk")
    public String paymentInfo()
    {
        String result = restTemplate.getForObject(INVOKE_URL+"/payment/zk", String.class);
        System.out.println("消费者调用支付服务(zookeeper)--->result:" + result);
        return result;
    }

}

        启动服务进行验证,可以在 zookeeper 看到注册的节点,如下图:

         还可以进行接口调用,如下图:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1497227.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ZYNQ linux笔记 --- Linux UIO框架下调用HLS ip核

目标 编写了 HLS IP核,实现对图像的缩放,已通过HLS 仿真验证和裸机验证,想在Linux端调用该IP 工程构建 简单描述一下,vdma出来连接 resize ip核,resize ip核后面接 vid_out ip resize ip核的 interrput 接口通过 xlconcat ip 连接到PS端中断接口IRQ,这一点很重要! 导…

透视和仿射变换的区别

仿射变换矩阵通常是2x3的矩阵。 三个特点&#xff1a; 直线依然是直线平行线依然平行 [ x ′ y ′ 1 ] [ a 11 a 12 b 1 a 21 a 22 b 2 0 0 1 ] [ x y 1 ] x ′ a 11 ∗ x a 12 ∗ y b 1 y ′ a 21 ∗ x a 22 ∗ y b 2 \begin{gathered} \begin{bmatrix}x\\y\\1\end{b…

2023年全国职业院校技能大赛 GZ073网络系统管理赛项 模块A:网络构建(运维配置)

1.完成整网连通后,进入网络监控运维阶段,运维软件已安装在PC的虚拟机中,通过运维平台监控拓扑中所有网络设备(AP除外)。考试现场提供运维平台登陆的用户名密码信息。 2.通过运维平台将被监控设备纳入监控范围;通过拓扑配置功能,将网络拓扑配置到平台中。

【视觉AIGC识别】误差特征、人脸伪造检测、其他类型假图检测

视觉AIGC识别——人脸伪造检测、误差特征 不可见水印 前言视觉AIGC识别【误差特征】DIRE for Diffusion-Generated Image Detection方法扩散模型的角色DIRE作为检测指标 实验结果泛化能力和抗扰动 人脸伪造监测&#xff08;Face Forgery Detection&#xff09;人脸伪造图生成 …

【Python】成功解决TypeError: ‘float‘ object is not iterable

【Python】成功解决TypeError: ‘float’ object is not iterable &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程&#x1f448; 希望得…

为什么桌面领域没有一款 Linux 或 UNIX 能与 Windows 鼎足而立?

为什么桌面领域没有一款 Linux 或 UNIX 能与 Windows 鼎足而立&#xff1f; 在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「Linux 的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享…

大数据揭秘!不得不提的企业数据查询的隐藏宝藏平台。

在现代社会&#xff0c;企业数据已经成为了衡量一个公司实力、潜力以及未来走势的重要指标之一。对于投资者、分析师和企业家来说&#xff0c;获取这些数据并进行深度分析显得尤为重要。那么&#xff0c;如何查询到企业的数据呢&#xff1f;本文将为您介绍一些可以查询企业数据…

sql server使用逗号,分隔保存多个id的一些查询保存

方案一&#xff0c;前后不附加逗号&#xff1a; 方案二&#xff0c;前后附加逗号&#xff1a; 其他保存方案&#xff1a; &#xff08;这里是我做一个程序的商家日期规则搞得&#xff0c;后面再补具体操作&#xff09;&#xff1a; 1,2,3 | 1,2,3 | 1,2,3; 1,2,3 &#xff1…

尤雨溪:Vue 未来展望新的一轮

十年&#xff0c;一个既漫长又短暂的时光跨度&#xff0c;对于技术世界来说&#xff0c;更是沧海桑田的瞬间。在这十年里&#xff0c;Vue.js 从无到有&#xff0c;从默默无闻到蜚声全球&#xff0c;不仅改变了前端开发的面貌&#xff0c;更成为了无数开发者手中的得力工具。 在…

【常见索引使用】⭐️Mysql中索引的类型以及使用方式和失效场景

目录 一、前言 二、数据准备 三、索引的分类 四、索引示例 示例1、主键索引&#xff08;Primary Key Index&#xff09;与 唯一索引&#xff08;Unique Index&#xff09; 示例2、前缀索引&#xff08;Prefix Index&#xff09; 示例3、联合索引&#xff08;复合索引&am…

鸿蒙 自定义弹窗对CustomDialogController二次封装

前言&#xff1a; 鸿蒙官方提供了自定义customdialog&#xff0c;调用代码很臃肿&#xff0c;必须在当前页面创建customDialogController&#xff0c;否则无法正常弹窗dialog 解决方案&#xff1a;目前就定义了两种类型的dialog 具体代码如下&#xff1a; 1. 用于代理dialog的…

2024内衣洗衣机测评推荐:希亦、小吉、鲸立综合对比哪个牌子好?

对于那些追求品质生活、分类洗涤的用户而言&#xff0c;小型洗衣机可以满足我们对不同类型衣物分开洗涤的需求&#xff0c;例如将内衣、袜子等小件衣物与外套等分开洗涤&#xff0c;以保持衣物的清洁和卫生。并且如果您家中有宝宝&#xff0c;或者您对个人卫生和健康有较高要求…

中国制造赢得世界 外贸独立站wordpress建站案例

孵化器wordpress外贸主题 孵化器、孵化设备wordpress企业主题&#xff0c;适合做孵化器 、孵化设备的企业使用。 https://www.jianzhanpress.com/?p3478 橡胶制品wordpress外贸主题 橡胶制品wordpress外贸主题&#xff0c;橡塑产品对外贸易公司官方网站wordpress模板。 ht…

论文解读:Channel Importance Matters in Few-Shot Image Classification

文章汇总 参考文章ICML2022&#xff5c;Channel Importance Matters in Few-Shot Image Classification - 知乎 一句话总结 Channel bias问题&#xff0c;使模型在迁移到下游小样本分类任务时泛化能力较差&#xff0c;本文在测试阶段仅用一行代码就减弱这个问题的影响。 Ch…

3.7作业

网络聊天室&#xff1a; 程序代码&#xff1a; ser.c #include <myhead.h> //定义消息类型结构体 struct xiaoxi {char type;char name[20];char text[100]; };int main(int argc, const char* argv[]) {// 创建套接字int sfd socket(AF_INET, SOCK_STREAM, 0);if (s…

flutter编译后的windows程序缺少msvcp140.dllvcruntime140.dllvcruntime140_1.dll解决办法

//就是很多博文中提到了三剑客 msvcp140.dll vcruntime140.dll vcruntime140_1.dll 1.没有vs环境的不要把生成的debug下的exe发给别人&#xff0c;会出现莫名其妙的问题。缺少各种DLL文件&#xff0c;我这边收集了上面三个的dll下载链接&#xff1a;flutterdll.rar - 蓝奏云 2…

【动态规划】【数论】【区间合并】3041. 修改数组后最大化数组中的连续元素数目

作者推荐 视频算法专题 本文涉及知识点 动态规划汇总 数论 区间合并 LeetCode3041. 修改数组后最大化数组中的连续元素数目 给你一个下标从 0 开始只包含 正 整数的数组 nums 。 一开始&#xff0c;你可以将数组中 任意数量 元素增加 至多 1 。 修改后&#xff0c;你可以从…

java网络编程 02 socket

01.socket定义 02.TCP编程 import java.io.IOException; import java.io.OutputStream; import java.net.InetAddress; import java.net.Socket;public class clientSocket {public static void main(String[] args) throws IOException {//创建客户端socket&#xff0c;…

Get了!原来朋友圈定时发布如此简单!

你是不是也有这样的烦恼&#xff1f;微信号太多&#xff0c;有时候会顾不上发朋友圈&#xff1f; 别担心&#xff01;微信管理系统来帮你解决这个问题&#xff0c;实现朋友圈定时发布&#xff01;让我们一起来看看如何利用微信管理系统的强大功能来提升我们的社交媒体效率吧。…

请说明Vue中的解耦能力

Vue中的解耦能力是指在Vue框架中&#xff0c;我们能够有效地将代码分离成独立的组件或模块&#xff0c;使得这些组件之间的依赖关系减少&#xff0c;实现高内聚、低耦合的设计目标。利用Vue中的组件化开发&#xff0c;可以让不同的模块之间更容易地通信和协作&#xff0c;提高代…