SpringBoot + Kubernetes云原生微服务实践 - (6) 微服务测试设计和实践

微服务测试设计和实践

微服务测试的最大挑战:依赖。
解决方案是采用分而治之的策略:
a.先针对每一个微服务进行隔离测试,在对每一个微服务进行测试的时候再按照分层的方式进行隔离测试;测试过程中采用mock等技术来隔离依赖简化测试;
b.在确保每个微服务通过隔离测试后,再进行整个应用的端到端集成测试

微服务测试分类和技术

  1. Spring(Boot)应用分层

    • controller

      • 服务的对外接口层,负责接收请求和发送响应
      • 中间涉及到消息,一般是json跟对象间的转换,术语叫做序列化,一般由框架封装
      • 控制器需要对输入数据进行校验,由开发人员定制
      • 控制器一般不包含业务逻辑,而是将业务逻辑代理给服务层去做
    • service + domain
      • 相互配合,共同实现服务的业务逻辑
      • 两种做法:一是业务逻辑主要包含在领域对象内,服务层是领域对象的协调器,称为胖领域模型;一是业务逻辑主要包含在服务层,领域对象除了状态字段基本不包含业务逻辑,称为瘦领域模型
    • proxy
      • 微服务一般不孤立存在,而是会和其他服务协作实现功能,这时服务会通过服务代理proxy去访问其他服务
      • 服务代理其实就是目标服务的一个客户端;如果采用feign这样的框架,可以实现基于接口代理直接访问目标服务,背后是采用反射+httpClient操作的方式来实现
    • repository
      • 封装领域对象的数据库存取逻辑,底层基于ORM框架,如hibernate、mybatis;hibernate相对较重,抽象层次较高;mybatis相对轻量、灵活
      • 领域驱动设计(DDD)中的一种数据访问设计模式
      • 在spring data中,直接支持基于repository接口的抽象数据访问层,背后是通过接口反射+调用ORM操作数据库
    • 重点
      • 每个层次及层次间的交互是测试需要覆盖的重点
      • 持久层和服务代理层,涉及到跨越网络边界的调用,较容易出问题
      • 控制器是对外的接口,对入参的校验也特别需要关注
  2. 单元测试(Unit Test):最细粒度的测试
    • 工作在方法、类、若干个协作类级别
    • 单元测试的点:
      • controller层的测试:入参校验、错误处理逻辑;spring提供MockMVC机制,方便对控制器进行单元测试;对于涉及service层的调用,或proxy调用的地方,采用mock进行隔离
      • service和domain的测试:如果有业务规则逻辑计算,需进行充分的单元测试,保证业务逻辑规则的正确性;对于涉及repository层的调用,或proxy调用的地方,采用mock进行隔离
      • repository层的测试:repository层涉及领域对象的存储和修改,是微服务应用的底层基础,为了保证数据存取逻辑的正确性,repository层需要充分的测试;因为持久层涉及和数据库进行跨进程网络的交互,为了方便单元测试,在单元测试时使用嵌入式内存数据库,通过隔离外部依赖,让测试做到自包含,这样单元测试更稳定,反馈周期更快;比如在spring进行单元测试时,可使用H2内存数据库暂时替代外部数据库
      • 对于proxy层,基本是目标服务的接口,没有特别的业务逻辑,没有必要进行单元测试;对于设计底层通讯和错误处理的部分,由集成测试进行覆盖
      • 对于微服务中抽取出来的公共类,由于逻辑相对独立,适合充分的单元测试
    • 工具:
      • junit
      • mockito
      • spring提供的MockMVC、@SpringBootTest
    • 单元测试即使有充分的覆盖度,最多也只能保证每个层次独立工作的正确性,不能保证层次间协作逻辑的正确性,更不能保证系统工作的正确性
  3. 集成测试(Integration Test)
    • 针对组件之间有交互的地方,保证服务接口和通讯链路的正确性
    • 微服务场景下,集成测试的点包括:
      • 服务通过proxy访问外部服务的地方:需要测试请求响应交互逻辑,包括成功和失败、底层序列化、http处理、请求参数(包括http头参数)校验、错误处理逻辑;对远端服务集成测试时,为避免状态依赖,可以mock后端或预置一些mock数据,因为集成重点是发现接口交互错误,而不是后台逻辑
      • 持久层通过网络存取远程数据库的地方
    • 与单元测试互为补充,一个关注交互,一个关注内核模块
  4. 组件测试(Component Test)
    • 讲一个微服务看作一个独立的逻辑组件,不关心内部细节,而是看作一个黑箱,仅对其暴露的公开接口进行测试,这时一般需要把微服务的外部依赖给mock掉,这样才能保证其逻辑独立性
    • 对外部依赖mock有两种方式:
      • 组件内部Mock:具有更好的自包含性和测试稳定性,是开发人员常用的方法;spring支持mockbean,直接把依赖的proxy给mock掉;wiremock工具也常用来mock外部依赖,比mockbean工作在更低层次的协议层次,可做更细粒度的mock控制
      • 组件外部Mock:这种方式对组件无侵入,通过搭建独立的mock service来实现,是测试人员常用的测试方法,可在mock service上定制各种灵活复杂的微服务测试场景;高级的mock service还支持录制回放的功能、模拟网络延迟、随机失败的场景;业界称为API simulation;工具有HoverFly、mbtest
  5. 契约测试(Contract Test)
    • 由来

      • 微服务作为业务服务的提供方,需要有消费者使用才能共同产生业务价值,而服务提供方和消费方之所以能够正确交互,是因为他们之间共同约定并遵守一个契约,契约规范了双方交互输入输出所必须的字段和格式;同时服务提供方一直在升级当中
      • 如果没有相应的测试手段,服务提供方可能由于某次疏忽而break了某个消费方,现实生产中经常发生
      • 所以需要契约测试,来保证提供方和消费方之间的契约没有被违反
    • 契约测试一般由消费方开发,根据自己的业务需要定义契约,先验证自己测试通过,然后将契约交给生产方去验证;相当于消费方提供给服务方的接口需求,可以用来驱动服务方的生产设计和开发,所以业界称为消费者驱动契约(Consumer Driven Contract)
    • 工具:PACT、Spring-cloud-contract
  6. 契约驱动测试
    • Contract is input for Mock Provider
    • Contract is input for Mock Consumer
  7. 端到端测试(End-to-End Test)
    • 将整个系统看作一个黑盒,通过其接口(gui或api)对整个应用整体进行业务功能和非功能的测试
    • 工具:Selenium、REST-assured
  8. 总结
    分类 功能
    单元测试 确保类、模块功能正确
    集成测试 确保组件间接口、交互、链路正确
    组件测试 确保微服务作为独立整体,接口功能正确
    契约测试 确保服务提供方和消费方都遵循契约规范
    端到端测试 确保整个应用满足用户需求
    探索测试 手工探索学习系统功能,改进自动化测试

