从 spring 中学习 -- D大教程学习笔记

date: 2017-12-18 13:28:54
title: 从 spring 中学习 -- D大教程学习笔记
description: swoft 一开始就有 spring cloud 这样的野心, 虽道阻且长, 吾辈亦当要上下求索

spring -> spring boot -> spring cloud

spring project: https://github.com/spring-projects
spring 教程: https://github.com/dyc87112

参与 swoft 开源项目 的开发工作, 开发组技术选型上比较倾向于 spring 的设计, 我在一块还比较薄弱, 所以开了这篇来补一补. 也许是给自己挖了一个巨大的坑, 不过我一向 乐天派 -- 万水千山只等闲, 且看今朝.

系列教程编写指南: 主题讲解 -> 代码示例 + 单元测试

spring boot

Spring-Boot基础教程

最为核心的两大要素: 依赖注入DI + 面向切面编程AOP

  • 类 -> bean; 配置 -> 类定义/类属性
  • 模板引擎: 静态资源位置 属性(数据)解析 默认参数配置
  • apidoc - swagger
  • 默认错误页面: 统一异常处理 + error page/json
  • security 安全控制: AOP/拦截器 是否需要登录
  • 数据库访问: JdbcTemplate
  • Spring-data-jpa: Hibernate Entity->Repository 不同DB连接
  • spring-data-redis: 存储对象 + 对象序列化接口
  • spring-data-mongodb: Entity->Repository 配置->连接池
  • mybatis-spring-boot-starter: Entity->Mapper
  • Flyway: 数据库版本控制 migration
  • spring-data-ldap: 轻量级目录访问协议 -> 用户管理体系
  • cache: cache vs buffer; SpEL CacheManager 缓存生命周期的控制
  • log: logger(commonLogging log4j logback) logFormat(时间/毫秒 日志级别/多环境/动态修改 进程id 分隔符 logger名 日志内容) logTarget(console/多彩 file-分类输出/package mongo)
  • AOP: 日志切面 同步问题/记录函数执行时间/ThreadLocal 优先级问题/同一切入点多个切面
  • mq: sender -> 队列/交换器/路由 -> receiver
  • task: 同步 异步/异步回调 线程池 优雅关闭(平滑重启) Future->Runnable/Callable->任务取消/是否完成/获取结果->阻塞
  • 邮件: 附件 静态资源 模板
  • Actuator: 应用配置(配置刷新) 度量指标 操作控制
  • cli StateMachine(state->event)
// 从注解中获取属性
@Value("${com.didispace.blog.name}")
private String name;

// web
@RestController() // 不需要 @ResponseBody
@Controller()
@ResponseBody()
@RequestMapping()
@PathVariable
@RequestParam
@ModelAttribute

// Swagger2
@ApiOperation(value="更新用户详细信息", notes="根据url的id来指定更新对象,并根据传过来的user信息来更新用户详细信息")
@ApiImplicitParams({
    @ApiImplicitParam(name = "id", value = "用户ID", required = true, dataType = "Long"),
    @ApiImplicitParam(name = "user", value = "用户详细实体user", required = true, dataType = "User")
})

// Spring-data-jpa
@Query("from User u where u.name=:name")
User findUser(@Param("name") String name);

// mybatis-spring-boot-starter
@Select("SELECT * FROM USER WHERE NAME = #{name}")
User findByName(@Param("name") String name);
@Insert("INSERT INTO USER(NAME, AGE) VALUES(#{name}, #{age})")
int insert(@Param("name") String name, @Param("age") Integer age);

// 数据库事务
@Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED)
@Rollback

@EnableCaching // application
@Cacheable // repository
@CachePut // 缓存更新

// task
@EnableScheduling
@Scheduled(fixedRate = 5000) // 5s
@Scheduled(fixedDelay = 5000)
@Scheduled(initialDelay=1000, fixedRate=5000)
@Scheduled(cron="*/5 * * * * *")
@EnableAsync
@Async

// 等待任务全部执行完
while(true) {
    if(task1.isDone() && task2.isDone() && task3.isDone()) {
        // 三个任务都调用完成,退出循环等待
        break;
    }
    Thread.sleep(1000);
}

// task 线程池
@Async("taskExecutor")
@Bean("taskExecutor")
public Executor taskExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(10);
    executor.setMaxPoolSize(20);
    executor.setQueueCapacity(200);
    executor.setKeepAliveSeconds(60);
    executor.setThreadNamePrefix("taskExecutor-");
    executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    // 优雅关闭
    executor.setWaitForTasksToCompleteOnShutdown(true);
    executor.setAwaitTerminationSeconds(60);
    return executor;
}

@Test
# 参数引用
com.didispace.blog.desc=${com.didispace.blog.name}正在努力写《${com.didispace.blog.title}》
# 随机数
com.didispace.blog.value=${random.value}

