【微服务】服务注册与发现、分布式配置管理 - Consul(day5)

news2024/11/27 9:55:48

概述

作用

Consul的两大作用就是服务发现和注册分布式配置管理

服务发现在介绍Eureka组件的时候已经进行过详细概述,大概就是将硬编码到服务中的IP地址和端口号进行解耦,从而实现动态扩缩容、容错处理、服务管理等功能,通过服务注册和发现实现的微服务代码就是俩字:太优雅了。

分布式配置管理的意思是将原先配置到项目中的配置信息配置到远程服务器上。原因是因为微服务将应用拆分成了粒度较小的子服务,因此系统中会出现大量的服务,而每个服务都需要必要的配置信息才能运行,所以会出现大量的配置文件。综上所述,一套集中式的、动态的配置管理设施是必不可少的。比如某些配置文件中的内容大部分是相同的,只有极个别是不同的。就拿数据库来说吧,如果每个微服务使用的技术栈相同,则每个微服务关于数据库的配置几乎是相同的,有时候主机迁移了,我希望一次修改、处处生效。对于原先的配置文件来说,就需要重新配置、重新打包上线。但是如果使用了分布式配置管理,那么就可以在远程进行修改,并且,有的分布式配置中心还能共享一套配置文件,这样更进一步减少了运维人员的工作量。

功能

服务发现

健康检测

KV存储

多数据中心

可视化Web界面

安装Consul

1. 访问官网进行下载,如下图:

 2. 查看版本:consul -version。下载好就是一个exe文件,通过cmd命令进行下载好的目录,输入上述命令即可查看版本。

 3. 启动Consul:consul agent -dev,依旧是下载好的exe文件对应的目录,直接运行上述命令即可启动Consul。

4. 查看Consul:127.0.0.1:8500,输入网址即可看到Consul的网站。

服务注册与发现

为了后续方便上传码云和复习,因此将学习Eureka和SpringCloudLoadBalancer的模块全部进行改名,并且学习Consul时新建立模块,不和前面的模块进行耦合。

搭建商品服务

建模块

写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">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.wbz</groupId>
        <artifactId>spring-cloud-test</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>cloud-consumer-order-consul-81</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>

        <!--SpringBoot通用模块-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--Lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!--Druid-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
        </dependency>

        <!--MySQL驱动-->
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
        </dependency>

        <!--MP-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
        </dependency>

        <!--Consul-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-consul-discovery</artifactId>
        </dependency>

    </dependencies>

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

</project>

新增的依赖就是Consul的依赖:

        <!--Consul-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-consul-discovery</artifactId>
        </dependency>

写yml文件

server:
  port: 81

spring:
  application:
    name: cloud-consumer-order-consul-81

  mvc:
    pathmatch:
      matching-strategy: ant_path_matcher # 路径匹配策略

  datasource:
    url: jdbc:mysql://127.0.0.1:3306/cloud_order?characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true&allowMultiQueries=true
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver

  profiles:
    active: dev

  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        service-name: ${spring.application.name}

mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  mapper-locations: classpath:mapper/**Mapper.xml
  type-aliases-package: com.wbz.domain

新增的yml文件就是配置Consul需要的配置文件:

spring:
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        service-name: ${spring.application.name}

改启动类

/**
 * consul组件的被调用方启动类
 */

@EnableDiscoveryClient // 服务注册
@MapperScan("com.wbz.mapper")
@SpringBootApplication
public class ProductProviderApplicationConsul8101 {

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

}

新增的内容就是@EnableDiscoveryClient注解,用来进行服务注册的。 

写业务类

// model
/**
 * 产品表
 */
 
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("product_detail")
public class Product {
 
    @TableId
    private Long id;
 
    @TableField
    private String productName;
 
    @TableField
    private Long productPrice;
 
    @TableField
    private Integer state;
 
    @TableField
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private LocalDateTime createTime;
 