测试金字塔和实践

  1. 测试金字塔

    • Unit -> Integration -> Component -> End-to-End -> Exploratory
    • 数量减少,粒度变粗,覆盖面增加,稳定性降低,测试变慢
  2. 端到端测试实践
    • 粒度最粗, 涉及到依赖、状态、异步等可变因素很多,是最不稳定的测试
    • 开发投入和维护成本最高
    • 最佳实践
      • 80/20,聚焦核心业务服务
      • 用户使用场景驱动
      • 适当使用Mock来覆盖不稳定的测试点
      • 规范测试环境和环境自动化:如k8s一键创建微服务测试环境
      • 测试数据管理:快速创建测试数据
      • 灰度测试:线上测试,一部分新功能让一部分用户使用,如beta测试,通过后再放量
      • 生产监控:弥补线下测试的不足

Test Case Review ~ 单元测试

  1. case1:repository层测试,使用In Memory DB

    • application.yml:account-svc\src\test\resources\application.yml
    • AccountRepoTest:xyz.staffjoy.account.repo.AccountRepoTest
    # ****** H2 In Memory Database Connection Info *******
    spring:
    ...
    datasource: # use in-memory db for unit testing
        url: jdbc:h2:mem:staffjoy_account;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE;MODE=MYSQL
        username: sa
        password:
        driver-class-name: org.h2.Driver
        continue-on-error: false
        platform: h2
        schema: classpath:/db/schema.sql
    h2:
        console:
        enabled: true
    jpa:
        hibernate:
        ddl-auto: validate
        show-sql: true
        properties:
        hibernate:
            format_sql: true
    output:
        ansi:
        enabled: always
    
    staffjoy:
    common:
        sentry-dsn: ${SENTRY_DSN:https://[email protected]/1234888} # mock for test
        deploy-env: ${DEPLOY:V2}
    signing-secret: ${SIGNING_SECRET:TEST_SECRET}
    email-service-endpoint: http://email-service
    company-service-endpoint: http://company-service
    bot-service-endpoint: http://bot-service
    account-service-endpoint: http://localhost:8080 # for testing only
    intercom-access-token: ${INTERCOM_ACCESS_TOKEN:TEST_INTERCOM_ACCESS_TOKEN}
    
    @SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.NONE)
    @RunWith(SpringRunner.class)
    public class AccountRepoTest {
    
        @Autowired
        private AccountRepo accountRepo;
    
        @Autowired
        private AccountSecretRepo accountSecretRepo;
    
        private Account newAccount;
    
        @Before
        public void setUp() {
            newAccount = Account.builder()
                    .name("testAccount")
                    .email("[email protected]")
                    .memberSince(LocalDateTime.of(2019, 1, 20, 12, 50).atZone(ZoneId.systemDefault()).toInstant())
                    .confirmedAndActive(false)
                    .photoUrl("https://staffjoy.xyz/photo/test.png")
                    .phoneNumber("18001801266")
                    .support(false)
                    .build();
            // sanity check
            accountRepo.deleteAll();
        }
    
        @Test//(expected = DuplicateKeyException.class)
        public void createSampleAccount() {
            accountRepo.save(newAccount);
            assertTrue(accountRepo.existsById(newAccount.getId()));
        }
    
        @Test
        public void getAccountById() {
            accountRepo.save(newAccount);
            assertEquals(1, accountRepo.count());
            Account foundAccount = accountRepo.findById(newAccount.getId()).get();
            assertEquals(newAccount, foundAccount);
        }
    
        ...
    }
  2. case2:controller层测试,校验输入输出是否合法
    • xyz.staffjoy.company.controller.ut.CompanyControllerUnitTest
    @RunWith(SpringRunner.class)
    @SpringBootTest
    @AutoConfigureMockMvc
    public class CompanyControllerUnitTest {
    
        @Autowired
        MockMvc mockMvc;
    
        @MockBean
        CompanyService companyService;
    
        @Autowired
        ObjectMapper objectMapper;
    
        CompanyDto newCompanyDto;
    
        @Rule
        public ExpectedException expectedEx = ExpectedException.none();
    
        @Before
        public void setUp() {
            newCompanyDto = CompanyDto.builder()
                    .archived(false)
                    .name("test-company")
                    .defaultDayWeekStarts("Monday")
                    .defaultTimezone(TimeZone.getDefault().getID())
                    .build();
        }
    
        @Test()
        public void testCreateCompanyAuthorizeMissing() throws Exception {
    
            MvcResult mvcResult = mockMvc.perform(post("/v1/company/create")
                    .contentType(MediaType.APPLICATION_JSON)
                    .content(objectMapper.writeValueAsBytes(newCompanyDto)))
                    .andExpect(status().isOk())
                    .andReturn();
    
            GenericCompanyResponse genericCompanyResponse = objectMapper.readValue(mvcResult.getResponse().getContentAsString(), GenericCompanyResponse.class);
            assertThat(genericCompanyResponse.isSuccess()).isFalse();
            assertThat(genericCompanyResponse.getCode()).isEqualTo(ResultCode.UN_AUTHORIZED);
        }
    
        @Test
        public void testCreateCompanyPermissionDeniedException() throws Exception {
    
            MvcResult mvcResult = mockMvc.perform(post("/v1/company/create")
                    .contentType(MediaType.APPLICATION_JSON)
                    .header(AuthConstant.AUTHORIZATION_HEADER, AuthConstant.AUTHORIZATION_COMPANY_SERVICE)
                    .content(objectMapper.writeValueAsBytes(newCompanyDto)))
                    .andExpect(status().isOk())
                    .andReturn();
    
            GenericCompanyResponse genericCompanyResponse = objectMapper.readValue(mvcResult.getResponse().getContentAsString(), GenericCompanyResponse.class);
            assertThat(genericCompanyResponse.isSuccess()).isFalse();
            assertThat(genericCompanyResponse.getCode()).isEqualTo(ResultCode.UN_AUTHORIZED);
        }
    
        ...
    }

