springboot mongodb配置解析

官网配置

前言

由于以前使用mongo都是简单的本地裸奔,根本没有设置过用户密码,昨天收到运维同事反馈说服务器被挖矿木马攻击了,吓得赶紧在docker里面开启认证,但是设置完后问题来了,开发同事又说不能用配置文件如下

spring:
  data:
    mongodb:
      host: 127.0.0.1
      port: 27017
      username: sunny
      password: 123456
      database: admin

看起来很ok但是在启动boot的时候在会连接两次(别问我为什么是两次,boot源码还没有看多少),第一次是失败了,但是第二次成功了,但是读取数据时候又用的是mongoTemplate(第一次创建的)这个导致一直卡死,没有办法只能用uri方式配置了,下面介绍boot怎么解析它

配置文件(yml)

spring:
  data:
    mongodb:
      uri: mongodb://sunny:123456@127.0.0.1:27017/file

对应的java代码是

@ConfigurationProperties(prefix = "spring.data.mongodb")
public class MongoProperties {
    public static final String DEFAULT_URI = "mongodb://localhost/test";
    // 默认uri=
    public String determineUri() {
		return (this.uri != null ? this.uri : DEFAULT_URI);
	}
	....
    // 关键代码 idea ctrl+g 169 就可以看到
    public String getMongoClientDatabase() {
		if (this.database != null) {
			return this.database;
		}
		return new MongoClientURI(determineUri()).getDatabase();
	}
}
// 读取yml文件前缀为(spring.data.mongodb)

由源码可以看出这里如果uri=null则使用DEFAULT_URI,然后进入MongoClientURI

public class MongoClientURI {
    private final ConnectionString proxied;
    private final MongoClientOptions.Builder builder;

    /**
     * Creates a MongoURI from the given string.
     * 从给定的字符串创建一个MongoURI
     * @param uri the URI
     */
    public MongoClientURI(final String uri) {
        this(uri, new MongoClientOptions.Builder());
    }
     /**
     * Mongo database URI. Cannot be set with host, port and credentials.
     */
    public MongoClientURI(final String uri, final MongoClientOptions.Builder builder) {
        this.builder = notNull("builder", builder);
        proxied = new ConnectionString(uri);
    }
}

到这里了就先看new MongoClientOptions.Builder() 方法

@Immutable
public class MongoClientOptions {
        /**
         * Creates a Builder for MongoClientOptions, getting the appropriate system properties            *for initialization.
         *  这里MongoClientOptions创建一个构建器,并且设置一些系统属性
         */
        public Builder() {
    /*设置心跳频率,在集群中*/        heartbeatFrequency(Integer.parseInt(System.getProperty("com.mongodb.updaterIntervalMS", "10000")));
    /*最小心跳频率*/ minHeartbeatFrequency(Integer.parseInt(System.getProperty("com.mongodb.updaterIntervalNoMasterMS", "500")));
    /*设置用于群集心跳的连接的连接超时*/        heartbeatConnectTimeout(Integer.parseInt(System.getProperty("com.mongodb.updaterConnectTimeoutMS", "20000")));
    /*socket 链接超时*/        heartbeatSocketTimeout(Integer.parseInt(System.getProperty("com.mongodb.updaterSocketTimeoutMS", "20000")));
    /*可接受的延迟差 15秒*/        localThreshold(Integer.parseInt(System.getProperty("com.mongodb.slaveAcceptableLatencyMS", "15")));
        }
}

System.getProperty(xxxx)可以在jvm里面设置或者直接在boot启动方法里面设置。

