【Dubbo】Dubbo 源码分析

Dubbo 框架设计

image-20210826221319345

该图描述了服务注册中心、服务提供方、服务消费方、服务监控中心之间的调用关系:

  • 服务提供者(Provider):暴露服务的服务提供方。服务提供者在启动时完成以下步骤进行服务暴露
    • 本地暴露:创建Netty服务端NettyServer,启动并监听配置文件中指定的Dubbo服务端口20880,等待消费者客户端发送远程调用当前服务的请求;
    • 注册服务信息到本地缓存:将服务信息注册到提供者注册表(本地缓存Map)中;
    • 注册服务信息到注册中心:向注册中心注册自己提供的服务信息,例如在ZooKeeper的Dubbo节点下创建提供的服务节点,该节点包含该服务的接口名、Provider服务端(通常为NettyServer)的URL等信息;服务信息将被保存成Map结构:服务名 : List<URL>
  • 服务消费者(Consumer): 调用远程服务的服务消费方。服务消费者在启动时,完成以下步骤进行服务引用
    • 获取注册中心地址并据此创建ZooKeeper注册中心类registry
    • 根据服务名称从注册中心registry订阅服务的URL列表 List<URL>(只订阅当前工程引用的服务,其他服务不订阅);
    • 获取到服务URL信息后,创建NettyClient客户端与其进行通讯,并创建invoker(包含了远程服务的信息,后续使用其进行远程服务调用);
    • invoker保存到消费者的本地缓存Map中,这样即使ZooKeeper宕机本地工程也能使用缓存中的invoker调用远程服务。
  • 服务消费者在进行服务调用时将进行以下步骤:
    • 使用服务的代理对象调用方法时将逐层调用其内嵌套的多个不同功能的invoker,基于软负载均衡算法,从服务URL列表中选择其中的一台提供者进行远程调用;
    • 多层invoker调用后将创建出Netty客户端NettyClient与服务端NettyServer进行通讯;
    • 将要调用的接口名、方法名和方法参数等信息经过编码序列化后发送给NettyServer,服务端收到该请求后创建目标服务对应的代理对象执行服务方法,待其执行完毕后再将方法的返回结果发送给客户端;
    • 远程调用原理:选择其中的某一个URL作为目标服务端NettyServer,创建NettyClient与其进行通讯,将要执行的服务接口名、方法名、方法参数类型列表与方法值列表发送给NettyServer,令其创建代理对象调用该方法,从而实现远程调用目标方法。如果选中的服务器调用失败,再选另一台调用。
  • 注册中心(Registry)
    • 注册中心负责保存服务提供者发来的服务信息,同时在消费者发来订阅请求时返回服务提供者地址列表(包含ip/port等信息)给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
    • 当注册中心检测到有Dubbo提供者宕机时,将从节点中删除该提供者信息(超时检测机制),同时通知所有消费者节点信息发生改变,修改消费者本地缓存中的服务数据(使用ZooKeeper里的监听机制,在消费者从注册中心订阅时就绑定监听了注册中心服务节点信息改变事件)
  • 监控中心(Monitor):服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
  • 框架容器(Container):Dubbo容器(Spring容器)。

图中线条代表含义:

  • 紫色虚线代表Dubbo容器启动时执行的步骤,先后为:
    • 0.start:启动Dubbo容器
    • 1.register:服务提供者在注册中心内注册信息(服务端的ip地址和端口号等信息)
    • 2.subscribe:服务消费者向注册中心订阅所有服务提供者的信息
  • 蓝色虚线代表异步执行,当注册中心发现服务提供者发生改变时,会通知服务消费者该变化。服务提供者和服务消费者会定期向监控中心发送数据;
  • 蓝色实线代表服务消费者同步执行服务提供者的方法。

image-20210827133552495

https://dubbo.apache.org/zh/docsv2.7/dev/design/

Dubbo 整体设计图:

/dev-guide/images/dubbo-framework.jpg

阅读全文