Test Case Review ~ 集成测试

  1. repository访问外部数据库,涉及到跨进程调用

    • 代码略
  2. proxy调用外部服务,涉及到跨进程调用
    • accountClient去调用accountController
    @RunWith(SpringRunner.class)
    @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
    @EnableFeignClients(basePackages = {"xyz.staffjoy.account.client"})
    @Import(TestConfig.class)
    @Slf4j
    public class AccountControllerTest {
    
        @Autowired
        AccountClient accountClient;
    
        @Autowired
        EnvConfig envConfig;
    
        @MockBean
        MailClient mailClient;
    
        @MockBean
        BotClient botClient;
    
        @Autowired
        private AccountRepo accountRepo;
    
        @Autowired
        private AccountSecretRepo accountSecretRepo;
    
        private Account newAccount;
    
        @Before
        public void setUp() {
            // sanity check
            accountRepo.deleteAll();
            // clear CURRENT_USER_HEADER for testing
            TestConfig.TEST_USER_ID = null;
        }
    
        @Test
        public void testUpdateAccount() {
            // arrange mock
            when(mailClient.send(any(EmailRequest.class))).thenReturn(BaseResponse.builder().message("email sent").build());
            when(botClient.sendSmsGreeting(any(GreetingRequest.class))).thenReturn(BaseResponse.builder().message("sms sent").build());
    
            // first account
            String name = "testAccount001";
            String email = "[email protected]";
            String phoneNumber = "18001801236";
            String subject = "Activate your Staffjoy account";
            CreateAccountRequest createAccountRequest = CreateAccountRequest.builder()
                    .name(name)
                    .email(email)
                    .phoneNumber(phoneNumber)
                    .build();
            // create
            GenericAccountResponse genericAccountResponse = accountClient.createAccount(AuthConstant.AUTHORIZATION_WWW_SERVICE, createAccountRequest);
            assertThat(genericAccountResponse.isSuccess()).isTrue();
            AccountDto accountDto = genericAccountResponse.getAccount();
    
            // update
            accountDto.setName("testAccountUpdate");
            accountDto.setConfirmedAndActive(true);
            accountDto.setPhoneNumber("18001801237");
            GenericAccountResponse genericAccountResponse1 = accountClient.updateAccount(AuthConstant.AUTHORIZATION_WWW_SERVICE, accountDto);
            log.info(genericAccountResponse1.toString());
            assertThat(genericAccountResponse1.isSuccess()).isTrue();
            AccountDto updatedAccountDto = genericAccountResponse1.getAccount();
            assertThat(updatedAccountDto).isEqualTo(accountDto);
    
            // capture and verify
            ArgumentCaptor<GreetingRequest> argument = ArgumentCaptor.forClass(GreetingRequest.class);
            verify(botClient, times(1)).sendSmsGreeting(argument.capture());
            GreetingRequest greetingRequest = argument.getValue();
            assertThat(greetingRequest.getUserId()).isEqualTo(accountDto.getId());
        }
    
    }

