【测试报告】博客系统(Java+Selenium+Jmeter自动化测试)

一、项目简介

博客系统作为一种在线交流平台,支持用户在平台下发布文章,并进行讨论。主要由登录页面,论坛页面,帖子编辑页,帖子详情页等页面组成。

二、项目功能

1.登录页面:输入正确的账号密码进行登录,跳转博客列表页。

2.博客列表页:加载出全部博客,左边显示用户信息与博客信息,右上角可发布新博客、页等按钮,选择一个博客可以进入查看博客详细信息。

3.博客详情页:能查看博客完整内容,当前登录用户查看自己博客有编辑与删除按钮,其余用户仅有查看博客权限。

4.博客编辑页:输入标题、正文选择提交按钮发布博客。

5.更新博客页:现当前需要修改的博客信息(标题,正文),修改完成点击提交按钮。

6.删除博客:在详情页中点击删除博客,可以正确删除,不在出现在列表页。

三、测试计划

(一)功能测试

1.测试用例设计

2.测试内容
(1)登陆页面

测试用例:

|--------|---------------|----------|--------|-----------|
| 测试用例编号 | 操作 | 账号 | 密码 | 预期结果 |
| 1 | 输入正常的账号密码 | zhangsan | 123456 | 登录成功 |
| 2 | 输入错误的账号和密码 | tt | 333 | 登录失败,提示错误 |
| 3 | 输入正确的账号和错误的密码 | zhangsan | 332 | 登录失败,提示错误 |
| 4 | 输入错误的账号和正确的密码 | tt | 123456 | 登录失败,提示错误 |
| 5 | 不填写账号和密码 |   |   | 登录失败,提示错误 |
| 6 | 填写账号不填写密码 | zhangsan |   | 登录失败,提示错误 |
| 7 | 不填写账号填写密码 |   | 123456 | 登录失败,提示错误 |

测试结果:

①测试用例1:

结论:登录成功,跳转至论坛页面,与预期结果一致。

②测试用例2:

结论:显示用户名或密码错误,与预期结果不一致,没有提示账号/密码错误

③测试用例3:

结论:显示密码错误,与预期结果一致
④测试用例4:

结论:显示用户名不存在,与预期结果一致

⑤测试用例5:

结论:显示用户名小于4位,与预期结果一致。

⑥测试用例6:

结论:显示密码错误,与预期结果一致。
⑦测试用例7:

结论:显示用户名小于4位,与预期结果一致。

(2)博客列表页面

|--------|--------------|------------|
| 测试用例编号 | 操作 | 预期结果 |
| 1 | 点击Gitee地址可跳转 | 跳转到相应Git地址 |
| 2 | 点击查看全文 | 查看博客详情 |
| 3 | 点击右上角写博客 | 进入编写博客 |
| 4 | 点击右上角注销 | 用户退出 |
| 5 | 点击右上角主页 | 回到列表页 |
| 6 | 点击右上角发布新博客 | 进入编写博客 |

①测试用例1:

结论:正常跳转与预期一样
②测试用例2:

结论:与预期结果一样
③测试用例3:

结论:与预期一致
④测试用例4:

结论:与预期一致
⑤测试用例5:

结论:与预期结果一致
⑥测试用例6:

结论:与预期结果一致

(3)博客详情页

|--------|--------------|------------|
| 测试用例编号 | 操作 | 预期结果 |
| 1 | 点击Gitee地址可跳转 | 跳转到相应Git地址 |
| 2 | 点击编辑按钮 | 进行更新界面 |
| 3 | 点击删除按钮 | 可以删除 |
| 4 | 点击右上角注销 | 用户退出 |
| 5 | 点击右上角主页 | 回到列表页 |
| 6 | 点击右上角发布新博客 | 进入编写博客 |

①测试用例1:

结论:正常跳转与预期一样
②测试用例2:

结论:正常跳转与预期一样
③测试用例3:

结论:正常跳转与预期一样
④测试用例4:

结论:与预期一致
⑤测试用例5:

结论:与预期结果一致
⑥测试用例6:

结论:与预期结果一致

(4)更新博客

|--------|--------------|------|
| 测试用例编号 | 操作 | 预期结果 |
| 1 | 只编辑标题点击发布 | 正常发布 |
| 2 | 只编辑内容点击发布 | 正常发布 |
| 3 | 编辑标题与内容点击发布 | 正常发布 |
| 4 | 不编辑标题与内容点击发布 | 正常发布 |

