2022工作中遇到的问题二

Posted 今夜月色很美

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2022工作中遇到的问题二相关的知识,希望对你有一定的参考价值。

1、双向绑定没有生效

下拉框有A、B、C三个选项,但是选了B之后显示的还是A,其实v-model中选中的值已经变成了B,但显示没变。

问题原因:v-model绑定的属性queryParams.xxx,queryParams是data中声明的属性,xxx在初始化的时候并没有写在data中,而vue不能检测对象属性的增加或删除,也就没办法监听到xxx这个属性的改变而进行双向绑定

解决方法:data中声明queryParams时,带上xxx这个属性

2、使用计算属性提示未定义

Property or method “XXX” is not defined on the instance but referenced during render

检查是否写了多个computed

3、oracle连接报错归档程序错误

错误信息:

ORA-00257: 归档程序错误。只有在解析完成后才以 AS SYSDBA 方式连接

进入oracle用户

su oracle

使用sqlplus命令连接oracle

# sqlplus username/password@ip:port/sid
sqlplus wwwcloud/xxx2020@134.xx:1521/ORCL

1、检查空间redo log\\recovery file\\flash_recovery_area_usage使用情况

select * from v$log;
select * from v$recovery_file_dest;
select * from v$flash_recovery_area_usage;

可以看出由于ARCHIVED LOG空间满导致了不能远程登录,ARCHIVED LOG已经达到99.61%

从oracle用户下进入rman

rman target /

删除7天以前的所有ARCHIVED LOG

DELETE ARCHIVELOG ALL COMPLETED BEFORE 'SYSDATE-7'; 

再次执行发现ARCHIVED LOG的空间已经降下来了

4、springcloud gateway报错403

同事在gateway中加了一个maven坐标,然后项目中的验证码接口就开始报错403,发现gateway启动报错:

**********************************************************

Spring MVC found on classpath, which is incompatible with Spring Cloud Gateway at this time. Please remove spring-boot-starter-web dependency.

**********************************************************

原来是springcloudgateway的内部是通过netty+webflux实现的,webflux实现和springmvc配置依赖冲突。

exclusion掉spring-boot-starter-web,gateway启动后验证码接口访问正常。

5、同包名同类名

对项目进行fat jar到thin jar改造,但是发现项目代码中出现和第三方依赖同包名同类名的类,导致外置lib后启动失败,启动脚本添加jvm参数-verbose:class打印类加载过程,发现拆包前加载的是同事自定义的XXXService,拆包后加载的是flowable-engine-6.4.1.jar中的XXXService

查看拆包前后META-INF/MANIFEST.MF文件

拆包前:

Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Created-By: Apache Maven 3.5.3
Built-By: yzliu
Build-Jdk: 11.0.8
Main-Class: org.springframework.boot.loader.JarLauncher
Start-Class: com.gykjit.wf.WorkFlowApplication
Spring-Boot-Version: 2.6.4
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx
Spring-Boot-Layers-Index: BOOT-INF/layers.idx

拆包后:

Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Created-By: Apache Maven 3.8.4
Built-By: root
Build-Jdk: 11.0.8
Main-Class: org.springframework.boot.loader.PropertiesLauncher
Start-Class: com.gykjit.wf.WorkFlowApplication
Spring-Boot-Version: 2.3.1.RELEASE
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx

在启动类中打印java虚拟机系统属性

		ClassLoader appClassLoader = ClassLoader.getSystemClassLoader();
		System.out.println(appClassLoader);
		Properties sp = System.getProperties();
		Enumeration e = sp.propertyNames();
		while (e.hasMoreElements()) 
			String key = (String) e.nextElement();
			System.out.println(key + "=" + sp.getProperty(key));
		

发现拆包前系统属性java.class.path中先加载了class,后加载外部依赖,拆包后java.class.path中没有外置依赖jar包,外置依赖通过loader.path加载,

总结:拆包前用的启动器是JarLauncher,通过java.class.path加载顺序是先加载class,拆包后的启动器是PropertiesLauncher,通过java.class.path和loader.path加载,应该是loader.path先加载了,导致classloader加载项目代码中的同包名同类名文件时判断重复而忽略。

6、服务器ping域名不通

服务器连了外网,但是ping maven.aliyun.com不通

修改域名解析

/etc/resolv.conf

注意:重启后这个文件会被还原

7、nacos配置修改后不更新问题

通过项目启动日志的这几行可以看出,1、LocalConfigInfoProcessor加载了nacos本地快照文件,快照文件位置在C:\\Users\\11244\\nacos\\config,猜测nacos修改配置后不生效原因和本地快照文件有关,2、NacosPropertySourceBuilder.loadNacosData是加载nacos配置的入口方法。