Test Case Review ~ 组件测试

  1. 示例:测试WWW服务

    • xyz.staffjoy.web.controller.LoginControllerTest
    @RunWith(SpringRunner.class)
    @SpringBootTest
    @AutoConfigureMockMvc
    @Slf4j
    public class LoginControllerTest {
        @Autowired
        MockMvc mockMvc;
    
        @MockBean
        AccountClient accountClient;
    
        @Autowired
        EnvConfig envConfig;
    
        @Autowired
        PageFactory pageFactory;
    
        @Autowired
        LoginController loginController;
    
        @Test
        public void testAleadyLoggedIn() throws Exception {
            MvcResult mvcResult = mockMvc.perform(post("/login")
                    .header(AuthConstant.AUTHORIZATION_HEADER, AuthConstant.AUTHORIZATION_AUTHENTICATED_USER))
                    .andExpect(status().is3xxRedirection())
                    .andExpect(view().name("redirect:" +
                            HelperService.buildUrl("http", "myaccount." + envConfig.getExternalApex())))
                    .andReturn();
        }
    
        @Test
        public void testGet() throws Exception {
            MvcResult mvcResult = mockMvc.perform(get("/login"))
                    .andExpect(status().isOk())
                    .andExpect(view().name(Constant.VIEW_LOGIN))
                    .andExpect(content().string(containsString(pageFactory.buildLoginPage().getDescription())))
                    .andReturn();
            log.info(mvcResult.getResponse().getContentAsString());
        }
    
        @Test
        public void testLoginAndLogout() throws Exception {
            String name = "test_user";
            String email = "[email protected]";
            Instant memberSince = Instant.now().minus(100, ChronoUnit.DAYS);
            String userId = UUID.randomUUID().toString();
            AccountDto accountDto = AccountDto.builder()
                    .id(userId)
                    .name(name)
                    .email(email)
                    .memberSince(memberSince)
                    .phoneNumber("18001112222")
                    .confirmedAndActive(true)
                    .photoUrl("http://www.staffjoy.xyz/photo/test_user.png")
                    .build();
            when(accountClient.verifyPassword(anyString(), any(VerifyPasswordRequest.class)))
                    .thenReturn(new GenericAccountResponse(accountDto));
    
            when(accountClient.trackEvent(any(TrackEventRequest.class)))
                    .thenReturn(BaseResponse.builder().message("event tracked").build());
            when(accountClient.syncUser(any(SyncUserRequest.class)))
                    .thenReturn(BaseResponse.builder().message("user synced").build());
    
            MvcResult mvcResult = mockMvc.perform(post("/login")
                    ).andExpect(status().is3xxRedirection())
                    .andExpect(view().name("redirect:" +
                            HelperService.buildUrl("http", "app." + envConfig.getExternalApex())))
                    .andReturn();
            Cookie cookie = mvcResult.getResponse().getCookie(AuthConstant.COOKIE_NAME);
            assertThat(cookie).isNotNull();
            assertThat(cookie.getName()).isEqualTo(AuthConstant.COOKIE_NAME);
            assertThat(cookie.getPath()).isEqualTo("/");
            assertThat(cookie.getDomain()).isEqualTo(envConfig.getExternalApex());
            assertThat(cookie.isHttpOnly()).isTrue();
            assertThat(cookie.getValue()).isNotBlank();
            assertThat(cookie.getMaxAge()).isEqualTo(Sessions.SHORT_SESSION / 1000);
    
            // remember-me
            mvcResult = mockMvc.perform(post("/login")
                    .param("remember-me", "true"))
                    .andExpect(status().is3xxRedirection())
                    .andExpect(view().name("redirect:" +
                            HelperService.buildUrl("http", "app." + envConfig.getExternalApex())))
                    .andReturn();
            cookie = mvcResult.getResponse().getCookie(AuthConstant.COOKIE_NAME);
            assertThat(cookie).isNotNull();
            assertThat(cookie.getName()).isEqualTo(AuthConstant.COOKIE_NAME);
            assertThat(cookie.getPath()).isEqualTo("/");
            assertThat(cookie.getDomain()).isEqualTo(envConfig.getExternalApex());
            assertThat(cookie.isHttpOnly()).isTrue();
            assertThat(cookie.getValue()).isNotBlank();
            assertThat(cookie.getMaxAge()).isEqualTo(Sessions.LONG_SESSION / 1000);
    
            // redirect-to
            mvcResult = mockMvc.perform(post("/login")
                    .param("return_to", "ical." + envConfig.getExternalApex() + "/test"))
                    .andExpect(status().is3xxRedirection())
                    .andExpect(view().name("redirect:http://ical." + envConfig.getExternalApex() + "/test"))
                    .andReturn();
    
            // redirect-to invalid
            mvcResult = mockMvc.perform(post("/login")
                    .param("return_to", "signalx." + envConfig.getExternalApex() + "/test"))
                    .andExpect(status().is3xxRedirection())
                    .andExpect(view().name("redirect:" +
                            HelperService.buildUrl("http", "myaccount." + envConfig.getExternalApex())))
                    .andReturn();
    
            // logout
            mvcResult = mockMvc.perform(get("/logout"))
                    .andExpect(status().is3xxRedirection())
                    .andExpect(view().name("redirect:/"))
                    .andReturn();
            cookie = mvcResult.getResponse().getCookie(AuthConstant.COOKIE_NAME);
            assertThat(cookie).isNotNull();
            assertThat(cookie.getName()).isEqualTo(AuthConstant.COOKIE_NAME);
            assertThat(cookie.getPath()).isEqualTo("/");
            assertThat(cookie.getDomain()).isEqualTo(envConfig.getExternalApex());
            assertThat(cookie.getValue()).isBlank();
            assertThat(cookie.getMaxAge()).isEqualTo(0);
        }
    }