    @TableField
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private LocalDateTime updateTime;
 
}
 
// 持久层接口
public interface ProduceMapper extends BaseMapper<Product> {
}
 
// 服务层接口
public interface ProductService extends IService<Product> {
 
    Product getProductById(Long productId);
 
}
 
// 服务层实现类
@Service
public class ProductServiceImpl extends ServiceImpl<ProduceMapper, Product> implements ProductService {
 
    @Override
    public Product getProductById(Long productId) {
        return this.getById(productId);
    }
 
}
 
// 控制层类
@RestController
@RequestMapping("/product")
public class ProductController {
 
    @Resource
    private ProductService productService;
 
    @GetMapping("/query/{productId}")
    public Product getProductById(@PathVariable Long productId) {
        return this.productService.getProductById(productId);
    }
 
}

启动项目之后,出现如下界面就表示商品服务依旧注册到注册中心了:

搭建订单服务

建模块

写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">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.wbz</groupId>
        <artifactId>spring-cloud-test</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>cloud-consumer-order-consul-81</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>

        <!--SpringBoot通用模块-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--Lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!--Druid-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
        </dependency>

        <!--MySQL驱动-->
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
        </dependency>

        <!--MP-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
        </dependency>

        <!--Consul-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-consul-discovery</artifactId>
        </dependency>

    </dependencies>

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

</project>

pom文件中新增的就是Consul的依赖:

        <!--Consul-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-consul-discovery</artifactId>
        </dependency>

写yml文件

server:
  port: 81

spring:
  application:
    name: cloud-consumer-order-consul-81

  mvc:
    pathmatch:
      matching-strategy: ant_path_matcher # 路径匹配策略

  datasource:
    url: jdbc:mysql://127.0.0.1:3306/cloud_order?characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true&allowMultiQueries=true
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver

  profiles:
    active: dev

  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        service-name: ${spring.application.name}

mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  mapper-locations: classpath:mapper/**Mapper.xml
  type-aliases-package: com.wbz.domain

新增的pom依赖就是配置Consul的内容:

spring:
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        service-name: ${spring.application.name}

写主启动类

@MapperScan("com.wbz.mapper")
@EnableDiscoveryClient // 服务注册
@SpringBootApplication
public class OrderConsumerApplicationConsul81 {

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

}

 新增的内容就是@EnableDiscoveryClient注解,用来进行服务注册的。 

写业务类

// JavaBean
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("order_detail")
public class Order {

    @TableId
    private Long id;

    @TableField
    private Long userId;

    @TableField
    private Long productId;

    @TableField
    private Integer num;

    @TableField
    private Long price;

    @TableField
    private Integer deleteFlag;

    @TableField
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private LocalDateTime createTime;

    @TableField
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private LocalDateTime updateTime;

    @TableField(exist = false)
    private Product product;

}

// RestTemplate控制类
@Configuration
public class RestTemplateConfig {

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

}

// mapper层
public interface OrderMapper extends BaseMapper<Order> {
}

// service接口
public interface OrderService extends IService<Order> {

    Order getOrderById(Integer id);

}

// service实现类
@Slf4j
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {

    @Resource
    private RestTemplate restTemplate;

    private final String PRODUCT_SERVICE_NAME = "cloud-provider-product-consul-8101";

    @Override
    public Order getOrderById(Integer id) {
        // 获取订单
        Order order = this.getById(id);
        // 拼接url
        String url = "http://" + this.PRODUCT_SERVICE_NAME + "/product/query/" + order.getProductId();
        log.info(url);
        // 远程调用
        Product product = this.restTemplate.getForObject(url, Product.class);
        order.setProduct(product);
        // 返回结果
        return order;
    }

}

// controller实现类
@RestController
@RequestMapping("/order")
public class OrderController {

    @Resource
    private OrderService orderService;

    @GetMapping("/query/{id}")
    public Order getOrderById(@PathVariable Integer id) {
        return this.orderService.getOrderById(id);
    }

}

启动项目之后,出现如下界面,就表示启动成功了:

bug

当两个服务都启动之后,就调用127.0.0.1:81/order/query/1来进行调用,查看远程调用是否成功。本来计划着没啥问题,但是,出错了,如下图:

原因是因为Consul自动实现了负载均衡,但是并没有引入负载均衡的依赖,于是我就去查看了一下pom依赖。原来是Consuljar包中带了负载均衡的依赖啊,如下图:

那这就说的通了,直接给RestTempalte加上@LoadBlanced注解。

@Configuration
public class RestTemplateConfig {

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

}

加上注解之后,再次测试,完全没毛病了。

总结 

Consul组件跟Eurekal相比,不用再搭建一个Consul服务了。只要简单的三步:

引入依赖、添加配置、启动类添加注解。如果是使用RestTemplate等远程调用的话,再加一个负载均衡的注解即可。非常好用。

Consul对于CAP理论来说,保证的是强一致性。

分布式配置管理

在最上面的概述中,已经将分布式配置管理给简单介绍了一下。总的来说,分布式配置管理就是把一些配置文件放到Consul服务器上,这样可以做到无感刷新配置文件,从而减少开发、运维人员的工作。

bootstrap.yml

application.yml是用户级的资源配置项,bootstrap.yml是系统级的资源配置项,优先级更高。

SpringCloud会创建一个BootStrap Context,作为Spring应用Application Context的父上下文。初始化的时候,BootStrap Context负责从外部源加载配置属性并解析配置,这两个上下文共享一个从外部获取的Environment。

BootStrap属性有高优先级,默认情况下,他们不会被本地配置覆盖。BootStrap Context和Application Context有着不同的约定,所以新增了一个bootstrap.yml文件,保证两个上下文配置的分离。

修改商品服务

添加依赖

        <!--Consul配置中心-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-consul-config</artifactId>
        </dependency>
        
        <!--BootStrap-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bootstrap</artifactId>
        </dependency>

修改配置文件

首先在Consul的Key/Value界面新建文件夹,用来存储配置文件:

在创建的config文件夹下,再创建三个文件:

 在每个文件夹下,再创建一个data文件:

在每一个data文件中,再分别加入内容,并且保证格式为yml格式。

注意,创建文件夹时要在名称后面带/,创建文件时则不需要。 

其次在商品服务下创建一个bootstrap.yml的文件,然后将原先appliaction.yml的文件全部迁移至bootstrap.yml文件中。

server:
  port: 8101

spring:
  application:
    name: cloud-provider-product-consul-8101

  mvc:
    pathmatch:
      matching-strategy: ant_path_matcher # 路径匹配策略

  datasource:
    url: jdbc:mysql://127.0.0.1:3306/cloud_product?characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true&allowMultiQueries=true
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver

  profiles:
    active: dev

  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        service-name: ${spring.application.name}
      config:
        profile-separator: '-' # default value is ",",we update '-'
        format: YAML

mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  mapper-locations: classpath:mapper/**Mapper.xml
  type-aliases-package: com.wbz.domain

在配置文件中,新增了如下配置:

spring:
  cloud:
    consul:
      config:
        profile-separator: '-' # default value is ",",we update '-'
        format: YAML

修改业务类

在业务类中添加如下代码,用来测试远程调用是否成功。

@RestController
@RequestMapping("/product")
public class ProductController {

    @Value("${test}")
    private String test;

    @Value("${info}")
    private String info;

    @GetMapping("/config")
    public String getConfig() {
        return test + " " + info;
    }

}

启动项目之后,输入127.0.0.1:8101/product/config,出现如下界面,表示分布式配置管理成功:

 出现上述内容的原因是因为在配置文件中配置的环境为dev环境,如果把环境改为prod环境,那么就会出现如下内容:

总结

为什么配置文件是要写成上述格式?

如下图,为官网的配置格式。大概内容是想要把配置文件放到Consul上,首先要建立一个config文件夹,官方才认为这是配置文件中的内容;其次,对应的服务就要再创建文件夹进行配置,这样官方才认为该文件夹中的文件是某服务的文件。例如在咋们自己配置的文件中,首先创建了一个config文件,然后在下面创建了以cloud-provider-product-consul-8101为开头的三种文件夹。

在三种文件夹中,没有环境配置的表示每个环境都能读取其中的内容,也就是公共配置;后面有dev、prod的则表示是对应环境的配置,当然可以有其他的,这里只是做演示。

官网使用的是逗号来分割服务名和生产环境的,但是我们使用的是-,这是可以在配置文件中进行配置的。当然了,最后的文件夹的data名字也是可以进行配置的,官方默认是data。

修改真正配置

商品服务

首先将原来测试的配置文件、业务类等全部删除,然后只保留dev环境的文件夹,其他文件夹也删除,暂时用不上。

Consul上的配置文件

server:
  port: 8101
  
spring:
  mvc:
    pathmatch:
      matching-strategy: ant_path_matcher # 路径匹配策略

  datasource:
    url: jdbc:mysql://127.0.0.1:3306/cloud_product?characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true&allowMultiQueries=true
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver

mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  mapper-locations: classpath:mapper/**Mapper.xml
  type-aliases-package: com.wbz.domain

 bootstrap.yml中的配置文件

spring:
  application:
    name: cloud-provider-product-consul-8101

  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        service-name: ${spring.application.name}
      config:
        profile-separator: '-' # default value is ",",we update '-'
        format: YAML

  profiles:
    active: dev

 修改完成之后,可以进行一个简单测试,输入127.0.0.1:8101/product/query/1001,只要出现结果就证明部署成功了。如果在部署过程中出错,可能是业务类没有删除,或者生产环境出错导致的。

订单服务

Consul上的配置文件

server:
  port: 81

spring:
  mvc:
    pathmatch:
      matching-strategy: ant_path_matcher # 路径匹配策略

  datasource:
    url: jdbc:mysql://127.0.0.1:3306/cloud_order?characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true&allowMultiQueries=true
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver

mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  mapper-locations: classpath:mapper/**Mapper.xml
  type-aliases-package: com.wbz.domain

bootstrap中的配置文件

spring:
  application:
    name: cloud-consumer-order-consul-81

  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        service-name: ${spring.application.name}
      config:
        profile-separator: '-'
        format: yaml

  profiles:
    active: dev

切记,在修改的时候要记住先加上依赖,再修改配置文件。

动态刷新

启动项目之后,我们在Consul上的配置文件中进行修改,发现并没有起作用,原因是因为默认的刷新时间为55秒,因此我们修改之后是不会立刻起作用的,我们就需要等待一段时间。

在实际生产中,这个时间太长了,因此我们可以在主启动类上加上@RefreshScope注解,该注解的意思就是动态刷新。

持久化配置

当我们关闭小黑框之后,发现所有的配置都消失了,这是一个致命的错误,因此我们需要把配置持久化。

1. 新建一个mydata文件夹

 2. 创建一个consul_start.bat的文件

3. 进入文件,将下述内容添加到文件中,注意下述内容中的文件地址要和自己的一样。

@echo.服务启动......  
@echo off  
@sc create Consul binpath= "E:\java\spring\springcloud\consul_1.19.1\consul.exe agent -server -ui -bind=127.0.0.1 -client=0.0.0.0 -bootstrap-expect  1  -data-dir E:\java\spring\springcloud\consul_1.19.1\mydata   "
@net start Consul
@sc config Consul start= AUTO  
@echo.Consul start is OK......success
@pause

4. 点击文件,右键管理员方式运行。

5. 运行成功之后,查看任务管理器,发现consul.exe已经变成了后台进程,那么表示此时就持久化成功了。 

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

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

相关文章

MAC备忘录空白解决方案

打开icloud->备忘录 取消勾选同步此MAC后再次勾选&#xff0c;然后点击完成即可。

<<迷雾>> 第7章 会变魔术的触发器(3)--R-S 触发器 示例电路

用来验证或非门反馈功能的完整电路 info::操作说明 如演示出现异常, 可点右侧面板的重置按钮重置 此处 R 和 S 都使用的是按钮开关 点击 R 可让 Q 熄灭 点击 S 可让 Q 亮起 primary::在线交互操作链接 https://cc.xiaogd.net/?startCircuitLinkhttps://book.xiaogd.net/cyjsjd…

针对线上消息积压的排查思路以及解决方案

一、背景 我们在日常工作中&#xff0c;经常会碰到线上告警&#xff0c;消息队列消息积压了&#xff0c;试想如果对消息的消费速率有要求的场景&#xff0c;消息积压一定会或多或少对自己本身的业务场景有影响&#xff0c;这里就针对消息积压的场景&#xff0c;谈谈具体的排查…

过滤器Filter【详解】

过滤器Filter 1、 现有问题 在以往的Servlet中&#xff0c;有冗余的代码&#xff0c;多个Servlet都有重复的代码 比如编码格式设置 登录信息认证 2、 概念 过滤器&#xff08;Filter&#xff09;是处于客户端与服务器目标资源之间的一道过滤技术。 过滤器 3、 过滤器作用 执…

Python办公自动化教程(006):Word添加标题

2.3 word标题 介绍&#xff1a; 在 python-docx 中&#xff0c;您可以使用 add_heading() 方法为文档添加标题。此方法允许您指定标题的文本和级别&#xff08;例如&#xff0c;一级标题、二级标题等&#xff09;。标题级别的范围是从 0 到 9&#xff0c;其中 0 表示文档的主标…

深度解析:从浏览器输入链接到页面展现的奇幻历程

〇、前言 当我们在浏览器中输入一个网址&#xff0c;例如&#xff1a;example.com&#xff0c;按下回车键后&#xff0c;会发生什么呢&#xff1f; 主要会发生以下这些过程&#xff1a;域名解析、建立HTTP连接、发送HTTP请求、数据传输、渲染网页、断开HTTP连接。 一、域名解…

类型转换【C++提升】(隐式转换、显式转换、自定义转换、转换构造函数、转换运算符重载......你想知道的全都有)

更多精彩内容..... &#x1f389;❤️播主の主页✨&#x1f618; Stark、-CSDN博客 本文所在专栏&#xff1a; C系列语法知识_Stark、的博客-CSDN博客 座右铭&#xff1a;梦想是一盏明灯&#xff0c;照亮我们前行的路&#xff0c;无论风雨多大&#xff0c;我们都要坚持不懈。 一…

【srm系统】供应商管理,招投标管理,电子采购系统,询价管理

前言&#xff1a; 随着互联网和数字技术的不断发展&#xff0c;企业采购管理逐渐走向数字化和智能化。数字化采购平台作为企业采购管理的新模式&#xff0c;能够提高采购效率、降低采购成本、优化供应商合作效率&#xff0c;已成为企业实现效益提升的关键手段。系统获取在文末…

[含文档+PPT+源码等]精品基于Python实现的美术馆网站的设计与实现

基于Python实现的美术馆网站&#xff0c;其设计与实现背景主要源于以下几个方面的需求和发展趋势&#xff1a; 一、文化艺术领域的发展需求 随着文化娱乐活动的日益丰富&#xff0c;美术馆作为展示艺术作品、传播文化的重要场所&#xff0c;其管理和服务模式的创新对于提升公…

LabVIEW提高开发效率技巧----使用动态事件

在LabVIEW开发过程中&#xff0c;用户交互行为可能是多样且不可预知的。为应对这些变化&#xff0c;使用动态事件是一种有效的策略。本文将从多个角度详细介绍动态事件的概念及其在LabVIEW开发中的应用技巧&#xff0c;并结合实际案例&#xff0c;说明如何通过动态事件提高程序…

【售后资料】软件售后服务方案(word原件)

软件售后服务方案的售后服务范围广泛&#xff0c;涵盖了多个方面&#xff0c;以确保客户在使用软件过程中得到全面、及时的支持。具体来说&#xff0c;这些服务范围通常包括以下几个核心内容&#xff1a; 技术支持服务维护与更新服务培训与教育服务定制化服务数据管理与服务客户…

如何获取网页内嵌入的视频?

如何获取网页内嵌入的视频&#xff1f; 有时插件无法识别的视频资源&#xff0c;可以通过手动使用浏览器的开发者工具来抓取。你可以按照以下步骤操作&#xff1a; 步骤&#xff1a; 打开网页并按 F12&#xff1a;在视频页面按下 F12 或右键点击网页并选择“检查”或“Inspe…

Spring Boot实现的大学生就业市场解决方案

1系统概述 1.1 研究背景 如今互联网高速发展&#xff0c;网络遍布全球&#xff0c;通过互联网发布的消息能快而方便的传播到世界每个角落&#xff0c;并且互联网上能传播的信息也很广&#xff0c;比如文字、图片、声音、视频等。从而&#xff0c;这种种好处使得互联网成了信息传…

【案例】距离限制模型透明

开发平台&#xff1a;Unity 2023 开发工具&#xff1a;Unity ShaderGraph   一、效果展示 二、路线图 三、案例分析 核心思路&#xff1a;计算算式&#xff1a;透明值 实际距离 / 最大距离 &#xff08;实际距离 ≤ 最大距离&#xff09;   3.1 说明 | 改变 Alpha 值 在 …

简易投影仪的制作

今天不做开发类的文章&#xff0c;来给大家整个活哈哈哈哈哈。由于前几天室友说看小屏幕的抖音太不舒服&#xff0c;比较累眼睛&#xff0c;所以我萌生出来一个制作投影仪的想法。于是查阅了资料最终完成以下的设计。 以下设计价格最高的是一部旧的可拆卸的智能手机 简易投影仪…

C++11新特性(基础)【2】

目录 1.范围for循环 2.智能指针 3.STL中一些变化 4.右值引用和移动语义 4.1 左值引用和右值引用 4.2 左值引用与右值引用比较 4.3 右值引用使用场景和意义 4.4 右值引用引用左值及其一些更深入的使用场景分析 4.5 完美转发 1.范围for循环 int main() {int array[10] { 1,2,3,4…

CSS | CSS中强大的margin负边距

css中的负边距(negative margin)是布局中的一个常用技巧&#xff0c;只要运用得合理常常会有意想不到的效果。很多特殊的css布局方法都依赖于负边距&#xff0c;所以掌握它的用法对于前端的同学来说&#xff0c;那是必须的。本文非常基础&#xff0c;老鸟可以略过。 一、负边距…

【宽搜】3. leetcode 515 在每个树行中找最大值

1 题目描述 题目链接&#xff1a;在每个树行中找最大值 2 题目解析 根据题目描述&#xff0c;是找出每一行中的最大值&#xff0c;这毋庸置疑是使用宽度优先遍历了。我在这篇文章中讲解了宽度优先遍历的模板&#xff0c;如果没有看的同学可以先去看一下。 这道题和模板的不…

基于微信小程序的调查问卷管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…

数据结构与算法(七)静态链表

目录 前言 一、静态链表的引入 二、线性表的静态链表存储结构 三、静态链表的插入操作 四、静态链表的删除操作 五、静态链表的优缺点总结 1、优点 2、缺点 3、小结 六、单链表小结——Tecent面试题 1、普通解法&#xff1a; 2、高级解法&#xff1a; 前言 静态链表…