0%

  • 服务器之间通过数据库进行数据共享
  • 用加锁的方式抢夺资源

核心组件

  • 核心调度工具类:Scheduler
  • 定义任务接口:Job
  • 配置Job的接口:JobDetail(Job信息)、Trigger(触发器)

核心数据库表

  • qrtz_job_details表:job相关信息
  • qrtz_simple_triggers表:trigger简单内容
  • qrtz_triggers表:trigger详细内容
  • qrtz_scheduler_state表:定时器的信息状态
  • qrtz_locks表:锁信息

定时任务创建基本步骤

  • 创建工具类,用于将创建的jobDetail和trigger存储到数据库,实现二者FactoryBean

    • FactoryBean作用:简化Bean的实例化过程
      • Spring通过FactoryBean封装Bean实例化过程
      • 将FactoryBean装配到Spring容器里
      • 将FactoryBean注入给其它Bean
      • 该Bean就得到了FactoryBean所管理对象的实例
  • 创建Job实体类,实现Job接口的execute方法

什么是SpringAMQP

AMQP(高级消息队列协议)

是用于在应用程序或之间传递业务消息的开放标准。该协议与语言和平台无关,更符合微服务中独立性的要求

Spirng AMQP

基于AMQP协议定义的一套API规范,提供了模板来发送和接收消息。包含两部分,其中spring-amqp是基础抽象,spring-rabbit是底层的默认实现

利用SpringAMQP实现基础消息队列功能

发送消息
  • 在父工程中引入spring-amqp依赖
  • 在publisher服务中利用RabbitTemplate发送消息到simple.queue这个队列
接受消息
  • 引入amqp的starter依赖
  • 配置RabbitMQ地址
  • 定义类,添加@Component注解
  • 类中声明方法,添加@RabbitListener注解,方法参数就是消息

Work Queue 工作队列

与基础消息队列区别:多个消费者,可以提高消息处理速度,避免队列消息堆积

消费预取限制

配置文件设置preFetch值,可以控制预取消息上限

1
2
3
4
5
spring:
rabbitmq:
listener:
simple:
prefetch: 1 #每次只能获取一条消息,处理完才能获取下一条,默认无限

发布、订阅模型(Publish&Subscribe)

允许将同一消息发送给多个消费者,实现方式是加入了交换机(exchange)

实现方式:通过交换机发送给多个队列

常见交换机类型:

  • Fanout:广播
  • Direct:路由
  • Topic:话题

注意:exchange负责消息路由,只负责转发消息,路由失败则消息丢失

发布订阅-FanoutExchange

会将接收到的消息路由到每一个与其绑定的queue

实现思路如下:

  • 在cunsumer服务中,声明队列、交换机,并将两者绑定
  • 在consumer服务中,编写消费者方法,分别监听各个队列
  • 在publisher中编写测试方法,发送消息
发布订阅-DirectExchange

会将接收到的消息根据规则路由到指定的Queue,因此称为路由模式

  • 每一个Queue都与Exchange设置一个BindingKey,可以绑定多个
  • 发布者发送消息时,指定消息的RoutingKey
  • Exchange将消息路由到BindingKey宇RoutingKey一致的队列
1
2
3
4
5
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "direct.queue1"),
exchange = @Exchange(name = "itcast.direct", type = ExchangeTypes.DIRECT),
key = {"red", "blue"}
))
发布订阅-TopicExchange

与DirectExchange类似,区别在于routingKey必须是多个单词的列表,并且以.分割

Queue与Exchange指定BindingKey时可以使用通配符:

  • :代指0个或多个单词

  • *:代指一个单词
1
2
3
4
5
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "topic.queue2"),
exchange = @Exchange(name = "itcast.topic", type = ExchangeTypes.TOPIC),
key = "#.news"
))

消息转换器

消息发送Java对象时,SpringAMQP会帮我们序列化为字节后发送,基于JDK的ObjectOutputStream完成序列化