2022-03-07 18:11:33.769 [main] INFO  c.a.n.c.c.i.LocalConfigInfoProcessor - [<clinit>,212] - LOCAL_SNAPSHOT_PATH:C:\\Users\\11244\\nacos\\config
2022-03-07 18:11:35.620 [main] INFO  c.a.n.c.c.i.Limiter - [<clinit>,54] - limitTime:5.0
2022-03-07 18:11:35.650 [main] WARN  c.a.c.n.c.NacosPropertySourceBuilder - [loadNacosData,87] - Ignore the empty nacos configuration and get it based on dataId[modules] & group[DEFAULT_GROUP]
2022-03-07 18:11:36.269 [main] WARN  c.a.c.n.c.NacosPropertySourceBuilder - [loadNacosData,87] - Ignore the empty nacos configuration and get it based on dataId[modules.yml] & group[DEFAULT_GROUP]

loadNacosData源码如下,configService.getConfig读取远程配置信息

private Map<String, Object> loadNacosData(String dataId, String group,
			String fileExtension) 
		String data = null;
		try 
			data = configService.getConfig(dataId, group, timeout);
			if (StringUtils.isEmpty(data)) 
				log.warn(
						"Ignore the empty nacos configuration and get it based on dataId[] & group[]",
						dataId, group);
				return EMPTY_MAP;
			
			if (log.isDebugEnabled()) 
				log.debug(String.format(
						"Loading nacos data, dataId: '%s', group: '%s', data: %s", dataId,
						group, data));
			
			Map<String, Object> dataMap = NacosDataParserHandler.getInstance()
					.parseNacosData(data, fileExtension);
			return dataMap == null ? EMPTY_MAP : dataMap;
		
		catch (NacosException e) 
			log.error("get data from Nacos error,dataId:, ", dataId, e);
		
		catch (Exception e) 
			log.error("parse data from Nacos error,dataId:,data:,", dataId, data, e);
		
		return EMPTY_MAP;
	

进入getConfig方法会发现实际调用的是getConfigInner:

private String getConfigInner(String tenant, String dataId, String group, long timeoutMs) throws NacosException 
        group = null2defaultGroup(group);
        ParamUtils.checkKeyParam(dataId, group);
        ConfigResponse cr = new ConfigResponse();
        
        cr.setDataId(dataId);
        cr.setTenant(tenant);
        cr.setGroup(group);
        
        // 优先使用本地配置
        String content = LocalConfigInfoProcessor.getFailover(agent.getName(), dataId, group, tenant);
        if (content != null) 
            LOGGER.warn("[] [get-config] get failover ok, dataId=, group=, tenant=, config=", agent.getName(),
                    dataId, group, tenant, ContentUtils.truncateContent(content));
            cr.setContent(content);
            configFilterChainManager.doFilter(null, cr);
            content = cr.getContent();
            return content;
        
        
        try 
            String[] ct = worker.getServerConfig(dataId, group, tenant, timeoutMs);
            cr.setContent(ct[0]);
            
            configFilterChainManager.doFilter(null, cr);
            content = cr.getContent();
            
            return content;
         catch (NacosException ioe) 
           ...
        
        ...
    

断点调试LocalConfigInfoProcessor.getFailover发现,通过快照三次读取的content都是null,实际是通过worker.getServerConfig得到的配置信息,而进入worker.getServerConfig方法我们会发现其实是通过httpget方法调用nacos服务端接口拿到的配置信息

public String[] getServerConfig(String dataId, String group, String tenant, long readTimeout)
            throws NacosException 
        String[] ct = new String[2];
        if (StringUtils.isBlank(group)) 
            group = Constants.DEFAULT_GROUP;
        
        
        HttpRestResult<String> result = null;
        try 
            Map<String, String> params = new HashMap<String, String>(3);
            if (StringUtils.isBlank(tenant)) 
                params.put("dataId", dataId);
                params.put("group", group);
             else 
                params.put("dataId", dataId);
                params.put("group", group);
                params.put("tenant", tenant);
            
            result = agent.httpGet(Constants.CONFIG_CONTROLLER_PATH, null, params, agent.getEncode(), readTimeout);
         catch (Exception ex) 
            String message = String
                    .format("[%s] [sub-server] get server config exception, dataId=%s, group=%s, tenant=%s",
                            agent.getName(), dataId, group, tenant);
            LOGGER.error(message, ex);
            throw new NacosException(NacosException.SERVER_ERROR, ex);
        
        ...
    

得出结论nacos配置未更新的问题和本地快照文件无关。那么为什么从nacos服务端读取到的配置信息不是最新的呢,我在刚刚断点agent.httpGet方法的时候取得了请求参数,在postman中调用nacos服务端,确实没有更新。

