中间件相关杂记

• Tomcat服务器

采用nio方式的话跟netty一样,也会有个Acceptor,网络接收线程池和事件处理线程池,处理一个HTTP请求的过程如下:

1. 用户点击网页内容,请求localhost/test/get被发送到本机端口8080,被在那里监听的Coyote HTTP/1.1 Connector获得。

2. Connector生成request和response并交给它所在的Service的Engine来处理。

3. Engine把请求分配到名为localhost的Host

4. Host 将请求分配到名为test的Context下

5. Context在它的解析web.xml中的路径找到相应的Servlet(extends HttpServlet),通过反射( !!!!反射必须要有无参构造器)获取Servlet实例(默认单例因此被共享线程不安全,可放入LocalThread,但是方法内部的变量是线程安全的-->在Java栈中),生成HttpServletResponse和HttpServletRequest对象,传入Servlet实例的 生命周期方法(init,service,destory) 并且依次调用他们,生成html发回给浏览器。我们只负责写Sevlet子类。

• HttpServlet继承GenericServlet(里面有生命周期方法),并重写其abstract的service(){ 根据method参数调用 doGet()或doPost() }方法,此时你写HttpServlet子类的时候只要重写doGet()和doPost()就可以了,如果没重写发回405

• Spring使用

web.xml 配置<listener>为ContextLoaderListener初始化Spring容器 配置<servlet> 为DispatcherServlet初始化springMVC前端控制器 通过contextConfigLocation加载配置文件 dao.xml 中dataSource 配置为 DruidDataSource(数据库连接池)并配置url等登录信息 <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean" 且<property name="dataSource"ref="dataSource" /> 配置<bean class="org.mybatis..MapperScannerConfigurer">自动扫描.dao包将其中Mapper接口生成代理类注入Spring service.xml 中扫描.service包和.common.utils包(这时@Service才有用) trans.xml 中transactionManager配置为springframework的并将数据源指向-dao.xml中的dataSource 配置事务advisor或者使能事务注解@Transactional扫描。 springmvc.xml:扫描.controller包  & 配置注解驱动<mvc:annotation-driven />  & 配置静态资源映射

• Spring源码 启动分析 

Spring和Spring Boot中应用程序引导的基本区别在于servlet ,默认情况下,Spring Boot使用嵌入式容器来运行应用程序。在这种情况下,Spring Boot使用public static void main入口点来启动嵌入式Web服务器。此外,它还负责将Servlet,Filter和ServletContextInitializer bean从应用程序上下文绑定到嵌入式servlet容器。Spring Boot的另一个特性是它会自动扫描同一个包中的所有类或Main类的子包中的组件。SpringBoot的启动引导类真的是XXApplication吗? SpringBoot源码分析之Spring容器的refresh过程

总在说SpringBoot内置了tomcat启动,那它的原理你说的清楚吗? - 动力节点的文章 - 知乎 https://zhuanlan.zhihu.com/p/182464474

SCF的spring.factories

# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ spring.boot.autoconfigure.SCFAutoConfiguration

spring.factories文件是帮助spring-boot项目包以外的bean(即在pom文件中添加依赖中的bean)注册到spring-boot项目的spring容器的结论。由于@ComponentScan注解的作用是扫描@SpringBootApplication所在的Application类(即spring-boot本项目的入口类)所在的包(basepackage)下所有的@component注解(或拓展了@component的注解)标记的bean,因此需要@EnableAutoConfiguration注解来注册项目包外的bean。而spring.factories文件,则是用来记录项目包外需要注册的bean类名

ContextLoaderListener是一个实现了ServletContextListener接口的监听器, 在启动项目时会触发contextInitialized方法,调用执行initWebApplicationContext方法,主要做三件事{ 1. this.context = createWebApplicationContext(servletContext);-->用户的web.xml自定义上下文 2. configureAndRefreshWebApplicationContext(this.context,servletContext);-->加载相关xml配置调用下边的refresh()  3. servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context));//存到Servlet的servletContext } refresh(){ //spring 核心方法,springboot也会走到这 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();//beanFactory用于解析xml中bean,通过getBean(beanName)可以实例化Bean。 postProcessBeanFactory(beanFactory);  invokeBeanFactoryPostProcessors(beanFactory); registerBeanPostProcessors(beanFactory); ... registerListeners();// Check for listener beans and register them. ... }