【Dubbo】Dubbo

image-20210826221319345

分布式简介

https://blog.csdn.net/qq_41157588/article/details/106737191

分布式系统是若干独立计算机的集合,这些计算机对于用户来说就像单个相关系统。老式系统(单一应用架构)就是把一个系统,统一放到一个服务器当中。如果说要更新代码的话,每一个服务器上的系统都要重新去部署十分的麻烦。

而分布式系统就是将一个完整的系统拆分成多个不同的服务,然后在将每一个服务单独的放到一个服务器当中

应用架构及发展演变

img

阅读全文

【Nginx】Nginx

nginx

Nginx 简介

Nginx 1.8.0 下载:https://nginx.org/ ,在Linux上安装见 Linux 开发环境配置文档

Nginx 是高性能的 HTTP 和反向代理的服务器,处理高并发能力是十分强大的,能经受高负载的考验,有报告表明能支持高达 50,000 个并发连接数。

Nginx 能用来:

  • 反向代理
  • 负载均衡
  • 动静分离

Nginx 常用命令

  1. 进入 Nginx 的目录 cd /usr/local/nginx/sbin
  2. 查看 Nginx 版本号:
1
./nginx -v
  1. 启动 Nginx(注意在防火墙开放相应的端口号80):
1
./nginx
  1. 停止 Nginx
1
./nginx -s stop
  1. 热部署重新加载 Nginx(修改配置文件后需要重新加载)
1
./nginx -s reload
阅读全文

【Linux】Linux 常用命令

Linux 和 Unix

https://www.zhihu.com/question/24217234/answer/360949879

Unix于1969年由贝尔实验室开发出来,使用至今已变更了很多个版本。目前主流的Unix系统有三种,分别是AIX、HP-UX、Solaris,这些Unix系统互不兼容。

Linux于1991年由芬兰大学生Linus开发出来,是一个类Unix系统,但是其代码不源自任何Unix版本,完全不是Unix的一个分支,而是一个开源版的模仿。

安装Linux虚拟机教程:https://blog.csdn.net/qq_41571900/article/details/84728480; https://mp.weixin.qq.com/s/onVwwEQ1DAwbvK7qS2YNxg

Linux 开发环境配置文档

Linux 目录结构

linux的文件系统是采用级层式的树状目录结构,在此结构中的最上层是根目录**“ / ”**,然后在此目录下再创建其他的目录。在Linux世界里,一切皆文件。

image-20210721140508417

  • /bin (/usr/bin、/usr/local/bin):是Binary的缩写,这个目录存放着最经常使用的命令
  • /sbin (/usr/sbin、/usr/local/sbin):Super User Binary的缩写,这里存放的是系统管理员使用的系统管理程序
  • /home:存放普通用户的主目录,在Linux中每个用户都有一个自己的目录,一般该目录名是以用户的账号命名的
  • /root系统管理员目录,也称作超级权限者的用户主目录
  • /lib:系统开机所需要最基本的动态连接共享库 .so,其作用类似于Windows里的DLL文件。几乎所有的应用程序都需要用到这些共享库
  • /lost+found:这个目录一般情况下是空的,当系统非法关机后,这里就存放了一些文件
  • /etc:所有的系统管理所需要的配置文件和子目录 my.conf
  • /usr:Unix System Resource的缩写,存放Unix系统的资源。这是一个非常重要的目录,用户的很多应用程序和文件都放在这个目录下,类似于windows下的program files目录
  • /boot:存放的是启动Linux时使用的一些核心文件,包括一些连接文件以及镜像文件
  • /proc:这个目录是一个虚拟的目录,它是系统内存的映射,访问这个目录来获取系统信息
  • /srv:service缩写,该目录存放一些服务启动之后需要提取的数据
  • /sys:这是linux2.6内核的一个很大的变化。该目录下安装了2.6内核中新出现的一个文件系统
  • /tmp:这个目录是用来存放一些临时文件的
  • /dev:类似于windows的设备管理器,把所有的硬件用文件的形式存储
  • /media:linux系统会自动识别一些设备,例如U盘、光驱等等,当识别后,linux会把识别的设备挂载到这个目录下。
  • /mnt:系统提供该目录是为了让用户临时挂载别的文件系统的,我们可以将外部的存储挂载在/mnt/上,然后进入该目录就可以查看里的内容了。比如共享文件夹 D:/myshare
  • /opt:这是存放应用程序压缩包的目录。如安装ORACLE数据库的压缩包就可放到该目录下。默认为空。
  • /usr/local:主要存放那些手动安装的软件的目录。一般是通过编译源码或解压压缩包的方式安装的程序。例如安装JDK环境。
  • /var:这个目录中存放着在不断扩充着的东西,习惯将经常被修改的文件和目录放在这个目录下。包括各种日志文件。
  • /selinux [security-enhanced linux]:SELinux是一种安全子系统,它能控制程序只能访问特定文件。