测试补充

  1. Mock vs. Spy

    • Mock针对接口场景,Spy针对类的场景,部分mock
    • 如 xyz.staffjoy.account.service.helper.ServiceHelperTest
  2. BDD:行为驱动测试
    • 面向用户的接受测试,通常由产品、开发、QA协作开发
    • 使用贴近用户产品的语言
    • faraday项目里使用的spock BDD框架
    • REST-assured也支持BDD
  3. 性能测试
    • JMeter:UI操作,可编程性不好
    • Gatling:基于脚本,CI/CD集成能力好

原文地址:https://www.cnblogs.com/wnzhong/p/12146117.html

时间: 2024-11-10 18:57:53

SpringBoot + Kubernetes云原生微服务实践 - (6) 微服务测试设计和实践的相关文章

SpringBoot + Kubernetes云原生微服务实践 - (1) 介绍与案例需求

学习目标 Dev 掌握微服务架构和前后分离架构设计 掌握基于Spring Boot搭建微服务基础框架 进一步提升Java/Spring微服务开发技能 掌握Spring Boot微服务测试和相关实践 理解SaaS多租户应用的架构和设计 Ops 理解可运维架构理念和相关实践 掌握服务容器化和容器云部署相关实践 理解云时代的软件工程流程和实践 案例需求:Staffjoy工时排班(Scheduling)SaaS服务 功能 管理员Admin管理公司和排班 雇员Worker管理个人信息 非功能 SaaS +

