mockito+junit搞定单元测试(2h)

news2024/9/22 1:59:13

一,简介

1.1 单元测试的特点

  • 配合断言使用(杜绝 System.out )
  • 可重复执行
  • 不依赖环境
  • 不会对数据产生影响
  • spring 的上下文环境不是必须的
  • 一般都需要配合 mock 类框架来实现

1.2 mock 类框架使用场景

要进行测试的方法存在外部依赖(如 db, redis, 第三方接口调用等), 为了能够专注于对该方法(单元)的逻辑进行测试,就希望能虚拟出外部依赖,避免外部依赖成为测试的阻塞项,一般都是测试 service 层即可。

1.3 常用 mock类框架

mock 类框架;用于 mock 外部依赖

1.3.1 mockito

名称:ito: input to output

官网: https://site.mockito.org

官网文档: https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html

限制:老版本对于 fianl class, final method, static method, private method 均不能被 mockito mock, 目前已支持final class, final method, static method 的 mock, 具体可以参考官网

原理:bytebuddy, 教程: https://www.bilibili.com/video/BV1G24y1a7bd

1.3.2 easymock
1.3.3 powermock

官网:https://github.com/powermock/powermock

与mockito的版本支持关系:https://gitee.com/mirrors/powermock/wikis/Mockito#supported-versions 对 mockito 或 easymock 的增强

1.3.4 JMockit

二,mockito 的单独使用

2.1 mock 对象与 spy 对象

方法插桩方法不插桩作用对象最佳实践
mock 对象执行插桩逻辑返回mock对象的默认值类,接口被测试类或其他依赖
spy 对象执行插桩逻辑调用真实方法类,接口被测试类

2.2 初始化 mock/spy 对象的方式

方法一方法二方法三
junit4@RunWith(MockitoJUnitRunner.class) + @Mock等注解Mockito.mock(X.class)等静态方法MockitoAnnotations.openMocks(this)+@Mock等注解
junit5@ExtendWith(MockitoExtension.class) + @Mock等注解
示例

image-20240921185259112

/**
* 初始化 mock/spy 对象的方式有三种,第一种
* @author zhangdaowen
*/
@ExtendWith(MockitoExtension.class)
public class InitMockOrSpyMethod1 {
    @Mock
    private UserService mockUserService;
    @Spy
    private UserService spyUserService;
    
    @Test
    public void test1() {
        // true 判断某对象是不是mock对象
        System.out.println(" " + Mockito.mockingDetails(mockUserService).isMock().isMOck());
        // false
        System.out.println(""+Mockito.mockingDetails(mockUserService).isMock().isSpy());
        System.out.println(""+Mockito.mockingDetails(spyUserService).isMock());
        System.out.println(""+Mockito.mockingDetails(spyUserService).isMock());
    }
}
/**
* 初始化 mock/spy 对象的方式有三种,第二种
* @author zhangdaowen
*/
public class InitMockOrSpyMethod2 {
    private UserService mockUserService;
    private UserService spyUserService;
    
    @BeforeEach
    public void init() {
        mockUserService = Mockito.mock(UserService.class);
        spyUserService = Mockito.spy(UserService.class);
    }
    
    @Test
    public void test1() {
        // true 判断某对象是不是mock对象
        System.out.println(" " + Mockito.mockingDetails(mockUserService).isMock().isMOck());
        // false
        System.out.println(""+Mockito.mockingDetails(mockUserService).isMock().isSpy());
        System.out.println(""+Mockito.mockingDetails(spyUserService).isMock());
        System.out.println(""+Mockito.mockingDetails(spyUserService).isMock());
    }
}
/**
* 初始化 mock/spy 对象的方式有三种,第三种
* @author zhangdaowen
*/
public class InitMockOrSpyMethod3 {
    @Mock
    private UserService mockUserService;
    @Spy
    private UserService spyUserService;
    
    @BeforeEach
    public void init() {
       MockitoAnnotations.openMocks(this);
    }
    
    @Test
    public void test1() {
        // true 判断某对象是不是mock对象
        System.out.println(" " + Mockito.mockingDetails(mockUserService).isMock().isMOck());
        // false
        System.out.println(""+Mockito.mockingDetails(mockUserService).isMock().isSpy());
        System.out.println(""+Mockito.mockingDetails(spyUserService).isMock());
        System.out.println(""+Mockito.mockingDetails(spyUserService).isMock());
    }
}

2.3 参数匹配