①测试用例1:

结论:与期望结果一致
②测试用例2:

结论:与期望结果一致
③测试用例3:

结论:与期望结果一致
④测试用例4:

结论:与期望结果一致

(5)发布博客

|--------|--------------|------|
| 测试用例编号 | 操作 | 预期结果 |
| 1 | 只编辑标题点击发布 | 发布失败 |
| 2 | 只编辑内容点击发布 | 发布失败 |
| 3 | 编辑标题与内容点击发布 | 正常发布 |
| 4 | 不编辑标题与内容点击发布 | 发布失败 |

①测试用例1:

结论:发布失败,与期望结果一致。

正文内容是系统默认的,需要正文内容不为空和不是默认的才可以进行发布
②测试用例2:

结论:与期望结果一致
③测试用例3:

结论:与期望结果一致
④测试用例4:

结论:发布失败,与期望结果一致。

正文内容是系统默认的,需要正文内容不为空和不是默认的才可以进行发布

(二)自动化测试(Java)

1.测试环境

系统:windows11

浏览器:Chrome137.0.7151.119

测试工具:IntelliJ IDEA 2025.1.2

2.测试内容
(1)项目结构
(2)工具类Utils
java 复制代码
public class Utils {

    public static WebDriver driver = null;
    public static WebDriverWait wait = null;
    public static String blogDetailUrl = "http://1.94.188.69:9090/blog_detail.html?blogId=3";

    public Utils(String url) {
        driver = createDriver();

        driver.get(url);

        wait = new WebDriverWait(driver, Duration.ofSeconds(3));

    }


    public static WebDriver createDriver() {
        if (driver == null) {
            WebDriverManager.chromedriver().setup();
            ChromeOptions options = new ChromeOptions();


            Map<String, Object> prefs = new HashMap<>();

            prefs.put("credentials_enable_service", false);
            prefs.put("profile.password_manager_enabled", false);
            prefs.put("password_manager_leak_detection", false);
            prefs.put("profile.default_content_settings.popups", 0);
            options.setExperimentalOption("prefs", prefs);


            options.addArguments("--remote-allow-origins=*");
            driver = new ChromeDriver(options);
            driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3));
        }
        return driver;
    }

    public void screenShot(String str) {
        try {
            SimpleDateFormat sim1 = new SimpleDateFormat("yyyy-MM-dd");
            SimpleDateFormat sim2 = new SimpleDateFormat("HHmmssSS");
            String dirTime = sim1.format(System.currentTimeMillis());
            String fileTime = sim2.format(System.currentTimeMillis());
            String fileName = "./src/test/java/images/" + dirTime + "/" + str + "_" + fileTime + ".png";
            File srcFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
            FileUtils.copyFile(srcFile, new File(fileName));
        } catch (IOException e) {
            e.getMessage();
        }
    }

    public String createTime() {
        SimpleDateFormat sim = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss-SS");
        String curTime = sim.format(System.currentTimeMillis());
        return curTime;
    }


    public static void quit() {
        if (driver != null) {
            driver.quit();
            driver = null; // 重置引用
        }
    }

}
(3)登录界面类BlogLoginPage
java 复制代码
public class BlogLoginPage extends Utils {
    public static String url = "http://1.94.188.69:9090/blog_login.html";

    public BlogLoginPage() {
        super(url);
    }

    public void checkPageRight() {
        driver.findElement(By.cssSelector("body > nav > div > div.flex.items-center.space-x-3"));
        driver.findElement(By.cssSelector("body > nav > div > div.hidden.md\\:flex.items-center.space-x-6 > a:nth-child(1)"));
        driver.findElement(By.cssSelector("body > nav > div > div.hidden.md\\:flex.items-center.space-x-6 > a:nth-child(2)"));
        driver.findElement(By.cssSelector("#username"));
        driver.findElement(By.cssSelector("#password"));
        driver.findElement(By.cssSelector("#submit"));
        driver.findElement(By.cssSelector("body > div"));
        screenShot("BlogLoginPage_checkPageRight");
    }

