• (仿牛客社区项目)Java开发笔记7.9:优化网站的性能


    优化网站的性能

    NowCoder_38_1

    1.添加依赖

    添加caffine的pom依赖包。

    		<dependency>
    			<groupId>com.github.ben-manes.caffeinegroupId>
    			<artifactId>caffeineartifactId>
    			<version>2.7.0version>
    		dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2.修改配置文件

    修改配置文件,添加caffeine相关配置。

    #caffeine
    caffeine.posts.max-size=15
    caffeine.posts.expire-seconds=180
    
    • 1
    • 2
    • 3

    3.service层

    修改DiscussPostService,添加init方法,修改findDiscussPostRows,findDiscussPosts方法。

    package com.gerrard.community.service;
    
    import com.gerrard.community.dao.DiscussPostMapper;
    import com.gerrard.community.entity.DiscussPost;
    import com.gerrard.community.util.SensitiveFilter;
    import com.github.benmanes.caffeine.cache.CacheLoader;
    import com.github.benmanes.caffeine.cache.Caffeine;
    import com.github.benmanes.caffeine.cache.LoadingCache;
    import org.checkerframework.checker.nullness.qual.NonNull;
    import org.checkerframework.checker.nullness.qual.Nullable;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Service;
    import org.springframework.web.util.HtmlUtils;
    
    import javax.annotation.PostConstruct;
    import java.util.List;
    import java.util.concurrent.TimeUnit;
    
    @Service
    public class DiscussPostService {
    
        private static final Logger logger= LoggerFactory.getLogger(DiscussPostService.class);
    
        @Autowired
        private DiscussPostMapper discussPostMapper;
    
        @Autowired
        private SensitiveFilter sensitiveFilter;
    
        @Value("${caffeine.posts.max-size}")
        private int maxSize;
    
        @Value("${caffeine.posts.expire-seconds}")
        private int expiredSeconds;
    
        //caffeine核心接口:Cache,LoadingCache,AsyncLoadingCache
    
        //帖子列表缓存
        private LoadingCache<String,List<DiscussPost>> postListCache;
    
        //帖子总数缓存
        private LoadingCache<Integer,Integer> postRowsCache;
    
        @PostConstruct
        public void init(){
            //初始化帖子列表缓存     缓存没有数据的时候将数据加入缓存,并且key就是方法传入的key
            postListCache= Caffeine.newBuilder()
                    .maximumSize(maxSize)
                    .expireAfterWrite(expiredSeconds, TimeUnit.SECONDS)
                    .build(new CacheLoader<String, List<DiscussPost>>() {
                        @Nullable
                        @Override
                        public List<DiscussPost> load(@NonNull String key) throws Exception {
                            if(key==null || key.length()==0){
                                throw new IllegalArgumentException("参数错误!");
                            }
    
                            String[] params=key.split(":");
    
                            if(params==null|| params.length!=2){
                                throw new IllegalArgumentException("参数错误!");
                            }
    
                            int offset=Integer.valueOf(params[0]);
                            int limit=Integer.valueOf(params[1]);
    
                            //此处可以创建二级缓存:Redis->MySql
    //                        即:先从Redis中查数据,再在MySql里查数据
                            logger.debug("load post list from DB.");
                            return discussPostMapper.selectDiscussPosts(0,offset,limit,1);
                        }
                    });
    
    
                //初始化帖子总数缓存
                postRowsCache=Caffeine.newBuilder()
                        .maximumSize(maxSize)
                        .expireAfterWrite(expiredSeconds,TimeUnit.SECONDS)
                        .build(new CacheLoader<Integer, Integer>() {
                            @Nullable
                            @Override
                            public Integer load(@NonNull Integer key) throws Exception {
                                logger.debug("load post rows from DB.");
                                return discussPostMapper.selectDiscussPostRows(key);
                            }
                        });
        }
    
        public List<DiscussPost> findDiscussPosts(int userId,int offset,int limit,int orderMode){
    //        if(userId==0 && orderMode==1){
    //            return postListCache.get(offset+":"+limit);
    //        }
            logger.debug("load post list from DB.");
            return discussPostMapper.selectDiscussPosts(userId,offset,limit,orderMode);
        }
        public int findDiscussPostRows(int userId){
    //        if(userId==0){
    //            return postRowsCache.get(userId);
    //        }
            logger.debug("load post rows from DB.");
            return discussPostMapper.selectDiscussPostRows(userId);
        }
    
       public int addDiscussPost(DiscussPost post){
            if(post==null){
                throw new IllegalArgumentException("参数不能为空!");
            }
    
            //转义HTML标记
           post.setTitle(HtmlUtils.htmlEscape(post.getTitle()));
            post.setContent(HtmlUtils.htmlEscape(post.getContent()));
            //过滤敏感词
           post.setTitle(sensitiveFilter.filter(post.getTitle()));
           post.setContent(sensitiveFilter.filter(post.getContent()));
    
           return discussPostMapper.insertDiscussPost(post);
       }
        public DiscussPost findDiscussPostById(int id) {
            return discussPostMapper.selectDiscussPostById(id);
        }
    
        public int updateCommentCount(int id,int commentcount){
            return discussPostMapper.updateCommentCount(id,commentcount);
        }
    
        public int updateType(int id, int type) {
            return discussPostMapper.updateType(id, type);
        }
    
        public int updateStatus(int id, int status) {
            return discussPostMapper.updateStatus(id, status);
        }
    
        public int updateScore(int id, double score) {
                return discussPostMapper.updateScore(id, score);
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142

    后续改进:修改帖子后,应将Caffeine中相关的帖子清除【4.6节的Redis可以根据userId精确清除,而本节的Caffeine场景下删除是整页删除帖子列表】。

    Caffeine键值对:

    1.【offset】:【limit】->List

    2.【userId】->(Integer)DiscussPostRows

    4.test包

    添加CaffeineTests类。

    package com.gerrard.community;
    
    import com.gerrard.community.entity.DiscussPost;
    import com.gerrard.community.service.DiscussPostService;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringRunner;
    
    import java.util.Date;
    
    @RunWith(SpringRunner.class)
    @SpringBootTest
    @ContextConfiguration(classes = CommunityApplication.class)
    public class CaffeineTests {
    
        @Autowired
        private DiscussPostService postService;
    
        @Test
        public void initDataForTest(){
            for(int i=0;i<300000;i++){
                DiscussPost post=new DiscussPost();
                post.setUserId(111);
                post.setTitle("互联网求职暖春计划");
                post.setContent("今年的就业形势,确实不容乐观。过了个年,仿佛跳水一般,整个讨论区哀鸿遍野!19届真的没人要了吗?!18届被优化真的没有出路了吗?!大家的“哀嚎”与“悲惨遭遇”牵动了每日潜伏于讨论区的牛客小哥哥小姐姐们的心,于是牛客决定:是时候为大家做点什么了!为了帮助大家度过“寒冬”,牛客网特别联合60+家企业,开启互联网求职暖春计划,面向18届&19届,拯救0 offer!");
                post.setCreateTime(new Date());
                post.setScore(Math.random() * 2000);
                postService.addDiscussPost(post);
            }
        }
    
    
        @Test
        public void testCache() {
            System.out.println(postService.findDiscussPosts(0, 0, 10, 1));
            System.out.println(postService.findDiscussPosts(0, 0, 10, 1));
            System.out.println(postService.findDiscussPosts(0, 0, 10, 1));
            System.out.println(postService.findDiscussPosts(0, 0, 10, 0));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    目的:查看打印的日志信息:第一次是从MySQL数据库中查数据,第二次及以后是从caffeine中查数据。

    5.功能测试

    controller.aspect:压力测试的时候把ServiceLogAspect类中的Component和Aspect注解注掉,防止打印太多日志。

    package com.gerrard.community.controller.aspect;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
    
    import javax.servlet.http.HttpServletRequest;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    //@Component
    //@Aspect
    public class ServiceLogAspect {
    
        private static final Logger logger= LoggerFactory.getLogger(ServiceLogAspect.class);
    
        @Pointcut("execution(* com.gerrard.community.service.*.*(..))")
        public void pointcut(){
    
        }
    
        @Before("pointcut()")
        public void before(JoinPoint joinPoint){
            //用户[1.2.3.4],在[xxx],访问了[com.gerrard.community.service.xxx()].
            ServletRequestAttributes attributes= (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
    
            //后加:可能会报空指针异常  消费者也可能掉service,此时request为空
            if(attributes==null){
                return;
            }
    
            HttpServletRequest request=attributes.getRequest();
            String ip=request.getRemoteHost();
            String now=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
            String target=joinPoint.getSignature().getDeclaringTypeName()+"."+joinPoint.getSignature().getName();
            logger.info(String.format("用户[%s],在[%s],访问了[%s].", ip, now, target));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    安装JMeter工具。

    NowCoder_38_3

    添加测试计划:

    NowCoder_38_4

    NowCoder_38_5

    NowCoder_38_6

    NowCoder_38_7

    不加缓存:

    NowCoder_38_8

    加缓存:NowCoder_38_9

    不加缓存:

    NowCoder_38_10

    加缓存:

    NowCoder_38_11

    可以看到,加caffeine缓存后,系统吞吐量大大提升。

  • 相关阅读:
    个人秋招面经——腾讯
    【学习笔记39】获取DOM标签对象
    MySQL默认的存储引擎InnoDB的最佳实践
    java计算机毕业设计师生交流平台源程序+mysql+系统+lw文档+远程调试
    重学设计模式之-桥接模式
    macbook m1 nacos集群启动失败报错的解决办法
    Kubernetes究竟是个容器应用程序还是集群操作系统,它这么复杂的原因出在哪?
    【JAVASE】String类
    Netty学习笔记(一)
    C#异步TCP客户端连接
  • 原文地址:https://blog.csdn.net/weixin_45700663/article/details/126002981