Idea+maven+spring-cloud项目搭建系列--13 整合MyBatis-Plus多数据源dynamic-datasource

news2025/4/8 16:38:12

前言:对于同一个系统,不同的租户需要自己独立分隔的数据库(每个数据库的表结构可以是相同的),同时也要支持跨数据源的查询;并且支持分布式事务,如果这里不使用分库分表插件,需要怎样实现?本文采用MyBatis-Plus下的dynamic-datasource 进行实现。

MyBatis-Plus的dynamic-datasource 官网;

开始整合:

1 spring-cloud 整合多数据源:

1.1 maven pom jar包,如果启动发生问题则需要排除版本jar 包冲突的问题:

<!-- mybatis 多数据源切换依赖jar -->
<!-- https://mvnrepository.com/artifact/com.baomidou/dynamic-datasource-spring-boot-starter -->
<dependency>
     <groupId>com.baomidou</groupId>
     <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
     <version>3.5.1</version>
 </dependency>
 <!-- mybatis plus 方便后续业务开发 -->
<!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter -->
 <dependency>
     <groupId>com.baomidou</groupId>
     <artifactId>mybatis-plus-boot-starter</artifactId>
     <version>3.1.1</version>
 </dependency>
  <dependency>
     <groupId>com.baomidou</groupId>
     <artifactId>mybatis-plus-extension</artifactId>
     <version>3.4.2</version>
 </dependency>
  <!-- mysql的连接器 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
  <!-- mysql的分页插件便于业务查询分页处理 -->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>4.1.6</version>
    <exclusions>
        <exclusion>
            <groupId>com.github.jsqlparser</groupId>
            <artifactId>jsqlparser</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>com.github.jsqlparser</groupId>
    <artifactId>jsqlparser</artifactId>
    <version>3.1</version>
</dependency>
 <!-- druid的连接池 -->
<dependency>
     <groupId>com.alibaba</groupId>
     <artifactId>druid-spring-boot-starter</artifactId>
     <version>1.2.8</version>
 </dependency>
  <!-- xxl job 定时任务 便于后续定时任务的扩展,如果不需要可以手动去除 -->
<dependency>
     <groupId>com.xuxueli</groupId>
     <artifactId>xxl-job-core</artifactId>
     <version>2.3.0</version>
 </dependency>
  <!-- web jar 便于hppt 通信 -->
 <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
  <!-- feign 接口 便于服务之间的通信,如果不需要可以手动去除 -->
 <dependency>
     <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-starter-openfeign</artifactId>
 </dependency>
   <!-- lombok 自动生成get set 等方法便于开发  -->
 <dependency>
     <groupId>org.projectlombok</groupId>
     <artifactId>lombok</artifactId>
     <optional>true</optional>
 </dependency>

1.2 bootstrap.yml 文件配置数据源信息:

server:
  port: 8010
  
spring:
  autoconfigure:
    # 排除原有的连接池装配
    exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
  # dynamic-datasource-spring-boot-starter 动态数据源的配置内容
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    dynamic:
      primary: master #设置默认的数据源或者数据源组,默认值即为master
      strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
      datasource:
        master:
          url: jdbc:mysql://localhost:3306/master
          username: root
          password: 123
          driver-class-name: com.mysql.jdbc.Driver # 3.2.0开始支持SPI可省略此配置
        slave1:
          url: jdbc:mysql://localhost:3306/slaveone
          username: root
          password: 123
          driver-class-name: com.mysql.jdbc.Driver
        slave2:
          url: jdbc:mysql://localhost:3306/slavetwo?useUnicode=true&characterEncoding=UTF-8&useAffectedRows=true&useSSL=false&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
          username: root
          password: 123
          driver-class-name: com.mysql.jdbc.Driver

      # druid 公共配置 参数: https://www.jianshu.com/p/f8b720737b20
      druid:
        # 连接池初始化大小
        initialSize: 5
        # 最小空闲连接数
        minIdle: 5
        # 最大连接数
        maxActive: 30
        # 配置获取连接等待超时的时间
        maxWait: 60000
        # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
        timeBetweenEvictionRunsMillis: 60000
        # 配置一个连接在池中最小生存的时间,单位是毫秒
        minEvictableIdleTimeMillis: 300000
        # 配置一个连接在池中最大生存的时间,单位是毫秒
        maxEvictableIdleTimeMillis: 900000
        # 配置检测连接是否有效
        validationQuery: SELECT 'x'
        # 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,
        # 如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效
        testWhileIdle: true
        # 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
        testOnBorrow: false
        # 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
        testOnReturn: false
        #  	是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,
        # 比如说oracle。在mysql下建议关闭。
        poolPreparedStatements: true
        # 	要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true
        maxPoolPreparedStatementPerConnectionSize: 20
        # 属性类型是字符串,通过别名的方式配置扩展插件,监控统计用的filter:stat;
        # 日志用的filter:log4j防御sql注入的filter:wall
        filters: stat,wall,slf4j,config
        # 公用监控数据
        useGlobalDataSourceStat: true
        # 慢日志输出
        stat:
          log-slow-sql: true
          merge-sql: true
          # 10 秒
          slow-sql-millis: 10000