Spring中BeanFactoryPostProcessor和BeanPostProcessor都是Spring初始化bean时对外暴露的扩展点。 前者是在Spring容器加载了bean的定义文件之后,在bean实例化之前执行。后者在spring容器实例化bean之后,在执行bean的初始化方法前,添加一些自己的处理逻辑。

spring 中为我们提供了两种类型的 bean,一种就是普通的 bean,我们通过 getBean(id) 方法获得是该 bean 的实际类型,另外还有一种 bean 是 FactoryBean,也就是工厂 bean,我们通过 getBean(id) 获得是该工厂所产生的 Bean 的实例,而不是该 FactoryBean 的实例。

看到基于ContextLoaderListener和DispatcherServlet都可以配置spring相关的XML,值得说明的是这两种方式加载spring的ApplicationContext上下文对象不是合并存储的。DispatcherServlet加载的context成功后,如果 publishContext属性的值设置为true的话(缺省为true) 会将this.context存放在org.springframework.web.servlet.FrameworkServlet.CONTEXT. + (servletName)的attribute中。因此如果只使用DispatcherServlet加载context的话,如果程序中有地方使用WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext) 来试图获取applicationContext时,就会抛出"no ContextLoaderListener registered?"的exception。

ApplicationContext(框架外部调用)建立在BeanFactory(框架内部调用)基础之上,都可以获取bean。使用BeanFactory则所有的bean都是在第一次使用该Bean的时候实例化 。如果你使用ApplicationContext 为SpringBean的工厂类,则又分为以下几种情况:如果bean的scope是singleton的,并且lazy-init为false(默认是false,所以可以不用设置),则ApplicationContext启动的时候就实例化该Bean,lazy-init为true,则该Bean的实例化是在第一次使用该Bean的时候进行实例化 ;如果bean的scope是prototype的(相当于new操作),则该Bean的实例化是在第一次使用该Bean的时候进行实例化 。

bean的生命周期 :

1. 扫描XML配置文件中提供的bean并实例化。

2. 按照XML配置文件中所指定的通过DI填充所有bean属性。

3. 通过将bean的id传递给BeanNameAware 接口提供的setBeanName() 方法设置bean名称。

4. 使用BeanFactoryAware 接口的setBeanFactory()方法设置bean工厂

5. 通过在bean类中实现BeanPostProcessor 接口的 postProcessBefore Initialization方法来完成预初始化。

6. 通过在bean类中实现InitiallzingBean接口(在xml配置中可用 init-method代替消除与Spring的藕合)的afterPropertiesSet ()方法来初始化(执行某些任务包括打开文件、打开网络或数据库连接以及分配内存)

7. 通过在bean类中实现BeanPostProcessor 接口的postProcessAfterInitialization() 方法来完成初始化后的任务

8. bean使用期。

9.如果bean实现DisposableBean接口,将调用destroy()方法。如果为bean声明了自定义destroy方法,将调用该方法销毁bean。

• IOC(Inversion of Control,把创建对象的权利交给框架):反转控制,原先需要new类的对象(要new对象就要写类名,如果类名改了呢?),现在配置spring并交由它来创建类的对象。能够通过getBean("xml中id的值")获得对象,原理是使用反射实现的简单工厂模式解耦合,beanFactory.getBean()先使用dom4j解析xml文件,通过

• DI(Dependency Injection,依赖注入)

构造方法注入:向 Bean实例对象的属性 注入值constructor标签有参构造。

设值注入(优先):property标签,Bean必须有对应的类中有set(),value 是值。ref 是对象引用(可以是上面的..注入数组。注入map。

注解注入:现在可以用注解Java代码的方式完成,xml中只需打开注解扫描。->> 使用构造函数依赖注入时,只有一个对象所有引用的对象先实例化后,才实例化这个对象。使用设值注入时,Spring首先把所有对象都实例化完成后(放到三级缓存中提前曝光),然后才实例化引用的对象(因此singleton +设值注入 的循环引用不会报错)。

• AOP(Aspect Oriented Programming) AOP原理

面向切面编程,原理是代理类持有原类的实例,扩展功能(增添日志)不修改原类。动态代理(底层用了反射)就是在运行时生成一个类,这个类会实现你指定的一组接口,而这个类没有.Java文件,是在运行时生成的,你也不用去关心它是什么类型的,你只需要知道它实现了哪些接口。而CGlib也能实现动态代理且不需要实现接口(底层用了字节码框架asm,对final方法无法处理)。

• 切面的方式配置事务advisor: 切面(aspect)是将通知/增强(Advice)织入切点(pointcut)方法的过程。

连接点(Joinpoint): 类里面 可以 被增强的方法。

<aop:advisor 是pointcut和advice 的适配器,也可定义切面。 <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes><tx:method name="save*" propagation="REQUIRED" /></tx:attributes>     <!--传播行为:REQUIRED:如果当前没有事务就新建一个事务,如果有一个事务加入到这个事务中。--> </tx:advice>     SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。 <aop:config> <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.taotao.service.*.*(..))" /> </aop:config>