于是我从github上git clone了nacos的源码,切换到develop分支,设置java启动参数为单机启动

-Dnacos.standalone=true

启动成功后,通过postman模拟httpget请求,进入nacos源码ConfigController.getConfig方法

	@GetMapping
    @Secured(action = ActionTypes.READ, signType = SignType.CONFIG)
    public void getConfig(HttpServletRequest request, HttpServletResponse response,
            @RequestParam("dataId") String dataId, @RequestParam("group") String group,
            @RequestParam(value = "tenant", required = false, defaultValue = StringUtils.EMPTY) String tenant,
            @RequestParam(value = "tag", required = false) String tag)
            throws IOException, ServletException, NacosException 
        // check tenant
        ParamUtils.checkTenant(tenant);
        tenant = NamespaceUtil.processNamespaceParameter(tenant);
        // check params
        ParamUtils.checkParam(dataId, group, "datumId", "content");
        ParamUtils.checkParam(tag);
        
        final String clientIp = RequestUtil.getRemoteIp(request);
        String isNotify = request.getHeader("notify");
        inner.doGetConfig(request, response, dataId, group, tenant, tag, isNotify, clientIp);
    

发现其实查询的是默认名称空间的配置信息,而我们使用的是自己定义的名称空间。
那么为什么程序启动获取的是默认名称空间下的配置信息呢,排查发现同事修改了springboot启动过程,在代码中设置了服务发现的名称空间spring.cloud.nacos.discovery.namespace属性,但是没有设置注册中心名称空间spring.cloud.nacos.config.namespace,导致服务发现的名称空间是自定义的,注册中心的名称空间是默认名称空间。

8、配置修改代码生成包名仍没变

经过问题7的排查步骤,我们在断点debug中已经看到获取到了修改后的配置信息,但是我们使用若依框架代码生成的包名仍然是默认名称空间下的配置,查看/batchGenCode接口对应的业务代码:

/**
     * 查询表信息并生成代码
     */
    private void generatorCode(String tableName, ZipOutputStream zip)
    
        // 查询表信息
        GenTable table = genTableMapper.selectGenTableByName(tableName);
        // 查询列信息
        List<GenTableColumn> columns = table.getColumns();
        setPkColumn(table, columns);

        Map<String,String> idMap = new HashMap<String, String>();
        // 生成菜单主键
        idMap.put("menuId", CodeBuilder.getId());
        idMap.put("funId", CodeBuilder.getId());
        idMap.put("maintainMenuId", CodeBuilder.getId());
        idMap.put("exportMenuId", CodeBuilder.getId());
        idMap.put("submitMenuId", CodeBuilder.getId());
        VelocityInitializer.initVelocity();

        VelocityContext context = VelocityUtils.prepareContext(table,idMap);

        // 获取模板列表
        List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory());
        for (String template : templates)
        
            // 渲染模板
            StringWriter sw = new StringWriter();
            Template tpl = Velocity.getTemplate(template, Constants.UTF8);
            tpl.merge(context, sw);
            try
            
                // 添加到zip
                zip.putNextEntry(new ZipEntry(VelocityUtils.getFileName(template, table)));
                IOUtils.write(sw.toString(), zip, Constants.UTF8);
                IOUtils.closeQuietly(sw);
				zip.flush();
                zip.closeEntry();
            
            catch (IOException e)
            
                log.error("渲染模板失败,表名:" + table.getTableName(), e);
            
        
    

进入VelocityUtils.getFileName方法

/**
	 * 获取文件名
	 */
	public static String getFileName(String template, GenTable genTable) 
		// 文件名称
		String fileName = "";
		// 包路径
		String packageName = genTable.getPackageName();
		// 模块名
		String moduleName = genTable.getModuleName();
		// 大写类名
		String className = genTable.getClassName();
		// 业务名称
		String businessName = genTable.getBusinessName();

		String javaPath = PROJECT_PATH + "/" + StringUtils.replace(packageName, ".", "/");
		String mybatisPath = MYBATIS_PATH + "/" + moduleName;
		String vuePath = "vue";

		...
		return fileName;
	

通过源码发现,包名是从table对象中获取的,而table对象是从数据库查询的gen_table表,所以其实在/importTable导入表结构的时候,这个接口读取了当时的包名配置,并写入gen_table表,所以后续修改配置包名仍然是之前的。

以上是关于2022工作中遇到的问题二的主要内容,如果未能解决你的问题,请参考以下文章

2022工作中遇到的问题四

2022工作中遇到的问题四

合并工作表名称略有不同的多个文件[重复]

2022工作中遇到问题三

2022工作中遇到问题五

2022工作中遇到问题五