Redis补充

Redis补充 1 Redis网络模型 Redis的核心业务部分(命令处理)是单线程,但是整个Redis是多线程的。 v4.0:引入多线程异步处理一些耗时较长的任务,例如异步删除命令unlink。 v6.0:在核心网络模型中引入多线程,进一步提高对于多核CPU的利用率,但是核心部分依然是单线程。 为什么选择单线程? 性能瓶颈是网络延迟,不是执行速度,多线程并不会带来巨大性能提升。 多线程会导致过多的上下文切换,带来不必要的开销。 引入多线程可能会面临线程安全问题,必须引入线程锁这样的安全手段,实现复杂度增高,性能也大打折扣。 原理篇-27.Redis网络模型-Redis单线程及多线程网络模型变更_哔哩哔哩_bilibili 2 Redis通信协议 RESP协议 原理篇-28.Redis通信协议-RESP协议_哔哩哔哩_bilibili 3 Redis事务 Redis事务的三个阶段 MULTI %事务开始 ... %命令入队 EXEC %事务执行 Redis事务特点 redis不支持回滚,事务失败,继续执行余下的命令 事物内部命令错误,所有命令都不会执行 事物内部出现运行错误,正确的命令会被执行 Redis事务没有原子性,持久性仅在开启AOF的always模式下支持。 Redis事务总是有隔离性(单线程)和一致性。 Redis事务相关命令 WATCH:乐观锁,给事务提供CAS机制,可以监控一个或者多个键,一旦其中有一个被修改,之后的事务就不会执行,监控一直持续到EXEC或者UNWATCH。 MULTI:用于开启事务,开启后可以继续送入命令,当EXEC被调用时,才被执行。 EXEC:执行事务块内所有命令,返回所有命令的返回值,按照命令先后排序。 DISCARD:清空事务队列,放弃执行事务。 UNWATCH:取消watch对所有key的监控。 4 缓存和数据库的一致性 设置有效期:给缓存设置有效期,到期自动删除,再次查询时更新 优势:简单方便 缺点:时效性差,缓存过期之前可能不一致 场景:更新频率较低,时效性要求较低的业务 同步双写:在修改数据库的同时,直接修改缓存 优势:时效性强,缓存与数据库强一致 缺点:有代码侵入,耦合度高 场景:对一致性,时效性要求较高的缓存数据 异步通知:在修改数据库时发送事件通知,相关服务监听到通知后修改缓存数据 优势:低耦合,可以同时通知多个缓存服务 缺点:时效性一般,可能存在中间不一致状态 场景:时效性要求一般,有多个服务需要同步 基于MQ的异步通知,对业务代码仍然有一定侵入性 基于Cannal的异步通知,可以做到几乎0侵入 4.1 缓存更新策略 删除缓存而不是更新缓存 先删数据,后删缓存 4.2 缓存不一致处理 使用消息队列,把要删除和删除失败的key放入消息队列,利用重试机制,删除对应的key 对代码有侵入性 数据库订阅+消息队列保证key被删除 利用canal或其他服务监听binlog 复杂度提升 延时双删防止脏数据 延迟时间需要具体的考量和测试 设置过期时间兜底 5 热key重建 使用互斥锁,保证只有一个线程重建,其他线程等待该线程重建完后,获取缓存数据即可。 不显示设置过期时间,而是设置逻辑过期字段,发现逻辑过期后,采用单独的线程构建缓存。 6 大key问题 单个简单key存储的value过大,hash、set、zset、list中存储过多元素。 ...

September 26, 2024 · 1 min · 166 words · RLTEA

TCP