# 命令行设置属性
java -jar xxx.jar --server.port=8888

# 多环境
spring.profiles.active=test
application-dev.properties # dev/test/prod

# 监控
/autoconfig // 配置信息
/beans
/configprops
/env
/mappings
/info
/metrics // 度量指标
/health
/dump
/trace
/shutdown // 操作控制

消息队列 MQ

message broker:

  • 消息路由到 n 个目的地
  • 消息转化为其他形式
  • 消息聚集/分解 -> 目的地 -> 重新组合相应返回
  • 调用web服务检索数据
  • 响应事件/错误
  • pub/sub 或 topic 的消息路由

AMQP:

  • 消息方向
  • 消息队列
  • 消息路由(p2p pub/sub)
  • 可靠性
  • 安全性

状态机

  • 状态/事件 枚举
  • 状态机: 所有状态 + 初始状态
  • 状态机: 状态迁移动作
  • 状态机: 监听器

spring cloud

Spring-Cloud基础教程

高可用: 多节点/前置LB/注册为服务

  • 共享资源的互斥访问 -> 分布式锁(全局锁) -> 基于redis 基于Zookeeper 基于Consul的KV存储实现分布式锁/信号量 -> 锁超时清理(超时时间)
  • 限制并发线程/进程数量(并发控制) -> 信号量 -> Zuul默认情况使用信号量限制每个路由的并发数 Consul实现分布式信号量 -> PV操作
  • 服务注册发现: Eureka Consul 服务注册中心(server)+服务提供方(client 服务清单->缓存)+服务消费者(consumer 客户端负载均衡)
  • 服务消费者: loadBalancerClient Ribbon 轮询服务端列表达到负载均衡
  • 服务消费工具 SC Feign: 声明式服务调用客户端 可插拔的注解 可插拔的编码器和解码器 整合Hystrix来实现服务的容错保护 引入Feign的扩展包实现文件上传
  • 分布式配置中心 SC Config: 服务端/客户端 独立微服务应用 配置信息/加解密信息(dev->devops 敏感信息) 默认采用git来存储配置信息 配置git.username/git.password->http可访问 配置刷新->请求客户端actuator模块
  • 服务保护机制 SC Hystrix: 服务降级fallback 服务熔断/断路器/快照时间窗/请求总数下限/错误百分比下限 线程隔离/为每一个依赖服务都分配一个线程池 信号量/延迟低/不能设置超时和异步访问 请求缓存 请求合并 服务监控 监控面板/turbine(消息聚合 http/mq)
  • 服务网关 SC Zuul: 对外提供服务/服务路由/LB/权限控制/请求限流(过滤器ZuulFilter) 注册中心->服务清单->映射 使用Swagger汇总API接口文档 处理cookie/重定向/异常处理
  • 消息驱动 SC Stream: RabbitMQ/Kafka pub/sub-消费组(避免消息重复消费)-消息分区(消息定向投递, 比如监控信息汇总) app->channel->binder(绑定器隐藏中间件细节, 暴露channel给app)->Middleware(mq中间件)
  • 分布式服务跟踪 SC Sleuth: 全链路调用跟踪/快速发现错误根源/监控分析性能瓶颈 请求链路TraceID 基本工作单元SpanID->抽样Sampler logstash(ELK)日志收集
  • 分布式服务跟踪整合 Zipkin: 收集器collector 存储组件Storage Api组件 WebUI http/mq方式收集
// mq
@StreamListener(Sink.INPUT)
@Input(Sink.INPUT)
SubscribableChannel input();

// 实现命名替换
@Component
spring.application.name=trace-1
server.port=9101

# 服务注册中心
eureka.client.serviceUrl.defaultZone=http://eureka.didispace.com/eureka/

# 服务消费者
ribbon.eager-load.enabled=true
ribbon.eager-load.clients=hello-service, user-service

# mq 生产
spring.cloud.stream.bindings.input.group=Service-A # 分组
spring.cloud.stream.bindings.input.destination=greetings
spring.cloud.stream.bindings.input.consumer.partitioned=true # 分区
spring.cloud.stream.instanceCount=2
spring.cloud.stream.instanceIndex=0

# mq 消费
spring.cloud.stream.bindings.output.destination=greetings # 分组
spring.cloud.stream.bindings.output.producer.partitionKeyExpression=payload # 分区
spring.cloud.stream.bindings.output.producer.partitionCount=2

# 随机端口
server.port=0 # spring 自动随机分配
eureka.instance.instance-id=${spring.application.name}:${random.int}
server.port=${random.int[10000,19999]} # 直接使用 random 指定

写在最后

郑重申明: 此篇系拜读 D大 博客教程后笔记汇集而成, 感谢 D大 的分享.

你可能感兴趣的:(从 spring 中学习 -- D大教程学习笔记)