# mybatis mapper 包扫描路径
mybatis-plus:
  mapper-locations: classpath*:mapper/**/*.xml

# xxl job 任务地址
xxl:
  job:
    admin:
      addresses: http://localhost:8080/xxl-job-admin
    accessToken: lgx123456
    executor:
      appname: dev-bluegrass-dynamic-service
      logpath:
      logretentiondays: 10
      ip: localhost
      port: 9087




1.3 启动类增加路径扫描:

package org.lgx.bluegrass.bluegrassdynamic;

import org.lgx.bluegrass.api.constant.BaseConstant;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
// Feign 路径扫描
@EnableFeignClients("xxxx")
// mappper 包路径扫描
@MapperScan(basePackages ={"xxx.bluegrassdynamic.mapper"})
public class BluegrassDynamicApplication {
    public static ConfigurableApplicationContext applicationContext;
    public static void main(String[] args) {
        applicationContext = SpringApplication.run(BluegrassDynamicApplication.class, args);
    }

}

2 多数据源主动切换:

考虑方法:虽然使用@Ds 可以在类中进行切换,但是需要在每个类或者方法上增加改注解,如果增加错误或者漏加,就会切错数据库或者默认访问到主库中;那么是否可以有一个切面或者拦截器,可以在http 请求进入到方法之前,就完成一次切换;
2.1 定义拦截器根据当前登录的用户的便签完成数据源的切换:

import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Slf4j
@Component
public class HttpRequestDynamic  extends HandlerInterceptorAdapter {
    final static ThreadLocal<Boolean> threadLocal = new ThreadLocal<>();
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (!(handler instanceof HandlerMethod)) {
            // 不是 httpreuqest 请求直接放行
            return true;
        }
        // 进行数据源的校验:通过@Master 注解标记本次http 请求是否访问主库,不对方法标记则默认需要访问从库
        // 1 如果本次访问公共库,则不需要进行数据源的切换,因为默认访问Master 库
        // 2 如果发现没有标记,则代表本次请求需要进行数据源的切换以便访问从库
        //   2.1 如果访问从库发现用户的header 没有userDb 标识则不允许访问
        //   2.2 如果有标识但是标识无效 也不允许访问
      
        HandlerMethod h = (HandlerMethod)handler;
        Object anotion = h.getMethodAnnotation(Master.class);
        if (null != anotion){
            // 访问公开库
            return true;
        }
        // 当前用户归属的db
        String userDb = request.getHeader("userDb");
        if (!StringUtils.hasText(userDb)){
            // 当前访问从库,没有数据归属标识,需要禁止访问
            throw new Exception("没有设置用户信息,不允许访问 ");
        }
        // 当前标识是否正确
        // 获取用户所属数据源
        String dbName = getDbName(userDb);
        if (!StringUtils.hasText(dbName)){
            // 数据源标识异常,禁止访问
            throw new Exception("用户信息异常,不允许访问 ");
        }
        // 切换数据源
        DynamicDataSourceContextHolder.push(dbName);
        threadLocal.set(true);

        return true;
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 在方法执行完毕或者执行报错后,移除数据源
        if (null != threadLocal.get() && threadLocal.get()){
            DynamicDataSourceContextHolder.clear();
        }
        threadLocal.remove();
    }
    private String getDbName(String userDb) {
        // 此处根据用户便签动态映射配置的数据库,可以在配置文件配置 userDb为key ,具体数据源为value,的map
        // 然后从Map 中获取 ,本文在nacos 配置映射关系,并当做bean 加载到spring 中
         return DynamicDbConfig.dbTypeMap.get(userDb);
    }
}

