Spring Cloud:从基础入门到实战应用与持续进阶

Spring Cloud:从基础入门到实战应用与持续进阶

一、Spring Cloud 基础与核心组件

(一)微服务基础概念

1. 微服务架构的定义与特点

微服务架构是一种将复杂应用程序拆分为一组小型、独立服务的架构风格。每个微服务都围绕特定的业务功能构建,具有独立的开发、部署和扩展能力。其主要特点包括:

独立性:每个微服务独立运行,可以独立部署和扩展,不会相互影响。

技术栈灵活性:不同的微服务可以使用不同的技术栈,开发团队可以根据需求选择最适合的技术。

快速迭代:微服务的独立性使得开发和部署更加灵活,可以快速迭代和更新。

容错性:一个微服务的故障不会导致整个系统崩溃,其他服务可以继续运行。

2. 微服务架构的优缺点

优点:

开发效率高:团队可以独立开发和部署微服务,减少相互依赖。

可扩展性强:可以根据业务需求独立扩展微服务。

技术选型灵活:可以使用最适合的技术栈开发每个微服务。

容错性强:一个服务的故障不会影响整个系统。

缺点:

复杂度增加:需要管理多个服务,增加了运维复杂度。

分布式事务管理复杂:跨服务的事务一致性难以保证。

网络延迟问题:服务间通信依赖网络,可能导致延迟增加。

数据一致性问题:分布式系统中数据一致性难以保证。

3. 微服务架构与单体架构、SOA 的对比

单体架构:

定义:将整个应用程序作为一个单一的、不可分割的单元构建和部署。

优点:简单,易于开发和部署;性能高,因为没有网络调用开销。

缺点:扩展性差,难以快速迭代;一旦某个模块出现问题,整个系统可能崩溃。

SOA(面向服务的架构):

定义:将应用程序分解为一组服务,服务之间通过标准的接口进行通信。

优点:服务可以复用,提高了系统的灵活性和可扩展性。

缺点:服务之间依赖性强,通常需要集中式的 ESB(企业服务总线)进行管理,增加了复杂度。

微服务架构:

定义:将应用程序分解为一组小型、独立的服务,每个服务独立运行和部署。

优点:开发和部署灵活,可扩展性强,容错性强。

缺点:分布式系统带来的复杂性,如分布式事务、数据一致性等问题。

4. CAP 定理、BASE 理论、服务治理概念

CAP 定理:

一致性(Consistency):所有节点在同一时间看到相同的数据。

可用性(Availability):每个请求都能在有限时间内返回响应。

分区容错性(Partition Tolerance):系统在部分网络分区的情况下仍然可以正常运行。

CAP 定理:在一个分布式系统中,不能同时满足一致性、可用性和分区容错性三个要求,最多只能同时满足其中的两个。

BASE 理论:

基本可用(Basically Available):分布式系统在出现不可预知故障时,允许部分功能不可用,但核心功能仍然可用。

软状态(Soft State):分布式系统中的数据可以有一段时间的不同步,但最终会达到一致状态。

最终一致性(Eventually Consistent):系统经过一段时间后,数据最终会达到一致状态。

服务治理:

定义:对微服务的生命周期进行管理,包括服务注册、发现、配置管理、熔断、降级、监控等。

目标:确保微服务的高可用性、高性能和可扩展性。

(二)服务注册与发现

1. Eureka 核心机制

Eureka 是 Netflix 开发的服务发现框架,Spring Cloud 对其进行了封装,使其可以轻松集成到 Spring Boot 应用中。Eureka 的核心机制包括:

服务注册:服务提供者启动时,向 Eureka Server 注册自己的信息,包括服务名称、IP 地址、端口号等。

心跳检测:服务提供者定期向 Eureka Server 发送心跳,表明自己仍然存活。如果 Eureka Server 在一定时间内没有收到服务提供者的心跳,则认为该服务实例不可用。

自我保护模式:当 Eureka Server 在短时间内收到大量服务实例的注销请求时,会进入自我保护模式,避免因网络问题导致大量服务被误认为不可用。

2. Eureka Server 与 Client 的配置

Eureka Server:

配置:需要配置 Eureka Server 的地址、端口号等信息。

集群部署:为了提高系统的可用性,通常会部署多个 Eureka Server 实例,形成集群。

Eureka Server 配置示例:

server:

port: 8761

eureka:

instance:

hostname: localhost

client:

register-with-eureka: false

fetch-registry: false

service-url:

defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

Eureka Client:

配置:服务提供者和消费者都需要配置 Eureka Client,指定 Eureka Server 的地址等信息。

功能:服务提供者向 Eureka Server 注册服务,消费者从 Eureka Server 获取服务列表。

Eureka Client 配置示例:

eureka:

client:

service-url:

defaultZone: http://localhost:8761/eureka/

3. 对比其他注册中心

Nacos:

特点:阿里巴巴开源的注册中心,支持服务注册、配置管理、服务发现等功能。

优势:支持多环境管理,配置中心功能强大。

Consul:

特点:由 HashiCorp 开发的注册中心,支持服务发现、健康检查、配置管理等功能。

优势:支持多种健康检查机制,支持 DNS 查询。

Zookeeper:

特点:由 Apache 开发的分布式协调服务,支持服务注册、发现、配置管理等功能。

优势:支持强一致性,适用于对一致性要求较高的场景。

(三)服务调用与负载均衡

1. 使用 RestTemplate 与 Ribbon 实现客户端负载均衡

RestTemplate:

定义:Spring 提供的 HTTP 客户端,用于发送 HTTP 请求。

使用:通过 RestTemplate 向服务提供者发送请求,实现服务调用。

RestTemplate 配置示例:

@Configuration

public class RestTemplateConfig {

@Bean

public RestTemplate restTemplate() {

return new RestTemplate();

}

}

Ribbon:

定义:Netflix 开发的客户端负载均衡框架,Spring Cloud 对其进行了封装。

功能:根据配置的负载均衡策略,从服务列表中选择一个服务实例进行调用。

Ribbon 配置示例:

ribbon:

ReadTimeout: 30000

ConnectTimeout: 30000

OkToRetryOnAllOperations: true

MaxAutoRetriesNextServer: 2

MaxAutoRetries: 1

集成:

配置:在 Spring Boot 应用中配置 Ribbon 的负载均衡策略,如轮询、随机、权重等。

使用:通过 RestTemplate 和 Ribbon 实现服务调用的负载均衡。

RestTemplate 使用示例:

@Service

public class UserService {

@Autowired

private RestTemplate restTemplate;

public User getUserById(Long id) {

String url = "http://user-service/users/" + id;

return restTemplate.getForObject(url, User.class);

}

}

2. Ribbon 的负载均衡策略

轮询策略:按照顺序依次选择服务实例。

随机策略:随机选择服务实例。

权重策略:根据服务实例的权重选择服务实例,权重高的实例被选中的概率更高。

自定义策略:可以根据业务需求自定义负载均衡策略。

自定义 Ribbon 负载均衡策略示例:

@Component

public class CustomLoadBalancerRule extends AbstractLoadBalancerRule {

@Override

public Server choose(Object key) {

ILoadBalancer lb = getLoadBalancer();

List servers = lb.getAllServers();

// 自定义选择逻辑

return servers.get(0); // 示例:选择第一个服务实例

}

}

3. OpenFeign 声明式服务调用

定义:OpenFeign 是 Netflix 开发的声明式服务调用框架,Spring Cloud 对其进行了封装。

功能:通过接口注解的方式定义服务调用,简化了服务调用的代码。

集成:

配置:在 Spring Boot 应用中配置 OpenFeign,指定服务接口和调用方式。

使用:通过接口注解的方式调用服务,OpenFeign 会自动处理服务发现和负载均衡。

OpenFeign 配置示例:

@FeignClient(name = "user-service")

public interface UserClient {

@GetMapping("/users/{id}")

User getUserById(@PathVariable("id") Long id);

}

OpenFeign 使用示例:

@Service

public class UserService {

@Autowired

private UserClient userClient;

public User getUserById(Long id) {

return userClient.getUserById(id);

}

}

与 Ribbon 和 Hystrix 的整合:

负载均衡:OpenFeign 默认集成了 Ribbon,支持多种负载均衡策略。

熔断:OpenFeign 也支持与 Hystrix 的整合,实现服务调用的熔断。

(四)服务熔断与降级

1. Hystrix 熔断原理

Hystrix 是 Netflix 开发的熔断框架,Spring Cloud 对其进行了封装。Hystrix 的熔断原理包括:

熔断器状态机:Hystrix 熔断器有三种状态:关闭、开启和半开启。

关闭状态:允许请求通过,当请求失败率达到一定阈值时,熔断器进入开启状态。

开启状态:拒绝所有请求,直接返回错误信息。经过一定时间后,熔断器进入半开启状态。

半开启状态:允许部分请求通过,如果请求成功,则熔断器回到关闭状态;如果请求失败,则熔断器回到开启状态。

滑动窗口:Hystrix 使用滑动窗口来统计请求的失败率,窗口大小可以配置。

2. 服务降级

定义:当服务调用失败时,返回一个默认的响应,而不是直接抛出异常。

实现:通过 Hystrix 的降级机制实现服务降级。可以在 Hystrix 的回退方法中定义降级逻辑,返回默认的响应。

Hystrix 降级示例:

@Service

public class UserService {

@Autowired

private RestTemplate restTemplate;

@HystrixCommand(fallbackMethod = "fallbackGetUserById")

public User getUserById(Long id) {

String url = "http://user-service/users/" + id;

return restTemplate.getForObject(url, User.class);

}

public User fallbackGetUserById(Long id) {

return new User(id, "default", "default");

}

}

3. 请求缓存与请求合并

请求缓存:Hystrix 支持请求缓存,可以缓存请求的结果,减少对服务提供者的调用。

请求合并:Hystrix 支持请求合并,可以将多个请求合并为一个请求,减少网络开销。

Hystrix 请求缓存示例:

@Service

public class UserService {

@Autowired

private RestTemplate restTemplate;

@CacheResult

@HystrixCommand

public User getUserById(Long id) {

String url = "http://user-service/users/" + id;

return restTemplate.getForObject(url, User.class);

}

}

4. 替代方案 Sentinel

定义:Sentinel 是阿里巴巴开源的分布式系统流量控制框架。

核心特性:

流量控制:根据配置的规则,对流量进行控制,避免系统过载。

熔断降级:支持熔断降级机制,当服务调用失败时,返回默认的响应。

系统负载保护:根据系统的负载情况,动态调整流量,避免系统崩溃。

与 Hystrix 的对比:

功能:Sentinel 的功能更丰富,支持更多的流量控制策略。

性能:Sentinel 的性能更高,对系统的性能影响更小。

易用性:Sentinel 的配置更简单,使用更方便。

Sentinel 配置示例:

@Service

public class UserService {

@Autowired

private RestTemplate restTemplate;

@SentinelResource(value = "getUserById", fallback = "fallbackGetUserById")

public User getUserById(Long id) {

String url = "http://user-service/users/" + id;

return restTemplate.getForObject(url, User.class);

}

public User fallbackGetUserById(Long id) {

return new User(id, "default", "default");

}

}

(五)API 网关

1. Spring Cloud Gateway 的核心概念

Spring Cloud Gateway 是 Spring Cloud 官方提供的 API 网关框架。其核心概念包括:

路由:定义了请求的转发规则,将请求转发到指定的服务。

过滤器:对请求进行处理,如修改请求头、添加日志等。

断言:用于判断请求是否符合路由规则,如路径匹配、请求头匹配等。

2. 动态路由配置与过滤器链开发

动态路由配置:Spring Cloud Gateway 支持动态路由配置,可以通过配置文件或数据库动态修改路由规则。

过滤器链开发:可以自定义过滤器,实现对请求的处理。过滤器可以组合成过滤器链,按照顺序对请求进行处理。

Spring Cloud Gateway 配置示例:

spring:

cloud:

gateway:

routes:

- id: user-service

uri: lb://user-service

predicates:

- Path=/users/**

filters:

- StripPrefix=1

3. 对比 Zuul

Zuul:

定义:Netflix 开发的 API 网关框架,Spring Cloud 对其进行了封装。

特点:支持动态路由、过滤器等功能。

Spring Cloud Gateway 与 Zuul 的对比:

性能:Spring Cloud Gateway 基于 WebFlux 框架,性能更高,支持异步处理。

功能:Spring Cloud Gateway 的功能更强大,支持更多的路由规则和过滤器。

易用性:Spring Cloud Gateway 的配置更简单,使用更方便。

(六)分布式配置中心

1. 使用 Spring Cloud Config 实现配置集中管理

Spring Cloud Config 是 Spring Cloud 提供的分布式配置中心框架。其主要功能包括:

配置集中管理:将配置信息集中存储在 Git 或其他存储系统中,方便管理。

动态刷新:支持配置的动态刷新,当配置信息发生变化时,可以自动更新配置。

2. 配置动态刷新

实现:通过 Spring Cloud Config 的动态刷新功能,可以实时更新配置信息。结合 Spring Cloud Bus 和消息中间件(如 RabbitMQ/Kafka),可以实现配置的动态刷新。

使用:在 Spring Boot 应用中配置 Spring Cloud Config,指定配置中心的地址和配置文件的路径。通过注解或 API 调用,可以实现配置的动态刷新。

Spring Cloud Config 配置示例:

spring:

application:

name: user-service

cloud:

config:

uri: http://localhost:8888

3. 对比 Nacos 配置中心

Nacos:

定义:阿里巴巴开源的分布式配置中心框架,支持服务注册、发现、配置管理等功能。

特点:支持多环境管理,配置中心功能强大。

Spring Cloud Config 与 Nacos 的对比:

功能:Nacos 的功能更丰富,支持更多的配置管理功能。

性能:Nacos 的性能更高,对系统的性能影响更小。

易用性:Nacos 的配置更简单,使用更方便。

Nacos 配置示例:

spring:

application:

name: user-service

cloud:

nacos:

config:

server-addr: 127.0.0.1:8848

二、进阶与分布式系统问题

(一)分布式链路追踪

1. 使用 Sleuth + Zipkin 实现全链路追踪

Sleuth:

定义:Spring Cloud Sleuth 是 Spring Cloud 提供的分布式链路追踪框架。

功能:通过在请求中添加 TraceID 和 SpanID,记录请求的链路信息。

Zipkin:

定义:Zipkin 是一个分布式链路追踪系统,支持数据的收集、存储和查询。

功能:通过 Zipkin 的 UI 界面,可以查看请求的链路信息,分析性能瓶颈。

集成:

配置:在 Spring Boot 应用中配置 Sleuth 和 Zipkin,指定 Zipkin 的地址等信息。

使用:通过 Sleuth 和 Zipkin 实现全链路追踪,记录请求的链路信息。

Sleuth + Zipkin 配置示例:

spring:

zipkin:

base-url: http://localhost:9411

sleuth:

sampler:

probability: 1.0

2. TraceID 与 SpanID 的传递原理

TraceID:表示一个完整的请求链路,从客户端发起请求到服务器返回响应的整个过程。

SpanID:表示链路中的一个节点,每个服务的处理过程都是一个 Span。

传递原理:在请求中添加 TraceID 和 SpanID,通过 HTTP 头或消息中间件传递给下游服务。下游服务接收到请求后,会记录自己的 SpanID,并将 TraceID 和 SpanID 传递给下一个服务。

3. 结合日志系统进行问题定位

日志系统:如 ELK(Elasticsearch、Logstash、Kibana)。

结合方式:将 Sleuth 的链路信息记录到日志中,通过日志系统进行查询和分析。当出现问题时,可以通过 TraceID 和 SpanID 快速定位问题。

日志记录示例:

@Slf4j

@Service

public class UserService {

public User getUserById(Long id) {

log.info("Getting user by id: {}", id);

return restTemplate.getForObject("http://user-service/users/" + id, User.class);

}

}

(二)消息驱动与事件总线

1. Spring Cloud Stream 整合消息中间件

Spring Cloud Stream:

定义:Spring Cloud Stream 是 Spring Cloud 提供的消息驱动框架,支持与多种消息中间件集成。

功能:通过定义消息通道和消息处理器,实现消息的发送和接收。

消息中间件:如 RabbitMQ、Kafka。

集成:

配置:在 Spring Boot 应用中配置 Spring Cloud Stream,指定消息中间件的地址等信息。

使用:通过定义消息通道和消息处理器,实现消息的发送和接收。

Spring Cloud Stream 配置示例:

spring:

cloud:

stream:

bindings:

output:

destination: user-topic

content-type: application/json

input:

destination: user-topic

content-type: application/json

rabbit:

bindings:

output:

producer:

exchangeType: topic

input:

consumer:

concurrency: 3

2. 消息分组、分区、重试机制

消息分组:将消息分为不同的组,每个组可以由不同的消费者处理。

消息分区:将消息存储在不同的分区中,提高消息的处理效率。

消息重试:当消息处理失败时,可以配置重试机制,重新发送消息。

消息发送与接收示例:

@EnableBinding(MessageChannels.class)

public class MessageService {

@Autowired

private MessageChannel output;

@Autowired

private MessageChannel input;

public void sendMessage(User user) {

output.send(MessageBuilder.withPayload(user).build());

}

@StreamListener("input")

public void receiveMessage(User user) {

log.info("Received user: {}", user);

}

}

public interface MessageChannels {

String OUTPUT = "output";

String INPUT = "input";

@Output(OUTPUT)

MessageChannel output();

@Input(INPUT)

SubscribableChannel input();

}

(三)分布式事务

1. 分布式事务场景与解决方案

场景:在微服务架构中,一个业务操作可能涉及多个服务的调用,需要保证这些操作的原子性。

解决方案:

两阶段提交:传统的分布式事务解决方案,但性能较低,容易出现死锁。

补偿事务(TCC):通过补偿机制实现分布式事务,当某个操作失败时,回滚前面的操作。

本地消息表:通过本地消息表实现分布式事务,将消息存储在本地数据库中,通过消息队列实现服务间的通信。

事件驱动:通过事件总线实现分布式事务,当某个操作完成时,发布事件,其他服务通过订阅事件完成后续操作。

2. Seata 的 AT、TCC 模式原理

Seata:

定义:阿里巴巴开源的分布式事务框架。

功能:支持多种分布式事务解决方案,如 AT、TCC 等。

AT 模式:

原理:通过两阶段提交实现分布式事务,第一阶段准备事务,第二阶段提交或回滚事务。

优点:对业务代码侵入性小,实现简单。

缺点:性能较低,容易出现死锁。

TCC 模式:

原理:通过补偿机制实现分布式事务,当某个操作失败时,回滚前面的操作。

优点:性能较高,不会出现死锁。

缺点:对业务代码侵入性大,实现复杂。

Seata 配置示例:

seata:

enabled: true

application-id: user-service

tx-service-group: my_tx_service_group

service:

vgroup-mapping:

my_tx_service_group: default

grouplist:

default: 127.0.0.1:8091

(四)服务安全

1. OAuth2 认证授权框架

定义:OAuth2 是一种开放的授权框架,允许第三方应用访问用户的资源,而不需要用户将用户名和密码提供给第三方应用。

功能:支持多种授权方式,如授权码模式、密码模式、客户端模式等。

使用:通过 OAuth2 实现微服务的安全认证和授权。

OAuth2 配置示例:

@Configuration

@EnableAuthorizationServer

public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

@Autowired

private AuthenticationManager authenticationManager;

@Override

public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {

endpoints.authenticationManager(authenticationManager);

}

@Override

public void configure(ClientDetailsServiceConfigurer clients) throws Exception {

clients.inMemory()

.withClient("client")

.secret("{noop}secret")

.authorizedGrantTypes("password", "refresh_token")

.scopes("read", "write");

}

}

2. Spring Cloud Security 整合微服务权限控制

Spring Cloud Security:

定义:Spring Cloud 提供的安全框架,支持 OAuth2、JWT 等安全机制。

功能:通过定义安全策略,实现微服务的权限控制。

JWT:

定义:JSON Web Token 是一种开放的、无状态的认证机制。

功能:通过 JWT 实现用户认证和授权,将用户的权限信息存储在 JWT 中。

集成:

配置:在 Spring Boot 应用中配置 Spring Cloud Security,指定认证服务器的地址等信息。

使用:通过定义安全策略,实现微服务的权限控制。

Spring Cloud Security 配置示例:

@Configuration

@EnableWebSecurity

public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

@Override

protected void configure(HttpSecurity http) throws Exception {

http

.authorizeRequests()

.antMatchers("/users/**").authenticated()

.and()

.oauth2ResourceServer()

.jwt();

}

}

(五)服务监控与健康检查

1. Spring Boot Actuator 端点管理

定义:Spring Boot Actuator 是 Spring Boot 提供的监控框架,支持多种监控端点。

功能:通过定义监控端点,可以获取应用的运行状态、配置信息、健康状态等。

使用:在 Spring Boot 应用中配置 Actuator,通过 HTTP 请求访问监控端点。

Actuator 配置示例:

management:

endpoints:

web:

exposure:

include: health,info,metrics

2. 使用 Prometheus + Grafana 搭建监控体系

Prometheus:

定义:Prometheus 是一个开源的监控系统,支持数据的收集、存储和查询。

功能:通过定义监控指标,可以收集应用的运行状态、性能指标等。

Grafana:

定义:Grafana 是一个开源的可视化工具,支持多种数据源。

功能:通过定义可视化面板,可以展示监控数据,分析性能瓶颈。

集成:

配置:在 Spring Boot 应用中配置 Prometheus,将监控数据发送到 Prometheus。

使用:通过 Grafana 查询 Prometheus 的数据,展示监控面板。

Prometheus 配置示例:

spring:

metrics:

export:

prometheus:

enabled: true

三、实战项目与案例

(一)构建微服务项目

1. 创建微服务架构项目

步骤:

创建服务注册中心:使用 Eureka 或 Nacos 创建服务注册中心。

创建服务提供者:创建多个服务提供者,实现业务逻辑。

创建服务消费者:创建服务消费者,通过服务发现调用服务提供者。

集成服务网关:使用 Spring Cloud Gateway 或 Zuul 创建服务网关,实现请求的转发和过滤。

2. 实现服务注册、发现、调用、熔断、降级等功能

服务注册与发现:通过 Eureka 或 Nacos 实现服务的注册和发现。

服务调用:通过 RestTemplate、Ribbon 或 OpenFeign 实现服务调用。

服务熔断与降级:通过 Hystrix 或 Sentinel 实现服务熔断和降级。

(二)整合第三方组件

1. 整合 Nacos、Sentinel、Seata 等组件

Nacos:

配置:在 Spring Boot 应用中配置 Nacos,指定 Nacos 的地址等信息。

使用:通过 Nacos 实现服务注册、发现、配置管理等功能。

Sentinel:

配置:在 Spring Boot 应用中配置 Sentinel,指定 Sentinel 的规则等信息。

使用:通过 Sentinel 实现流量控制、熔断降级等功能。

Seata:

配置:在 Spring Boot 应用中配置 Seata,指定 Seata 的事务管理器等信息。

使用:通过 Seata 实现分布式事务管理。

(三)性能优化与问题排查

1. 对微服务项目进行性能优化

优化方法:

服务拆分:将复杂的服务拆分为多个小服务,减少服务间的依赖。

缓存优化:使用缓存减少对数据库的访问,提高性能。

异步处理:使用异步处理提高系统的响应速度。

负载均衡:通过负载均衡提高系统的可用性。

2. 学习排查常见问题的方法和工具

排查方法:

日志分析:通过分析日志,定位问题。

性能监控:通过监控工具,分析系统的性能瓶颈。

链路追踪:通过链路追踪工具,分析请求的链路信息。

工具:

日志工具:如 ELK。

监控工具:如 Prometheus + Grafana。

链路追踪工具:如 Sleuth + Zipkin。

四、持续学习与拓展

(一)关注最新技术动态

1. 关注 Spring Cloud 的新版本特性

新版本特性:关注 Spring Cloud 的新版本特性,如新功能、性能优化等。

学习方法:通过阅读官方文档、技术博客等方式,了解新版本特性。

2. 学习新兴的微服务技术

Service Mesh:

定义:Service Mesh 是一种新兴的微服务技术,通过独立的基础设施层管理服务间的通信。

功能:支持服务发现、负载均衡、熔断、安全等功能。

学习方法:通过阅读技术文章、参加技术会议等方式,学习 Service Mesh 的相关技术。

(二)参与开源项目

1. 参与 Spring Cloud 相关的开源项目

开源项目:如 Spring Cloud、Spring Cloud Alibaba 等。

参与方式:通过提交代码、修复问题等方式,参与开源项目。

2. 提交代码、修复问题,提升实战能力

提交代码:通过提交代码,修复开源项目中的问题,提升实战能力。

学习方法:通过阅读开源项目的代码,学习优秀的代码风格和设计模式。

相关推荐

红米手机电池首次充电多长时间(红米新手机第一次充电多长时间?)
NEW 3DSLL 到手几天了,来说下感想,顺便请教几个问题:
北京哪里有进货女装市场?哪家市场女装款式多?