阅读全文

【Spring MVC】Spring MVC 源码分析

Spring MVC 执行流程

Spring MVC 原理概述:在Tomcat中注册配置了一个DispatcherServlet,该Servlet的默认请求映射路径为 "/",当客户端收到符合该路径的请求时,将执行该Servlet的doService()方法,其都会执行一个doDispatch()方法,在其内进行请求映射、参数解析、返回值处理、内容协商、页面解析跳转等操作。

image-20220312203816076

可以将整个流程分成三个阶段:

  • 准备阶段
  • 匹配阶段
  • 执行阶段

准备阶段

  1. 在 Web 容器第一次用到 DispatcherServlet 的时候,会创建其对象并执行 init 方法

  2. init 方法内会创建 Spring Web 容器,并调用容器 refresh 方法

  3. refresh 过程中会创建并初始化 SpringMVC 中的重要组件, 例如 MultipartResolver,HandlerMapping,HandlerAdapter,HandlerExceptionResolver、ViewResolver 等

  4. 容器初始化后,会将上一步初始化好的重要组件,赋值给 DispatcherServlet 的成员变量,留待后用

image-20210903140657163

匹配阶段

  1. 用户发送的请求统一到达前端控制器 DispatcherServlet

  2. DispatcherServlet 遍历所有 HandlerMapping ,找到与路径匹配的处理器

    ① HandlerMapping 有多个,每个 HandlerMapping 会返回不同的处理器对象,谁先匹配,返回谁的处理器。其中能识别 @RequestMapping 的优先级最高

    ② 对应 @RequestMapping 的处理器是 HandlerMethod,它包含了控制器对象和控制器方法信息

    ③ 其中路径与处理器的映射关系在 HandlerMapping 初始化时就会建立好

image-20210903141017502
  1. 将 HandlerMethod 连同匹配到的拦截器,生成调用链对象 HandlerExecutionChain 返回
image-20210903141124911
  1. 遍历HandlerAdapter 处理器适配器,找到能处理 HandlerMethod 的适配器对象,开始调用
image-20210903141204799

调用阶段

  1. 执行拦截器 preHandle
image-20210903141445870
  1. 由 HandlerAdapter 调用 HandlerMethod

    ① 调用前处理不同类型的参数

    ② 调用后处理不同类型的返回值

image-20210903141658199
  1. 第 2 步没有异常

    ① 返回 ModelAndView

    ② 执行拦截器 postHandle 方法

    ③ 解析视图,得到 View 对象,进行视图渲染

image-20210903141749830
  1. 第 2 步有异常,进入 HandlerExceptionResolver 异常处理流程
image-20210903141844185
  1. 最后都会执行拦截器的 afterCompletion 方法
  2. 如果控制器方法标注了 @ResponseBody 注解,则在第 2 步,就会生成 json 结果,并标记 ModelAndView 已处理,这样就不会执行第 3 步的视图渲染
阅读全文

【Spring】Spring5 事务源码分析

Spring声明式事务原理