Master 标记:

import java.lang.annotation.*;

/**
 * 主库数据源
 *
 * @author ruoyi
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Master {

}

对需要访问Maser 库的方法或者类进行标记:
在这里插入图片描述

数据源标识配置文件:DynamicDbConfig

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

@Configuration
public class DynamicDbConfig {
    public static Map<String , String> dbTypeMap = new HashMap<>(20);

    /**
     * 数据源与userDb 的 映射关系
     * @param dbTypeMap
     */
    @Value("#{${dynamic.dbtype:{}}}")
    public void platformCoursePageLimitMap(Map<String, String> dbTypeMap) {
        DynamicDbConfig.dbTypeMap = dbTypeMap;
    }
}

nacos 中配置Map 映射关系格式如下:

dynamic:
  # 数据源与租户配置
  dbtype: '{"不同的userdb1":"配置的数据源1","不同的userdb1":"配置的数据源2","不同的userdb2":"配置的数据源3"}'

增加异常处理类,对throw new Exception 做异常的封装返回:

@RestControllerAdvice
public class GlobalExceptionHandler{
	/**
	 * 系统异常
	 */
	@ExceptionHandler(Exception.class)
	public AjaxResult handleException(Exception e, HttpServletRequest request){
	    String requestURI = request.getRequestURI();
	    log.error("请求地址'{}',发生系统异常.", requestURI, e);
	    return AjaxResult.error(e.getMessage());
	}
}

AjaxResult 返回实体可以根据自身业务自行封装:

public class AjaxResult extends HashMap<String, Object> {
    private static final long serialVersionUID = 1L;

    /** 状态码 */
    public static final String CODE_TAG = "code";

    /** 返回内容 */
    public static final String MSG_TAG = "msg";

    /** 数据对象 */
    public static final String DATA_TAG = "data";

    /**
     * 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。
     */
    public AjaxResult(){
    }
     public static AjaxResult error(String msg) {
        return AjaxResult.error(msg, null);
    }
}

其中header 头中的数据源标识可以在gateway 中统一进行添加处理:

@Component
@RefreshScope
public class AuthFilter implements GlobalFilter, Ordered {
	 @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    	  ServerHttpRequest.Builder mutate = request.mutate();
    	  addHeader(mutate, "userDb", "自行填充当前用户归属的数据源key 标识");
    	  return chain.filter(exchange.mutate().request(mutate.build()).build());
    }
}

2.2 将拦截器交由spring 管理:


import org.lgx.bluegrass.bluegrassdynamic.interceptor.HttpRequestDynamic;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfiguration implements WebMvcConfigurer {
    @Autowired
    private HttpRequestDynamic httpRequestDynamic;

    /**
     * 拦截器配置
     *
     * @param registry 注册类
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    	// 除了请求报错和请求静态资源外拦截一切http 请求
        registry.addInterceptor(httpRequestDynamic).addPathPatterns("/**")
                .excludePathPatterns(
                        "/file/get/*",
                        "/image/view/*",
                        "/**/error"
                );
    }
}

2.3 此处扩展,对xxl job 定时任务进行aop拦截:



import com.xxl.job.core.context.XxlJobHelper;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class XxlJobHandlerMonitor {

    @Pointcut("@annotation(com.xxl.job.core.handler.annotation.XxlJob)")
    public void xxlJobCut() {
    }

    @Before(value = "xxlJobCut()")
    public void permissionCheck(JoinPoint joinPoint) {

    }

    @Around(value = "xxlJobCut()")
    public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable {
        String param = XxlJobHelper.getJobParam();
        // 解析参数
        // 根据参数所带的目标表数据库,可以进行

        System.out.println("\"123\" = " + "123");

        return joinPoint.proceed();
    }
}

2.4 此处扩展,对feign 进行拦截(可以将user的便签信息设置到header中,同各个服务的拦截器协同作用):
方式1 在feign 的http 请求发送之前,构建header:



import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.lgx.bluegrass.api.constant.Constant;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

/**
 * @Description
 * @Date 2021/4/16 15:39
 * @Author lgx
 * @Version 1.0
 */