1 TCP基础 1.1 头部字段 序列号:建立时生成随机数,后续每发送一次数据加一,用于解决网络包乱序的问题 确认应答号:表示下一次「期待」收到的序列号,发送端收到这个确认应答后认为在这个序列号之前的数据都被正常接受,用来解决丢包问题 控制位: ACK:为1表示「确认应答」字段有效,除SYN包之外该位必须设置为1 RST:为1表示TCP连接异常,强制断开 SYN:为1时表示希望建立连接,并初始化「序列号」 FIN:为1时表示之后不会再有数据发送,表示希望断开连接 1.2 TCP的特点 面向连接:一对一建立连接,通过「源地址,源端口,目的地址,目的端口」确定唯一连接 可靠:TCP协议尽自己最大努力保证报文一定能够到达接收端 字节流:通过TCP传输的数据,可能会被操作系统拆分为多个TCP报文,如果接收方不知道「消息边界」就无法正确读取消息。并且 TCP 报文是「有序的」,当「前一个」TCP 报文没有收到的时候,即使它先收到了后面的 TCP 报文,那么也不能扔给应用层去处理,同时对「重复」的 TCP 报文会自动丢弃。 2 TCP三次握手 2.1 三次握手如何建立 首先,客户端和服务器都处于close状态,然后服务器主动监听某个端口,处于Listen状态。 客户端随机初始化序号,同时把SYN标识为设为1,表示SYN报文,把SYN送给服务端后,变换为SYN-SENT状态。 服务端收到客户端的SYN报文后,随机初始化自己的序号,同时把确认应答号设置为客户端初始序号+1,然后把SYN和ACK标识为设置为1,最后送给客户端,变换为SYN-RCVD状态。 客户端收到服务端报文后,向服务器回应最后一个应答报文,将ACK标识为设置为1,确认应答号填入服务器初始序号+1,这次报文可以携带数据,然后送给服务端,状态变化为ESTABLISHED。 服务器收到应答报文后,状态也设置为ESTABLISHED。 2.2 如何在Linux中查看TCP状态 netstat -napt 2.3 为什么是三次握手?而不是两次、四次? 三次的原因: 三次可以阻止重复历史连接的初始化: 两次握手对于新旧TCP连接请提无法避免初始化步骤。 三次可以同步双方的初始序列号:可以保证双方初始的序列号被可靠的同步给对方,四次握手没有必要,服务器确认和初始化自己的两步可以合并为一步。 三次可以避免浪费资源:两次握手由于服务器没有中间状态,无法知道客户端是否收到自己回复的ACK,所以每收到一个SYN报文就需要建立一个连接,同一个连接由于网络阻塞而重发SYN报文时,服务端会产生资源浪费。 为什么每次建立TCP连接初始化序列号都要求不同: 为了防止历史报文被下一个相同四元组接受。 为了安全性, 防止黑客伪造TCP报文。 2.4 IP层即然会分片,为什么TCP还需要MSS呢? MTU:指的是一个网络包的最大长度,以太网中一般为1500字节 MSS:除去IP和TCP头部之后,一个网络包能容纳的TCP数据的最大长度 当IP层有一个超过MTU大小的数据要发送时,IP层就要进行分片,但是当一个分片丢失时,整个IP报文就需要重传,因为IP层没有超时重传机制,所以为了达到最佳的传输效率,建立TCP连接时通常需要协商双方的MSS值,当TCP层发现报文超过MSS时,就会进行分片,保证在IP层形成的IP包长度不会大于MTU,所以也就不会分片了。 2.5 三次握手信息丢失会发生什么 第一次握手丢失 ...

September 26, 2024 · 2 min · 425 words · RLTEA

创建型模式

创建型模式 在创建对象的同时,隐藏创建逻辑,不使用new直接实例化对象,程序判断需要创建哪些对象时更加灵活。 1 单例模式 一个单例类在任何情况下只存在一个实例,构造方法私有,由自己创建一个静态变量存储实例,对外提供一个静态共有方法获取实例。 只有一个实例,避免了开销 没有抽象层,难以拓展,与单一职责原则冲突。 1.1 常见写法 1.1.1 饿汉式,线程安全 类一加载就创建对象,比较常用,但是容易产生垃圾对象。 线程安全,不加锁,执行效率高 缺点:不是懒加载,浪费内存空间 public class Singleton{ private Singleton(){} private final static Singleton instance = new Singleton(); public static Singleton getInstance(){ return instance; } } 使用反射破坏单例 public class Main{ public static void main(String[] args) throws Exception{ Constructor<Singleton> declaredConstructor = Singleton.class.getDeclaredConstructor(null); declaredConstructor.setAccessible(true); Singleton singleton = declaredConstructor.newInstance(); } } 1.1.2 懒汉式,线程不安全 public class Singleton{ private Singleton(){} private static Singleton instance; public static Singleton getInstance(){ if ( instance == null ) { instance = new Singleton(); } return instance; } } 多线程破坏单例 public class Main(){ public static void main(String[] args){ for(int i=0;i<3;i++){ new Thread(() -> { System.out.println("multi thread created singleton:" + Singleton.getInstance()); }).start() } } } 1.1.3 懒汉式,线程安全 加锁 ...

