java web项目性能优化之五花八门

       最近是做了半年的项目到了最后测试准备上线了,流程走通后开始做一些性能测试,在此期间做了很多性能优化的工作,在此做下笔记,分享一下。交流一下,希望同道中人有新的东西欢迎补充。在此就不做太多的具体操作,主要还是从思路上出发。

      性能优化主要从几个方面着手。

      1.从架构设计的角度

           现在的web项目不再像七八年前以前的项目单个的动态web工程就能满足性能的要求了,现如今项目只要是抱着一个美好前景的话,一般都会假设自己的项目未来是PB级数据量,亿级用户量,几万并发的赚暴的独角兽项目,无论是电商,新浪,搜狐等门户网站都会有大量数据,大量的用户,现在热门的物联网虽然用户量没有大规模,然而一堆一堆的传感器从不觉得累地同时访问你,产生大量的数据。

          为此,要想你的系统在满足要求的情况下扛住压力实现高可用,靠提高硬件已经性价比上不能接收。如下是一张简单流行的分布式架构图,不全面,只用来说明一下性能方面相关:

      

     上图web服务器一般以集群的形式,用lvs,Nginx等开源工具做反向代理和API层的负载均衡。业务层service可以用Dubbo等RPC框架实现分布式调用,达到多节点同时处理计算,现在又有一种新的趋势,以springboot框架做微服务进行服务间以restful接口调用,两种形式各有千秋,前者较后都就目前来说更流行一些,在此只关注对性能相关的话题。

    另外,使用redis,memcache等开源工具做缓存对性能也有较大的提高,当然也会有一些管理难的代价,管理不好经常出现数据不一致。

2.从数据库的角度

    关系形数据库在数据量达到一定规模查询效果较差,像一些操作纪录等数据可以用elasticsearch,redis,mongodb等nosql非关系形数据库来存储,查询性能比关系形数据库好很多,但是比如金钱,订单,用户信息等“贵重”信息只能用关系形数据库来存储。关系形数据库性能提高常的方法一般包括建立索引,视图等。有些数据库如mysql官方还提供代理工具实现水平拆分,垂直拆分等,Mysql proxy代理工具可以实现数据库的读写分离,都能一定程序提高关系数据库的性能。

3.从代码的角度

        进入一家新公司后,一般架构都已经定了,为了性能动架构的机会不是很多,除非决定整个项目重构,难道在性能方面就没有办法了吗?答案是否定的,java真的是一门神奇的语言,可能简洁度上不如php语言,性能不如c++,api没有scala丰富,也没有golang那么高效,然而java是最中庸的,综合实力最强。在此为java点个赞,好了,还是上代码吧。

          比如有个需求,第一步要调用北京总公司的中控服务器拿token等验证信息,平均耗时要1秒,调本系统查询订单处理要2秒,调百度上传图片要2秒,调阿里支付要3秒,有一个方法里面全部完成这些操作,普通的写法如下:

package com.web.service.back.impl;

import java.util.concurrent.TimeUnit;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy;

@Controller
public class PayOrder {

    @RequestMapping("pay")
    @ResponseBody
    public Boolean payGood(String userName, String password, double money) throws InterruptedException {
        String token = getToken(userName, password);// 1秒
        String url = upLoadPic();// 2秒
        double totalFee = dealOrder(userName);// 返回总金额 2秒
        payMoney(totalFee);//3秒

        return true;

    }

    private String upLoadPic() throws InterruptedException {
        TimeUnit.SECONDS.sleep(2);
        return "url";
    }

    private void payMoney(double totalFee) throws InterruptedException {
        TimeUnit.SECONDS.sleep(3);
    }

    private double dealOrder(String userName) throws InterruptedException {
        double sum = 0;
        for (int i = 0; i < 20; i++) {
            TimeUnit.MILLISECONDS.sleep(100);// 模拟处理单个订单消耗00毫秒,20个订单为2秒
            sum += i * 50.00;
        }

        return sum;
    }