/**
* 参数匹配; 通过方法签名(参数)来指定哪些方法调用需要被处理(插桩,verify验证)
* @author zhangdaowen
*/
@ExtendWith(MockitoExtension.class)
public class ParamMatcherTest {
    @Mock
    private UserService mockUserService;
    
    @Test
    public void test4(){ 
        List<String> features = new ArrayList<>();
        mockUserService.add("乐之者Java", phone:"123", features);
        // 校验参数为 "乐之者Java", "123", features 的 add 方法调用了1次
        Mockito.verify(mockUserService,MOckito.times(wantedNumberOfInvocations:1)).add("乐之者Java", phone:"123", features);
        // 报错:When using matchers, aLL arguments have to be provided by matches
      //Mockito.verify(mockUserService,Mockito.times(wantedNumberOfInvocations:1)).add(ArgumentMatchers.anyString(), "123", features);
        Mockito.verify(mockUserService,Mockito.times(wantedNumberOfInvocations:1)).add(ArgumentMatchers.anyString(), anyString, anyList());
        
    }
    /**
    *  ArgumentMatchers.any 拦截 UserUpdateReq 类型的任意对象
    * 除了any, 还有anyXX(anyLong, anyString) 注意:它们都不包括null
    */
    @Test
    public void test3() {
    Mockito.doReturn(toBeReturned:99).when(mockUserService).modifyById(ArgumentMatchers.any(UserUpdateReq.class));
        
        UserUpdateReq userUpdateReq1 = new UserUpdateReq();
        userUpdateReq1.setId(1L);
        userUpdateReq1.setPhone("1L");
        Mockito.doReturn(toBeReturned: 99).when(mockUserService).modifyById(userUpdateReq1); 
        int result1 = mockUserService.modifyById(userUpdateReq1); 
        
        UserUpdateReq userUpdateReq2 = new UserUpdateReq();
        userUpdateReq2.setId(2L);
        userUpdateReq2.setPhone("2L");
        Mockito.doReturn(toBeReturned: 99).when(mockUserService).modifyById(userUpdateReq2); 
        int result2 = mockUserService.modifyById(userUpdateReq2);
    }
    /**
    * 测试插桩时的参数匹配, 只拦截userUpdateReq1
    */
    @Test
    public void test2() {
        UserUpdateReq userUpdateReq1 = new UserUpdateReq();
        userUpdateReq1.setId(1L);
        userUpdateReq1.setPhone("1L");
        //指定参数为userUpdateReq1时调用mockUserService.modifyById返回99
        Mockito.doReturn(toBeReturned: 99).when(mockUserService).modifyById(userUpdateReq1); // 此处并不产生结果
        int result1 = mockUserService.modifyById(userUpdateReq1); // 此处产生结果
        
        UserUpdateReq userUpdateReq2 = new UserUpdateReq();
        userUpdateReq2.setId(2L);
        userUpdateReq2.setPhone("2L");
        //指定参数为userUpdateReq1时调用mockUserService.modifyById返回99
        Mockito.doReturn(toBeReturned: 99).when(mockUserService).modifyById(userUpdateReq2); 
        int result2 = mockUserService.modifyById(userUpdateReq2); 
    }
    /**
    * 对于 mock 对象不会调用真实方法,直接返回 mock对象的默认值
    * 默认值(int), null(UserVO), 空集合(List)
    */
    @Test
    public void test1() {
        UserVO userVO = mockUserService.selectById(1);
        // null
        System.out.println("userVO = " + userVO);
        UserUpdateReq userUpdateReq1 = new UserUpdateReq();
        int i = mockUserService.modifyById(UserUpdateReq1);
        System.out.println("i=" + i);
    }
}

2.4 方法插桩

