略。
我们的文件是存储在MongoDB中的。
MongoDB单个文档的存储限制是16M,如果要存储大于16M的文件,就要用到MongoDB GridFS。
GridFS是Mongo的一个子模块,使用GridFS可以基于MongoDB来持久存储文件。并且支持分布式应用(文件分布存储和读取)。作为MongoDB中二进制数据存储在数据库中的解决方案,通常用来处理大文件。
GridFS使用两个集合(collection)存储文件。一个集合是chunks, 用于存储文件内容的二进制数据;一个集合是files,用于存储文件的元数据。
GridFS会将两个集合放在一个普通的buket中,并且这两个集合使用buket的名字作为前缀。MongoDB的GridFs默认使用fs命名的buket存放两个文件集合。因此存储文件的两个集合分别会命名为集合fs.files ,集合fs.chunks。
GridFS存储文件示意图
直接将文件使用通过FTP上传到文件服务器,并将文件地址存储到MySQL数据库。这种方式也是可行的。
但是,文件系统到了后期会变的很难管理,同时不利于扩展,此外我想做分布式文件系统也显得不那么容易。而GridFS却正好相反,它基于MongoDB的文件系统,便于管理和扩展。
当然了,还有其它的一些分布式文件存储系统如FastDFS,可以根据文件存储的实际情况来进行选择。
目前是手动备份。
后面计划写一个自动备份的脚本来每日备份。
略。
略。
略。
#{}是预编译处理,${}是字符串替换。
(1)mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值。
(2)mybatis在处理${}时,就是把${}替换成变量的值。
(3)使用#{}可以有效的防止SQL注入,提高系统安全性。原因在于:预编译机制。
预编译是提前对SQL语句进行预编译,而其后注入的参数将不会再进行SQL编译。我们知道,SQL注入是发生在编译的过程中,因为恶意注入了某些特殊字符,最后被编译成了恶意的执行操作。而预编译机制则可以很好的防止SQL注入。预编译完成之后,SQL的结构已经固定,即便用户输入非法参数,也不会对SQL的结构产生影响,从而避免了潜在的安全风险。
主要用到了String、Hash、Set。
String:常规key-value缓存应用。用来存一些计数。
Hash: 键值(key => value)对集合。用来存一些对象,对应Java集合中的HashMap。
Set: set是string类型的无序集合。对应Java中的HashSet,用来存一些需要去重的数据。
保证线程安全有以下几种方式:
通常,会使用线程池来管理线程。
在 JDK 1.5 之后推出了相关的 api,常见的创建线程池方式有以下几种:
其实看这三种方式创建的源码就会发现:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
实际上还是利用 ThreadPoolExecutor 类实现的。
通常我们都是使用:
threadPool.execute(new Job());
这样的方式来提交一个任务到线程池中,所以核心的逻辑就是 execute() 函数了。
线程池一共有五种状态, 分别是:
下图为线程池的状态转换过程:
再看看Excute方法的执行:
1、获取当前线程池的状态。 2、当前线程数量小于 coreSize 时创建一个新的线程运行。 3、如果当前线程处于运行状态,并且写入阻塞队列成功。 4、双重检查,再次获取线程状态;如果线程状态变了(非运行状态)就需要从阻塞队列移除任务,并尝试判断线程是否全部执行完毕。同时执行拒绝策略。 5、如果当前线程池为空就新创建一个线程并执行。 6、如果在第三步的判断为非运行状态,尝试新建线程,如果失败则执行拒绝策略。
当前SpringBoot比较流行,我们可以发挥Spring的特性,由Spring来替我们管理线程:
@Configuration
public class TreadPoolConfig {
/**
* 消费队列线程
* @return
*/
@Bean(value = "consumerQueueThreadPool")
public ExecutorService buildConsumerQueueThreadPool(){
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
.setNameFormat("consumer-queue-thread-%d").build();
ExecutorService pool = new ThreadPoolExecutor(5, 5, 0L, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<Runnable>(5),namedThreadFactory,new ThreadPoolExecutor.AbortPolicy());
return pool ;
}
}
使用时:
@Resource(name = "consumerQueueThreadPool")
private ExecutorService consumerQueueThreadPool;
@Override
public void execute() {
//消费队列
for (int i = 0; i < 5; i++) {
consumerQueueThreadPool.execute(new ConsumerQueueThread());
}
}
其实也挺简单,就是创建了一个线程池的 bean,在使用时直接从 Spring 中取出即可。
可以通过Collections类的sort方法。但需要注意,使用sort方法的时候:
public class User implements Comparable<User> {
private int age;
private String name;
private String sex;
@Override
public int compareTo(User o) {
if (this.getAge() > o.getAge()) {
return 1;
} else if (this.getAge() < o.getAge()) {
return -1;
} else {
return 0;
}
}
// ……
}
List<User> userList=new ArrayList();
userList.add(new User(10, "王二", "男"));
userList.add(new User(8, "张三", "男"));
userList.add(new User(17, "李四", "女"));
Collections.sort(userList);
System.out.println(userList);
List<User> userList=new ArrayList();
userList.add(new User(10, "王二", "男"));
userList.add(new User(8, "张三", "男"));
userList.add(new User(17, "李四", "女"));
//Collections.sort(userList);
Collections.sort(userList, new Comparator<User>() {
@Override
public int compare(User o1,User o2) {
if(o1.getAge()>o2.getAge()) {
return 1;
}else if(o1.getAge()<o2.getAge()) {
return -1;
}else {
return 0;
}
}
});
System.out.println(userList);
在Java8以后可以使用Lamda表达式来进行函数式地编程:
userList.sort((a, b) -> Integer.compare(a.getAge(), b.getAge()));
Stream 作为 Java 8 的一大亮点,它与 java.io 包里的 InputStream 和 OutputStream 是完全不相关的东西。
Java 8 中的 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation)。 下面是使用流的过程:
下面是一个使用流的实例,用于List的迭代:
List<String> stringList = new ArrayList<String>();
stringList.add("one");
stringList.add("two");
stringList.add("three");
stringList.add("one");
Stream<String> stream = stringList.stream();
stream.forEach( element -> { System.out.println(element); });
JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案。
传统的session认证一般是这样的流程:
* 2、服务器验证通过后,在当前对话(session)里面保存相关数据,比如用户角色、登录时间等等。
这种模式的问题在于,扩展性(scaling)不好。单机当然没有问题,如果是服务器集群,或者是跨域的服务导向架构,就要求 session 数据共享,每台服务器都能够读取 session。
一种解决方案是 session共享,将session持久化或者存入缓存。各种服务收到请求后,都向持久层或缓存请求数据。这种方案的优点是架构清晰,缺点是工程量比较大。另外,持久层或者缓存万一挂了,就会认证失败。
另一种方案是服务器索性不保存 session 数据了,所有数据都保存在客户端,每次请求都发回服务器。JWT 就是这种方案的一个代表。
JWT认证流程:
事务(TRANSACTION)是作为单个逻辑工作单元执行的一系列操作, 这些操作作为一个整体一起向系统提交,要么都执行、要么都不执行 。
事务是一个不可分割的工作逻辑单元事务必须具备以下四个属性,简称 ACID 属性:
通过在方法加注解 @Transactional 来实现声明式的事务。
Spring 事务管理分为编码式和声明式的两种方式。编程式事务指的是通过编码方式实现事务;声明式事务基于 AOP,将具体业务逻辑与事务处理解耦。声明式事务管理使业务代码逻辑不受污染, 因此在实际使用中声明式事务用的比较多。声明式事务有两种方式,一种是在配置文件(xml)中做相关的事务规则声明,另一种是基于 @Transactional 注解的方式。
(这里应该是想问用过的命令)日产工作中,下面这些命令经常用到:
参考:
【1】:SpringBoot学习笔记(十一:使用MongoDB存储文件 ) 【2】:GridFS 基于 MongoDB 的分布式文件存储系统 【3】:Linux下shell脚本实现mongodb定时自动备份 【4】:Mybatis中#{}和${}的区别是什么 【5】:Redis五种数据类型及应用场景 【6】:Redis五种数据类型及应用场景 【7】:面试官:说说什么是线程安全?一图带你了解java线程安全 【8】:如何优雅的使用和理解线程池 【9】:深入理解 Java 线程池:ThreadPoolExecutor 【10】:透彻的掌握 Spring 中 @Transactional的使用 【11】:SpringBoot学习笔记(十三:JWT ) 【12】:Java8 Stream