0%

缓存的用途

高性能

用户直接访问数据库时,需要从硬盘上读取数据,速度较慢。如果将用户查询数据库中的数据存在缓存中,下次可以直接从内存的缓存中读取,性能更高。

高并发

直接操作缓存能承受的请求远远大于直接访问数据库,将数据库中的数据存放在缓存中能显著提高系统的并发能力

常见数据类型

Redis常见的数据结构包括:String、List、Set、SortedSet、Hash、HyperLogLog

Redis支持的数据结构可以与Java中对应的类来理解,String对应Object类,因为任意对象都可以以string的形式来存储。List数据结构对应java.util.List接口的实现类LinkedList,Set数据结构对应HashSet类,SortedSet对应SortedSet接口,Hash数据结构对应HashMap类。

string:最基础的数据类型,Redis中String类型的value最多可容纳数据长度为512M。适用于value较小、模型简单的value。

List:按插入顺序排序的字符串链表。可以在头部(left)和尾部(right)添加新元素。头尾添加元素效率很高,中间插入效率较低。适用于新闻列表、评论列表等列表类型数据存储。

阅读全文 »

线程池

线程池的优点

  • 通过复用线程减少创建和销毁线程的资源消耗
  • 不用重新创建线程,提高响应速度
  • 方便管理,线程是系统稀缺资源,如果无限制创建会消耗系统资源,降低稳定性。使用线程池可以进行统一分配、调优和监控

线程池创建

1
2
3
4
5
6
7
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
  • corePoolSize: 线程池核心线程数
  • maximumPoolSize:线程池最大线程数大小
  • keepAliveTime:线程池中非核心线程空闲的存活时间
  • unit:线程空闲存活时间单位
  • workQueue:存放任务的阻塞队列
  • threadFactory:创建线程的工厂,可以给创建的线程设置有意义的名字,便于排查问题
  • handler:线程池的饱和策略

线程池组成

线程池由工作线程、任务队列组成,其中工作线程 Worker 继承AQS,维护在一个HashSet 中任务队列是 BlockingQueue,防止在多线程环境下出现并发问题。

线程池执行

  • 提交一个任务,线程池里存活的核心线程数小于线程数corePoolSize时,线程池会创建一个核心线程去处理提交的任务。
  • 如果线程池核心线程数已满,即线程数已经等于corePoolSize,一个新提交的任务,会被放进任务队列workQueue排队等待执行。
  • 当线程池里面存活的线程数已经等于corePoolSize了, 并且任务队列workQueue也满,判断线程数是否达到maximumPoolSize,即最大线程数是否已满,如果没到达,创建一个非核心线程执行提交的任务。
  • 如果当前的线程数达到了maximumPoolSize,还有新的任务过来的话,直接采用拒绝策略处理。
阅读全文 »

工厂模式

Spring工厂模式通过BeanFactory或ApplicationContext创建对象,BeanFactory仅提供最基本的依赖注入支持,ApplicationContext扩展了BeanFactory,除了具有BeanFactory的功能外还有更多额外功能,因此一般使用ApplicationContext。

单例模式

单例模式的好处:

  1. 减少创建对象花费的时间,对于频繁使用的重量级对象来说,是非常可观的系统开销
  2. 由于new操作的次数减少,系统内存的使用频率也会降低,可以降低GC压力,缩短GC停顿时间

Spring中bean的默认作用域是singleton的,它通过ConcurrentHashMap实现单例模式。

代理模式

