第⼀部分:⼀致性Hash算法
第⼆部分:集群时钟同步问题
第三部分:分布式ID解决⽅案
数据表A(ID),A的数据量很⼤的情况下,我们会进⾏分表操作,A(ID)表拆分成了A1表
(ID)+A2表(ID),需要⼀种在分布式集群架构中能够产⽣全局唯⼀ID的⽅案
第四部分:分布式调度问题(定时任务的分布式)
1.下列对定时任务描述正确的是()
您的回答: A可以实现异步处理B可以实现应用解耦:C可以实现流量削峰
2.以下属于服务器端定时任务主要实现方式的是()
您的回答: A JDK中的Timer机制 c线程机制 D Quartz任务调度框架
3.分布式调度体现的两层主要含义是()
您的回答: B任务的多实例高可用机制: D任务的拆分机制(任务分片),并行执行
4.下列属于轻量级体现的是()
您的回答: A使用简便,不需安装过多过重的其他服务或者组件B使用一个框架的时候,只把其Jar包引入调用即可ic开发
完成后不需要独立部署
5.下列属于ElasticJob无中心化体现的是()
您的回答: A执行节点对等!C服务自发现
第五部分:Session共享(⼀致性)问题
浏览器—>Nginx—>Tomcat1(Session中记录⽤户信息)
分布式和集群
分布式和集群是不⼀样的,分布式⼀定是集群,但是集群不⼀定是分布式(因为集群就是多个实例⼀起⼯作,分布式将⼀个系统拆分之后那就是多个实例;集群并不⼀定是分布式,因为复制型的集群不是拆分⽽是复制)
作业
作业一:
1)基于SpringBoot整合SSS(Spring+SpringMVC+SpringDataJPA)框架(即整合第一阶段模块三作业第二题内容,含有登录拦截验证)
2)在 1 的基础上开发SpringSession进行Session一致性控制
3)将工程打成war包
4)将war包部署到分布式集群架构中,要求一个Nginx节点,两个Tomcat节点
—> Nginx(轮询策略) —> Tomcat1—> Tomcat2
5)完成测试
思路和原理:
请求通过 tomcat到达 servlet容器的时候,通过过滤器对请求做了一次封装,如果没有过滤器, servet就会从 tomcat中获取 Session
有了过滤器之后,取出来的 Session就是 Redis 中的 Session,有的话就从 Redis中获取,没有的话就创建并提交到 Redis中去
- 使用数据库 test, 执行以下 sql 脚本, 创建了 tb_resume 表, 以及一些初始数据.
SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for tb_resume -- ---------------------------- DROP TABLE IF EXISTS `tb_resume`; CREATE TABLE `tb_resume` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `address` varchar(20) DEFAULT NULL, `name` varchar(20) DEFAULT NULL, `phone` varchar(20) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of tb_resume -- ---------------------------- BEGIN; INSERT INTO `tb_resume` VALUES (1, '北京', '张三', '131000000'), (2, '上海', '李四', '151000000'), (3, '⼴州', '王五', '153000000'), (4, '深圳', '小不点', '157000000'), (5, '九江', '大王八', '158000000'), (6, '衡阳', '绿豆汤', '159000000'), (7, '永州', '凉拌粉', '188000000'), (8, '南昌', '南昌拌粉', '18812345566'); COMMIT; SET FOREIGN_KEY_CHECKS = 1;
- 配置 nginx 反向代理
upstream myProxy { server 127.0.0.1:8080; server 127.0.0.1:8081; } server { listen 80; server_name localhost; #charset koi8-r; #access_log logs/host.access.log main; #location / { # root html; # index index.html index.htm; # } location / { proxy_pass http://myProxy; proxy_set_header Host $host; } location /favicon.ico { }
- 修改 conf/server.xml, 配置两个 tomcat。
8080的保持默认, 8081的修改一下两个端口.
<Server port="8006" shutdown="SHUTDOWN"> ... <Connector port="8081" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
- 配置 redis
yum install redis -y
, 并修改配置文件 /etc/redis.conf
,重启 redis 服务
redis.conf 修改说明
- 将
bind 127.0.0.1 ::1
绑定的主机地址这一行注释掉 或者将127.0.0.1改为0.0.0.0。
- 将
protected-mode
要设置成no (默认为yes的, 防止了远程访问,在redis3.2.3版本后)
- 按需设置密码
- 开启守护进程
daemonize no
redis默认是yes ,以守护进程的方式. 所以无需修改.
若在 docker run命令里边有一个参数-d
这个参数也是以守护进程执行。则需要打开配置文件把守护进程修改为 no
# bind 127.0.0.1 protected-mode no requirepass 12345 daemonize yes
- 将模块 1-3 的作业转换为 spring boot 项目
以下分别从代码 和 配置文件 进行阐述.
代码讲解
配置拦截器从 xml 改为 java 代码中配置
package com.lagou.edu.sss.interceptor; @Configuration public class MyWebAppConfigurer implements WebMvcConfigurer { @Autowired private MyInterceptor myInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(myInterceptor) .addPathPatterns("/**") .excludePathPatterns("/user/**") .excludePathPatterns("/page/login.htm"); } }
在这里新建了 ServletInitializer 类. 因为若打包成war包,则需要继承 import org.springframework.boot.web.servlet.support.SpringBootServletInitializer
类,覆盖其configure(SpringApplicationBuilder)方法
package com.lagou.edu.sss; public class ServletInitializer extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { return application.sources(SssApplication.class); } }
Application 上加入 @EnableCaching 和 @EnableRedisHttpSession 注解.
@SpringBootApplication @EnableCaching @EnableRedisHttpSession public class SssApplication { public static void main(String[] args) { SpringApplication.run(SssApplication.class, args); } }
文件配置讲解
修改 WEB-INF 下的 web.xml 信息
因为打包成war的话,如果打包之后的文件中没有web.xml文件的话自己可以加进去一个最简单的web.xml(只有根节点的定义,而没有子元素),防止因缺乏web.xml文件而部署失败
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>作业2</display-name> </web-app>
application 配置文件配置
spring.http.encoding.force=true # jsp 支持 spring.mvc.view.suffix=.jsp spring.mvc.view.prefix=/WEB-INF/jsp/ # 关闭默认模板引擎 spring.thymeleaf.cache=false spring.thymeleaf.enabled=false # mysql 配置 spring.datasource.url=jdbc:mysql:///test?characterEncoding=utf-8&serverTimezone=UTC spring.datasource.username=root spring.datasource.password=123456 # redis 配置 spring.redis.database=0 spring.redis.host=localhost spring.redis.port=6379 spring.redis.timeout=5000 spring.redis.password=
建立 webapp 和 page子文件夹,从原项目中拷贝edit.jsp, list.jsp, login.jsp.
pom 文件中引入 jsp 相关支持 和 session支持
<!-- servlet 依赖. --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <scope>provided</scope> </dependency> <!--jstl--> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> </dependency> <!-- tomcat 的支持.--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope> </dependency> <!--引入springBoot 内嵌的Tomcat对JSP的解析包--> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> <scope>provided</scope> </dependency> <!-- session 支持--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> </dependency>
- 通过
spring-boot:run
先自行验证功能是否一切正常
- 打 war 包, 最好在每次 mvn pacakge 前 clear一下, 打好包后重点查看 classes下是否类文件为空, 否则部署会确认内容. 最终重命名为
sss.war
. 然后分别发送到对应的 webapp 目录下。部署好以后等待片刻即可访问。
外网版演示地址
http://117.50.94.8/sss/page/list.htm
这次的主要坑点:
- docker 下启动nginx, 但是配置反代理后, 两台tomcat 报502, 最后索性还是 yum 安装 nginx 靠谱. 自己用低配版的1G 版云服务器, 启动两个tomcat 和 一个redis, 一个 mysql8. 最后 docker 强制把我的mysql 销毁了, 感觉是不是扛不住压力, 所以最终docker 下只跑一个 mysql. 然后把 redis 容器, 两 tomcat 容器先停止, 然后直接部署在云服务器上. 最终结果部署花了很多时间.
- 部署war包访问 controller 一直访问 404, 竟然发现 classes 下没有类文件. 所以以后打好 war 包以后一定要检查内容是否确认
- 一定要有java 类继承 org.springframework.boot.web.servlet.support.SpringBootServletInitializer 类,覆盖其configure(SpringApplicationBuilder)方法, 否则 tomcat 的日志不走.
- 程序查看 tomcat 日志是否有异常信息, 因为部署完后不代表成功了.
linux 服务器如何查看日志
1、先切换到:cd usr/local/tomcat5/logs
2、tail -f catalina.out
3、这样运行时就可以实时查看运行日志了
作业二:
请描述你对分布式调度的理解(结合Elastic-Job-lite图文并茂描述)
什么是分布式任务调度?有两层含义
1)运⾏在分布式集群环境下的调度任务(同⼀个定时任务程序部署多份,只应该有⼀个定时任务在执 ⾏)
2)分布式调度—>定时任务的分布式—>定时任务的拆分(即为把⼀个⼤的作业任务拆分为多个⼩的作业任务,同时执⾏)
ElasticJob - 分布式作业调度解决方案
官方网站: http://shardingsphere.apache.org/elasticjob/
ElasticJob 是一个分布式调度解决方案,由 2 个相互独立的子项目 ElasticJob Lite 和 ElasticJob Cloud 组成。
ElasticJob Lite 定位为轻量级无中心化解决方案,使用 jar 的形式提供分布式任务的协调服务;
ElasticJob Cloud 使用 Mesos + Docker(TODO)的解决方案,额外提供资源治理、应用分发以及进程隔离等服务。
ElasticJob 的各个产品使用统一的作业 API,开发者仅需要一次开发,即可随意部署。
架构图
ElasticJob Lite
功能列表
- 弹性调度
- 支持任务在分布式场景下的分片和高可用
- 能够水平扩展任务的吞吐量和执行效率
- 任务处理能力随资源配备弹性伸缩
- 资源分配
- 在适合的时间将适合的资源分配给任务并使其生效
- 相同任务聚合至相同的执行器统一处理
- 动态调配追加资源至新分配的任务
- 作业治理
- 失效转移
- 错过作业重新执行
- 自诊断修复
- 作业依赖(TODO)
- 基于有向无环图(DAG)的作业间依赖
- 基于有向无环图(DAG)的作业分片间依赖
- 作业开放生态
- 可扩展的作业类型统一接口
- 丰富的作业类型库,如数据流、脚本、HTTP、文件、大数据等
- 易于对接业务作业,能够与 Spring 依赖注入无缝整合
- 可视化管控端
- 作业管控端
- 作业执行历史数据追踪
- 注册中心管理
快速入门
引入maven依赖
<!-- 引入elasticjob-lite核心模块 --> <dependency> <groupId>org.apache.shardingsphere.elasticjob</groupId> <artifactId>elasticjob-lite-core</artifactId> <version>${latest.release.version}</version> </dependency> <!-- 使用springframework自定义命名空间时引入 --> <dependency> <groupId>org.apache.shardingsphere.elasticjob</groupId> <artifactId>elasticjob-lite-spring</artifactId> <version>${latest.release.version}</version> </dependency>
作业开发
public class MyElasticJob implements SimpleJob { @Override public void execute(ShardingContext context) { switch (context.getShardingItem()) { case 0: // do something by sharding item 0 break; case 1: // do something by sharding item 1 break; case 2: // do something by sharding item 2 break; // case n: ... } } }
作业配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:elasticjob="http://shardingsphere.apache.org/schema/elasticjob" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://shardingsphere.apache.org/schema/elasticjob http://shardingsphere.apache.org/schema/elasticjob/elasticjob.xsd "> <!--配置作业注册中心 --> <elasticjob:zookeeper id="regCenter" server-lists="yourhost:2181" namespace="elastic-job" base-sleep-time-milliseconds="1000" max-sleep-time-milliseconds="3000" max-retries="3" /> <!--配置任务快照服务 --> <elasticjob:snapshot id="jobSnapshot" registry-center-ref="regCenter" dump-port="9999"/> <!--配置作业类 --> <bean id="simpleJob" class="xxx.MyElasticJob" /> <!--配置作业 --> <elasticjob:simple id="oneOffElasticJob" job-ref="simpleJob" registry-center-ref="regCenter" cron="0/10 * * * * ?" sharding-total-count="3" sharding-item-parameters="0=A,1=B,2=C" /> </beans>
其他笔记
拦截器与Filter的区别
Spring的拦截器与Servlet的Filter有相似之处,比如二者都是AOP编程思想的体现,
都能实现权限检查、日志记录等。不同的是:
- 使用范围不同: Filter 是Servlet 规范规定的,只能用于Web程序中。而拦截器既
可以用于Web程序,也可以用于Application、Swing 程序中。
- 规范不同: Filter 是在Servlet规范中定义的,是Servlet容器支持的。而拦截器是
在Spring容器内的,是Spring框架支持的。
- 使用的资源不同:同其他的代码块一样, 拦截器也是一个Spring的组件,归Spring
管理,配置在Spring文件中,因此能使用Spring里的任何资源、对象,例如Service
对象、数据源、事务管理等,通过IoC注入到拦截器即可;而 Filter 过滤器不能。
- 深度不同: Filter 在只在Servlet前后起作用。而拦截器能够深入到方法前后、异常抛出前后等,因此拦截器的使用具有更大的弹性。所以在 Spring 构架的程序中,要优先使用拦截器。