    public void checkPublishButton() {
        driver.findElement(By.cssSelector("body > nav > div > div.hidden.md\\:flex.items-center.space-x-6 > a:nth-child(2)")).click();
        driver.findElement(By.cssSelector("#submit"));
        screenShot("BlogLoginPage_checkPublishButton");
        driver.navigate().back();

    }

    public void checkErrorLogin() {
        driver.findElement(By.cssSelector("#username")).sendKeys("ssssss");
        driver.findElement(By.cssSelector("#password")).sendKeys("gssaf");
        driver.findElement(By.cssSelector("#submit"));
        screenShot("BlogLoginPage_checkErrorLogin");
    }

    public void checkCorrectLogin() {
        driver.findElement(By.cssSelector("#username")).clear();
        driver.findElement(By.cssSelector("#password")).clear();

        driver.findElement(By.cssSelector("#username")).sendKeys("zhangsan");
        driver.findElement(By.cssSelector("#password")).sendKeys("123456");
        screenShot("BlogLoginPage_checkCorrectLogin");
        driver.findElement(By.cssSelector("#submit")).click();
        driver.findElement(By.cssSelector("body > div > div > div.right.flex-1 > div.mb-8.flex.justify-between.items-center > a"));
        screenShot("BlogLoginPage_checkCorrectLogin");

    }

}
(4)博客列表页界面类BlogListPage
java 复制代码
public class BlogListPage extends Utils {
    public static String url = "http://1.94.188.69:9090/blog_list.html";

    public BlogListPage() {
        super(url);
    }

    public void CheckPageRight() {
        String title = driver.findElement(By.cssSelector("body > div > div > div.right.flex-1 > div.space-y-6.blog-list > div:nth-child(1) > div.title.text-xl.font-bold.text-slate-800.hover\\:text-primary.transition-custom.mb-2")).getText();
        String createTiem = driver.findElement(By.cssSelector("body > div > div > div.right.flex-1 > div.space-y-6.blog-list > div:nth-child(1) > div.date.text-slate-500.text-sm.flex.items-center.mb-4")).getText();
        String content = driver.findElement(By.cssSelector("body > div > div > div.right.flex-1 > div.space-y-6.blog-list > div:nth-child(1) > div.desc.text-slate-600.line-clamp-3.mb-4")).getText();
        String button = driver.findElement(By.cssSelector("body > div > div > div.right.flex-1 > div.space-y-6.blog-list > div:nth-child(1) > a")).getText();
        String text = driver.findElement(By.cssSelector("body > div > div > div.left.md\\:w-1\\/4.lg\\:w-1\\/5 > div > div.px-6.pb-6.-mt-12 > h3")).getText();
        String git = driver.findElement(By.cssSelector("body > div > div > div.left.md\\:w-1\\/4.lg\\:w-1\\/5 > div > div.px-6.pb-6.-mt-12 > a")).getText();
        driver.findElement(By.cssSelector("body > nav > div > div.hidden.md\\:flex.items-center.space-x-6 > a.nav-span.text-primary.font-medium.flex.items-center.gap-1\\.5"));
        driver.findElement(By.cssSelector("body > nav > div > div.hidden.md\\:flex.items-center.space-x-6 > a.nav-span.text-slate-600.hover\\:text-primary.transition-custom.flex.items-center.gap-1\\.5"));
        driver.findElement(By.cssSelector("body > nav > div > div.hidden.md\\:flex.items-center.space-x-6 > a.nav-span.text-slate-600.hover\\:text-red-500.transition-custom.flex.items-center.gap-1\\.5"));
        assert !title.isEmpty();
        assert !createTiem.isEmpty();
        assert !content.isEmpty();
        assert !text.isEmpty();
        assert !git.isEmpty();
        assert button.equals("查看全文");


        screenShot("BlogListPage_" + "CheckPageRight");
    }

    public void CheckPageGitAndPush() {
        //检查git按钮
        String beforHandle = driver.getWindowHandle();
        driver.findElement(By.cssSelector("body > div > div > div.left.md\\:w-1\\/4.lg\\:w-1\\/5 > div > div.px-6.pb-6.-mt-12 > a")).click();
        String lastHandle = driver.getWindowHandle();
        List<String> list = driver.getWindowHandles().stream().toList();
        for (String handle : list) {
            if (!list.equals(beforHandle)) {
                driver.switchTo().window(handle);
            }
        }
        screenShot("BlogListPage_" + "CheckPageGitAndPush");
        driver.switchTo();
        driver.close();
        driver.switchTo().window(beforHandle);
        driver.findElement(By.cssSelector("body > nav > div > div.hidden.md\\:flex.items-center.space-x-6 > a:nth-child(2)")).click();
        String title = driver.findElement(By.cssSelector("body > div > div > div.mb-6.animate-fade-in > h1")).getText();
        assert title.equals("编辑博客");
        driver.navigate().back();

    }