@EnableTransactionManagement 注解向容器中添加AutoProxyRegistrarProxyTransactionManagementConfiguration组件,二者作用分别为:

  • AutoProxyRegistrar:类似于AOP中的AspectJAutoProxyRegistrar,用于向容器中注册InfrastructureAdvisorAutoProxyCreator组件(类似于AOP里的自动代理器,一种后置处理器)来为普通组件进行代理包装,创建代理对象
  • ProxyTransactionManagementConfiguration:用于注册事务增强器,该增强器内设置有事务拦截器,将在代理对象执行目标方法时进行拦截,并调用其invoke()方法,由事务管理器控制事务的提交与回滚

Spring事务原理与AOP原理十分相似,都包含有后置处理器拦截器思想,在组件创建后包装出代理对象、在代理对象执行目标方法时进行拦截,使用事务管理器控制事务的提交与回滚。

阅读全文

【Spring Boot】Spring Boot2

Spring Boot 简介

Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run” —— 能快速创建出生产级别的Spring应用

Spring Boot 官方

Spring Boot 官方手册

Spring Boot 优点

  • Create stand-alone Spring applications:创建独立Spring应用
  • Embed Tomcat, Jetty or Undertow directly (no need to deploy WAR files):内嵌web服务器
  • Provide opinionated ‘starter’ dependencies to simplify your build configuration:自动starter依赖,简化构建配置
  • Automatically configure Spring and 3rd party libraries whenever possible:自动配置Spring以及第三方功能
  • Provide production-ready features such as metrics, health checks, and externalized configuration:提供生产级别的监控、健康检查及外部化配置
  • Absolutely no code generation and no requirement for XML configuration:无代码生成、无需编写XML
  • Spring Boot是整合Spring技术栈的一站式框架
  • Spring Boot是简化Spring技术栈的快速开发脚手架
阅读全文

【Memo】常见问题汇总

IDEA设置

项目字符编码设置

新建项目后,首先设置项目的字符编码为UTF-8:

image-20210809212949065

设置 Tab 为四个空格

https://www.cnblogs.com/pcheng/p/12567734.html

阿里巴巴 Java 规范手册规定,使用空格而不是 Tab,原因:

  • 在不同的编辑器里Tab的长度可能会不一致。这会导致有Tab的代码,用不同的编辑器打开时,格式可能会乱。
  • 代码压缩时,空格会有更好的压缩率。这里面是信息量的问题,使用了Tab的代码,仍然会有空格,比如代码注释、运算符之间的间隔等等,但使用了空格的代码,是可以没有Tab的。Tab也是一个字符,这就决定了,用Tab的代码虽然不压缩的时候更小,但熵更高,因此压缩率会较差,压缩之后反而更大。

image-20211114185155293

开启支持注解

image-20210809213138786

设置Java版本

image-20210809213305683

将项目的构建和运行操作交给Maven

在不勾选的情况下对项目的构建和运行是 intelliJ idea 去做的,就可能导致构建和运行时无法找到maven仓库中的相关jar包,勾选后在对项目进行构建和运行等操作直接交给了maven。

image-20210810155531212

配置文件

https://blog.csdn.net/CoderBruis/article/details/80721841

IDEA 的properties文件中默认是不会将中文转为ASCII码的。只需要在File->Settings->File Encodings里面设置编码的自动转变即可,如图操作:

img

【Spring】Spring5 AOP 源码分析

AOP原理

本质原理:为每个需要被增强的组件(带有切入点的组件)创建一个代理对象,在需要执行切入点方法时,调用代理对象的相应方法,并在其中按顺序切入通知方法。

