动态数据增强及构造方案解决

news2024/9/20 20:33:14

前言

随着数据量的增长以及业务的调整变更,我们需要选择合适的技术及存储引擎对数据进行归类,调整,达到高并发、秒响应、低延迟及可扩展对现有程序的改造升级

问题&现状

  1. 任务重,时间紧,人力不足,不能够重构业务
  2. 平滑切换,客户无感知
  3. 自建git服务器一套代码,要兼容不同分支发布

解决方案

1.对于问题一,我们要采用oop to aop 的变成思维的转变,即通过切面来完成,尽量不改动原来的业务逻辑,尤其是之前的老系统,多分组,一个改动,可能导致几十个bug 的,横空出世,太太可怕了吧
2. 第二个问题就是兼容,略带框架思维,走配置化和开关,不仅可以起到有效的解耦,而且还可以做到有效的补偿,有问题及时解决
3. 总体方案 切面 + 配置化

案例分享

直接更换数据库引擎 &根据标记做数据源切换

spring &Mybatis

案例一:基于包扫描的单数据源

1. xml 配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/tx
           http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">

     <bean id="parentDataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="maxActive" value="${windBird.maxActive}"/>
        <property name="initialSize" value="${windBird.initialSize}"/>
        <property name="maxWait" value="${windBird.maxWait}"/>
        <property name="minIdle" value="${windBird.minIdle}"/>
        <property name="timeBetweenEvictionRunsMillis" value="${windBird.timeBetweenEvictionRunsMillis}"/>
        <property name="minEvictableIdleTimeMillis" value="${windBird.minEvictableIdleTimeMillis}"/>
        <property name="validationQuery" value="${windBird.validationQuery}"/>
        <property name="testWhileIdle" value="${windBird.testWhileIdle}"/>
        <property name="testOnBorrow" value="${windBird.testOnBorrow}"/>
        <property name="testOnReturn" value="${windBird.testOnReturn}"/>
        <property name="poolPreparedStatements" value="${windBird.poolPreparedStatements}"/>
        <property name="maxOpenPreparedStatements" value="${windBird.maxOpenPreparedStatements}"/>
    </bean>

    <bean id="newDataSource" parent="parentDataSource">
        <property name="url">
            <value>${jdbc_new.url}</value>
        </property>
        <property name="username">
            <value>${jdbc_new.username}</value>
        </property>
        <property name="password">
            <value>${jdbc_new.password}</value>
        </property>
    </bean>
	
	<bean id="oldDataSource" parent="parentDataSource">
        <property name="url">
            <value>${jdbc_old.url}</value>
        </property>
        <property name="username">
            <value>${jdbc_old.username}</value>
        </property>
        <property name="password">
            <value>${jdbc_old.password}</value>
        </property>
    </bean>
<!--构造一个动态数据源 --> 
    <bean id="enhanceDynamicDataSource" class="com.windBird.service.impl.dataSource.enhanceDynamicDataSource"></bean>
<!--添加一个拦截器 -->  	
    <bean id="enhanceMapperInterceptor" class="com.windBird.service.impl.dataSource.EnhanceMapperInterceptor"></bean>
    <bean id="sqlSessionFactoryTestDataSource" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="enhanceDynamicDataSource"/>
        <property name="typeAliasesPackage" value="com.windBird.service.entity"/>
        <property name="mapperLocations">
            <list>
                <value>classpath:/sqlmap/test/*Mapper.xml</value>
                <value>classpath:/sqlmap/test/enhance/*Mapper.xml</value>
            </list>
        </property>
        <property name="plugins">
            <list>
                <ref bean="testMapperInterceptor"/>
            </list>
        </property>
    </bean>

    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryTestDataSource"/>
        <property name="basePackage" value="com.windBird.service.impl.test.dao"/>
        <property name="annotationClass" value="com.windBird.mybatis.MyBatisRepository"/>
    </bean>

    <bean id="txManagerTestHistory"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
         <property name="dataSource" ref="enhanceDynamicDataSource"/>
		 <qualifier value="testHistory"/>
    </bean>
    <tx:annotation-driven transaction-manager="txManagerTestHistory"/>

</beans>

2. 构造的动态数据源
package com.wind.bird.service.impl.dataSource;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import javax.annotation.Resource;
import javax.sql.DataSource;

/**
 * @author windbird
 * @Date 2022/12/4 22:03
 * @ClassName EnhanceDynamicDataSource
 * @desc:  构造动态数据源
 */