    public void CheackDteail() {
        String beforTitle = driver.findElement(By.cssSelector("body > div > div > div.right.flex-1 > div.space-y-6.blog-list > div:nth-child(1) > div.title.text-xl.font-bold.text-slate-800.hover\\:text-primary.transition-custom.mb-2")).getText();
        driver.findElement(By.cssSelector("body > div > div > div.right.flex-1 > div.space-y-6.blog-list > div:nth-child(1) > a")).click();
        String lastTitle = driver.findElement(By.cssSelector("#blog-content > div.p-6.md\\:p-8.border-b.border-slate-100 > h1")).getText();
        assert beforTitle.equals(lastTitle);
        blogDetailUrl = driver.getCurrentUrl();
        driver.findElement(By.cssSelector("body > nav > div > div.hidden.md\\:flex.items-center.space-x-6 > a:nth-child(1)")).click();
    }
}
(5)详情页界面类BlogDetailPage
java 复制代码
public class BlogDetailPage extends Utils {
    public static String url = blogDetailUrl;
    public BlogDetailPage() {
        super(url);
    }

    public void checkDetailRight() {
        String buttonEdit = driver.findElement(By.cssSelector("#blog-content > div.p-6.md\\:p-8.border-t.border-slate-100.bg-slate-50\\/50 > div.operating.flex.flex-wrap.gap-4 > button.bg-primary.hover\\:bg-primary\\/90.text-white.px-5.py-2\\.5.rounded-lg.transition-custom.flex.items-center.gap-1\\.5.shadow-sm.btn-hover")).getText();
        String title = driver.findElement(By.cssSelector("#blog-content > div.p-6.md\\:p-8.border-b.border-slate-100 > h1")).getText();
        String text = driver.findElement(By.cssSelector("#blog-content > div.p-6.md\\:p-8.detail-content")).getText();
        String buttonDelete = driver.findElement(By.cssSelector("#blog-content > div.p-6.md\\:p-8.border-t.border-slate-100.bg-slate-50\\/50 > div.operating.flex.flex-wrap.gap-4 > button.bg-red-500.hover\\:bg-red-600.text-white.px-5.py-2\\.5.rounded-lg.transition-custom.flex.items-center.gap-1\\.5.shadow-sm.btn-hover")).getText();
        driver.findElement(By.cssSelector("body > div > div > div.left.md\\:w-1\\/4.lg\\:w-1\\/5 > div > div.px-6.pb-6.-mt-12"));
        driver.findElement(By.cssSelector("body > nav > div > div.hidden.md\\:flex.items-center.space-x-6 > a:nth-child(1)"));
        driver.findElement(By.cssSelector("body > nav > div > div.hidden.md\\:flex.items-center.space-x-6 > a:nth-child(2)"));
        driver.findElement(By.cssSelector("body > nav > div > div.hidden.md\\:flex.items-center.space-x-6 > a.nav-span.text-slate-600.hover\\:text-red-500.transition-custom.flex.items-center.gap-1\\.5"));
        assert buttonEdit.equals("编辑");
        assert buttonDelete.equals("删除");
        assert !title.isEmpty();
        assert !text.isEmpty();
        driver.findElement(By.cssSelector("#blog-content > div.p-6.md\\:p-8.border-t.border-slate-100.bg-slate-50\\/50 > div.operating.flex.flex-wrap.gap-4 > button.bg-primary.hover\\:bg-primary\\/90.text-white.px-5.py-2\\.5.rounded-lg.transition-custom.flex.items-center.gap-1\\.5.shadow-sm.btn-hover")).click();
        String upPageTitle = driver.findElement(By.cssSelector("#title")).getText();
        assert title.equals(upPageTitle);
    }