    private String getToken(String userName, String password) throws InterruptedException {
        TimeUnit.SECONDS.sleep(1);// 模拟调用时间为2秒
        return "123456";
    }
}
   这样的结果有点吓人,总的消耗时间为 1+2+2+3 =8秒,这在生产环境是不能被忍受的。上面代码是串行运行,我们可以做以下处理
  第一步:异步处理,上代码
   
 @RequestMapping("pay")
    @ResponseBody
    public Boolean payGood(String userName, String password, double money) throws InterruptedException {

        ExecutorService pool = Executors.newCachedThreadPool();
        Future<Double> totalFeeFuture = pool.submit(new Callable<Double>() {

            @Override
            public Double call() throws Exception {
                return dealOrder(userName);// 返回总金额 2秒
            }
        });

        Future<String> tokenFuture = pool.submit(new Callable<String>() {

            @Override
            public String call() throws Exception {
                return getToken(userName, password);// 2秒
            }
        });

        Future<String> picUrlFuture = pool.submit(new Callable<String>() {

            @Override
            public String call() throws Exception {
                return upLoadPic();// 2秒
            }
        });

        pool.submit(new Runnable() {

            @Override
            public void run() {
                try {
                    payMoney(totalFeeFuture.get(2, TimeUnit.SECONDS));// 设置超时设置
                } catch (InterruptedException | ExecutionException | TimeoutException e) {
                    e.printStackTrace();
                } // 3秒
            }
        });

        try {
            String url = picUrlFuture.get(2, TimeUnit.SECONDS);
            tokenFuture.get(2, TimeUnit.SECONDS);
        } catch (ExecutionException | TimeoutException e) {
            e.printStackTrace();
        }

        return true;

    }

   这样的结果大约就是    2秒左右;future模式,可以先返回一个Future给调用者,主线程可以立即得到返回,往下运行,等需要得到结果时调用future.get()方法获取结果,此方法会阻塞,当然可以设置一个超时时间, 防止程序死在这里,提醒一下,向这种异步处理应该在依赖的返回结果的情况下,有两个原则:
   a.有回调的也就是传Callable参数的应该越早越省时间。
   b.消耗时间越长的调用越先执行。
  如果你参与的项目有幸用的是java8,java8中有CompletableFuture增强Future,自带forkJorkPool线程池。也可以自已指定线程池。上代码,上面主方法可改为:
  @RequestMapping("pay")
    @ResponseBody
    public Boolean payGood(String userName, String password, double money)  {
        
        try {
            CompletableFuture<Double> totalFeeFuture = CompletableFuture.supplyAsync(() -> dealOrder(userName));
            CompletableFuture<String> tokenFuture = CompletableFuture.supplyAsync(() -> getToken(userName, password));
            CompletableFuture<String> picUrlFuture = CompletableFuture.supplyAsync(() -> upLoadPic());
            CompletableFuture.runAsync(() -> payMoney(totalFeeFuture.get(2, TimeUnit.SECONDS)));
    
      String token = tokenFuture.get();
      String url =  picUrlFuture.get();
        } catch (InterruptedException | ExecutionException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return true;

    }

 简洁吧? lambda表达式威力是不是很大?来来,我们继续,
private double dealOrder(String userName) throws InterruptedException {
        double sum = 0;
        for (int i = 0; i < 20; i++) {
            TimeUnit.MILLISECONDS.sleep(100);// 模拟处理单个订单消耗00毫秒,20个订单为2秒
            sum += i * 50.00;
        }

        return sum;
    }
处理订单是在一个串行20次循环中处理,感觉也糟糕透了。下面提供两程优化。第一种countDownLatch


     double sum = 0;
    private double dealOrder(String userName) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(20);
        ExecutorService pool = Executors.newFixedThreadPool(10);
        pool.execute(new Runnable() {
            AtomicInteger i = new AtomicInteger(0);
            @Override
            public void run() {
                sum += i.doubleValue()*50.00;
                i.incrementAndGet();
                latch.countDown();
            }
        });
        latch.await();

        return sum;
    }

这里await()方法会阻塞.
第二种,java8并行流:
private double dealOrder(String userName) throws InterruptedException {
       
        double sum = IntStream.rangeClosed(0, 20).parallel().asDoubleStream().map((i) -> i*50.00).reduce(0, Double::sum);

        return sum;
    }

好了就写这么多吧。下次再写吧。孩子想爸爸了,回家

a



引用:Word源码Java问题由来最开始是项目组出现了很多项目文档,这些文档格式不一,可以说是五花八门,故需要一个统一的文档处理工具来方便操作和管理这些文档。Java2word是一个在Java程序中调用MS Office Word文档的组件(类库),提供了一组简单的接口,以便进行文档的创建、编辑和导出等操作。 引用:Java作为一种广泛使用的计算机编程语言,具有跨平台性、易于学习和使用、安全性、大量的开源库和框架以及高性能等优点。尽管Java的速度较慢,内存占用较大,难以调试,GUI编程困难等劣势也存在,但是它仍然是开发企业级应用程序的首选语言之一。因此,使用Java编写一个心形图案是可以实现的。 要使用Java编写一个心形图案,可以通过绘制图形的方式实现。可以使用Java的图形库,如Java Swing或JavaFX来创建一个窗口,在窗口中绘制一个心形图案。可以通过计算心形曲线上的点的坐标,并连接这些点来实现心形图案的绘制。可以使用循环结构来计算并绘制这些点,从而形成整个心形图案。可以根据具体的需求来调整心形的大小、颜色和位置等参数,以达到所需的效果。 此外,还可以使用Java的文本处理库,如Apache POI,来生成一个心形形状的Word文档。可以使用Java代码创建一个Word文档,设置文档的格式和样式,将文本内容以心形的形状进行排版,并保存为Word文件。这样就可以通过Java程序生成一个包含心形图案的Word文档。 综上所述,要用Java编写一个心形图案,可以通过图形绘制库或文本处理库来实现,具体的实现方式可以根据需求选择合适的库和算法来完成。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [word源码java-BuildWord:帮我整理word文档的工具](https://download.csdn.net/download/weixin_38506103/19407634)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [java语言的优势和劣势](https://download.csdn.net/download/milk416666/88251355)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [阿里面试失败后,一气之下我图解了Java中18把锁](https://blog.csdn.net/guoguo527/article/details/118004077)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

老柳聊技术

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值