public class EnhanceDynamicDataSource  extends AbstractRoutingDataSource {
    /**
     * 仅仅历史dao层切mapper
     */
    private static final ThreadLocal<Boolean> testReadMark = ThreadLocal.withInitial(() -> false);

    public static void setTestReadMark(Boolean Value){
        testReadMark.set(Value);
    }

    public static Boolean getTestReadMark(){
        return testReadMark.get();
    }


    /**
     * 即将使用的新数据源
     */
    @Resource
    private DruidDataSource newDataSource;

    /**
     * 改造之前的旧数据源
     */
    @Resource
    private DruidDataSource oldDataSource;

    @Override
    protected DataSource determineTargetDataSource() {
        return  testReadMark.get()?newDataSource:oldDataSource;
    }

    @Override
    protected Object determineCurrentLookupKey() {
        return null;
    }

    @Override
    public void afterPropertiesSet() {

    }
}

3. 拦截器
package com.wind.bird.dataSource;

import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.lang.reflect.InvocationTargetException;
import java.util.Properties;

/**
 * @author windbird
 * @Date 2022/12/4 22:15
 * @ClassName EnhanceMapperInterceptor
 * @desc:
 */
@Intercepts({
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class,
                RowBounds.class, ResultHandler.class}),
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class,
                RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class})
})
public class EnhanceMapperInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        if(isInterceptor()){
            /**
             * 获取操作类型 update || query
             */
            String opType = invocation.getMethod().getName();
            /**
             * 采用切dao 层
             */
            if("query".equals(opType)){
                EnhanceDynamicDataSource.setTestReadMark(true);
                Object obj = changeMapperToEnchange(invocation,true);
                EnhanceDynamicDataSource.setTestReadMark(false);
                return obj;
            }
        }
        return invocation.proceed();
    }

    private Object changeMapperToEnchange(Invocation invocation, boolean isChange) throws InvocationTargetException, IllegalAccessException {
        if(isChange){
            Object[] args = invocation.getArgs();
            // 准备执行的MappedStatement
            MappedStatement currMs = (MappedStatement) args[0];
            Configuration config = currMs.getConfiguration();
            String currMsId = currMs.getId();
            // 对应的enhance版本MappedStatement
            MappedStatement stockReportMs = config.getMappedStatement("enhance." + currMsId);
            // 读操作切换sql
            Executor executor = (Executor) invocation.getTarget();
            args[0] = stockReportMs;
            if (args.length == 6) {
                // 替换BoundSql
                args[5] = stockReportMs.getBoundSql(args[1]);
                // 生成新的CacheKey
                args[4] = executor.createCacheKey((MappedStatement) args[0],
                        args[1], (RowBounds) args[2], (BoundSql) args[5]);
            }
        }
        return invocation.proceed();
    }

    /**
     * todo:
     * 是否拦截 默认拦截 具体的根据业务自己实现
     * @return
     */
    private boolean isInterceptor() {
        return true;
    }

    @Override
    public Object plugin(Object target) {
        if(target instanceof Executor){
            return  Plugin.wrap(target, this);
        }
        return target;
    }

    @Override
    public void setProperties(Properties properties) {

    }
}

