欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

对自己写的代码的反思 编程 

程序员文章站 2024-02-02 08:40:10
...
代码review
写这篇文章的原因是昨天在工作期间完成业务要求的时候,根据组长的要求写了自己的代码,结果在之后组长review的时候,发现了很多不足的地方,所以写这篇博客进行记录。
话不多说,先上原始代码。
public class DocValuesWarmUpListener extends AbstractSolrEventListener{
    private static final Logger logger = LoggerFactory.getLogger(DocValuesWarmUpListener.class);
    private List<String> fields;
    public DocValuesWarmUpListener(SolrCore core) {
        super(core);
    }


    @Override
    public void init(NamedList args) {
        super.init(args);
        fields = new ArrayList<>();
        NamedList o;
        if(args != null) {
            o = (NamedList) args.get("fields");
            if(o!=null && o instanceof NamedList) {
                for (int i = 0; i < o.size(); i++) {
                    fields.add((String) o.getVal(i));
                }
            }
        }


    }

    @Override
    public void newSearcher(SolrIndexSearcher newSearcher, SolrIndexSearcher currentSearcher) {
        long start = System.currentTimeMillis();
        ExecutorService executorService = Executors.newFixedThreadPool(fields.size());
        try {
            //排序所需要用到的字段,用一个线程池多线程预热
            for(int i=0;i<fields.size();i++) {
                executorService.execute(new WarmUpDocValueThread(fields.get(i),newSearcher));
            }
        } catch (Exception e) {
            throw new IllegalStateException(e);
        }finally {
            logger.info("warming up consumes ["+(System.currentTimeMillis()-start)+"] Millis");
        }

    }

    class WarmUpDocValueThread implements Runnable{

        private String field;
        private SolrIndexSearcher newSearcher;
        public WarmUpDocValueThread(String field,SolrIndexSearcher newSearcher) {
            this.field = field;
            this.newSearcher = newSearcher;
        }

        @Override
        public void run() {
            try {
                DocValues.getSorted(this.newSearcher.getLeafReader(), this.field);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

这里先大概解释一下这段代码的业务需求。
由于在业务方进行一个查询工作的时候,发现有一段查询语句的时间抖动非常大,最长耗时可能到6000ms,快的时候在400ms左右。所以组长分析是该查询语句涉及到排序,而solr周期性的softCommit之后,会需要对整个docValue进行重新merge,所以会造成周期性的抖动。所以需要在softComiit之后先进行对docValue的预热。
确定逻辑后,让我上手完成这个逻辑。整个代码逻辑也不复杂,就是写一个eventListener,在init()方法中初始化solrconfig.xml的配置,在newSearcher()方法中对docValue进行预热。
所以写了以上这一版本,在组长review的时候,发现了很多不足,现在记录一下,也是以后尽量避免。
1: 在多线程并发情况下,没有设置主线程的堵塞,及在多线程同时对docValue进行预热的情况下,主线程并不等待预热完成而直接继续执行。针对这个问题,在修改的第一版本中,在多线程开启后,加入thread.join(),完成主线程的等待,在组长的建议下修改成了countDownLatch。这里顺便写一下countDownLatch和join用法的区别。
    本质上countDownLatch和join都实现同一个目的,即多线程并发编程时,当各别线程需要等待特定的其他线程任务执行完成后再执行时,用到以上两个方法。具体使用方法这里先不赘述了,主要讲讲不同的使用场景。
    在使用join时,阻塞线程必须等待其他线程任务完全结束后才能继续执行,而使用countDownLatch时,可以任意指定其他线程的执行程度。
    举个例子,此时有A,B,C三个线程进行任务执行,假设三个线程任务均为执行100的计数且A线程必须等到B,C计数完成再执行,此时用join或者countDownLatch都能完成任务要求。但是如果任务改成,A线程必须等到B,C都计数到50时,再开始执行,此时join不能完成这个要求,但是用countDownLatch时可以实现,即先
CountDownLatch cdl = new CountDownLatch(2)
//A线程
cld.await()
//B线程完成50次计数时
cld.countDown()
//C线程完成50次计数时
cld.countDown()

以上代码即可实现要求,以上仅为伪代码,实际编程时注意要把cld.countDown()写在finally里面,避免有一线程崩溃而造成死锁。
    第二个不足在于对代码的可配置化程度上,第一版的代码将代码参数写的过于生硬,很难被以后的代码复用,经过组长提示,对AbstractSolrEventListener源码的阅读发现有一个init方法,可以实现对schema.xml文件的配置参数的读取,果断覆写这个方法,将所需要预热的field配置在xml文件中。
    本文主要记录一下这次的经验。
相关标签: 编程