指定调用某个方法时的行为(stubbing),达到相互隔离的目的

  • 返回指定值

  • void返回值方法插桩

  • 插桩的两种方式

    • when(obj.someMethod()).thenXxx():其中 obj 可以是 mock 对象
    • doXxx(.when(obj).someMethod(): 其中 obj 可以是 mock/spy 对象 或者是无返回值的方法进行插桩
  • 抛异常

  • 多次插桩

  • thenAnswer

  • 执行真正的原始方法

  • verify的使用

/**
* @author zhaodaowen
* @see <a href="http://www.roadjava.com">乐之者java</a>
*/
@ExtendWith(MockitoExtension.class)
public class StubTest {
    @Mock
    private List<String> mockList;
    @Mock
    private UserServiceImp1 mockUserServiceImp1;
    @psy
    private UserServiceImp1 spyUserServiceImp1;
    
    
    /**
    * 测试verigy
    */
    @Test
    public void test8() {
        mockList.add("one");
        // true: 调用mock对象的写操作方法是没效果的
        Assertions.assertEquals(0, mockList.size());
        mockList.clear();
        // 验证调用过1次add方法,且参数必须是one
        verify(mockList)
            // 指定要验证的方法和参数,这里不是调用,也不会产生作用效果
            .add("oen");
        
        // 等价于上面的verigy(mockedList)
        verify(mockList, times(1)).clear();
        
      	// 检验没有调用的两种方式
        verify(mockList, times(0)).clear();
        verify(mockList, never()).get(1);
        
        // 检验最少或最多调用了多少次
        verify(List, atLeast(1)).clear();
        verify(List, atMost(3)).clear();
        
    }
     /**
    * 执行真正的原始方法
    */
    @Test
    public void test7() {
        when(mockUserServiceImpl.getNUmber()).thenCallRealMethod();
        int number = mockUserServiceImp1.getNumber();
        Assertions.assertEquals(0, number);
        
        // spy对象默认就会调用真实方法,如果不想让它调用,需要单独为它进行插桩
        int spyResult = spyUserServiceImpl.getNumber();
        Assertions.assertEquals(0, spyResult);
        
        // 不让spy对象调用真实方法
        doReturn(1000).when(spyUserServiceImpl).getNumber();
        spyResult = spyUserServiceImpl.getNumber();
        Assertions.assertEquals(1000, spyResult);
    }
    /**
    * thenAnswer 实现指定逻辑的插桩
    */
    @Test
    public void Test6() {
        when(mockList.get(anyInt())).thenAnswer(new Answer<String>() {
            /**
            * 泛型表示要插桩方法的返回值类型
            */
            @Override
            public String answer(InvocationOnMock invocation) throws Throwable {
                // getArgument 表示获取插桩方法(此处就是List.get)的第几个参数值
                Integer argument = invocation.getArgument(0, Integer.class);
                return String.vaLueOf(argument * 100);
            }
        });
        String result = mockList.get(3);
        Assertions.assertEquals("300", result);
    }
     /**
    * 多次插桩
    */
    @Test
    public void test5() {
        // 第一次调用返回1, 第二次调用返回2, 第3次及之后的调用都返回3
   //     when(mockList.size()).thenReturn(1).thenReturn(2).thenReturn(3);
        // 可间接写为
        when(mockList.size()).thenReturn(1, 2, 3);
        Assertions.assertEquals(1, mockList.size());
        Assertions.assertEquals(2, mockList.size());
        Assertions.assertEquals(3, mockList.size());
    }
    /**
    * 抛出异常
    */
    @Test
    public void test4() {
        // 方法一
        doThrow(RuntimeException.class).when(mockList).clear();
        try {
            mockList.clear();
            // 走到下面这一行,说明插桩失败了
            Assertions.fail();
        } catch (Exception e) {
            // 断言表达式为真
            Assertions.assertTrue(e instanceof RuntimeException);
        }
        // 方法二
        when(mockList.get(anyInt)).thenThrow(RuntimeException.class);
        try {
            mockList.get(4);
            Assertions.fail();
        } catch (Exception e) {
            Assertions.assertTrue(e instanceof RuntimeException);
        }
    }
    /**
    * 插桩的两种方式
    */
    @Test
    public void test3() {
        when(mockUserServiceImp1.getNumber()).thenReturn(99);
        // mockUserServiceImpl.getNumber() = 99 不调用真实方法
        System.out.println("" + mockUserServiceImp1.getNumber());
        
        when(spyUserServiceImp1.getNumber()).thenReturn(99);
        // getNumber
        // spyUserServiceImpl.getNumber() = 99
        // spy对象在没有插桩时是调用真实方法的,写在when中会导致先执行一次原方法,达不到mock的目的
        // 需使用doXxx.when(obj).someMethod();其中obj可以是mock/spy对象
        System.out.println("" + spyUserServiceImp1.getNumber());
        
        doReturn(1000).when(spyUserServiceImpl).getNumber();
    }
    /**
    * void 返回值插桩
    */
    @Test
    public void test2() {
        // 调用mockList.clear的时候什么也不做
        doNothing().when(mockList).clear();
        mockList.clear();
        // 验证调用了一次clear
        verfy(mockList,times(wantedNumberOfInvocations:1)).clear();
    }
    /**
    * 指定返回值
    */
    @Test
    public void Test1 {
        // 方法一
        doReturn("zero").when(mockList).get(0);
        // 如果返回值不相等则本单元测试会失败
        Assertions.assertEquals("zero", mockList.get(0));
        
        //方法二
        when(mockList.get(1)).thenReturn("one");
        Assertions.assertEquals("one", mockList.get(1));
    }
}

2.5 @InjectMocks注解的使用

  • 作用:若@InjectMocks 声明的变量需要用到 mock/spy 对象,mockito 会自动使用当前类里的 mock 或 spy 成员进行按类型或名字的注入
  • 原理:构造器注入、setter注入、字段反射注入
@ExtendWith(MockitoExtension.class)
public class InjectMocksTest {
    /**
    * 1.被@InjectMocks标注的属性必须是实现类,因为mockito会创建对应的示例对象,默认创建的对象就是未经过mockito处理的普通对象,因此常配合@Spy注解使其变为默认调用真实方法的mock对象
    * 2.mockito会使用spy或mock对象注入到@InjectMocks对应的示例对象中
    */
    @Spy
    @InjectMocks
    private UserService userService;
    
    @Mock
    private UserFeatureService userFeatureService;
    
    @Mock
    private List<String> mockList;
    
    @Test
    public void test1() {
        int number = userService.getNumber();
        Assertions.assertEquals(0, number);
    }
}

2.6 断言工具

namcrest:junit4 中引入的第三方断言库,junit5 中被移出,从 1.3 版本后,坐标由 org.hamcrest:hamcrest 变为org.hamcrest:hamcrest

assert: 常用的断言库

junit4 原生断言

junit5 原生断言

@ExtendWith(MockitoExtension.class)
public class AssertTest {
    @Mock
    private List<String> mockList;
    
    @Test
    public void test1() {
        when(mockList.size()).thenReturn(999);
        // 测试hamcrest的断言
        MatchAssert.assertThat(mockList.size(), IsEqual.equalTo(999));
        // 测试 assertJ assertThat: 参数为实际的值
        Assertions.assertThat(mockList.size().isEquaTo(999));
        // junit5原生断言
        orj.junit.jupiter.api.Assertions.assertEquals(999, mockList.size());
        // junit4原生断言
        org.junit.Assert.assertEquals(999, mockList.size());
    }
    
}

三,实战讲解

四,mockito在springboot环境使用(不推荐-)

生成的对象受 spring 管理,相当于自动替换对应类型 bean 的注入

@MockBean

  • 类似@Mock
  • 用于通过类型或名字 替换 spring 容器中已经存在的bean,从而达到对这些bean进行mock的目的

@SpyBean

  • 作用类似@Spy
  • 用于通过类型或名字包装spring容器中已经存在的bean,当需要mock被测试类的某些方法时可以使用
/**
* Mock配合spring使用
**/
@SpringBootTest(class = MockitoApp.class)
public class UserServiceImplInSpringTest {
    
    /**
    * 不能配置@Spy: Argument passed to when() is not mock!
    */
    @SpyBean
    @Resource
    private UserServiceImp1 userService;
    
    @Mock
    private UserFeatureService userFeatureService;
    
    @Mock
    private UserMapper userMapper;
    
    @Test
    public void testSelectById3() {
        //配置方法getById的返回值
        UserDo ret = new UserDo();
        ret.setId(1L);
        ret.setUsername("乐之者java");
        ret.setPhone("http://www.roadjava.com");
        doReturn(ret).when(userService).getById(1L);
        //配置userFeatureService.seLectByUserId的返回值
        List<UserFeatureDO> userFeatureDoList = new ArrayList<>();
        UserFeatureDO userFeatureDO = new UserFeatureDo();
        userFeatureD0.setId(88L);
        userFeatureD0.setUserId(1L);
        userFeatureDO.setFeatureValue("aaaa");
        userFeatureDoList,add(userFeatureDo);
        doReturn(userFeatureDoList).when(userFeatureService).selectByUserId(lL);
        // 执行测试
        UserVo userVO = userService,selectById(userld:1L);
        // 断言
        Assertions.assertEquals(expected:1,userVO.getFeatureValue().size());
    }
    
    @Test
    public void testSelectById2() {
        // 配置方法getById的返回值
        UserDo ret = new UserDo();
        ret.setId(1L);
        ret.setUsername("乐之者java");
        ret.setPhone("http://www.rodajava.com"); // up主的广告
        doReturn(ret).when(userService).getById(1L);
        UserVo userVO = userService.selectById(1L);
        Assertions.assertNotNUll(userVO);
    }
    
    @Test
    public void testSelectById1() {
        // 配置
        doReturn(userMapper).when(userService).getBaseMapper();
        UserVO userVO = userService.selectById(1L);
        Assertions.assertNull(userVO);
    }
}

漫谈

image-20240921152749306

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2153870.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HashMap扩容时机是插入前还是插入后?

结论 不管是HashMap还是ConcurrentHashMap都是插入后。 过程为&#xff1a; 先计算哈希值。对应的哈希槽插入数据&#xff0c;决定是红黑树还是链表插入完毕才计算是否需要扩容&#xff0c;假如需要则扩容 源码 源码如下&#xff1a; 其中addCount方法里面写入扩容。

如何设置 Django 错误邮件通知 ?

Django 是一个强大的 web 框架&#xff0c;非常适合那些想要完美快速完成任务的人。它有许多内置的工具和特性&#xff0c;一个有用的特性是 Django 可以在出现错误时发送电子邮件提醒。这对开发人员和管理员非常有用&#xff0c;因为如果出现问题&#xff0c;他们会立即得到通…

STM32F407单片机编程入门(十二) FreeRTOS实时操作系统详解及实战含源码

文章目录 一.概要二.什么是实时操作系统三.FreeRTOS的特性四.FreeRTOS的任务详解1.任务函数定义2.任务的创建3.任务的调度原理 五.CubeMX配置一个FreeRTOS例程1.硬件准备2.创建工程3.调试FreeRTOS任务调度 六.CubeMX工程源代码下载七.小结 一.概要 FreeRTOS是一个迷你的实时操…

智慧校园建设解决方案建设系统简介

一、建设背景 1.1 政策背景 1.2 班牌的演变 1.3 建设愿景 二、 智慧班牌简介 三、智慧班牌系统 3.1 系统概述 3.2 软件平台功能交互简介 3.2.1 智慧班牌与管理平台间的功能关联 3.2.2 手机客户端&#xff08;管理员、教师、家长端&#xff09; 3.2.3 手机客户端&#x…

数据采集与预处理,前后端结合案例(有代码),Python连接MySQL,对MySQL的增删改查

Python对MySQL的增删改查 通过Python连接MySQL """连接MySQL数据库&#xff0c;并进行增删改查&#xff0c;同时查询了MySQL版本号&#xff0c;并做了动态注册的账号&#xff0c;实现过程&#xff1a;先向userinfo当中添加account、password新字段&#xff0c…

数据结构:内部排序

文章目录 1. 前言1.1 什么是排序&#xff1f;1.2 排序的稳定性1.3 排序的分类和比较 2. 常见的排序算法3. 实现常见的排序算法3.1 直接插入排序3.2 希尔排序3.3 直接选择排序3.4 堆排序3.5 冒泡排序3.6 快速排序3.6.1 hoare思想3.6.2 挖坑法3.6.3 lomuto前后指针法3.6.4 非递归…

软考(中级-软件设计师)计算机系统篇(0921)

I 计算机系统知识 一、考纲要求 数值及其转换 二进制、十进制和十六进制等常用数制及其相互转换 计算机内数据的表示 数值的表示&#xff08;原码、反码、补码、移码表示&#xff0c;整数和实数的机内表示&#xff0c;精度和溢出&#xff09;非数值表示&#xff08;字符和汉字…

AI直播新浪潮:无人视频自动直播,出圈再造辉煌,创业者首选!

AI直播新浪潮:无人视频自动直播&#xff0c;出圈再造辉煌&#xff0c;创业者首选&#xff01; 在数字化浪潮的汹涌澎湃中&#xff0c;AI技术正以前所未有的速度重塑着各行各业的边界&#xff0c;而直播行业作为数字内容消费的前沿阵地&#xff0c;正迎来一场由AI驱动的深刻变革…

MQ(RabbitMQ)笔记

初识MQ 同步调用优缺点 异步调用优缺点 总结&#xff1a; 时效性要求高&#xff0c;需要立刻得到结果进行处理--->同步调用 对调用结果不关心&#xff0c;对性能要求高&#xff0c;响应时间短--->异步调用

2024年华为杯-研赛F题论文问题一二讲解+代码分享

X射线脉冲星光子到达时间建模 摘要 脉冲星是一类高速自转的中子星&#xff0c;其自转形成规律性脉冲信号&#xff0c;类似于“宇宙中的灯塔”&#xff0c;因此被认为是极为精确的时钟。X射线脉冲星导航利用脉冲星信号为航天器提供时间和空间参考。通过比较脉冲信号到达航天器…

查找算法 01分块查找

自己设计一个分块查找的例子&#xff0c;不少于15个数据元素&#xff0c;并建立分块查找的索引 基于上述例子&#xff0c;计算查找成功的ASL、查找失败的ASL 拓展&#xff1a; ‌‌分块查找的平均查找长度&#xff08;‌ASL&#xff09;的计算公式如下‌&#xff1a;‌ ‌顺序…

Camunda流程引擎并发性能优化

文章目录 Camunda流程引擎一、JobExecutor1、工作流程2、主要作用 二、性能问题1、实际场景&#xff1a;2、性能问题描述3、总结 三、优化方案方案一&#xff1a;修改 Camunda JobExecutor 源码以实现租户 ID 隔离方案二&#xff1a;使用 max-jobs-per-acquisition 参数控制上锁…

ThreadLocal与AsyncLocal

简介 ThreadLocal 用于在多线程环境中创建线程局部变量&#xff0c;可以让每个线程独立地访问自己的变量副本&#xff0c;互不影响。 而 AsyncLocal 是 ThreadLocal 的异步版本&#xff0c;专门用于异步编程场景&#xff0c;在异步操作中它可以正确处理上下文切换。 ThreadLo…

ftp服务的管理及安全优化

1.ftp介绍 ftp : file transfer proto 互联中最老牌的文件传输协议 2.vsftpd安装及启用 环境 #server 主机 &#xff1a; # R3 # 192.168.10.130 # selinux 关闭 # 火墙开启 # dnf 安装设定完成 # #client 主机 &#xff1a; # R4 # 192.168.10.131 # selinux 关闭 …

C++之职工管理系统(细节Q)

指针初始化类 && 普通变量初始化类 抽象基类worker&#xff0c;只需编写 .h &#xff0c;无需 .cpp 底层实现 类 记得声明权限public&#xff01;&#xff01;&#xff01;不然默认private&#xff0c;主函数访问不了 记得继承父类 Worker * worker&#xff1a;指向Wo…

source insight学习笔记

目录 目的 基础配置 1、护眼的保护色 2、行号显示 基础操作 目的 记录一下使用source insight中遇到的问题。比如常见好用的基础配置&#xff0c;常用的基础操作等。主要是为了自己以后忘记了好找。自己写的东西总归看起来更舒服。 PS&#xff1a;目前是第一个版本&#…

Linux相关概念和重要知识点(5)(权限的修改、时间属性)

1.权限的修改 &#xff08;1&#xff09;提权行为 普通用户是会受到权限的限制&#xff0c;但root账户无视一切权限限制&#xff0c;因此当我们要获取更高的权限&#xff0c;有一种办法就是将自己变成root或者短暂拥有和root一样的权力。 普通用户 -> root &#xff1a;s…

NoSql数据库Redis知识点

数据库的分类 关系型数据库 &#xff0c;是建立在关系模型基础上的数据库&#xff0c;其借助于集合代数等数学概念和方法来处理数据库 中的数据主流的 MySQL 、 Oracle 、 MS SQL Server 和 DB2 都属于这类传统数据库。 NoSQL 数据库 &#xff0c;全称为 Not Only SQL &a…

网络丢包定位记录(二)

网卡驱动丢包 查看&#xff1a;ifconfig eth1/eth0 等接口 1.RX errors: 表示总的收包的错误数量&#xff0c;还包括too-long-frames错误&#xff0c;Ring Buffer 溢出错误&#xff0c;crc 校验错误&#xff0c;帧同步错误&#xff0c;fifo overruns 以及 missed pkg 等等。 …

K8S介绍+集群部署

Kubernetes介绍 官网&#xff1a;https://kubernetes.io/ 一、应用部署方式演变 1、传统部署&#xff1a;互联网早期&#xff0c;会直接将应用程序部署在物理机上 优点&#xff1a;简单&#xff0c;不需要其他技术的参与 缺点&#xff1a;不能为应用程序定义资源使用边界&a…