September 26, 2024 · 2 min · 254 words · RLTEA

操作系统

操作系统 1 基础概念 1.1 操作系统功能 **进程和线程管理:**进程的创建、撤销、阻塞、唤醒,进程间的通信。 **存储管理:**内存的分配和管理、外存(磁盘)的分配和管理。 **文件管理:**文件的读写、创建及删除。 **设备管理:**完成设备的请求或释放,以及设备的启动等功能。 **网络管理:**操作系统负责管理计算机网络的使用,管理网络的配置、连接、通信、安全等。 **安全管理:**用户身份认证,访问控制、文件加密等。 1.2 用户态和内核态 用户态(User Mode) : 用户态运行的进程可以直接读取用户程序的数据,拥有较低的权限。当应用程序需要执行某些需要特殊权限的操作,例如读写磁盘、网络通信等,就需要向操作系统发起系统调用请求,进入内核态。 内核态(Kernel Mode):内核态运行的进程几乎可以访问计算机的任何资源包括系统的内存空间、设备、驱动程序等,不受限制,拥有非常高的权限。当操作系统接收到进程的系统调用请求时,就会从用户态切换到内核态,执行相应的系统调用,并将结果返回给进程,最后再从内核态切换回用户态。 内核态相比用户态拥有更高的特权级别,因此能够执行更底层、更敏感的操作。不过,由于进入内核态需要付出较高的开销(需要进行一系列的上下文切换和权限检查),应该尽量减少进入内核态的次数,以提高系统的性能和稳定性。 用户态和内核态的切换: 系统调用:用户态进程主动切换 中断:当外围设备完成用户请求的操作后,会向 CPU 发出相应的中断信号,这时 CPU 会暂停执行下一条即将要执行的指令转而去执行与中断信号对应的处理程序,如果先前执行的指令是用户态下的程序,那么这个转换的过程自然也就发生了由用户态到内核态的切换。比如硬盘读写操作完成,系统会切换到硬盘读写的中断处理程序中执行后续操作等。 异常:当 CPU 在执行运行在用户态下的程序时,发生了某些事先不可知的异常,这时会触发由当前运行进程切换到处理此异常的内核相关程序中,也就转到了内核态,比如缺页异常。 在系统的处理上,中断和异常类似,都是通过中断向量表来找到相应的处理程序进行处理。区别在于,中断来自处理器外部,不是由任何一条专门的指令造成,而异常是执行当前指令的结果。 1.3 系统调用 我们运行的用户程序中,凡是与系统态级别的资源有关的操作(如文件管理、进程控制、内存管理等),都必须通过系统调用方式向操作系统提出服务请求,并由操作系统代为完成。 系统调用和普通库函数调用非常相似,只是系统调用由操作系统内核提供,运行于内核态,而普通的库函数调用由函数库或用户自己提供,运行于用户态。 系统调用过程: 用户态发起系统调用,用户态权限不足,中断执行(Trap) CPU执行的程序终端,跳转到终端处理程序,内核程序开始执行。 内核处理后,主动出发Trap,再次发生中断,切换回用户态工作。 2 进程和线程 2.1 进程 进程(Process) 是指计算机中正在运行的一个程序实例。资源分配的基本单位。 PCB是什么? PCB(Process Control Block) 即进程控制块,是操作系统中用来管理和跟踪进程的数据结构,每个进程都对应着一个独立的 PCB。你可以将 PCB 视为进程的大脑。 当操作系统创建一个新进程时,会为该进程分配一个唯一的进程 ID,并且为该进程创建一个对应的进程控制块。当进程执行时,PCB 中的信息会不断变化,操作系统会根据这些信息来管理和调度进程。 PCB 主要包含下面几部分的内容: 进程的描述信息,包括进程的名称、标识符等等; 进程的调度信息,包括进程阻塞原因、进程状态(就绪、运行、阻塞等)、进程优先级(标识进程的重要程度)等等; 进程对资源的需求情况,包括 CPU 时间、内存空间、I/O 设备等等。 进程打开的文件信息,包括文件描述符、文件类型、打开模式等等。 处理机的状态信息(由处理机的各种寄存器中的内容组成的),包括通用寄存器、指令计数器、程序状态字 PSW、用户栈指针。 进程有哪些状态?(生命周期) ...