看到这里后发现使用了一个proxied(代理)继续往下看

  public class ConnectionString {  
  /**
     * Creates a ConnectionString from the given string.
     * 
     * @param connectionString     the connection string
     * @since 3.0
     */
    public ConnectionString(final String connectionString) {
        this.connectionString = connectionString;
        //这里可以看出支持两种协议:mongodb://和mongodb+srv://
        boolean isMongoDBProtocol = connectionString.startsWith(MONGODB_PREFIX);
        boolean isSRVProtocol = connectionString.startsWith(MONGODB_SRV_PREFIX);
        if (!isMongoDBProtocol && !isSRVProtocol) {
            throw new IllegalArgumentException(format("The connection string is invalid. "
                    + "Connection strings must start with either '%s' or '%s", MONGODB_PREFIX, MONGODB_SRV_PREFIX));
        }
        // 截取协议头,eg:sunny:123456@127.0.0.1:27007/file?connectTimeoutMS=400000
        String unprocessedConnectionString;
        if (isMongoDBProtocol) {
            unprocessedConnectionString = connectionString.substring(MONGODB_PREFIX.length());
        } else {
            unprocessedConnectionString = connectionString.substring(MONGODB_SRV_PREFIX.length());
        }

        // 分割出用户和主机信息 eg:sunny:123456@127.0.0.1:27007
        String userAndHostInformation;
        int idx = unprocessedConnectionString.lastIndexOf("/");
        if (idx == -1) {
            if (unprocessedConnectionString.contains("?")) {
                throw new IllegalArgumentException("The connection string contains options without trailing slash");
            }
            userAndHostInformation = unprocessedConnectionString;
            unprocessedConnectionString = "";
        } else {
            //userAndHostInformation=sunny:123456@127.0.0.1:27007
            userAndHostInformation = unprocessedConnectionString.substring(0, idx);
             // unprocessedConnectionString=file?connectTimeoutMS=400000
            unprocessedConnectionString = unprocessedConnectionString.substring(idx + 1);
        }

        // Split the user and host information
        String userInfo;
        String hostIdentifier;
        String userName = null;
        char[] password = null;
        idx = userAndHostInformation.lastIndexOf("@");
        if (idx > 0) {
            // userInfo=sunny:123456
            userInfo = userAndHostInformation.substring(0, idx);
            //  hostIdentifier=127.0.0.1:27007
            hostIdentifier = userAndHostInformation.substring(idx + 1);
            // 判断userInfo是否包含@和:出现次数大于1
            int colonCount = countOccurrences(userInfo, ":");
            if (userInfo.contains("@") || colonCount > 1) {
                throw new IllegalArgumentException("The connection string contains invalid user information. "
                        + "If the username or password contains a colon (:) or an at-sign (@) then it must be urlencoded");
            }
            if (colonCount == 0) {
                userName = urldecode(userInfo);
            } else {
                // idx=5
                idx = userInfo.indexOf(":");
                // userName=sunny
                userName = urldecode(userInfo.substring(0, idx));
                //passpword=123456
                password = urldecode(userInfo.substring(idx + 1), true).toCharArray();
            }
        } else {
            hostIdentifier = userAndHostInformation;
        }

        // 验证host
        List<String> unresolvedHosts = unmodifiableList(parseHosts(asList(hostIdentifier.split(",")), isSRVProtocol));
        this.hosts = isSRVProtocol ? resolveHostFromSrvRecords(unresolvedHosts.get(0)) : unresolvedHosts;

        // 解析参数
        String nsPart;
        idx = unprocessedConnectionString.indexOf("?");
        if (idx == -1) {
            nsPart = unprocessedConnectionString;
            unprocessedConnectionString = "";
        } else {
            // nsPart=file 获取数据库名称
            nsPart = unprocessedConnectionString.substring(0, idx);
            // unprocessedConnectionString=connectTimeoutMS=400000
            unprocessedConnectionString = unprocessedConnectionString.substring(idx + 1);
        }
        if (nsPart.length() > 0) {
            nsPart = urldecode(nsPart);
            idx = nsPart.indexOf(".");
            // 是否指定集合
            if (idx < 0) {
                database = nsPart;
                collection = null;
            } else {
                database = nsPart.substring(0, idx);
                collection = nsPart.substring(idx + 1);
            }
        } else {
            database = null;
            collection = null;
        }
        /*数据库健康链接 mongodb+srv 协议才使用*/
        String txtRecordsQueryParameters = isSRVProtocol ? resolveAdditionalQueryParametersFromTxtRecords(unresolvedHosts.get(0)) : "";
        /*链接参数  connectTimeoutMS=400000*/
        String connectionStringQueryParamenters = unprocessedConnectionString;

        Map<String, List<String>> connectionStringOptionsMap = parseOptions(connectionStringQueryParamenters);
        Map<String, List<String>> txtRecordsOptionsMap = parseOptions(txtRecordsQueryParameters);
        //判断是否设置 authsource、replicaset
        if (!ALLOWED_OPTIONS_IN_TXT_RECORD.containsAll(txtRecordsOptionsMap.keySet())) {
            throw new MongoConfigurationException(format("A TXT record is only permitted to contain the keys %s, but the TXT record for "
            + "'%s' contains the keys %s", ALLOWED_OPTIONS_IN_TXT_RECORD, unresolvedHosts.get(0), txtRecordsOptionsMap.keySet()));
        }
        Map<String, List<String>> combinedOptionsMaps = combineOptionsMaps(txtRecordsOptionsMap, connectionStringOptionsMap);
        if (isSRVProtocol && !combinedOptionsMaps.containsKey("ssl")) {
            combinedOptionsMaps.put("ssl", singletonList("true"));
        }
        translateOptions(combinedOptionsMaps);
        credential = createCredentials(combinedOptionsMaps, userName, password);
        warnOnUnsupportedOptions(combinedOptionsMaps);
    }
  }

完结

mongo这块代码是临时看的,可能没有太认真,如果有误请指出,还有就是mongodb+srv 协议没有使用过,有使用经验的欢迎留言交流,谢谢

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值