实践作业1:测试管理工具实践(小组作业)每日任务记录1

记录日期:2017/11/13 会议主题:高级软件测试与质量的实践作业1的主题与分工 会议成员:王晨懿.余晨晨.郑锦波.杨潇.侯欢.汪元 会议记录人:侯欢 会议内容:今天是我们小组第一次例会,这次会议主要是对于第一次实践作业的测试管理工具实践的主题的确立与分工.我们组选择的测试管理工具是禅道.说起禅道,熟悉软件测试的小伙伴应该不会陌生,因为直到2016年公司常用测试管理工具统计,禅道仍旧以34%的比例高居榜首.我们组的具体分工如下:工具手册的撰写---王晨懿/于晨晨:视频制作---郑锦波/杨潇:

从内部自用到对外服务,配置管理的演进和设计优化实践

本文整理自阿里巴巴中间件技术专家彦林在中国开源年会上的分享,通过此文,您将了解到: 微服务给配置管理所带来的变化配置管理演进过程中的设计思考配置管理开源后的新探索配置中心控制台设计实践"为什么相对于传统的软件开发模式,微服务要强调配置中心,是出于什么样的诉求需要我们专门设计一个配置中心?厘清了这些问题,我们就知道如何去设计配置中心,并获得一个比较好的用户体验,和一个生产可用的结果." 微服务给配置管理所带来的变化在单机的情况下,我们把配置放在代码里边,发布的时候直接重启,非常轻量,但在