@Configuration
public class FeignConfiguration implements RequestInterceptor {
    private Logger logger = LoggerFactory.getLogger(FeignConfiguration.class);

    @Override
    public void apply(RequestTemplate requestTemplate) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder
                .getRequestAttributes();
        if (attributes != null) {
            String authorization = attributes.getRequest().getHeader(Constant.X_AUTHORIZATION);
            if (null != authorization) {
                requestTemplate.header(Constant.X_AUTHORIZATION, authorization);
            }
            String dbName = attributes.getRequest().getHeader(Constant.dbName);
            if (null != dbName) {
                requestTemplate.header(Constant.dbName, dbName);
            }
            String userDb = attributes.getRequest().getHeader(Constant.userDb);
            if (null != userDb) {
                requestTemplate.header(Constant.userDb, userDb);
            }
        }
    }
}

Constant.java:

public class Constant{
        public static final String BASIC_AUTH_TOKEN_PREFIX = "Basic";
        public static final String X_AUTHORIZATION = "X-Authorization";
        public static final String dbName = "dbName";
        public static final String userDb = "userDb";
}

方式二,feign 方法中增加header 参数:
feign 接口定义:

import org.lgx.bluegrass.api.service.impl.SyncFeignDynamicServiceFallBack;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@FeignClient(name = "dynamic", url = "http://localhost:8010", fallback = SyncFeignDynamicServiceFallBack.class)
public interface SyncFeignDynamicService {
	// @RequestHeader("userDb") 将用户所属数据库信息放入header 请求头中
    @RequestMapping("/user/getUserById")
    Object getUserById(@RequestParam("userId") Integer userId,@RequestHeader("userDb") String userDb) ;

}

controller测试方法,配合拦截器使用在进入该方法之前已经完成数据源切换:

  @RequestMapping("/getUserById")
    public User getUserById(@RequestParam("userId") Integer userId) {
        return userService.getUserById(userId);
    }

3 业务中跨数据源使用 :

3.1 增加bean 的获取类,通过改工具获取spring 容器的bean:



import org.lgx.bluegrass.bluegrassdynamic.BluegrassDynamicApplication;
import org.springframework.stereotype.Component;

@Component
public class ApplicationContextUtils {

    public static <T> T getBean(String beanName){
        return (T) BluegrassDynamicApplication.applicationContext.getBean(beanName);
    }
    public static <T> T getBean(Class classz){
        return (T) BluegrassDynamicApplication.applicationContext.getBean(classz);
    }
}

BluegrassDynamicApplication.applicationContext 对应启动类,在类启动后已经对其进行赋值:
在这里插入图片描述
3.2 service 中业务调用:
首先需要明确自己需要跨哪几个库进行数据的增删改查;然后在创建方法后,有几种选择可以完成数据源的切换;
(1)可以在方法上增加改数据源的标识:
在这里插入图片描述
(2)也可以手动切换数据库:

//切换数据源
* DynamicDataSourceContextHolder.push("配置的数据库名称");
* //中间的业务操作。。。
* //可以最后选择清理掉此数据源
* DynamicDataSourceContextHolder.clear();

手动切换请务必保证push和clear 的成对性;

3.2 跨表操作数据要想保证各个数据库数据的一致性,需要使用@DSTransactional 对方法进行标记,开启分布式事务;
在这里插入图片描述

3.3 切换数据源查询需要注意数据源切换失效的几个事项:

  • 同一个方法中进行方法this调用,因为使用的不是spring 加强的bean ,此时即使后面方法使用@DS 进行标记也会失败,此时要么从spring 容器中获取需要的bean,或者将方法移入其它的类;可以通过本文中已经放入的工具类完成 bean 的获取:ApplicationContextUtils.getBean(类.getClass());
  • 在跨数据源进行数据的修改时需要使用 @DSTransactional 而不是 sprin原生的@Transactional 注解,进行分布式事务的保证;
  • 因为@DSTransactional 实际上也是采用的aop 模式进行的加强,所有原有对@Transactional 事务失效的方式@DSTransactional 也会失效;

4 总结:

通过引入多数据源dynamic-datasource ,druid ,mysql-connector,pagehelper,mybatis-plus 完成对spring-cloud 的多数据源整合,通过使用@DS 和 @DSTransactional 保证数据源的切换和分布式事务的支持;