如果要修改只需要定义一个MessageConverter的Bean即可。推荐使用JSON方式序列化,步骤如下:

发送消息
  • 在publisher服务引入依赖
1
2
3
4
5
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.9.10</version>
</dependency>
  • 在publisher服务声明MessageConverter
1
2
3
4
@Bean
public MessageConverter jsonMessageConverter() {
return new Jackson2JsonMessageConverter();
}
  • 发送消息即可
接收消息
  • 在consumer服务引入Jackson依赖
1
2
3
4
5
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.9.10</version>
</dependency>
  • 在consumer服务定义MessageConverter:
1
2
3
4
@Bean
public MessageConverter jsonMessageConverter() {
return new Jackson2JsonMessageConverter();
}
  • 接收消息即可

常见消息模型

MQ的官方文档中给出了5个MQ的Demo示例,对应了几种不同的用法:

  • 基本消息队列(BasicQueue)
  • 工作消息队列(WorkQueue)
  • 发布订阅(Publish、Subscribe),又根据交换机类型不同分为三种:
    • Fanout Exchange:广播
    • Direct Exchange:路由
    • Topic Exchange:主题

基本消息队列(官方提供API)

消息发送流程
  • 建立connection
  • 创建channel
  • 利用channel声明队列
  • 利用channel向队列发送消息
基本消息队列的消息接收流程
  • 建立connection
  • 创建channel
  • 利用channel声明队列
  • 定义consumer的消费行为handleDelivery()
  • 利用channel将消费者与队列绑定

1
2
3
4
5
6
7
8
9
docker run \
-e RABBITMQ_DEFAULT_USER=用户名 \
-e RABBITMQ_DEFAULT_PASS=密码\
--name mq \
--hostname mq1 \
>p 15672:15672 \
-p 5672:5672 \
-d \
rabbitmq:3-management

基本概念

  • channel:操作MQ的工具
  • exchange:路由消息到队列中
  • queue:缓存消息
  • virtual host:虚拟主机,是对queue、exchange等资源的逻辑分组

  • UV(Unique Visitor)
    • 独立访客,需通过用户IP排重统计数据
    • 每次访问都要进行统计
    • HyperLogLog,性能好,存储空间小
  • DAU(Daily Active User)
    • 日活跃用户,需通过用户ID排重统计数据
    • 访问过一次,则认为其活跃
    • Bitmap,性能好、且可以精确统计结果

  • 功能实现
    • 点击置顶,修改帖子类型
    • 点击加精、删除,修改帖子状态
  • 权限管理
    • 版主可以执行置顶、加精操作
    • 管理员可以执行删除操作
  • 按钮显示
    • 版主可以看到置顶、加精按钮
    • 管理员可以看到删除按钮

简介

SpringSecurity是一个专注于为Java应用程序提供身份认证和授权的框架,它的强大之处在于它可以轻松扩展以满足自定义的需求

特征

  • 对身份的认证和授权提供全面的、可扩展的支持
  • 防止各种攻击,如会话固定攻击、点击劫持、csrf攻击等
  • 支持与Servlet API、Spring MVC等Web技术集成