    public void DetalUpdatepage() {
        String beforeTitle=driver.findElement(By.cssSelector("#title")).getText();
        //更新
        driver.findElement(By.cssSelector("#title")).clear();
        driver.findElement(By.cssSelector("#title")).sendKeys("自动化更新标题:" + createTime());
        driver.findElement(By.cssSelector("#submit")).click();
        String lastTitle = driver.findElement(By.cssSelector("body > div > div > div.right.flex-1 > div.space-y-6.blog-list > div:nth-child(1) > div.title.text-xl.font-bold.text-slate-800.hover\\:text-primary.transition-custom.mb-2")).getText();
        assert !beforeTitle.equals(lastTitle);
    }


    public void DetalDetelepage() {

        driver.findElement(By.cssSelector("body > div > div > div.right.flex-1 > div.space-y-6.blog-list > div:nth-child(1) > a")).click();
        //删除
        driver.findElement(By.cssSelector("#blog-content > div.p-6.md\\:p-8.border-t.border-slate-100.bg-slate-50\\/50 > div.operating.flex.flex-wrap.gap-4 > button.bg-red-500.hover\\:bg-red-600.text-white.px-5.py-2\\.5.rounded-lg.transition-custom.flex.items-center.gap-1\\.5.shadow-sm.btn-hover")).click();
        wait.until(ExpectedConditions.alertIsPresent());
        Alert alert = driver.switchTo().alert();
        alert.accept();
        wait.until(ExpectedConditions.alertIsPresent());
        alert = driver.switchTo().alert();
        alert.accept();
    }
}
(6)博客编辑类BlogEditPage
java 复制代码
public class BlogEditPage extends Utils {
    public static String url = "http://1.94.188.69:9090/blog_edit.html";

    public BlogEditPage() {
        super(url);
    }

    public void checkEditPage() {
        String editNoun = driver.findElement(By.cssSelector("body > div > div > div.mb-6.animate-fade-in > h1")).getText();
        driver.findElement(By.cssSelector("#submit"));
        driver.findElement(By.cssSelector("body > nav > div"));
        driver.findElement(By.cssSelector("body > div > div > div.mb-6.animate-fade-in > p"));
        driver.findElement(By.cssSelector("#title"));
        driver.findElement(By.cssSelector("#editor > div.CodeMirror.cm-s-default.CodeMirror-wrap > div.CodeMirror-scroll > div.CodeMirror-sizer > div > div"));
        assert editNoun.equals("编辑博客");

        //driver.findElement(By.cssSelector("#title")).clear();

    }

    public void EditText() {
        driver.findElement(By.cssSelector("#title")).sendKeys("自动化创建博客标题:" + createTime());
        // 定位 CodeMirror 编辑器元素
        WebElement element = driver.findElement(By.cssSelector("#editor > div.CodeMirror.cm-s-default.CodeMirror-wrap > div.CodeMirror-scroll > div.CodeMirror-sizer > div"));

        // 构建输入内容
        String content = "自动化内容生成:" + createTime() + "\n" +
                "自动化内容生成:" + createTime() + "\n" +
                "自动化内容生成:" + createTime() + "\n" +
                "自动化内容生成:" + createTime();

        // 清除现有内容并输入新内容
        new Actions(driver)
                // 移动到编辑器并点击获取焦点
                .moveToElement(element)
                .click()

                // 全选内容(兼容多平台)
                .keyDown(System.getProperty("os.name").contains("Mac") ? Keys.COMMAND : Keys.CONTROL)
                .sendKeys("a")
                .keyUp(System.getProperty("os.name").contains("Mac") ? Keys.COMMAND : Keys.CONTROL)

                // 删除选定内容
                .sendKeys(Keys.DELETE)

                // 添加短暂等待确保删除完成
                .pause(Duration.ofMillis(200))

                // 输入新内容
                .sendKeys(content)

                // 确保输入完成后按回车结束
                .sendKeys(Keys.ENTER)
                .perform();

        driver.findElement(By.cssSelector("#submit")).click();
        wait.until(ExpectedConditions.alertIsPresent());
        Alert alert = driver.switchTo().alert();
        alert.accept();
    }
}
(7)Main函数
java 复制代码
public class RunTests {