tx:advice 代表用aop方式管理事务 annotation-driven 代表注解方式管理事务(在类里加@Transactional)

事务传播(propagation属性)A.f1()有事务X ,B.f2()有事务Y,当A.f1()调用B.f2()的时候,B.f2()中的代码执行哪个事务。

• service包下的 @Service注解

@Service public class ItemServiceImpl implements ItemService{ @Autowired private TbItemMapper itemMapper;//未注入前=null,TbItemMapper是接口

当Spring发现@Autowired注解时,将自动在代码上下文中找到和其匹配(默认类型匹配)的itemMapper中。@Autowired默认按照byType方式进行bean匹配(Spring的),@Resource默认按照byName方式进行bean匹配(JavaEE的,减少与Spring的耦合),功能一样。

@Service注解,其实做了两件事情:1、声明ItemServiceImpl 是一个bean,这点很重要,因为ItemServiceImpl 是一个bean,其他的类才可以使用@Autowired将ItemServiceImpl 作为一个成员变量自动注入2、ItemServiceImpl 在bean中的id是"itemServiceImpl ",即类名且首字母小写

• 子容器可以访问父容器的对象(反之不行),但容器之间的属性不能互相访问。

• SpringBoot源码

• Springmvc:启动过程 DispatcherServlet继承自FrameworkServlet继承自HttpServletBean

① 调用HttpServletBean.init方法中执行initServletBean方法进行初始化操作,当然该方法在HttpServletBean是空方法,所以需要子类重写。

② FrameworkServlet.initServletBean子类不负众望,重写了initServletBean方法,该方法最核心的操作就是调用initWebApplicationContext()执行上下文Bean初始化。

③ FrameworkServlet.initWebApplicationContext方法首先获取自己的双亲上下文(也就是ContextLoaderListener初始化成功的WebApplicationContext);然后创建或者获取当前Servelet的WebApplicationContext。

④ 无论是自己创建还是获取现有的 WebApplicationContext,最终都会让Servelt级别的WebApplicationContext执行configureAndRefreshWebApplicationContext()方法进行上下文容器初始化。-->调用onRefresh初始化下面的各组件。

当来请求时,调用FrameworkServlet重写的doGet和doPost方法,他们调用FrameworkServlet中proceRequest方法,他调用DispatcherServlet重写了的doService()方法,他调用DispatcherServlet的doDispatch方法执行下面步骤。通过web.xml 中配置进行拦截请求。

源码1,源码2,源码3:  

< url-pattern >/  会匹配到/login这样的路径型url,不会匹配到模式为*.jsp这样的后缀型url

< url-pattern > /* 会匹配所有url:路径型的和后缀型的url(包括/login,*.jsp,*.js和*.html等)

• ModelMap implements Model,用于向jsp等页面传递attribute。

• 要想@Response生效,返回的对像中必须有setget方法才会生成json串,否则返回406。

• 参数绑定:前端的数据传入controller方法的形参(执行交给Spring)

拦截器的实现:拦截类收到信息后,去查找配置文件,并根据配置实例化相对的拦截器对象,然后串成一个列表(List),最后一个一个的调用列表中的拦截器(用反射实现可插拔)。

• Mybatis:设计

硬编码写死了,修改时必须重编译。可以将改变的东西(针对Staff类的sql命令方法)写到到映射文件StaffMapper.xml,同时其中的id,resultType和paramterType要与对应的StaffMapper.java接口中的方法名称、返回类型、参数类型一致。首先实例化MapperScannerConfigurer这个bean时调用实现的BeanDefinitionRegistryPostProcessor接口的postProcessBeanDefinitionRegistry方法扫描在.dao包下的StaffMapper.java和StaffMapper.xml并动态代理自动生成接口StaffMapper的实现类bean(对其insert()方法的实现会调用注册好的org.mybatis.spring.SqlSessionFactoryBean的bean实例的FactoryBean接口的getObject()方法(工厂bean与普通bean不同) 获取SqlSessionFactory实例(保存一些Configuration如dataSource配置信息)创建线程安全的sqlsession -->此时一定会用org.mybatis.spring.SqlSessionTemplate来替代原先的DefaultSqlSession),当Spring需要@AutoWired StaffMapper staffMapper的时候会在容器里找到这些bean。

注:SqlSessionFactoryBean还继承了InitializingBean, InitializingBean的初始化方法就是afterPropertiesSet(),一般检查dataSource和sqlSessionFactoryBuilder。

•  在基本的MyBatis 中,SqlSessionFactoryBuilder可以从XML配置文件或一个预先定制的Configuration的实例构建出SqlSessionFactory的实例,进而来创建 SqlSession,但是不是线程安全的。为什么mybatis-spring.jar中的SqlSessionTemplate可以被多个dao复用

因为在他构造方法中{ this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(), new Class[] { SqlSession.class}, new SqlSessionInterceptor()); } 在SqlSessionInterceptor的invoke方法中{ 在method.invoke(sqlSession)之前会获取SqlSession sqlSession = SqlSessionUtils.getSqlSession(){ 根据sqlSessionFactory从TransactionSynchronizationManager中获取SqlSessionHolder(<sqlSessionFactory, sqlSession>对), 如果获取不到就SqlSessionFactory实例.openSession并将新创建的SqlSessionHolder对象注册到TransactionSynchronizationManager中

Spring使用TransactionSynchronizationManager的bindResource方法将当前线程与一个事务绑定,采用的方式就是ThreadLocal。  

SqlSession中执行:

• dao中有StaffMapper.xml:(resultType默认是对应名称相同的映射,resultMap指的是自定义映射)

在级联查询中,<association <collection 中的column是指使用column的值作为参数进行后的<select 查询。 <association property="article" column="id" select="test.mybatis.dao.articleMapper.selectArticleByUserId" /> <collection property="articleList" column="id" select="test.mybatis.dao.articleMapper.selectArticleListByUserId" />

• 延迟加载:按用户需求查询,不要一次都查询出来(如过大的列就新建一个表然后关联,提高主表访问速度)

• mybatis的xml中占位符是#{email}(编译时会变成PreparedStatement语句的?,然后pst.setString(1,email); pst.executeUpdate(); 同时会将email里特殊字符如'转义成\' --> 动态 SQL 解析阶段),最好不用${email}(直接变成相应的字符串,可用于order by ${colname}),可能引起sql注入攻击。

• 非递增主键:BEFORE insert,getUUID()生成主键一起insert

自增主键:先insert (自增主键生成),AFTER 返回LAST_INSERT_ID,填到staff对象中的id属性

SELECT LAST_INSERT_ID()

理解一下#{xxx} ,select时xxx指的是要传入的,insert时xxx指的是映射对象中定义的

• 没办法进行注释继承的,只能继承方法,不能继承注释的。

• 缓存:一次Sqlsession(与数据库的会话连接)共用一个一级缓存。同一个Mapper的多个sqlsession可以共用一个二级缓存(配置文件cacheEnabled=true后开启)。而增删改后commit都会清空缓存。SqlSession是MyBatis的关键对象, 它是应用程序与持久层之间执行交互操作的一个对象,它的底层封装了JDBC连接,可以用SqlSession实例来直接执行被映射的SQL语句。

• 选择类目中,针对打开下一级的这个动作,当前目录id传入controller,在数据库中寻找parentid等于此id的item的list返回。

• 数据导出在再导入时自增ID会发生变化,会影响相关的外键,造成耦合。

• itemParamMapper.selectByExampleWithBLOBs()处理LONGVARCHAR类型,否则读取为null。

• 什么时候rollback:运行期异常runtimeException,非运行期异常不会触发rollback。必须uncheck (没有catch)。不管什么异常,只要你catch了,spring就会放弃管理,就不会自动rollback。blog.csdn.net/xuxurui007/article/details/52947746

• 客户端1在获得锁之后发生了很长时间的GC pause,在此期间,它获得的锁过期了,而客户端2获得了锁。当客户端1从GC pause中恢复过来的时候,它不知道自己持有的锁已经过期了,它依然向共享资源(上图中是一个存储服务)发起了写数据请求,而这时锁实际上被客户端2持有,因此两个客户端的写请求就有可能冲突(锁的互斥作用失效了)。除了GC pasue场景,时钟跳跃也会引发同样的问题,解决这个问题,Martin提出可以引入fencing token,即锁的版本号,在锁持有者放生变更时fencing token递增更新,客户端访问共享资源时携带着这个fencing token,这样提供共享资源的服务就能根据它进行检查,拒绝掉延迟到来的访问请求(避免了冲突),如下图所示。使用自动续约机制,需要在秘钥申请时,打开自动续约开关,否则客户端自动续约设置不生效。若业务启动自动续约机制,建议续约周期为锁有效期的1/3(最小为1秒)。业务在加锁处理逻辑的上层一定添加try catch 异常获,在finally逻辑中释放锁,否则业务线程异常退出时,锁自动续约还会一直执行,导致锁永远不过期,出现死锁。

• 令牌桶可能造成突发流量(令牌桶非常满的时候瞬间来了好多流量都拿到了令牌去请求后端的服务,但是不满的时候是可以控制速度的),而漏桶算法最终请求后台的一定是匀速,这是区别,但是最终两个都能实现一段时间内的请求数量限制 。在开放api优化中,用redis来做多机器的统一限速,请求一次上报一次,请求成功后上报一次,改为限流后上报一次,因为限流的毕竟是少数。

英文网站的帮助文档说得很清楚了,如果要多个消费者,只有一个来处理一个事件,则给每个消费者线程一个序列号,当消费时,判断是否是自己的菜即可。

private final long ordinal; //当前消费线程的编号,需要指定

private final long numberOfConsumers; //总共多少个消费者

if ((sequence % numberOfConsumers) == ordinal) {

//这是我的菜,开始业务处理

提前分配好就会减少锁竞争

Git

git reset是直接删除指定的commit,把HEAD向后移动了一下

git revert是一次新的特殊的commit,HEAD继续前进,本质和普通add commit一样,仅仅是commit内容很特殊:提交的内容是与前面普通commit文本变化的反操作

储藏(Stashing): 经常有这样的事情发生,当你正在进行项目中某一部分的工作,里面的东西处于一个比较杂乱的状态,而你想转到其他分支上进行一些工作。问题是,你不想提交进行了一半的工作,否则以后你无法回到这个工作点。解决这个问题的办法就是git stash命令。储藏可以获取你工作目录的中间状态——也就是你修改过的被追踪的文件和暂存的变更——并将它保存到个未完结变更的堆栈中,随时可以重新应用。

• Redis分布式缓存问题:缓存尽量要做到与存储层同步(分布式要考虑更新失败的问题) ;命中的才叫缓存,没命中的叫浪费空间。

缓存穿透:查询一个不存在的数据,而从存储层查不到数据是不写入缓存,这将导致每次请求都要到存储层去查询。-->解决> 缓存空对象(过期时间很短);布隆过滤器拦截。

缓存雪崩:我们设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到DB,DB瞬时压力过重雪崩。-->>可以在原有的失效时间基础上增加一个随机值。

缓存击穿:缓存在某个时间点过期的时候,恰好在这个时间点对这个热点的Key有大量的并发请求过来。-->>互斥锁(下面)或永不过期  

https://blog.csdn.net/zeb_perfect/article/details/54135506 public String get(key) {   String value = redis.get(key);                              if (value == null) { //代表缓存值过期--> 大量线程都到了这,只让一个线程去请求数据库 if (redis.setnx(key_mutex, 1, 3 * 60) == 1) { value = db.get(key); redis.set(key, value, expire_secs); redis.del(key_mutex); } else { //设置3min的超时,防止他的del操作失败时,下次缓存过期一直不能load db sleep(50); get(key);  }          } else { //redis.setnx:SET if Not eXists,也就是只有不存在的时候才设置,实现锁 return value; }                 }

布隆过滤器 是一个m位的位数组,插入时每个元素经过k个hash函数生成k个值并将对应的k个比特位置1。首先每个元素随机,所以等概率地hash到m个比特位上。在插入一个元素时,一个特定比特位没有被某个哈希函数置为1的概率是:1- 1/m 。插入一个元素后,这个比特没有被任意哈希函数置为1的概率是:(1- 1/m)^k。在插入了n个元素后,这个特定比特仍然为0的概率是:(1- 1/m)^kn。这个比特被置为1的概率是:1-(1- 1/m)^kn。现在检测一个不在集合里的元素,但是这k个位置都为1(误判)的概率是:[ 1-(1- 1/m)^kn ]^k。对于给定的m和n,让“误报率”最小的k值为(最优解):k= ln2 * m/n。

缓存策略:在项目发布前期,缓存命中率较低时,就直接查DB,一段时间再切换到查询Cache,这样能够增加缓存的命中率;设置批量缓存开关,当更新请求太多就直接走数据库吧;① A更新数据库->B更新数据库->B更新缓存->A更新缓存 ② A更新缓存->B更新缓存->B更新数据库->A更新数据库 最后两者数据最终都不一致。③先删缓存再更新数据库,如果A线程删除缓存,然后B线程读不到缓存然后读数据库旧数据写到缓存,然后A线程再写新数据到数据库。所以缓存采用弱一致性策略(最终一致性),先更新数据库,然后删除缓存,不回去更新缓存。这个的主要问题是:1. 读的延时有时会很大 2. 如果删除缓存失败会有脏数据( 删除失败后重试或者是可以每一个缓存加一个过期时间)3. 还有一个并发问题,A没读到缓存从库里取旧数据,B此时又更新了数据,最后A写旧数据到缓存,GG,不过几率很小,读缓存时缓存失效而且并发着有一个写操作。 数据库灰度为0则不走缓存,灰度为100则全部走缓存,因为发布初期缓存里面没有什么值,读缓存读不到什么东西,还不如直接走数据库,发布一段时间后才从0到100 ;

一致性Hash算法:将机器hash到圆上,存数据时计算出数据的hash打到圆上,顺时针走,遇到的第一个机器就把数据存进去。如果此机器挂掉,只影响此机器的下一台。

•  Dubbo源码:抽任何远程调用过程都可以看成是消费者去消费生产者的产品,但请求量过大就会产生产品爆满的情况。Dubbo能与Zookeeper做到集群部署,当提供者出现断电等异常停机时,Zookeeper注册中心能自动删除提供者信息,当提供者重启时,能自动恢复注册数据,以及订阅请求。

分析源代码,基本原理如下:一个线程调用远程接口,生成一个唯一的ID(使用AtomicLong从0开始累计数的)。将打包的方法调用信息(如调用的接口名称,方法名称,参数值列表等),和处理结果的回调对象callback,全部封装在一起,组成一个对象object。向专门存放调用信息的全局ConcurrentHashMap里面put(ID, object)。将ID和打包的方法调用信息封装成一对象connRequest,使用IoSession.write(connRequest)异步发送出去。当前线程再使用callback的get()方法试图获取远程返回的结果,在get()内部,则使用synchronized获取回调对象callback的锁,再先检测是否已经获取到结果,如果没有,然后调用callback的wait()方法,释放callback上的锁,让当前线程处于等待状态。服务端接收到请求并处理后,将结果(此结果中包含了前面的ID,即回传)发送给客户端,客户端socket连接上专门监听消息的线程收到消息,分析结果,取到ID,再从前面的Concurrent HashMap里面get(ID),从而找到callback,将方法调用结果设置到callback对象里。监听线程接着使用synchronized获取回调对象callback的锁(因为前面调用过wait(),那个线程已释放callback的锁了),再notifyAll(),唤醒前面处于等待状态的线程继续执行(callback的get()方法继续执行就能拿到调用结果了),至此,整个过程结束。

scf创建代理过程

多个线程池充分利用多核,这多个线程池每个都在循环的取task队列,而多个task队列减轻多个线程池取task产生的锁的竞争。

dubbo 相关:天池中间件大赛dubboMesh优化总结(qps从1000到6850) - 云+社区 - 腾讯云

RabbitMQ:消息队列,比如订单系统(处理速度快)和库存系统(处理速度慢)通过消息队列交互,就算库存系统出现故障,消息队列也能保证消息的可靠投递,不会导致消息丢失,解耦。在一个流量削峰,超过此一定阀值的订单直接丢弃,防止系统挂掉。本身就是一个生产者消费者模型,为了解决速度不匹配问题。

• Kafka的顺序保证。Kafka保证同一个partition中的消息是有序的,即如果生产者按照一定的顺序发送消息,broker就会按照这个顺序把他们写入partition,消费者也会按照相同的顺序读取他们。注意!!!flush是一个堵塞操作

无环生产着消费者队列 : 英文网站的帮助文档说得很清楚了,如果要多个消费者,只有一个来处理一个事件,则给每个消费者线程一个序列号,当消费时,判断是否是自己的菜即可。 

private final long ordinal; //当前消费线程的编号,需要指定  private final long numberOfConsumers; //总共多少个消费者  if ((sequence % numberOfConsumers) == ordinal) { //这是我的菜,开始业务处理 }

• Bigtable(Hbase,LevelDB,RockDB)原理:分布式内存NoSql的key-value数据库,主要用LSM树,其中分不同的层,每层Key有序,最上层的C0层在内存保存最近写入的数据,下面n层也是key有序但是在磁盘,如果C0层增大到某一阈值将会和C1层合并(归并排序)顺序写磁盘,删除掉老的C1层。C1层满了再和C2层合并替换C2层依次类推。查询时从最新的C0层依次往下查询,有就返回(老数据都在下层,不怕),每层有布隆。适用读少写多。参考1 参考2

 GFS(HDFS)原理: 分布式文件系统,文件本身的属性(即元数据NameNode)与文件所持有的数据(DataNode)分离;由于hdfs数据分布在不同机器上,要让网络的消耗最低,并提高系统的吞吐量,最佳方式是将运算的执行移到离它要处理的数据更近的地方,而不是移动数据

• MapReduce原理:假设我们有一个巨大的数据集,里面有海量规模的元素,元素的个数为M,每个元素都需要进行同一个函数处理。于是Master将M分成许多小份,然后每一份分给一个Mapper来做,Mapper干完活儿(执行完函数),将自己那一份儿活儿的结果传给Reducer。Reducer之后统计汇总各个Mapper传过来的结果,得到最后的任务的答案。Google三大论文 alex翻译 。而Spark(hadoop运算主要基于磁盘,而spark运算主要基于内存)核心的概念是 Resilient Distributed Dataset (RDD):一个可并行操作的有容错机制的数据集合。Spark中val data = Array(1, 2, 3, 4, 5)  val distData = sc.parallelize(data)  我们可以调用 distData.reduce((a, b) => a + b)将这个数组中的元素相加,并且是并行执行。

• 分布式id生成算法SnowFlake 时间戳+工作机器id+序列号

elasticsearch:对中文支持分词 , 原理

ES_Java_OPTS="-Xms256m -Xmx256m" ./bin/elasticsearch -d(后台) SearchResponse response = client.prepareSearch("articles").setTypes("item")                .setQuery(queryBuilder)//queryBuilder可以.should或者.must或.boost                .setFrom(0).setSize(2)//分页      .execute().actionGet(); queryBuilder有term(全匹配)和match(会分词)

Elasticsearch ⇒ 索引 ⇒ 类型 ⇒ 文档 ⇒ 字段(Fields),Elasticsearch分别为每个field都建立一个倒排索引

Elasticsearch的索引思路:将磁盘里的东西尽量搬进内存,减少磁盘随机读取次数(同时也利用磁盘顺序读特性),结合各种奇技淫巧的压缩算法,用及其苛刻的态度使用内存。所以,对于使用Elasticsearch进行索引时需要注意:不需要索引的字段,一定要明确定义出来,因为默认是自动建索引的。对于String类型的字段,不需要analysis的也需要明确定义出来,因为默认也是会analysis的选择有规律的ID很重要,随机性太大的ID(比如Java的UUID)不利于查询。

RPC框架 服务端分析:

 getBean(A) 发现A持有 B,则getBean(B),发现B又持有A,就getBean(A),不断循环下去;打破这个这个问题在于,getBean(A)的时候把当前未设置属性的A放到map里提前暴露,getBean(B)发现map中有有办成品的A也就直接持有了,打破循环;

Redis分布式锁的租约续租实现 - 知乎

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值