实践作业1:测试管理工具实践 Day2

1.尝试配置TestLink所需环境 安装配置php+apache+mysql时遇到一系列稀奇古怪的错误. 2.百度之后发现有可行的替代工具:Vertrigoserv(VertrigoServ是一个Windows平台下的非常专业的.易于安装的免费网络开发环境,它集成了Apache, PHP, MySQL, SQLite, SQLiteManager, PhpMyAdmin, Zend Optimizer,具有卸载程序). (考试原因,次日再研究)

从 SOA 到微服务,企业分布式应用架构在云原生时代如何重塑?

作者 | 易立 阿里云资深技术专家 导读:从十余年前的各种分布式系统研发到现在的容器云,从支撑原有业务到孵化各个新业务,企业的发展离不开统一的.与时俱进的技术架构.本篇文章从企业分布式应用架构层面介绍了云原生计算架构带来的变化,希望能够帮助更多企业的 IT 转型,利用云计算技术推动其成为市场竞争中的敏捷力量. 进入 21 世纪以来,我们见证了企业分布式应用架构从 SOA(Service-oriented Architecture),到微服务架构,再到云原生应用架构的演化. 为了说明企业架构演化背

云原生时代,微服务到底应该怎么玩儿?

在微服务诞生之初,并没有太多方案的选择:选一个注册中心用来做服务注册和发现,通过客户端SDK进行负载均衡和容错,再搭配上日志.监控.调用链全套观测手段,一套微服务架构便建立起来了. 作为最流行的业务开发语言,Java体系里诞生了很多微服务架构,例如Spring Cloud.使用Spring Cloud,Spring技术栈的开发人员可以快速的开发和管理微服务,丰富的功能让其他语言体系的开发者们羡慕不已. 在云原生时代,Kubernetes快速普及,除了解决微服务所需要的应用编排.伸缩.保活等功能外

云原生应用的10大关键属性

"云原生(Cloud Native)"是用于描述基于容器的环境的术语.云原生技术被用于开发应用程序,这些应用程序是使用容器打包的服务构建的.被部署为微服务.并通过灵活的DevOps流程和持续交付工作流在弹性基础架构上进行管理. 在运维团队手动管理传统应用程序的基础架构资源分配的情况下,云原生应用程序部署在抽象了底层计算.存储和网络原语的基础架构上.处理这种新型应用程序的开发人员和运维人员不直接与基础架构提供商公开的API交互.相反的,编排器会根据DevOps团队制定的策略自动进行资源分

始于阿里,回归社区:阿里8个项目进入CNCF云原生全景图

破土而出的生命力,源自理想主义者心底对技术的信念. 云原生技术正席卷全球,云原生基金会在去年KubeCon +CloudNativeCon NA的现场宣布: 其正在孵化的项目已达14个,入驻的厂家或产品已超过300家,并吸引了2.2万开发者参与项目代码贡献,其明星产品Kubenetes 的GitHub 上Authors 和 Issues 量已排行开源领域的第二名. 今年,KubeCon + CloudNativeCon 首次来到中国. 在2018 KubeCon + CloudNativeCon

360&#176;透视:云原生架构及设计原则

欢迎访问网易云社区,了解更多网易技术产品运营经验. 云原生(Cloud Native)的概念,由来自Pivotal的MattStine于2013年首次提出,被一直延续使用至今.这个概念是Matt Stine根据其多年的架构和咨询经验总结出来的一个思想集合,并得到了社区的不断完善,内容非常多,包括DevOps.持续交付(Continuous Delivery).微服务(MicroServices).敏捷基础设施(Agile Infrastructure)和12要素(TheTwelve-Factor