    public static void main(String[] args) throws InterruptedException {
        //登录界面
        BlogLoginPage blogLoginPage = new BlogLoginPage();
        blogLoginPage.checkPageRight();         //检查界面元素是否加载正常
        blogLoginPage.checkPublishButton();     //检查博客按钮是否正常
        blogLoginPage.checkErrorLogin();        //账号与密码错误时
        blogLoginPage.checkCorrectLogin();      //账号与密码正确时


        //博客列表界面
        BlogListPage blogListPage = new BlogListPage();
        blogListPage.CheckPageRight();  //检查界面元素是否加载正常
        blogListPage.CheckPageGitAndPush();//检查git与发布按钮是否可以跳转
        blogListPage.CheackDteail();     //进入详情页


        //博客更新/删除
        BlogDetailPage blogDetailPage = new BlogDetailPage();
        blogDetailPage.checkDetailRight();
        blogDetailPage.DetalUpdatepage();
        blogDetailPage.DetalDetelepage();


        //编辑博客
        BlogEditPage blogEditPage = new BlogEditPage();
        blogEditPage.checkEditPage();
        blogEditPage.EditText();

        Utils.quit();


    }
}

(三)性能测试

使用JMeter工具对论坛系统的接口进行简单的性能测试:

1.添加HTTP相关接口信息添加以及HTTP头文件管理器

(1)登录接口测试

以JSON格式发送

添加CSV文件,可以保证多个用户轮流登录

(2)博客列表接口测试

添加基本接口信息

写的token进行请求接口

配置JSON提取器,方便其他接口访问当前信息

(3)博客详情页接口测试

(4)更新博客接口测试

(5)发布博客接口

(6)删除接口测试

2.添加同步定时器
3.压力性能测试报告

一、核心指标概览

1、测试基础信息

测试脚本:test.jmx

持续时间:仅1分钟(11:54 PM → 11:55 PM),时长过短可能导致数据偶然性。

总请求量:1974次(平均并发≈33请求/秒)

2、成功率与失败率

通过率:99.8%(1970次成功)

失败率:0.2%(4次失败)

二、深度问题诊断

1. 错误分析(致命问题)

错误类型 次数 影响接口 根本原因
java.net.SocketException 3 登录接口 TCP连接超时
HttpHostConnectException 1 登录接口 目标服务器无响应

错误细节:

Connect to 1.94.188.69:9090 failed: Connection timed out

问题定位:

目标服务器 1.94.188.69:9090 的网络连通性故障或端口阻塞

服务器过载未及时响应(登录接口响应中位数达 18.4秒!)

2. 登录接口性能瓶颈

指标 登录接口 其他接口(对比)
请求次数 343次 ≈325次/接口
错误次数 3次 ≤1次
响应中位数 18415ms 约2ms~5ms
P95响应时间 3388ms <10ms

结论:

登录接口存在严重性能缺陷(响应延迟高达18秒)

错误率0.87%(远超其他接口0%~0.31%),是系统瓶颈点

3. Apdex指数分析

全局Apdex:0.879(尚可接受,但局部异常)

登录接口Apdex:0.542(远低于均值)

用户对登录接口的体验满意度显著劣化(500ms容忍阈值下)

以上为本次报告全部内容,感谢观看。

相关推荐
Deutsch.8 分钟前
负载均衡Haproxy
运维·负载均衡·haproxy
-XWB-32 分钟前
【安全漏洞】网络守门员:深入理解与应用iptables,守护Linux服务器安全
linux·服务器·网络
不做无法实现的梦~40 分钟前
mid360连接机载电脑,远程桌面连接不上的情况
运维·服务器·电脑
运维成长记1 小时前
关于linux运维 出现高频的模块认知
运维·职场和发展·云计算
kura_tsuki1 小时前
[Linux入门] Linux 远程访问及控制全解析:从入门到实战
linux·服务器·安全
张火火isgudi2 小时前
CentOS8 使用 Docker 搭建 Jellyfin 家庭影音服务器
服务器·docker·容器
IT成长日记2 小时前
【自动化运维神器Ansible】Ansible常用模块之archive模块详解
运维·自动化·ansible·常用模块·archive
晴天¥2 小时前
阶段1--域名服务器
运维·服务器·网络
Rover.x2 小时前
内存泄漏问题排查
java·linux·服务器·缓存
禁默2 小时前
进程调度的艺术:从概念本质到 Linux 内核实现
linux·运维·服务器