mapper 改造
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="gy.windbird.impl.dao.OldTableDao">
    <resultMap id="ResultMap" type="oldTable">
        <result property="id" column="id"></result>
        <result property="createDate" column="create_date"></result>
        <result property="modifyDate" column="modify_date"></result>
        <result property="version" column="version"></result>
        <result property="amount" column="amount"></result>
        <collection property="oldDetailList" column="{amount=amount,pid=id}"
                    select="gy.windbird.impl.dao.OldTableDao.getListByPidAndAmount"></collection>
    </resultMap>

    <select id="getEntity" resultMap="ResultMap">
		select id,create_date,modify_date,version,amount
		from old_table where id = #{id}
    </select>

    <insert id="create" parameterType="SupplierStock">
		insert into old_table (id,create_date, version,amount)
		values(#{id},now(), 0,ifnull(#{amount},0))
    </insert>

    <update id="updateAmount" parameterType="SupplierStock">
		update old_table set version = version + 1,
		amount=ifnull(amount,0)+ifnull(#{amount},0) where id = #{id}
    </update>
</mapper>


<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="enhance.gy.windbird.impl.dao.OldTableDao">
    <resultMap id="ResultMap" type="oldTable">
        <result property="id" column="id"></result>
        <result property="createDate" column="create_date"></result>
        <result property="modifyDate" column="modify_date"></result>
        <result property="version" column="version"></result>
        <result property="amount" column="amount"></result>
        <collection property="oldDetailList" column="{amount=amount,pid=id}"
                    select="enhance.gy.windbird.impl.dao.OldTableDao.getListByPidAndAmount"></collection>
    </resultMap>

    <select id="getEntity" resultMap="ResultMap">
		select id,create_date,modify_date,version,amount
		from old_table where id = #{id}
    </select>

    <insert id="create" parameterType="SupplierStock">
		insert into old_table (id,create_date, version,amount)
		values(#{id},now(), 0,ifnull(#{amount},0))
    </insert>

    <update id="updateAmount" parameterType="SupplierStock">
    update old_table set version = version + 1,
    amount=ifnull(amount,0)+ifnull(#{amount},0) where id = #{id}
   </update>
</mapper>

mapper 层 改造 包结构包结构

备注

 <collection property="oldDetailList" column="{amount=amount,pid=id}"                        select="enhance.gy.windbird.impl.dao.OldTableDao.getListByPidAndAmount"></collection>
1. 这个的功能是一个主表查其对应的明细表
2. 改造的话,也要变动入=如上
3. 属性介绍:
    property  value="oldDetailList"  主类实体字段名
    column  对应的调用子类传参
    只有一个: column="id"   id 是数据库字段
    多个传参语法:column="{Dao层@Param Value值=数据库字段,Dao层@Param Value值=数据库字段,...}"
    example:column="{amount=amount,pid=id}" 
    select 语法  对应命名空间+ 方法Mapper id

案例二:基于动态数据源改造

1.第一个跑通了,第二个就方便了,只是二次封装了一个
2. 基于自己项目封装的动态数据源
3. bean 替换增强
4. 直接上代码,mapper层就不再赘述

# bean 定义替换增强
package com.wind.bird.dataSource;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.stereotype.Component;

@Component
public class DynamicDataSourcePostProcessor implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        if (registry.containsBeanDefinition("dynamicDataSource")) {
            registry.removeBeanDefinition("dynamicDataSource");
        }
        if (registry.containsBeanDefinition("enhanceDynamicDataSource")) {
            registry.registerBeanDefinition("dynamicDataSource", registry.getBeanDefinition("enhanceDynamicDataSource"));
            registry.removeBeanDefinition("enhanceDynamicDataSource");
        }
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

    }
}
# 扩张动态数据源
package com.wind.bird.dataSource;

import com.alibaba.druid.pool.DruidDataSource;

/**
 * @Author: windbird
 */
@Service
public class EnhanceDynamicDataSource extends DynamicDataSource {

 
    private static final ThreadLocal<Boolean> changeMark = ThreadLocal.withInitial(() -> false);

    public static void setChangeMark(Boolean value) {
        changeMark.set(value);
    }

    public static Boolean getChangeMark() {
        return changeMark.get();
    }
   
    
    /**
     * @param userId
     */
    @Override
    public void selectDynamicDataSourceByUserId(Long userId) {
        changeCrmMark.set(false);
        /**
		* 自己逻辑
		*/
        super.selectDsReportByTenantId(tenantId);
    }
	.
	.
	.
	@Override
    public void selectXXXXByUserId(Long userId) {
        changeCrmMark.set(false);
        super.selectXXXXByUserId(userId);
    }
	

   
    @Override
    protected DataSource determineTargetDataSource() {
        if (getChangeMark()) {
            return enhanceDataSource;
        }
      
        return super.determineTargetDataSource();
    }

}

# 事务
package com.wind.bird.dataSource;

import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
/**
 * @desc: 事务处不分割用统一数据源,用完还原原来的状态
 */
public class EnhanceDataSourceTransactionManager extends DataSourceTransactionManager {

    private static final ThreadLocal<Boolean>  changeMark = ThreadLocal.withInitial(()->false);

    @Override
    protected void doBegin(Object transaction, TransactionDefinition definition) {
        if (ExtendReportHologresDataSource.getChangeMark()) {
            changeMark.set(true);
            ExtendReportHologresDataSource.setChangeMark(false);
        }else{
            changeMark.set(false); //防止多线程导致的并发错误
        }
        super.doBegin(transaction, definition);
    }

    @Override
    protected void doCleanupAfterCompletion(Object transaction) {
        ExtendReportHologresDataSource.setChangeMark(changeMark.get());
        super.doCleanupAfterCompletion(transaction);
    }
}
# 通过切面获取自己想要的数据
package com.wind.bird.enhance;

import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Component;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

/**
 * @author windbird
 */
@Component
@Aspect
public class GetNeedMarkAop implements Serializable {
    private static final Logger logger = LoggerFactory.getLogger(GetTenantAop.class);

    
    @Around(value="execution(* com.wind.bird.service.enhance..*.*(..))")
    public Object assignTenantMark(ProceedingJoinPoint joinPoint) throws Throwable {
        String needMark =null;
        List<Object> validArgs = new ArrayList<>(); 
        Object[] args = joinPoint.getArgs();
        if(null!=args && args.length>0){
		//根据实际业务看是否需要排除 接口类
		 for (Object arg: args){
                if(arg instanceof MultipartFile || arg instanceof HttpServletRequest || arg instanceof HttpServletResponse){
                    continue;
                }else{
                    validArgs.add(arg); 
                }
            }
            int size = validArgs.size();
            if(size>0){
                for (int i = 0; i <size ; i++) {
                    Object inputArg= validArgs.get(i);
					//一个字段的话,必需约定顺序,或者构造对象
                    if(inputArg instanceof Long){
                        try {
                            needMark =String.valueOf(inputArg);
                            if(StringUtils.isNotBlank(needMark) || !"NULL".equals(needMark.toUpperCase())){
                                //todo:自己逻辑处理
                                break;
                            }
                        } catch (Exception e) {
                           logger.error("获取报错:{}",e);
                        }
                    }

                    if(inputArg instanceof List){
                        try {
                            Object unitList = ((List) inputArg).get(0);
                            Field[] declaredFields = unitList.getClass().getSuperclass().getDeclaredFields();
                            for(Field f :declaredFields){
                                f.setAccessible(true);
                                if("needMark".equals(f.getName()) || "need_mark".equals(f.getName())){
                                    needMark = String.valueOf(f.get(unitList));
                                    if(StringUtils.isNotBlank(needMark) || !"NULL".equals(needMark.toUpperCase())){
                                        //todo:自己逻辑处理
                                        break;
                                    }
                                }
                            }
                            break;
                        } catch (Exception e) {
                           logger.error("获取集合里面某一对象里面的失败:{}",e);
                            return joinPoint.proceed();
                        }
                    }
                    try {
                        Class<?> aClass = validArgs.get(i).getClass();
                        Field[] fs = aClass.getDeclaredFields();
                        for(Field f :fs){
                            f.setAccessible(true);
                            if("needMark".equals(f.getName()) || "need_mark".equals(f.getName())){
                                needMark = String.valueOf(f.get(validArgs.get(i)));
                            }
                        }
                    } catch (Exception e) {
                        logger.error("获取对象里面的,失败:{}",e);
                        return joinPoint.proceed();
                    }
                    if(StringUtils.isNotBlank(needMark) || !"NULL".equals(needMark.toUpperCase())){
                        //todo:自己逻辑处理
                    }
                }
            }
        }
        return joinPoint.proceed();
    }
}

# 拦截器 & mapper 同第一个

结束语

这个是自己的改造历程,希望可以帮助大家,一起成长

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

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

相关文章

[附源码]计算机毕业设计基于SpringBoot的玉石交易系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

源码深度剖析Spring Bean标签的解析及注册

在博客《一步一步带你深入源码看Spring是如何加载XML配置文件的》中把Spring对XML配置文件如何加载的说明白了&#xff0c;XML配置文件加载完成后就是对标签的解析&#xff0c;本篇博客就是针对Spring bean 标签的解析以及bean definition 的注册。 Spring 中的标签包括默认标…

Dreamweaver网页设计与制作100例 餐饮主题简洁日式料理餐饮网页设计(4页)HTML+CSS+JavaScript

&#x1f380; 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…

[附源码]计算机毕业设计点餐系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

音视频技术开发周刊 | 274

每周一期&#xff0c;纵览音视频技术领域的干货。新闻投稿&#xff1a;contributelivevideostack.com。「紧急通知」LiveVideoStackCon 2022 音视频技术大会北京站改期各位LVSer们&#xff1a;因疫情影响&#xff0c;北京近期不再允许举办大型线下活动&#xff0c;我们无奈且抱…

【消息中间件】为什么选择RocketMQ及使用案例

目录 一、为什么选择RocketMQ 1、为什么是为什么选择RocketMQ 2、RocketMQ、ActiveMQ和Kafka之间的比较 2.1、对比1 2.2、对比2&#xff0c;接着上表 二、使用案例 1、引入依赖 2、编写启动类 3、编写application.yml配置文件 4、创建rocketmq文件夹 4.1、创建生产者…

OpenCV实战(4)——像素操作

OpenCV实战&#xff08;4&#xff09;——像素操作0. 前言1. 图像的基本组成2. 访问像素值2.1 修改图像像素2.2 cv::Mat_ 模板类2.3 完整代码示例3. 用指针扫描图像3.1 图像扫描3.2 其他减色公式3.3 使用输入和输出参数3.4 高效扫描连续图像3.5 低阶指针算法4. 使用迭代器扫描图…

linux操作系统期末考试题库

1. cal命令 目录 1. cal命令 2.cat命令 3.cd命令 4.date命令 5.echo命令 6.grep命令 7.head 命令 8.ls 命令 9.touch 命令 10.more命令 11. ln创建链接命令 12.查看进程 13.mkdir命令 cal -3 cal 查看指定日期的日历 cal 4 2022 cal 2018 2.cat命令 cat -n /etc…

认识MyBatis

MyBatis是什么&#xff1f; MyBatis是dao层&#xff08;持久层&#xff09;框架&#xff0c;它支持自定义SQL、存储过程以及高级映射。 MyBatis 免除了几乎所有的JDBC代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的XML或注解来配置和映射原始类型、接口和Java …

2022 NCTF

MISC 炉边聚会 卡组代码是 Base64 编码的字节串&#xff0c;exp <?php $deckstring "AAEDAZoFKIwGngXIBrwFzgnQBfIHygf0CIgJkAiBogJ1gjMCPIHtgeeBeAD6AfyB7YHvgbgAAD4AO2B7wFkgnMCMwIga2B/QImgi6BJAIiAn2BOIJAAA"; #这是⼀个⾮常有趣的萨满卡组 $binary bas…

非零基础自学Golang 2 开发环境 2.2 配置GOPATH

非零基础自学Golang 学习文档地址&#xff1a;https://www.topgoer.cn/ 本文仅用于学习记录&#xff0c;不存在任何商业用途&#xff0c;如侵删【已联系过文档作者】 文章目录非零基础自学Golang2 开发环境2.2 配置GOPATH2.2.1 配置GOPATH2.2.2 go的项目目录2.2.3 适合个人开发…

[附源码]Python计算机毕业设计Django剧本杀交流分享平台

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

年产2万吨山楂酒工厂的设计-发酵工段及车间的设计(lunwen+任务书+cad图纸)

目录 1前 言 1 2总论 2 2.1设计依据 2 2.2设计指导思想和原则 2 2.3设计范围 2 2.3.1生产部门 2 2.3.2设计图纸 3 2.4工艺设计基本数据和指标 3 2.5生产工艺概述 3 2.6生产设备概述 4 2.7生产工艺流程图 4 2.8生产方法的简单介绍 6 3全程物料衡算 7 3.1全程总物料概算 7 3.1.1山…

MySQL主从同步

©网络研究院 安装环境 基本需求 ——采用CentOS7系统搭建MySQL服务器 ——关闭防火墙 ——关闭SELinux ——软件 MySQL-5.7.17-1 安装MySQL 续&#xff08;1&#xff09; 从官方下载RPM软件包 ——http://dev.mysql.com/downloads/mysql/ ——适用于当前系统的b…

数图互通高校房产管理——校园电子地图

数图互通房产管理系统在这方面做得比较全面&#xff1b; 1、校园电子地图建设方案 支持地图和房间双向无缝对接。通过电子地图选择建筑物&#xff08;平面或立体&#xff09;能够查看建筑物信息、楼层平面布局图或立体图&#xff0c;点击楼层上的房间能够编辑或查看房间信息。…

非零基础自学Golang 2 开发环境 2.4 Git 安装

非零基础自学Golang 学习文档地址&#xff1a;https://www.topgoer.cn/ 本文仅用于学习记录&#xff0c;不存在任何商业用途&#xff0c;如侵删【已联系过文档作者】 文章目录非零基础自学Golang2 开发环境2.4 Git 安装2.4.1 安装git2 开发环境 2.4 Git 安装 2.4.1 安装git 虽…

Web 性能测试

Web 性能测试 作为网站应用的开发者或维护者&#xff0c;我们需要时常关注网站当前的健康状况&#xff0c;譬如在主流程运行正常的情况下&#xff0c;各方面性能体验是否满足期望&#xff0c;是否存在改进与提升的空间&#xff0c;如何进行快速且准确的问题定位等&#xff0c;…

mongoDB操作文档(全部)

mongoDB 1、创建、查询数据库 创建数据库 use dade 查询数据库 show dbs 2、创建集合、查看 创建集合插入数据 db.集合名.insert({}) db.dade.insert({dade:大得,age:18}) ​ 查看集合 show tables ​ 查看集合中的数据 db.集合名.find() 查询所有 db.dade.find() ​ db.集…

微服务框架 SpringCloud微服务架构 16 SpringAMQP 16.1 基本介绍

微服务框架 【SpringCloudRabbitMQDockerRedis搜索分布式&#xff0c;系统详解springcloud微服务技术栈课程|黑马程序员Java微服务】 SpringCloud微服务架构 文章目录微服务框架SpringCloud微服务架构16 SpringAMQP16.1 基本介绍16.1.1 什么是SpringAMQP16 SpringAMQP 16.1 …

Android -- 每日一问:你在Android开发中遇到的技术难题是什么,你是怎么解决的?

经典回答 一个工作过几年的程序员肯定会有工作中遇到技术难点问题&#xff0c;虽然这个问题有可能对于别人不是技术难点&#xff0c;但只要对于当时的你是技术难点&#xff0c;只要让你抓耳挠腮毫无头绪就往往会在你的大脑中留下深刻的印象。 这个问题&#xff0c;我也比较难…