AOP使用 @EnableAspectJAutoProxy注解通过@Import的方式向容器中添加一个AnnotationAwareAspectJAutoProxyCreator组件,该组件本质上是一个后置处理器,它会随着其他后置处理器一起注册到容器中(在普通bean注册之前)。在普通bean实例化前(此时还未实例化,普通的后置处理器在组件调用构造器之后执行),调用该组件的applyBeanPostProcessorsBeforeInstantiation()方法把带有Aspect相关的切面类添加到增强器集合中,在bean实例化后(此时才会执行普通的后置处理器内的方法),再调用applyBeanPostProcessorsAfterInstantiation()方法为带有切入点的组件(待增强的组件)创建一个代理对象(其内含有相应的增强器)。之后在执行该组件代理对象的切入点方法时,则使用CglibAopProxy.intercept()拦截该方法的执行,并在其中获取满足当前方法的所有拦截器链(由增强器包装而成),使用拦截器链的链式机制按顺序执行通知方法与业务代码。

AOP中两个非常重要的组件:后置处理器拦截器(增强器)

  • 后置处理器:拦截组件的创建,为其创建代理对象
  • 拦截器(增强器):在代理对象执行目标方法时进行拦截,切入相应的通知方法

AOP简易执行流程

注册阶段:

  • 配置类开启 @EnableAspectJAutoProxy 注解。
  • @EnableAspectJAutoProxy注解会注册 AnnotationAwareAspectJAutoProxyCreator 组件(通过@Import(AspectJAutoProxyRegistrar.class)的方式)。 AnnotationAwareAspectJAutoProxyCreator本质上是一个后置处理器(实现了相应接口)
  • 容器刷新( refresh() 方法栈中):
    • registerBeanPostProcessors():创建并注册所有的后置处理器,此时创建了AnnotationAwareAspectJAutoProxyCreator组件,在创建时会执行invokeAwareMethods()方法,回调地执行其实现的setBeanFactory()方法以获取容器中的BeanFactory并创建BeanFactoryAspectJAdvisorsBuilderAdapter对象。
    • finishBeanFactoryInitialization():创建并注册其他普通单实例组件(非后置处理器)。AnnotationAwareAspectJAutoProxyCreator拦截每个组件的创建。Before(在组件调用构造器实例化前调用:若拦截到的组件是切面类,则加入到增强器(advisor)集合中(之后被包装为拦截器);After(在组件实例化完毕后调用,此时其他普通后置处理器已经完成了相应操作):若拦截到的组件需要被增强(满足切入点表达式的条件),则为该组件创建一个代理对象(其内含有相应的增强器),之后执行业务组件的被增强的方法时就会执行该代理对象的相应方法。

执行阶段:

  • 代理对象准备执行目标方法(带有切入点的目标方法)时, CglibAopProxy.intercept() 会进行拦截,加入增强的通知方法:
    • 先遍历找到有效(符合目标方法)拦截器链chain(之前的增强器包装成拦截器)
    • 利用拦截器的链式机制依次进入每一个拦截器进行执行(每个拦截器都会执行相应的通知方法)
    • 通过不断回调CglibMethodInvocation.proceed()方法链式地调用下一个拦截器
    • 执行效果:前置通知 -> 目标方法 -> 正常返回通知或异常返回通知 -> 后置通知
  • 代理对象执行普通方法(不带有切入点的目标方法)时, CglibAopProxy.intercept() 进行拦截后,因找不到该方法匹配的增强器(因为普通方法没有被切入增强),则直接执行代理对象的普通方法。
  • 没有被增强的普通对象不被拦截。
阅读全文

【Spring】Spring5 源码中常用接口的底层原理

本文将介绍Spring源码中较为常用的几个接口的底层实现原理:

  • ImportBeanDefinitionRegistrar
  • BeanPostProcessor
  • BeanFactoryPostProcessor
  • BeanDefinitionRegistryPostProcessor
  • Aware

ImportBeanDefinitionRegistrar

ImportBeanDefinitionRegistrar接口的实现类用在@Import注解中,用于向IoC容器中自定义注册组件

1
2
3
@Import({MyImportBeanDefinitionRegistrar.class})
public class SpringConfig {
}

@Import 注解中可以传入ImportBeanDefinitionRegistrar接口的实现类。

image-20210628212948066

阅读全文