5 参考:

5.1 mybatis-plus 多数据源;
5.2 多数源配置其一(dynamic-datasource);
5.3 mybatis-plus 多数据源,源码解析;

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

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

相关文章

python-day4(字符串、列表、生成式和生成器、使用元组、集合、字典)

字符串和常用数据结构 简单用法 所谓字符串&#xff0c;就是由零个或多个字符组成的有限序列&#xff0c;一半记为sa1a2a3…an(0<n<∞)。在python中&#xff0c;如果我们把单个或多个字符用单引号或者双引号包围起来&#xff0c;就可以表示一个字符串。 s1 hello, wo…

【生物信息】用隐马尔可夫模型对生物序列进行建模

文章目录 Modeling biological sequences with HMMSParsing longer sequences. 举例子Our frst HMM: Detecting GC-rich regionsRunning the model: Probability of a sequence 维特比算法 Viterbi一个摸球例子回到课堂 求解参数 来自Manolis Kellis教授的课 教了隐马尔可夫在基…

FPGA开发之HDMI Transmitter接口设计

HDMI简介&#xff1a; High Definition Multimedia 高清多媒体接口&#xff0c;一种全数字化视频和声音发送接口&#xff0c;可以发送未压缩的音频及视频信号 物理接口&#xff1a; 电气介绍&#xff1a; TMDS&#xff08;Transition Minimized Differential Signaling&#x…

自定义类型——位段

什么是位段&#xff1f; 位段又叫做位域&#xff0c;具体是一种可以把数据以位的形式紧凑的存储&#xff0c;并允许程序员对此结构位进行操作的数据结构 当结构体的成员变量定义之后浪费了较大的空间 &#xff08;比如int a 2&#xff0c;则浪费了30个比特位的空间&#xff0…

Mail 服务器

Mail 服务器 1. 概念及协议2. 工具2.1 Postfix2.2 dovecot2.3 bind 3. 搭建3.1 DNS服务设置3.2 安装配置 postfix3.3 安装配置 dovecot 4. foxmail验证 1. 概念及协议 邮件服务器也采用的是C/S工作模式&#xff0c;通过SMTP,POP,IMAP协议来是实现邮件的发送和接收的。 SMTP 的…

Java入坑之IO操作

目录 一、IO流的概念 二、字节流 2.1InputStream的方法 2.2Outputstream的方法 2.3资源对象的关闭&#xff1a; 2.4transferTo()方法 2.5readAllBytes() 方法 2.6BufferedReader 和 InputStreamReader 2.7BufferedWriter 和 OutputStreamWriter 三、路径&#xff1a;…

Qt5 编译QtXlsx并添加为模块[Windows]

00.QtXlsx是什么&#xff1f;能干什么&#xff1f; QtXlsx是一个可以读写Excel文件的库。它不需要Microsoft Excel&#xff0c;可以在Qt5支持的任何平台上使用。 可以创建、读取、编辑.xlsx文件。 01.如何编译&#xff1f; 1.1编译环境&#xff1a; Windows10平台&#xff1b;…

es6笔记-let、const、var的区别

let、const、var的区别 变量提升 var 声明的变量存在变量提升,在声明前可以调用&#xff0c;直为undefindconsole.log(a); var a 1;相当于&#xff1a;var a; console.log(a); a 1;let和const不存在变量提升&#xff0c;变量要在声明前调用&#xff0c;否则报错console.log(a…

DNS服务器配置

一&#xff0c;正向解析 1>安装软件bind 提供DNS服务的软件叫bind&#xff0c;服务名是named [rootserver ~]# yum install bind -y 2>对三个配置文件进行修改 - /etc/named.conf : 主配置文件&#xff0c;共59行&#xff0c;去除注释和空行之和有效行数仅30行左右&…

Golang程序报错:fatal error: all goroutines are asleep - deadlock

文章目录 1.原始代码2.错误原因分析3. 解决方案4. 经验总结5. 练习 完整的报错信息如下&#xff1a; fatal error: all goroutines are asleep - deadlock!goroutine 1 [chan receive]: main.(*WorkerManager).KeepAlive(0xc000088f60)/root/go_workspace/studygoup/05.go:66 0…