用户权限管理

  • User实体类实现UserDetails接口

    • isAccountNonExpired()方法:返回true,账号未过期
    • isAccountNonLocked()方法:返回true,账号未锁定
    • isCredentialsNonExpired()方法:返回true,凭证未过期
    • isEnabled()方法:返回true,账号可用
    • getAuthorities()方法:返回用户的权限集合,权限实现GrantedAuthority接口
  • UserService实现UserDetailsService接口

    • loudUserByUsername()方法:通过用户名查找用户
  • SecurityConfig配置类,要求继承于WebSecurityConfigurerAdapter类

    • configure(WebSecurity web)方法

      • web.ignoring(),antMatchers(“资源路径”):忽略静态资源访问
    • configure(AuthenticationManagerBuilder auth)方法

      • AuthenticationManager:认证核心接口
      • AuthenticationManagerBuilder:用于构建AuthenticationManager实例
      • ProviderManager:AuthenticationManager接口的默认实现类
      • AuthenticationProvider:每个ProviderManager持有一组AuthenticationProvider,每个AuthenticationProvider负责一种认证(委托模式)
      • Aythentication:用于封装认证信息的接口,不同实现类代表不同认证信息
      • 内置认证规则:auth.userDetailsService(xxxService).passwordEncoder(内置密码加密方式);
      • 自定义认证规则:auth.authenticationProvider(new AuthenticationProvider()),实现 AuthenticationProvider的接口
        • authenticate()方法:实现认证逻辑,reutrn UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities()),信息、证书、权限
        • supports()方法:返回当前接口支持哪种认证类型,例如return UsernamePasswordAuthenticationToken.class.equals(aClass)。UsernamePasswordAuthenticationToken:Aythentication常用实现类,代表账号密码认证
    • configure(HttpSecurity http)方法,登陆相关配置

      • http.formLogin().loginPage()

        .loginProcessingUrl()

        .successHandler(new AuthenticationSuccessHandler())

        .failureHandler(new AuthenticationFailureHandler())

      • 退出相关配置:

      ​ http.logout().logoutUrl().logoutSuccessHandler()

      • 授权配置

      ​ http.authorizeRequests()

      ​ .antMatchers(“路径”).hasAnyAuthority(权限,权限)

      ​ .and().exceptionHandling().accessDeniedPage(“权限不足页面”)

      • 增加Filter,处理验证码

      ​ http.addFilterBefore(new Filter())

      • 记住我功能

      ​ http.rememberMe()

RabbitMQ ActiveMQ RocketMQ Kafka
公司/社区 Rabbit Apache 阿里/Apache Apache
开发语言 Erlang Java Java Scala&Java
协议支持 AMQP,XMPP,SMTP,STOMP OpenWire,STOMP,REST,XMPP,AMQP 自定义协议 自定义协议
可用性 一般
单机吞吐量 一般 非常高
消息延迟 微秒级 毫秒级 毫秒级 毫秒以内
消息可靠性 一般 一般

同步调用存在的问题

  • 耦合度高
  • 性能下降
  • 资源浪费
  • 级联失败

异步调用

  • 异步调用常见实现是事件驱动模式
  • 优势
    • 服务解耦
    • 性能提升,吞吐量提高
    • 服务没有强依赖,不担心级联失败问题,故障隔离
    • 流量削峰(缓冲)
  • 缺点
    • 依赖于Broker的可靠性、安全性、吞吐能力
    • 架构复杂,业务没有明显的流程线,不好追踪管理

常见镜像仓库服务

镜像仓库有公有和私有的两种形式

  • 公共仓库:官方的Docker Hub,网易云镜像,DaoCloud镜像,阿里云镜像等。
  • 本地搭建私有Docker Registry

创建私有镜像仓库(可视化界面)

配置Docker信任地址

我们的私服采用的是http协议,默认不被Docker信任,所以需要做一个配置:

1
2
3
4
5
6
7
8
# 打开要修改的文件
vi /etc/docker/daemon.json
# 添加内容:
"insecure-registries":["http://{ip地址}:8080"]
# 重加载
systemctl daemon-reload
# 重启docker
systemctl restart docker
使用DockerCompose部署带有图象界面的DockerRegistry
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
version: '3.0'
services:
registry:
image: registry
volumes:
- ./registry-data:/var/lib/registry
ui:
image: joxit/docker-registry-ui:static
ports:
- 8080:80
environment:
- REGISTRY_TITLE=私有仓库1
- REGISTRY_URL=http://registry:5000
depends_on:
- registry
在私有镜像仓库推送或拉取镜像
  • 重新tag本地镜像,名称前缀为私有仓库地址
1
docker tag nginx:latest 116.62.168.80:8081/nginx:1.0
  • 推送镜像
1
docker push 116.62.168.80:8081/nginx:1.0
  • 拉取镜像
1
docker pull 116.62.168.80:8081/nginx:1.0