C# 相关记录
程序转成Windows服务
var options = new WebApplicationOptions
{
Args = args,
ContentRootPath = WindowsServiceHelpers.IsWindowsService() ? AppContext.BaseDirectory : default
};
var builder = WebApplication.CreateBuilder(options);
webservices服务转成cs类文件:wsdl D:\hdzwservice.wsdl /out:D:\hdzwservice.cs
1.
委托与事件是什么关系?为什么要使用委托
委托提供了封装方法的方式,事件是某动作已发生的说明,事件是建立于委托之上的。
(1)程序运行时同一个委托能够用来调用不同的方法只要改变它引用的方法即可。
(2)同一个委托能够用来调用不同的方法,只要改变它引用的方法即可,因此委托调节器用的方法不是在编译时决定的,而是在运行时确定的。
2.线程:
多线程保证安全:通常涉及到同步机制,如锁、原子操作、ConcurrentCollections等
1.使用锁(Lock),ReaderWriterLockSlim适合多读少写的情况;2.使用Interlocked类提供了一组静态方法,对多个线程共享的变量进行原子操作;3.使用ConcurrentQueue或ConcurrentBag等线程安全的集合类;4.volatile关键字用于确保多个线程总是看到变量的最新值;5.异步编程模型:使用async和await关键字来处理异步操作,自然实现线程安全。
线程间通信:
线程间通信的常见方式包括使用事件(Event)、信号量(Semaphore)、锁(Lock)、 volatile 字段、线程同步的集合类(如ConcurrentQueue或ConcurrentBag)以及进度改进的Action和Func委托,互斥体(Mutex),共享内存(MemoryMappedFile),消息队列;
1.Invoke为同步委托,调用线程需要等待控件对象处理完后才可以继续执行。
BeginInvoke为异步委托,调用线程向控件对象发送了处理操作后即继续执行,无需等待。
2.SynchronizationContext类实现一个线程到另一个线程的同步上下文通信
Send(SendOrPostCallback, object)为同步方法,调用线程需等待该方法执行完毕才能继续执行。
Post(SendOrPostCallback, object)为异步方法,调用线程不需要等待执行完毕即继续执行。
Task的最大线程数通常是通过使用TaskCreationOptions.LongRunning选项来实现的,这会导致任务在专门的线程池线程上运行,而不是使用默认的任务调度器。但是,这并不直接限制最大线程数。如果你需要限制Task使用的线程数,你可以使用自定义的线程池。
3.设计模式:
1、创建型模式
对象实例化的模式,创建型模式用于解耦对象的实例化过程。
单例模式:某个类智能有一个实例,提供一个全局的访问点。
工厂模式:一个工厂类根据传入的参量决定创建出哪一种产品类的实例。
抽象工厂模式:创建相关或依赖对象的家族,而无需明确指定具体类。
建造者模式:封装一个复杂对象的创建过程,并可以按步骤构造。
原型模式:通过复制现有的实例来创建新的实例。
2、结构型模式
把类或对象结合在一起形成一个更大的结构。
装饰器模式:动态的给对象添加新的功能。
代理模式:为其它对象提供一个代理以便控制这个对象的访问。
桥接模式:将抽象部分和它的实现部分分离,使它们都可以独立的变化。
适配器模式:将一个类的方法接口转换成客户希望的另一个接口。
组合模式:将对象组合成树形结构以表示“部分-整体”的层次结构。
外观模式:对外提供一个统一的方法,来访问子系统中的一群接口。
享元模式:通过共享技术来有效的支持大量细粒度的对象。
3、行为型模式
类和对象如何交互,及划分责任和算法。
策略模式:定义一系列算法,把他们封装起来,并且使它们可以相互替换。
模板模式:定义一个算法结构,而将一些步骤延迟到子类实现。
命令模式:将命令请求封装为一个对象,使得可以用不同的请求来进行参数化。
迭代器模式:一种遍历访问聚合对象中各个元素的方法,不暴露该对象的内部结构。
观察者模式:对象间的一对多的依赖关系。
仲裁者模式:用一个中介对象来封装一系列的对象交互。
备忘录模式:在不破坏封装的前提下,保持对象的内部状态。
解释器模式:给定一个语言,定义它的文法的一种表示,并定义一个解释器。
状态模式:允许一个对象在其对象内部状态改变时改变它的行为。
责任链模式:将请求的发送者和接收者解耦,使的多个对象都有处理这个请求的机会。
访问者模式:不改变数据结构的前提下,增加作用于一组对象元素的新功能。
三、设计模式的几种原则
1、单一职责原则
对于一个类,只有一个引起该类变化的原因;该类的职责是唯一的,且这个职责是唯一引起其他类变化的原因。
2、接口隔离原则
客户端不应该依赖它不需要的接口,一个类对另一个类的依赖应该建立在最小的接口上。
3、依赖倒转原则
依赖倒转原则是程序要依赖于抽象接口,不要依赖于具体实现。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。
4、里式代换原则
任何基类可以出现的地方,子类一定可以出现。里氏代换原则是继承复用的基石,只有当衍生类可以替换基类,软件单位的功能不受影响时,基类才能真正的被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。
5、开闭原则
(1)对于扩展是开放的(Open for extension)。这意味着模块的行为是可以扩展的。当应用的需求改变时,我们可以对模块进行扩展,使其具有满足那些改变的新行为。也就是说,我们可以改变模块的功能。
(2)对于修改是关闭的(Closed for modification)。对模块行为进行扩展时,不必改动模块的源代码或者二进制代码。模块的二进制可执行版本,无论是可链接的库、DLL或者.EXE文件,都无需改动。
6、迪米特法则
迪米特法则又叫做最少知识原则,就是说一个对象应当对其它对象又尽可能少的了解,不和陌生人说话。
7、合成复用原则
合成复用原则要求在软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。如果要使用继承关系,则必须严格遵循里氏替换原则。合成复用原则同里氏替换原则相辅相成的,两者都是开闭原则的具体实现规范。
4.数据库事务:
事务是数据库运行中的一个逻辑工作单位,由DBMS中的事务管理子系统负责事务的处理。ACID(原子性、一致性、隔离性和持久性)属性。
事务的4个特性
原子性(Atomic):事务必须是原子工作单元;对于其数据修改,要么全都执行,要么全都不执行。通常,与某个事务关联的操作具有共同的目标,并且是相互依赖的。如果系统只执行这些操作的一个子集,则可能会破坏事务的总体目标。原子性消除了系统处理操作子集的可能性。
一致性(Consistency):事务的一致性指的是在一个事务执行之前和执行之后数据库都必须处于一致性状态。这种特性称为事务的一致性。假如数据库的状态满足所有的完整性约束,就说该数据库是一致的。
隔离性(Isolation):由并发事务所作的修改必须与任何其它并发事务所作的修改隔离。事务查看数据时数据所处的状态,到底是另一个事务执行之前的状态还是中间某个状态,相互之间存在什么影响,是可以通过隔离级别的设置来控制的。
持久性(Durability):事务结束后,事务处理的结果必须能够得到固化,即写入数据库文件中即使机器宕机数据也不会丢失,它对于系统的影响是永久性的。
数据库的隔离级别实现一般是通过数据库锁实现的
读未提交(Read Uncommitted):该隔离级别指即使一个事务的更新语句没有提交,但是别的事务可以读到这个改变,几种异常情况都可能出现。极易出错,没有安全性可言,基本不会使用。
读已提交(Read Committed):该隔离级别指一个事务只能看到其他事务的已经提交的更新,看不到未提交的更新,消除了脏读和第一类丢失更新,这是大多数数据库的默认隔离级别,如Oracle,Sqlserver。
可重复读(Repeatable Read):该隔离级别指一个事务中进行两次或多次同样的对于数据内容的查询,得到的结果是一样的,但不保证对于数据条数的查询是一样的,只要存在读改行数据就禁止写,消除了不可重复读和第二类更新丢失,这是Mysql数据库的默认隔离级别。
串行化(Serializable):意思是说这个事务执行的时候不允许别的事务并发执行.完全串行化的读,只要存在读就禁止写,但可以同时读,消除了幻读。这是事务隔离的最高级别,虽然最安全最省心,但是效率太低,一般不会用。
第一类丢失更新(回滚覆盖),第二类丢失更新( 提交覆盖)
级别\异常 第一类更新丢失 脏读 不可重复读 第二类丢失更新 幻读
读未提交 Y Y Y Y Y
读已提交 N N Y Y Y
可重复读 N N N N Y
串行化 N N N N N
原文链接:https://blog.csdn.net/aluomaidi/article/details/52460844
脏读:指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。
不可重复读:是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的。
幻读:在于某一个范围内的数据行变多或者是变少了,侧重说明的是数据集不一样导致了产生了幻读。数据集发生了改变,查询得到的数据集与真实的数据集不匹配。
5.分布式锁:
常见的分布式锁有:数据库、zookeeper、redis.
1.分布式锁场景:
效率:使用分布式锁可以避免不同节点重复相同的工作,比如避免重复执行定时任务等;
正确性:使用分布式锁同样可以避免破坏数据正确性,如果两个节点在同一数据上面操作,可能会出现并发问题;
2.分布式锁特点:
一个完善的分布式锁需要满足以下特点:
互斥性:互斥是所得基本特性,分布式锁需要按需求保证线程或节点级别的互斥;
可重复性:同一个节点或同一线程获取锁,可以再次重入获取这个锁;
锁超时:支持锁超时释放,防止某个节点不可用后,持有的锁无法释放;
高效性:加锁和解锁的效率高,可以支持高并发;
高可用:需要有高可用机制预防锁服务不可用的情况,如增加降级;
阻塞性:支持阻塞获取锁和非阻塞获取锁两种方式;
公平性:支持公平锁和非公平锁两种类型的锁,公平锁可以保证安装请求锁的顺序获取锁,而非公平锁不可以;
3. 分布式锁种类/原理
(1).阻塞锁
尝试在redis中创建一个字符串结构缓存,方法传入key和过期时间(AcquireLock), 其中key对应的value为锁的过期时间timeout的时间戳。
若redis中没有这个key,则创建成功(即抢到锁),然后立即返回;若已经有这个key,则先watch,然后校验value中的时间戳是否已经超过当前时间。
若已超过,则尝试使用提交事务的方式覆盖新的时间戳,事务提交成功(即抢到锁),然后立即返回;若未超过当前时间或事务提交失败(即被别人抢到锁),则进入一个内部优化过的微循环,不断重试。
传入的timeout还有一个作用,就是控制重试时间,重试超时后则抛异常,using完成方法调用或者显式调用dispose,都会直接清除key。
总结:
timeout有两个意思:一是如果成功加锁后锁的过期时间, 二是未成功加锁后阻塞等待的时间。数据锁服务通过检查value中时间戳来判断是否过期,并不是利用redis在key上设置expire time来通过key的过期实现的。
(2).非阻塞锁
尝试在redis中创建一个字符串结构缓存项,方法传入key、value、timeout(Add),其中value无实际意义,过期时间为传入的timeout。
若redis中没有这个key,则创建成功(即抢到锁),然后立即返回true.若已经有这个key,则立即返回false。以上过程为全局单线程原子操作,整个过程为独占式操作。IsLock可以检测key是否存在。
注意:
timeout即成功加锁后锁的过期时间,利用redis在key上设置expire time来通过key的过期实现。不要先用IsLock判断是否有锁再用Add加锁,因为这两个操作非原子性操作,期间会被其他操作干扰。
(3).底层实现主要用到以下几个指令
A.setnx
setnx key val:当且仅当key不存在时,set一个key为val的字符串,返回1;若key存在,则什么都不做,返回0
B.expire
expire key timeout:为key设置一个超时时间,单位为second,超过这个时间锁会自动释放,避免死锁
C.delete
delete key:删除key
6. Redis缓存
Redis中的缓存雪崩、缓存击穿、缓存穿透问题
1.缓存雪崩是指在某一时刻发生大量的缓存失效,导致瞬间大量的请求直接打到了数据库,可能会导致数据库瞬间压力过大甚至宕机。尤其在高并发的系统中,这种情况会导致连锁反应,整个系统可能会崩溃。
1.1 缓存雪崩的成因
大量缓存同时设置相同的过期时间:如果在某一时刻设置了大量的缓存数据,并为它们都设置了相同的过期时间,那么在未来的某一时刻,这些缓存数据都会同时失效。
Redis宕机:如果因为某种原因,Redis服务器突然宕机,那么所有的缓存数据都会消失,导致所有的请求都直接访问数据库。
误删缓存数据:如果由于某种原因,大量的缓存数据被误删除,那么也会引发缓存雪崩。
1.2 如何预防缓存雪崩
设置随机过期时间:为了防止大量的缓存同时失效,我们可以为每个缓存设置一个随机的过期时间,这样就可以确保不会有太多的缓存同时失效。
使用熔断机制:在系统中加入熔断机制,当数据库请求到达一定的阈值时,直接拒绝部分请求,以保护数据库不被过度访问。
双层缓存策略:可以使用两层缓存,一层是热数据的缓存,另一层是冷数据的缓存。热数据缓存失效后,可以先访问冷数据缓存,而不是直接访问数据库。
数据预热:在系统启动后,预先加载部分常用的数据到缓存中,减少在高峰期的数据库访问。
使用高可用架构:如使用Redis的哨兵模式或者集群模式,确保Redis服务的高可用。
备份和恢复策略:定期备份Redis数据,并确保在Redis宕机后,可以快速恢复数据。
2.缓存穿透是一种指请求查询一个不存在的数据,由于缓存层不存在这个数据,所以请求会穿过缓存层直接查询数据库,导致数据库压力增加。缓存穿透通常发生在恶意攻击或者系统设计不当的情况下。
2.1 导致缓存穿透的原因
恶意攻击:攻击者有意请求不存在的数据,以使得系统频繁访问数据库,造成拒绝服务攻击。
大量请求:如果一个不存在的热点数据被大量请求,会导致这些请求穿透缓存层直接访问数据库,增加数据库压力。
缓存数据失效不及时:当缓存中的数据未及时更新或失效,而恰好有大量请求访问这部分失效的数据时,也会导致缓存穿透问题。
2.2 缓解缓存穿透的方法
布隆过滤器:在缓存层前使用布隆过滤器,对所有可能的数据建立一个布隆过滤器,用于快速判断一个请求的数据是否存在于数据库中。
空结果缓存:对于数据库中不存在的数据,也将其缓存起来,但设置一个较短的过期时间,避免大量的无效请求直接访问数据库。
合理设置缓存时间:根据业务场景,合理设置缓存时间,避免缓存数据过早失效导致大量请求穿透。
3.缓存击穿是指在高并发环境下,大量请求同时访问缓存中不存在的数据,导致这些请求穿透到数据库。这会对数据库造成严重的压力,降低性能。
3.1 缓存穿透与缓存击穿的区别
缓存穿透 是指请求一个不存在于缓存中的数据,导致每次请求都直接查询数据库。
缓存击穿 是指大量请求同时请求一个不存在于缓存中的数据,导致数据库压力骤增。
3.2 缓存击穿的原因
缓存击穿通常发生在以下情况下:
热点数据:某个数据非常热门,但缓存中没有。
缓存失效:缓存中的数据过期,但大量请求仍在访问。
同时到期:多个数据同时过期,导致并发查询数据库。
3.3. 解决缓存击穿问题
使用互斥锁: 通过在缓存中设置互斥锁,只允许一个线程查询数据库,其他线程等待结果。这可以防止多个请求同时穿透到数据库。
预加载数据: 在缓存中设置数据预加载,避免等待请求引发缓存击穿。这需要定期刷新缓存,确保热门数据始终可用。
布隆过滤器: 使用布隆过滤器检查请求的数据是否存在于缓存中,如果不存在,不查询数据库。这可以减少数据库查询次数。
优化数据库查询: 优化数据库查询性能,减少查询时间,可以降低缓存击穿的风险。使用合适的索引和查询优化策略。
www.cnblogs.com/lianshuiwuyi/p/17771618.html
7.消息队列
RabbitMQ内部又细分为很多种模式,但是大的方向,就是这两种的。
1.点对点式:
消息发送者发送消息,消息代理将其放入一个队列中,消息接收者监听该队列,从队列中获取消息内容,消息读取后被移出队列。
消息只有唯一的发送者和接受者,但并不是说只能有一个接收者。【意思是说可以有很多接收者监听该消息队列,但是某一个消息最终只能被一个接收者接收】
2.发布订阅式:
发送者(发布者)发送消息到主题,多个接收者(订阅者)监听(订阅)这个 主题,那么就会在消息到达时同时收到消息。
RabbitMQ中发送消息给扇出交换机,与该扇出交换机绑定的所有队列都能接受到该消息,不管发送的路由键是什么。消费者绑定队列来消费消息。但是,如果多个消费者绑定同一个队列,一旦某一个消费者消费了该队列的消息,那么该队列就会将该消息删除掉,其它消费者就无法获取了。当然,绑定其他队列的消费者不受影响。因为消费后删除的只是自己所在队列中的消息,与该交换机绑定的其他队列不受影响。
Kafka中队列的消息,一旦消费后不会立即删除。它会主动控制什么时候删除,这个后面再说。
原文链接:https://blog.csdn.net/YuanFudao/article/details/131412544
RabbitMq消息丢失情况
1、producer生产者丢失消息
2、broker消息中间件自身丢失消息
3、consumer消费者丢失消息
RabbitMq消息丢失原因及解决方案
1、producer生产者丢失消息
原因:生产者发送消息由于网络等原因并没有发送到RabbitMq
解决方案:
1.1、开启RabbitMq事务机制
生产者发送数据之前开启 RabbitMQ 事务channel.txSelect,然后发送消 息,如果消息没有成功被 RabbitMQ 接收到,那么生产者会收到异常报错,此时就可以回滚事务channel.txRollback,然后重试发送消息;如果收到了消息,那么可以提交事务channel.txCommit,类似我们数据库数据库事务机制。
1.2、开启 confirm 模式
在生产者端设置开启 confirm 模式之后,你每次写的消息都会分配一个唯一的 ID,然后如果写入了 RabbitMQ 中,RabbitMQ 会给你回传一个 ack 消息,告诉你说这个消息已经收到。如果 RabbitMQ 没能处理这个消息,会回调你的一个 nack 接口,告诉你这个消息接收失败,你可以重试。而且可以结合这个机制在自己业务里维护每个消息 ID 的状态,如果超过一定时间还没接收到这个消息的回调,那么可以业务主动重发。
事务机制和 confirm 机制优劣:
事务机制是同步的,提交一个事务之后会阻塞,吞吐量会下来,耗性能。
confirm 机制是异步的,流程不会阻塞,吞吐量较高,性能较好。
2、broker消息中间件自身丢失消息
原因:RabbitMq收到生产者的消息后还没有来得及持久化到磁盘,又或者创建队列没有持久化以及消息并没有设置为持久化,在Mq故障宕机后都会有消息丢失的情况。
解决方案:
2.1、创建队列queue的时候设置队列持久化
2.2、mq配置deliveryMode == 2 消息持久化
重点:必须同时设置队列持久化和消息持久化,再结合生产者的confrim模式,才能保证消息准确投递到broker并保证进入磁盘。
3、consumer消费者丢失消息
原因:消费者自动ack配置情况下,业务代码异常或者其他故障消息并没有处理完成也会自动ack。RabbitMq消息ack后就会丢弃,这就导致异常情况下的消息丢失了。
解决方案:
3.1 关闭RabbitMq自动ack,业务代码成功消费了消息手动调用Mq ack,让Mq丢弃消息;如果业务代码异常则直接nack,让Mq重新推送消息进行处理。当然,在要求比较高的情况下也可以异常数据进入死信队列,保证数据的完整性。
原文链接:https://blog.csdn.net/weixin_39970883/article/details/127492133
第一种:生产者弄丢了数据。生产者将数据发送到 RabbitMQ 的时候,可能数据就在半路给搞丢了,因为网络问题啥的,都有可能。
第二种:RabbitMQ 弄丢了数据。MQ还没有持久化自己挂了
第三种:消费端弄丢了数据。刚消费到,还没处理,结果进程挂了,比如重启了。
二.RabbitMQ消息丢失解决方案
1.针对生产者
方案1 :开启RabbitMQ事务
可以选择用 RabbitMQ 提供的事务功能,就是生产者发送数据之前开启 RabbitMQ 事务channel.txSelect,然后发送消息,如果消息没有成功被 RabbitMQ 接收到,那么生产者会收到异常报错,此时就可以回滚事务channel.txRollback,然后重试发送消息;如果收到了消息,那么可以提交事务channel.txCommit。
缺点:
RabbitMQ 事务机制是同步的,你提交一个事务之后会阻塞在那儿,采用这种方式基本上吞吐量会下来,因为太耗性能。
方案2: 使用confirm机制
事务机制和 confirm 机制最大的不同在于,事务机制是同步的,你提交一个事务之后会阻塞在那儿,但是 confirm 机制是异步的
在生产者开启了confirm模式之后,每次写的消息都会分配一个唯一的id,然后如果写入了rabbitmq之中,rabbitmq会给你回传一个ack消息,告诉你这个消息发送OK了;如果rabbitmq没能处理这个消息,会回调你一个nack接口,告诉你这个消息失败了,你可以进行重试。而且你可以结合这个机制知道自己在内存里维护每个消息的id,如果超过一定时间还没接收到这个消息的回调,那么你可以进行重发。
2.针对RabbitMQ
说三点:
(1)要保证rabbitMQ不丢失消息,那么就需要开启rabbitMQ的持久化机制,即把消息持久化到硬盘上,这样即使rabbitMQ挂掉在重启后仍然可以从硬盘读取消息;
(2)如果rabbitMQ单点故障怎么办,这种情况倒不会造成消息丢失,这里就要提到rabbitMQ的3种安装模式,单机模式、普通集群模式、镜像集群模式,这里要保证rabbitMQ的高可用就要配合HAPROXY做镜像集群模式
(3)如果硬盘坏掉怎么保证消息不丢失
(1)消息持久化
RabbitMQ 的消息默认存放在内存上面,如果不特别声明设置,消息不会持久化保存到硬盘上面的,如果节点重启或者意外crash掉,消息就会丢失。
所以就要对消息进行持久化处理。如何持久化,下面具体说明下:
要想做到消息持久化,必须满足以下三个条件,缺一不可。
Exchange 设置持久化
Queue 设置持久化
Message持久化发送:发送消息设置发送模式deliveryMode=2,代表持久化消息
(2)设置集群镜像模式
我们先来介绍下RabbitMQ三种部署模式:
单节点模式: 最简单的情况,非集群模式,节点挂了,消息就不能用了。业务可能瘫痪,只能等待。
普通模式: 消息只会存在与当前节点中,并不会同步到其他节点,当前节点宕机,有影响的业务会瘫痪,只能等待节点恢复重启可用(必须持久化消息情况下)。
镜像模式: 消息会同步到其他节点上,可以设置同步的节点个数,但吞吐量会下降。属于RabbitMQ的HA方案
为什么设置镜像模式集群,因为队列的内容仅仅存在某一个节点上面,不会存在所有节点上面,所有节点仅仅存放消息结构和元数据。下面自己画了一张图介绍普通集群丢失消息情况:
如果想解决上面途中问题,保证消息不丢失,需要采用HA 镜像模式队列。
下面介绍下三种HA策略模式:
同步至所有的
同步最多N个机器
只同步至符合指定名称的nodes
命令处理HA策略模版:
rabbitmqctl set_policy [-p Vhost] Name Pattern Definition [Priority]
1)为每个以“rock.wechat”开头的队列设置所有节点的镜像,并且设置为自动同步模式
rabbitmqctl set_policy ha-all "^rock.wechat" '{"ha-mode":"all","ha-sync-mode":"automatic"}'
rabbitmqctl set_policy -p rock ha-all "^rock.wechat" '{"ha-mode":"all","ha-sync-mode":"automatic"}'
2)为每个以“rock.wechat.”开头的队列设置两个节点的镜像,并且设置为自动同步模式
rabbitmqctl set_policy -p rock ha-exacly "^rock.wechat" \
'{"ha-mode":"exactly","ha-params":2,"ha-sync-mode":"automatic"}'
3)为每个以“node.”开头的队列分配指定的节点做镜像
rabbitmqctl set_policy ha-nodes "^nodes\." \
'{"ha-mode":"nodes","ha-params":["rabbit@nodeA", "rabbit@nodeB"]}'
但是:HA 镜像队列有一个很大的缺点就是:系统的吞吐量会有所下降
(3)消息补偿机制
为什么还要消息补偿机制呢?难道消息还会丢失,没错,系统是在一个复杂的环境,不要想的太简单了,虽然以上的三种方案,基本可以保证消息的高可用不丢失的问题,
但是作为有追求的程序员来讲,要绝对保证我的系统的稳定性,有一种危机意识。
比如:持久化的消息,保存到硬盘过程中,当前队列节点挂了,存储节点硬盘又坏了,消息丢了,怎么办?
1)生产端首先将业务数据以及消息数据入库,需要在同一个事务中,消息数据入库失败,则整体回滚。
2)根据消息表中消息状态,失败则进行消息补偿措施,重新发送消息处理。
3.针对消费者
方案一:ACK确认机制
多个消费者同时收取消息,比如消息接收到一半的时候,一个消费者死掉了(逻辑复杂时间太长,超时了或者消费被停机或者网络断开链接),如何保证消息不丢?
使用rabbitmq提供的ack机制,服务端首先关闭rabbitmq的自动ack,然后每次在确保处理完这个消息之后,在代码里手动调用ack。这样就可以避免消息还没有处理完就ack。才把消息从内存删除。
这样就解决了,即使一个消费者出了问题,但不会同步消息给服务端,会有其他的消费端去消费,保证了消息不丢的case。
三.总结
如果需要保证消息在整条链路中不丢失,那就需要生产端、mq自身与消费端共同去保障。
生产端: 对生产的消息进行状态标记,开启confirm机制,依据mq的响应来更新消息状态,使用定时任务重新投递超时的消息,多次投递失败进行报警。
mq自身: 开启持久化,并在落盘后再进行ack。如果是镜像部署模式,需要在同步到多个副本之后再进行ack。
消费端: 开启手动ack模式,在业务处理完成后再进行ack,并且需要保证幂等。
通过以上的处理,理论上不存在消息丢失的情况,但是系统的吞吐量以及性能有所下降。
在实际开发中,需要考虑消息丢失的影响程度,来做出对可靠性以及性能之间的权衡。
https://dalin.blog.csdn.net/article/details/129543568?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-129543568-blog-127492133.235%5Ev43%5Epc_blog_bottom_relevance_base6&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-129543568-blog-127492133.235%5Ev43%5Epc_blog_bottom_relevance_base6&utm_relevant_index=1
8.负载均衡算法
负载均衡算法:轮询、加权轮询、随机、加权随机、IPHash、Least Connections最小连接数、Least Response Time最短响应时间
haproxy与nginx的区别主要体现在以下几个方面:
1.功能定位:
Nginx 不仅作为负载均衡器,还可以作为Web服务器、反向代理、缓存服务器等。123
haproxy 专注于负载均衡功能,不包含其他额外功能。1
2.负载均衡算法:
Nginx 支持多种负载均衡算法,如轮询、IP哈希、加权轮询等。
haproxy 支持更丰富的调度算法,并且可以自定义脚本来实现特定的逻辑。
3.稳定性:
haproxy 在长时间运行后对资源占用比较稳定,在高并发情况下表现也较为优秀。
Nginx 在处理大量请求时可能会出现内存泄漏或进程崩溃等问题。
4.配置方式:
haproxy 采用类似C语言的配置文件进行配置,相对比较复杂。
Nginx 使用简单易懂的语法进行配置。
5.协议支持:
haproxy 支持TCP、HTTP以及HTTPS等协议。
Nginx 主要用于HTTP和HTTPS协议上。
9.Polly的7种策略
Polly的7种策略
Polly是一个.NET Core中的弹性和瞬态故障处理库,它提供了多种策略来处理服务故障,确保系统的健壮性和可用性。以下是Polly的一些主要功能:
1.重试策略(Retry):当服务调用失败时,Polly可以自动进行重试,这有助于处理那些可能因为暂时性问题导致的服务不可用情况。
2.断路器(Circuit Breaker):当检测到服务连续不可用时,断路器策略会介入,快速返回错误响应,避免对下游服务的持续请求,从而预防服务雪崩现象。
3.超时策略(Timeout):为服务调用设置一个最大执行时间,超过这个时间的服务调用将被认为失败,可以采取预设的应对措施。
4.舱壁隔离(Bulkhead Isolation):通过限制对服务的并发调用数量,防止因某个服务的问题影响到整个系统的稳定性。
5.缓存策略(Cache):提供一种机制,可以在服务调用的结果不变的情况下直接使用缓存结果,减少不必要的服务调用。
6.降级策略(Fallback):当服务调用失败时,可以提供一个备用的逻辑或者数据作为响应,提高用户体验。
7.策略组合(PolicyWrap) : Polly针对不同的故障有不同的策略,我们可以灵活的组合策略,上述的六种策略可以灵活组合使用。
Polly的策略定义了“故障”和“动作”两个部分,其中“故障”可以包括异常、超时等情况,而“动作”则包括降级、重试、熔断等应对措施。这些策略可以单独使用,也可以组合使用,以适应不同的业务需求和场景。
10.Socket 编程
Socket 编程中,如何处理大量并发连接?
1.线程数量爆炸:如果每连接创建一个线程,大量连接会导致线程数量过多,系统资源消耗过大。
2.频繁线程切换:大量线程会导致频繁的线程切换,降低CPU利用率,增加上下文切换消耗。
3.阻塞问题:如果服务端处理一个连接出现阻塞,会导致其他连接也无法得到及时响应。
常用的处理大量并发连接的方法:
1、 使用线程池:
线程池可以限制线程数量,避免线程数量爆炸的问题。
线程池中的线程可以复用,避免频繁创建和销毁线程的开销。
使用有界队列可以避免线程阻塞对其他任务的影响。 rejected方法设置拒绝策略。
2、 设置最大连接数:
对ServerSocket设置backlog参数,指定最大连接请求队列长度。
当连接请求超过队列容量时,新连接将被拒绝,避免系统资源消耗过大。
3、 超时时间设置
对线程资源执行操作时,设置超时时间。如果某连接超时,需要关闭连接释放资源。
这可以避免连接出现长时间阻塞问题,影响其他连接的及时响应。