AOP(Aspect Oriented Programming,面向切面编程)将那些与业务无关的、被业务模块共同调用的逻辑(事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,有利于未来的可扩展性和可维护性。

Spring AOP基于动态代理,如果要代理的对象实现了某个接口,Spring AOP会使用JDK Proxy创建代理对象,对于没有实现接口的对象,无法使用JDK Proxy进行代理,Spring AOP会使用Cglib生成被代理对象的子类来作为代理。

也可以使用AspectJ在编译时做静态代理,这种代理方式基于字节码操作(Bytecode Manipulation)。

阅读全文 »

BeanFactory是Bean的容器,Bean在代码层面可以认为是BeanDefinition的实例。BeanDefinition中保存了Bean信息,如这个Bean指向哪个类、是否是单例、是否懒加载、依赖了哪些Bean等。

读取配置的操作在XmlBeanDefinitionReader中,它负责加载配置和解析。

如果是普通bean,直接返回sharedInstance,如果是factoryBean,返回它创建的那个实例对象

创建Bean容器

初始化Bean

  • factoryBean
阅读全文 »

Servlet

Servlet是基于Java技术的Web组件,由容器托管,用于生成动态内容。与其它基于Java的组件技术一样,Servlet也基于平台无关的Java类格式,被编译为平台无关的字节码,可以被基于Java技术的web server动态加载并运行。客户端通过Servlet容器实现的请求/应答模型与Servlet交互。

Servlet容器是web server或application server的一部分,提供基于请求/响应发送模型的网络服务,解码基于MIME的请求,并格式化基于MIME的响应。Servlet容器也包含了管理Servlet生命周期。

典型事件序列如下:

  1. 客户端(如web浏览器)发送一个HTTP请求到web服务器
  2. Web服务器接收到请求并交给servlet容器处理,servlet容器可以运行在与宿主web服务器同一个进程中,也可以是同一主机的不同进程,或位于不同主机的web服务器中对请求进行处理。
  3. servlet容器根据servlet配置选择相应的servlet,并带上请求和响应参数调用它
  4. servlet执行业务逻辑,然后动态产生响应内容发送回客户端,发送数据到客户端是通过响应对象完成的
  5. 一旦servlet完成请求处理,servlet容器必须确保响应正确刷出,并将控制权还给宿主web服务器

Servlet生命周期

Servlet按照严格定义的生命周期被管理,该生命周期定义了Servlet如何被加载、实例化、初始化、处理客户端请求,以及何时结束服务。

  1. 加载和实例化

Servlet容器负责加载和实例化Servlet,加载和实例化可以发生在容器启动时,或延迟初始化直到有请求需要处理时。Servlet容器使用普通的Java类加载器加载Servlet类。可以从本地文件系统、远程文件系统或其它网络服务加载。

  1. 初始化
阅读全文 »

TensorFlow读取数据的方式主要有2种,一般选择错误会造成性能问题,两种方式为:

  1. Feed_dict 通过feed_dict将数据喂给session.run函数,这种方式的好处是思路很清晰,易于理解。缺点是性能差,性能差的原因是feed给session的数据需要在session.run之前准备好,如果之前这个数据没有进入内存,那么就需要等待数据进入内存,而在实际场景中,这不仅仅是等待数据从磁盘或者网络进入内存的事情,还可能包括很多前期预处理的工作也在这里做,所以相当于一个串行过程。而数据进入内存后,还要串行的调用PyArrayToTF_Tensor,将其copy成tensorflow的tensorValue。此时,GPU显存处于等待状态,同时,由于tf的Graph中的input为空,所以CPU也处于等待状态,无法运算。

  2. RecordReader 这种方式是tf在Graph中将读取数据这个操作看做图中一个operation节点,减少了一个copy的过程。同时,在tf中还有batch与threads的概念,可以异步的读取数据,保证在GPU或者CPU进行计算的时候,读取数据这个操作也可以多线程异步执行。静态图中各个节点间的阻塞:在一个复杂的DAG计算图中,如果有一个点计算比较慢时,会造成阻塞,下游节点不得不等待。此时,首先要考虑的问题是图中节点参数所存储的位置是否正确。比如如果某个计算节点是在GPU上运算,那么如果这个节点所有依赖的variable对象声明在CPU上,那么就要做一次memcpy,将其从内存中copy到GPU上。因为GPU计算的很快,所以大部分时间花在拷贝上了。总之,如果网络模型比较简单,那么这种操作就会非常致命;如果网络结构复杂,比如网络层次非常深,那么这个问题倒不是太大的问题了。

参考资料

  1. 深度学习在美团搜索广告排序的应用实践
阅读全文 »

编程语言

Java

基础

  1. Object有哪些方法
  2. Java内存模型(Happends before)
  3. JVM运行时数据区
  4. 垃圾收集算法,垃圾收集器
  5. 类加载机制

集合

  1. HashMap底层实现,1.8之前之后有什么区别

并发

  1. synchronized和ReenterLock的区别
  2. 有哪几种线程池,各自的使用场景是什么,如何创建一个线程池
  3. volatile原理,使用场景

Python

  1. Python有哪些内置的数据结构
  2. 如何管理内存
  3. 垃圾回收机制
  4. django执行http请求的流程
  5. django内建缓存机制
阅读全文 »

术语

Topic

主题,承载消息的逻辑容器,在实际使用中用于区分业务类型。

Record

消息,Kafka处理的主要对象

Partition

分区,每个topic包含一个或多个partition,是一个有序的消息序列。物理上,每个partition对应一个文件夹,该文件夹下存储该partition的数据和索引文件。每条消息被发送到broker之前,会根据分区规则选择存储到哪个分区。

Offset

消息位移,表示分区中每条消息的位置信息,是一个单调递增且不变的值。offset是消息在分区中的唯一标识,kafka通过offset来保证消息在分区中的顺序性,kafka保证的是分区有序而不是主题有序。

image-20190819170404800

阅读全文 »

image-20190815175530399

关注设计思想、实现原理和实践方法论

消息队列的作用

1.异步处理

非必要的业务逻辑以异步方式运行,提高响应速度。多个消费者并发执行,提升系统整体性能。

2.流量控制

下游来不及处理的流量堆积在消息队列中,起到削峰填谷、保护下游的作用。

3.服务解藕

上游将消息写入消息队列,需要消息的系统自己从消息队列中订阅,上游不需要做任何修改

消息队列的缺点

阅读全文 »

ZooKeeper的设计目标是将那些复杂且容易出错的分布式一致性服务封装起来,构成一个高效可靠的原语集,并以一系列简单易用的接口提供给用户使用。可以基于它实现数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master选举、分布式锁和分布式队列等功能。zk可以保证如下分布式一致性:

  • 顺序一致性

    从同一个客户端发起的事务请求,最终会严格按照发起顺序被应用到zk中

  • 原子性

    所有事务请求的处理结果在整个集群中所有机器上的应用情况是一致的,要么整个集群所有机器都成功应用了某一个服务,要么都没有应用。一定不会出现集群中部分机器应用了该事务,另外一部分没有应用的情况。

  • 可靠性

    一旦服务端成功应用了一个事务,并完成对客户端的响应,那么该事务所引起的服务端状态变更将会被一直保留下来,除非有另一个事务对其进行了变更。

  • 实时性

    zk保证在一定时间段内,客户端最终一定能从服务端读取到最新的数据状态。但由于网络延时等原因,Zookeeper不能保证两个客户端能同时得到刚更新的数据,如果需要最新数据,应该在读数据之前调用sync()接口

组成zk集群的每台机器都会在内存中维护当前服务器状态,每台机器之间都互相保持通信。只要集群中超过一半的机器能够正常工作,整个机器就能对外提供服务。

基本概念

集群角色

Leader:leader服务器为客户端提供读和写服务,全局事务 Id(zxid)只能由 leader分配

Follower:提供读服务(非事务请求),参与Leader选举过程,不参与过半写成功策略

Observer:只提供读服务,不参与Leader选举和写服务

Leader、Follower 和 Observer这三种角色有四种状态:leading、following、observing 和looking

LOOKING:当前Server不知道leader是谁,正在搜寻。

阅读全文 »