September 26, 2024 · 3 min · 635 words · RLTEA

操作系统-面试重点问题

操作系统-面试重点问题

September 26, 2024 · 1 min · word · RLTEA

数据结构

数据结构 1 线性数据结构 1.1 数组 1.2 链表 1.3 栈 1.4 队列 2 图 2.1 基本概念 2.2 图的存储 2.3 图的搜索 2.3.1 DFS public int[] findOrder(int numCourses, int[][] prerequisites) { // adjacency list Set<Integer>[] graph = new Set[numCourses]; for (int[] e : prerequisites) { // e[0] depends on e[1] // e[1] --> e[0] if (graph[e[1]] == null) { graph[e[1]] = new HashSet<>(); } graph[e[1]].add(e[0]); } List<Integer> list = new ArrayList<>(numCourses); boolean[] globalVisited = new boolean[numCourses]; boolean[] localVisited = new boolean[numCourses]; // to check cycle for (int i = 0; i < numCourses; ++i) { if (!dfs(graph, i, globalVisited, localVisited, list)) { return new int[0]; } } // copy and reverse int[] result = new int[numCourses]; for (int i = 0; i < numCourses; ++i) { result[i] = list.get(numCourses - i - 1); } return result; } // return: can finish public boolean dfs(Set<Integer>[] graph, int node, boolean[] globalVisited, boolean[] localVisited, List<Integer> list) { if (localVisited[node]) return false; if (globalVisited[node]) return true; localVisited[node] = true; globalVisited[node] = true; Set<Integer> next = graph[node]; if (next != null) { for (Integer n : next) { if (!dfs(graph, n, globalVisited, localVisited, list)) { // return false and exit, no need to reset localVisited return false; } } } localVisited[node] = false; // reset list.add(node); return true; } 2.4 图的路径 2.4.1 弗洛伊德 class Solution { public int networkDelayTime(int[][] times, int N, int K) { // w[i][j]: time from [i] to [j], Integer.MAX_VALUE: inf int[][] w = new int[N+1][N+1]; for (int i = 1; i <= N; ++i) { Arrays.fill(w[i], Integer.MAX_VALUE); w[i][i] = 0; } for (int[] e : times) { int u = e[0], v = e[1], t = e[2]; w[u][v] = t; } for (int k = 1; k <= N; ++k) { for (int i = 1; i <= N; ++i) { for (int j = 1; j <= N; ++j) { int sum; if (w[i][k] == Integer.MAX_VALUE w[k][j] == Integer.MAX_VALUE) { sum = Integer.MAX_VALUE; } else { sum = w[i][k] + w[k][j]; } w[i][j] = Math.min(w[i][j], sum); } } } int max = -1; for (int j = 1; j <= N; ++j) { if (w[K][j] == Integer.MAX_VALUE) return -1; max = Math.max(max, w[K][j]); } return max; } } 2.4.2 Dijkstra class Solution { public int networkDelayTime(int[][] times, int N, int K) { // graph[i]: List<int[]>, [to node, w] List<int[]>[] graph = new List[N+1]; for (int i = 1; i <= N; ++i) { graph[i] = new LinkedList<>(); } for (int[] e : times) { int from = e[0], to = e[1], w = e[2]; graph[from].add(new int[]{to, w}); } // [distance, node] PriorityQueue<int[]> heap = new PriorityQueue<>((a, b) -> a[0] - b[0]); // node --> min distance HashMap<Integer, Integer> dist = new HashMap<>(); heap.offer(new int[]{0, K}); while (heap.size() > 0) { int[] n = heap.poll(); int distance = n[0]; int node = n[1]; if (dist.containsKey(node)) continue; // already determined dist.put(node, distance); // node determined for (int[] g : graph[node]) { int nextNode = g[0]; int w = g[1]; // K --> ... --> node --> nextNode if (dist.containsKey(nextNode)) continue; // alreay determined heap.offer(new int[]{distance + w, nextNode}); } } if (dist.size() != N) return -1; int max = -1; for (int d : dist.values()) { max = Math.max(max, d); } return max; } } 2.5 图的出入度问题 class Solution { public int[] findOrder(int numCourses, int[][] prerequisites) { Map<Integer, List<Integer>> graph = new HashMap<>(); int[] indegree = new int[numCourses]; for (int[] e : prerequisites) { // e[0] depends on e[1] // e[1] --> e[0] int pre = e[1], cur = e[0]; List<Integer> list = graph.get(pre); if (list == null) { list = new LinkedList<>(); graph.put(pre, list); } list.add(cur); indegree[cur]++; } Queue<Integer> queue = new LinkedList<>(); for (int i = 0; i < numCourses; ++i) { if (indegree[i] == 0) { queue.add(i); } } int[] result = new int[numCourses]; int size = 0; while (queue.size() > 0) { int node = queue.poll(); result[size++] = node; List<Integer> next = graph.get(node); if (next != null) { for (int n : next) { indegree[n]--; if (indegree[n] == 0) { queue.offer(n); } } } } if (size != numCourses) return new int[0]; return result; } } 2.6 最小生成树 2.6.1 Kruskal class Solution { public int minimumCost(int N, int[][] connections) { // sort connections by cost from small to large Arrays.sort(connections, (a,b) -> a[2]-b[2]); int[] parent = new int[N+1]; for (int i = 1; i <= N; ++i) { parent[i] = i; } int cost = 0; for (int[] edge : connections) { if (union(edge[0], edge[1], parent)) { cost += edge[2]; } } // check if all the roots are the same int p = -1; for (int i = 1; i <= N; ++i) { int root = findRoot(i, parent); if (p == -1) { p = root; } else if (p != root) { return -1; } } return cost; } public int findRoot(int x, int[] parent) { while (x != parent[x]) { parent[x] = parent[parent[x]]; x = parent[x]; } return x; } public boolean union(int a, int b, int[] parent) { a = findRoot(a, parent); b = findRoot(b, parent); if (a == b) return false; parent[a] = b; return true; } } 2.6.2 Prim class Solution { public int minimumCost(int N, int[][] connections) { int INF = Integer.MAX_VALUE; // graph[i][j]: // INF: not reachable // x: distance int[][] graph = new int[N+1][N+1]; for (int i = 1; i <= N; ++i) { for (int j = 1; j <= N; ++j) { if (i == j) graph[i][j] = 0; else graph[i][j] = INF; } } for (int[] edge : connections) { int u = edge[0], v = edge[1], w = edge[2]; graph[u][v] = graph[v][u] = w; } // dist[i] // d: current min distance from one of added nodes // INF: distance is inf, not reachable int[] dist = new int[N+1]; Arrays.fill(dist, INF); // added nodes boolean[] added = new boolean[N+1]; // set node [1] as candidates dist[1] = 0; int cost = 0; for (int k = 0; k < N; ++k) { // N nodes to add // find node with min distance int min = INF; int node = -1; for (int i = 1; i <= N; ++i) { if (!added[i] && dist[i] < min) { min = dist[i]; node = i; } } // no reachable node found if (node == -1) { return -1; } // add [node] cost += dist[node]; added[node] = true; // update dist[i] with distance from [node] to [i] for (int i = 1; i <= N; ++i) { if (added[i]) continue; if (graph[node][i] == INF) continue; dist[i] = Math.min(dist[i], graph[node][i]); } } return cost; } } 3 堆 3.1 概念 3.2 分类 3.3 存储 3.4 操作 3.5 排序 4 树 4.1 分类 4.2 存储 4.3 遍历 5 红黑树 5.1 概念与特点 红黑树是一种自平衡二叉查找树。JDK中,TreeMap、TreeSet以及HashMap都使用了红黑树。 ...

September 26, 2024 · 7 min · 1372 words · RLTEA

消息队列RabbitMQ

消息队列RabbitMQ 消息队列是一种消息中间件,不负责处理消息,仅负责消息的接受存储和转发. RabbitMQ特点: **消息传递模式:**支持多种消息传递模式,包括发布订阅、点对点和工作队列等。 **消息路由和交换机:**RabbitMQ引入了交换机(Exchange)概念,用于将消息路由到一个或者多个队列。允许根据消息内容、标签或者路由键进行灵活的消息路由。 **消息确认机制:**RabbitMQ支持消息确认机制,保证消息不会被重复消费。 **可扩展性:**RabbitMQ可以通过添加更多的节点和集群来增加吞吐量和可用性。 支持多种编程语言。 **消息持久性:**Rabbit允许消息和队列进行持久化设置,确保消息在RabbitMQ重启后不会丢失。 **灵活的插件系统:**RabbitMQ具有丰富的插件系统,可以拓展多种功能。 具有易于管理的Web界面。 为什么要使用消息队列 流量削峰:队列缓存请求 应用解耦 异步处理 1 AMQP AMQP(Advanced Message Queuing Protocol)不是一个具体的消息中间件产品,而是一个协议规范,一种为面向消息的中间件设计的应用层协议。AMQP提供了一种统一的消息服务,是的不同程序之间可以通过消息队列进行通信。SpringBoot框架默认就提供了对AMQP协议的支持。 AMQP 本质上是一个开放的标准,他不光可以被 RabbitMQ 实现,也可以被其他产品实现。通过这种标准的协议,实际上是可以在不同的消息中间件系统之间进行灵活的消息传递。只不过,目前具体实现这种标准的产品目前并不多,RabbitMQ 则是最有影响力的一个产品。因此,RabbitMQ 成了 AMQP 协议事实上的代表。SpringBoot 框架默认提供的 AMQP 协议支持底层也是基于 RabbitMQ 产品实现的。 AMQP 协议的三层: Module Layer:协议最高层,主要定义了一些客户端调用的命令,客户端可以用这些命令实现自己的业务逻辑。 Session Layer:中间层,主要负责客户端命令发送给服务器,再将服务端应答返回客户端,提供可靠性同步机制和错误处理。 TransportLayer:最底层,主要传输二进制数据流,提供帧的处理、信道复用、错误检测和数据表示等。 AMQP 模型的三大组件: 交换器 (Exchange):消息代理服务器中用于把消息路由到队列的组件。 队列 (Queue):用来存储消息的数据结构,位于硬盘或内存中。 绑定 (Binding):一套规则,告知交换器消息应该将消息投递给哪个队列。 2 RabbitMQ各组件及其功能 **Broker:**RabbitMQ服务器,接受客户端连接,实现AMQP实体服务 **Virtual Host:**虚拟主机,实现逻辑隔离,用于隔离不同环境或不同应用的消息流。每个虚拟主机都有自己的Exchange和Queue。 **Connection:**连接,管理和维护RabbitMQ服务器的TCP链接,生产者和消费者通过这个连接与Broker建立物理网络连接(RabbitMQ消息基于TCP进行传输)。 **Channel:**信道,是在Connection中创建的轻量级通道,客户端可以建立多个信道,可以减小建立TCP Connection的开销,信道数量没有限制。 **Exchange:**交换机,负责接受来自生产者的消息,并将其路由到一个或多个队列。有direct、topic、fanout、headers四种模式。 **Queue:**队列是消息的存储位置,每个队列都有唯一的名称。 **Binding:**绑定,是Exchange和Queue之间的关联规则,定义了消息如何从交换机路由到特定队列。 此外生产者和消费者也是消息队列中的核心组件,生产者负责发送消息到Exchange或者Queue,消费者负责从Queue中订阅和处理消息。 生产者:生产者是消息的发送方,负责产生并发送消息到 RabbitMQ。生产者通常将消息发送到交换机(Exchange)。 消费者:消费者是消息的接收方,负责从队列中获取消息并进行处理。消费者通过订阅队列来接收消息。 消息:消息是生产者和消费者之间传递的数据单元。消息通常包含消息体和可选的属性,如路由键等。 3 RabbitMQ中交换机类型 Direct Exchange:根据消息的路由键(Routing Key)将消息发送到与之完全匹配的队列。 ...

September 26, 2024 · 2 min · 258 words · RLTEA

结构性模式

结构性模式 通过类和接口间的继承和引用关系船舰结构复杂的对象。 1 适配器模式 当需要将两个不同接口的类进行通信时,在不修改这两个类的前提下,我们可以用中间件完成衔接过程。这个中间件就是适配器,适配器模式就是将一个类的接口,转换为客户期望的另一个接口,让原本不兼容的接口完成无缝对接。 类适配器 通过类的继承实现适配,继承Target的接口,继承Adaptee的实现 对象适配器 通过类对象的组合实现适配 target:定义Client真正需要的接口。 Adaptee:其中定义了一个已经存在的接口,也就是我们需要进行适配的接口。 Adapter:对Adaptee和Target的接口进行适配,保证对target中接口的调用可以间接转换为对Adaptee中接口的调用。 优点: 提高了类的复用 组合若干关联对象对外提供统一服务接口 扩展性、灵活性号 缺点: 过多使用适配器模式容易导致代码功能和逻辑意义混淆。 部分语言对继承的限制,可能至多只能适配一个适配者类么日期目标类必须是抽象类。 2 桥接模式 3 组合模式 4 装饰模式 5 外观模式 6 享元模式 7 代理模式 代理模式本质是一个中间件,主要目的时解耦合服务提供者和使用者。 使用者通过代理间接访问服务提供者,便于后者的封装和控制。 RealSubject:真正的目标对象 Proxy:目标对象的代理,负责控制和管理目标对象,并间接传递外部对目标对象的访问 Remote Proxy:对本地的请求以及参数进行序列化,向远程对象发送请求,并对响应结果进行反序列化,将最终结果反馈给调用者。 Virtual Proxy:当目标对象创建开销比较大时,可以使用延迟或者异步的方式创建目标对象 Protection Proxy:细化对目标对象访问权限的控制 静态代理和动态代理的区别 动态代理更加灵活,不需要必须实现接口,可以直接代理实现类,并且可以不需要整堆每个目标类都创建一个代理类。 静态代理中,一旦新增方法,目标对象和代理对象都要修改。 JVM中,静态代理在编译时就将接口、实现类、代理类编译成class文件,动态代理是在运行时动态生成字节码文件,加载到JVM中的。

September 26, 2024 · 1 min · 45 words · RLTEA

行为型模式

行为型模式 通过类之间不同的通信方式实现不同行为。 1 访问者模式 2 模板模式 3 策略模式 属于对象的行为模式,针对一组算法,将每一个算法封装到具有共同接口的独立类中,使得他们可以相互替换。 4 状态模式 5 观察者模式 Subject:抽象被观察对象,仅提供注册和删除观察者对象的接口声明。 ConcreteSubject:具体被观察对象,该对象中收集了所有需要被通知的观察者,可以动态增删集合中的观察者,当状态发生变化时,会通知所有观察者对象。 Observer:抽象观察者,为所有观察者定义获得通知的同一接口。 ConcreteObserver:观察者对象,关注对象为Subject,能接受Subject变化是发出的通知并更新自身状态 优点: 被观察者和观察者之间时抽象耦合的 耦合度较低,两者之间关联仅在于消息通知 被观察者者无需关心他的观察者 支持广播通信 缺点: 观察者只知道被观察对象发生了变化,不知道过程和原因 观察者同时可能是被观察者,消息链路可能过长 如果观察者和被观察者之间产生循环依赖,会导致无限循环 6 备忘录模式 7 中介者模式 8 迭代器模式 9 解释器模式 10 装饰器模式 对现有类对象进行包装,在不改变类对象和定义情况下,添加额外功能。 是一种对象结构型模式。 Component:对象的接口类,定义装饰对象和被装饰对象的共同接口 ConcreteComponent:被装饰对象的定义 Decorator:装饰对象的抽象类,持有一个具体被修饰的对象,并实现接口类继承的公共接口 ConcreteDecorator:具体的装饰器,负责王被装饰对象添加额外功能。 final修饰的类无法使用继承来拓展对象行为,此时可以使用装饰模式进行拓展。 11 命令模式 12 责任链模式 一个请求沿着一条链传递,直到该链上某个处理者处理它为止。 当程序需要使用不同方法处理不同种类请求时,而且请求类型和顺序预先未知时。使用责任链模式,收到请求后,按顺序询问每个处理者是否能够处理

September 26, 2024 · 1 min · 49 words · RLTEA

计算机网络

计算机网络 1 分层模型 OSI七层模型: 口诀:物联网书会使用 优点:概念结构清楚,理论完整 缺点:复杂不实用、某些功能在多个层中重复出现。 TCP/IP四层模型: 为什么要分层? 使得各层之间独立。 提高灵活性和可替换性(高内聚低耦合)。 大问题化小,复杂问题分解。 2 常见网络协议 应用层: HTTP SMTP POP3/IMAP FTP Telnet SSH RTP DNS 传输层: TCP UDP 网络层: IP ARP ICMP NAT OSFP RIP BGP 3 HTTP 3.1 从输入URL到页面展示发生了什么? 过程: 用户输入URL。 浏览器查找域名IP地址(通过DNS:浏览器缓存、路由器缓存、DNS缓存)。 浏览器根据IP地址和端口号,向目标服务器发起TCP连接请求。 浏览器与服务器建立TCP连接,并发送HTTP请求报文。 服务器收到报文进行处理请求,并返回HTTP响应报文给浏览器。 浏览器收到HTTP响应报文后,解析响应体中的内容,并进行网页渲染,同时根据HTML中其他资源URL再次发起HTTP请求,直到网页完全加载显示。 浏览器在不需要和服务器通信时,可主动关闭TCP连接,或者等待服务器关闭请求。 使用到的协议:DNS、TCP、IP、OPSF、ARP、HTTP 3.2 HTTP和HTTPS的区别 URL前缀不同(http和https) 端口号不同(80和443) 安全性,HTTPS大于HTTP 资源消耗:HTTP优于HTTPS SEO搜索引擎优化:优先显示HTTPS网页 ::HTTPS中的S如何实现?:: 3.3 HTTP/1.0和HTTP/1.1区别 1.1实现了长连接 1.1加入了大量状态码 1.1有更多的缓存控制机制 1.1支持断点续传 3.4 HTTP/1.1和HTTP/2.0区别 2.0实现了多路复用 2.0使用二进制帧传输(1.1使用文本格式报文) 2.0支持对头部压缩(1.1仅 支持对body压缩) 2.0支持服务器推送,可以减少客户端请求次数。 3.5 HTTP/2.0和HTTP/3.0区别 传输协议:3.0基于QUIC(UDP升级版),提供与TLS/SSL相当的安全性 连接建立:2.0需要经过TCP三次握手外加一个TLS安全握手,需约3个RTT;而3.0由于QUIC的特性,仅需要0个或1个RTT。 队头阻塞:2.0复用一个TCP连接,一旦包丢失,会阻塞所有HTTP请求。3.0一个连接有多个不同的数据流,互不影响。 错误恢复:3.0具有更快的恢复和重传机制,2.0需要依赖于TCP的错误恢复机制。 安全加密:2.0使用TLS协议进行加密,3.0基于QUIC协议,内置加密和身份验证机制,可以提供更强的安全性。 3.6 HTTP是不保存状态的协议,那么如何保存用户状态? Session机制:通过服务端记录用户状态,时间限制到后销毁Session ...

September 26, 2024 · 4 min · 681 words · RLTEA