Doris(7):数据导入(Load)之Routine Load

例行导入功能为用户提供了义中自动从指定数据源进行数据导入的功能 1 适用场景 当前仅支持kafka系统进行例行导入。 2 使用限制 支持无认证的 Kafka 访问&#xff0c;以及通过 SSL 方式认证的 Kafka 集群。支持的消息格式为 csv 文本格式。每一个 message 为一行&#xff0c;…

【Cpp】手撕搜索二叉树(K模型)

文章目录 二叉搜索树概念详解二叉搜索树的概念二叉搜索树的操作(大致思路)二叉搜索树的查找二叉搜索树的插入二叉搜索树的删除(最重点) 手撕搜索二叉树代码结点定义(以key型为例,KV型将在下一篇博客中介绍)树结构定义深拷贝构造函数与构造函数赋值重载析构函数遍历(结果按从小到…

软件测试的当下分析

在没有清晰能见度的情况下驾驶汽车不仅非常危险&#xff0c;也十分鲁莽。这会让我们和我们周边的人随时面临着碰撞、受伤、甚至死亡的风险。如果不能看到前方的道路&#xff0c;我们就无法预测潜在的危险或障碍&#xff0c;从而无法做出明智的决定并采取适当的行动。 同样&…

什么是ddos攻击?ddos攻击有哪些危害?

一、什么是 DDoS 攻击&#xff1f; DDoS 是 Distributed Denial of Service 的缩写&#xff0c;翻译成中文就是 “分布式拒绝服务”。DDoS 攻击将处于不同位置的多个计算机联合起来作为攻击平台&#xff0c;对一个和多个目标发动 DDoS 攻击&#xff0c;从而成倍提高攻击威力。…

分布式系统概念和设计-进程通信中的(网络API设计)

分布式系统概念和设计 进程间通信 中间件层 请求-应答协议 编码和外部数据表示 因特网协议的API 进程间通信的特征 一对进程间进行消息传递需要两个消息通信操作的支持&#xff08;send和receive&#xff09;&#xff0c;用于定义目的地和消息的定义。 为了能够使一个进程能…

煤化工废水除总氮除硬度,矿井水除砷除氟解决方案

随着环保标准的提升&#xff0c;大部分煤矿企业对矿井水要求执行地表三类水标准&#xff0c;氟化物要求小于1mg/l&#xff0c;这类项目存在体量大、氟含量低、水质偏差等特点。 RO工艺制备纯水是煤化工行业生产的一个辅助环节&#xff0c;会产生大量的浓盐水&#xff0c;由于浓…

十五分钟带你学会 Electron

文章目录 什么是 Electron为什么要选择 Electron安装 Electron桌面CSDN实战Electron 基础配置Electron 进程主进程渲染进程主进程与渲染进程的区别主进程与渲染进程的通信 Electron 跨平台问题Electron 部署打包应用程序发布应用程序 Electron 跨端原理总结 什么是 Electron E…

NE555 Motor LED Chaser

文章目录 1.前言2.资料下载 1.前言 这个是从YouTube上搬运来的&#xff0c;如图所示 2.资料下载 所需材料 #1# 10k resistor 1 #2# 10k variable resistor 1 #3# 10uf capacitor 1 #4# 3mm blue led 4 #5# 3mm yellow led 4 #6# 3mm red led 4 #7# 3mm green led 4 #8# 3mm…

【Linux网络】网络基础(TCP/IP协议栈、局域网通信原理、封装与解包、有效载荷分用)

文章目录 1、认识网络1.1 重新看待计算机结构1.2 网络的问题1.3 初识网络协议1.4 TCP/IP五层结构 2、网络与操作系统2.1 网络和OS的关系2.2 局域网&#xff08;以太网&#xff09;通信原理和MAC地址2.3 主机的跨网络2.4 有效载荷的分用 1、认识网络 在早年计算机之间是相互独立…

关于自身存在的严重问题总结_4/19

今早二次面试喜马拉雅&#xff0c;面试官给我的评价是&#xff1a; 1.经验不足&#xff1b; 2.实用方面生疏、理解不到位&#xff1b; 原因很正常&#xff0c;我项目自己亲手实操的太少了&#xff0c;一直在背&#xff0c;但是背 不是去读源码 去理解&#xff1b; 项目也大…