
ACID,指数据库事务正确执行的四个基本要素的缩写。包含:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。一个支持事务(Transaction)的数据库,必需要具有这四种特性,否则在事务过程(Transaction processing)当中无法保证数据的正确性,交易过程极可能达不到交易方的要求。
原子性
整个事务中的所有 *** 作,要么全部完成,要么全部不完成,不可能停滞在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
一致性
一个事务可以封装状态改变(除非它是一个只读的)。事务必须始终保持系统处于一致的状态,不管在任何给定的时间并发事务有多少。
也就是说:如果事务是并发多个,系统也必须如同串行事务一样 *** 作。其主要特征是保护性和不变性(Preserving an Invariant),以转账案例为例,假设有五个账户,每个账户余额是100元,那么五个账户总额是500元,如果在这个5个账户之间同时发生多个转账,无论并发多少个,比如在A与B账户之间转账5元,在C与D账户之间转账10元,在B与E之间转账15元,五个账户总额也应该还是500元,这就是保护性和不变性
隔离性
隔离状态执行事务,使它们好像是系统在给定时间内执行的唯一 *** 作。如果有两个事务,运行在相同的时间内,执行相同的功能,事务的隔离性将确保每一事务在系统中认为只有该事务在使用系统。这种属性有时称为串行化,为了防止事务 *** 作间的混淆,必须串行化或序列化请求,使得在同一时间仅有一个请求用于同一数据。
持久性
在事务完成以后,该事务对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。
由于一项 *** 作通常会包含许多子 *** 作,而这些子 *** 作可能会因为硬件的损坏或其他因素产生问题,要正确实现ACID并不容易。ACID建议数据库将所有需要更新以及修改的资料一次 *** 作完毕,但实际上并不可行。
目前主要有两种方式实现ACID:第一种是Write ahead logging,也就是日志式的方式(现代数据库均基于这种方式)。第二种是Shadow paging。
通过使用一些辅助性工具来找到程序中的瓶颈,然后就可以对瓶颈部分的代码进行优化。一般有两种方案:即优化代码或更改设计方法。我们一般会选择后者,因为不去调用以下代码要比调用一些优化的代码更能提高程序的性能。而一个设计良好的程序能够精简代码,从而提高性能。
下面将提供一些在JAVA程序的设计和编码中,为了能够提高JAVA程序的性能,而经常采用的一些方法和技巧。
1.对象的生成和大小的调整。
JAVA程序设计中一个普遍的问题就是没有好好的利用JAVA语言本身提供的函数,从而常常会生成大量的对象(或实例)。由于系统不仅要花时间生成对象,以后可能还需花时间对这些对象进行垃圾回收和处理。因此,生成过多的对象将会给程序的性能带来很大的影响。
例1:关于String ,StringBuffer,+和append
JAVA语言提供了对于String类型变量的 *** 作。但如果使用不当,会给程序的性能带来影响。如下面的语句:
String name=new String("HuangWeiFeng");
Systemoutprintln(name+"is my name");
看似已经很精简了,其实并非如此。为了生成二进制的代码,要进行如下的步骤和 *** 作:
(1) 生成新的字符串 new String(STR_1);
(2) 复制该字符串;
(3) 加载字符串常量"HuangWeiFeng"(STR_2);
(4) 调用字符串的构架器(Constructor);
(5) 保存该字符串到数组中(从位置0开始);
(6) 从javaioPrintStream类中得到静态的out变量;
(7) 生成新的字符串缓冲变量new StringBuffer(STR_BUF_1);
(8) 复制该字符串缓冲变量;
(9) 调用字符串缓冲的构架器(Constructor);
(10) 保存该字符串缓冲到数组中(从位置1开始);
(11) 以STR_1为参数,调用字符串缓冲(StringBuffer)类中的append方法;
(12) 加载字符串常量"is my name"(STR_3);
(13) 以STR_3为参数,调用字符串缓冲(StringBuffer)类中的append方法;
(14) 对于STR_BUF_1执行toString命令;
(15) 调用out变量中的println方法,输出结果。
由此可以看出,这两行简单的代码,就生成了STR_1,STR_2,STR_3,STR_4和STR_BUF_1五个对象变量。这些生成的类的实例一般都存放在堆中。堆要对所有类的超类,类的实例进行初始化,同时还要调用类极其每个超类的构架器。而这些 *** 作都是非常消耗系统资源的。因此,对对象的生成进行限制,是完全有必要的。
经修改,上面的代码可以用如下的代码来替换。
StringBuffer name=new StringBuffer("HuangWeiFeng");
Systemoutprintln(nameappend("is my name")toString());
系统将进行如下的 *** 作:
(1) 生成新的字符串缓冲变量new StringBuffer(STR_BUF_1);
(2) 复制该字符串缓冲变量;
(3) 加载字符串常量"HuangWeiFeng"(STR_1);
(4) 调用字符串缓冲的构架器(Constructor);
(5) 保存该字符串缓冲到数组中(从位置1开始);
(6) 从javaioPrintStream类中得到静态的out变量;
(7) 加载STR_BUF_1;
(8) 加载字符串常量"is my name"(STR_2);
(9) 以STR_2为参数,调用字符串缓冲(StringBuffer)实例中的append方法;
(10) 对于STR_BUF_1执行toString命令(STR_3);
(11)调用out变量中的println方法,输出结果。
由此可以看出,经过改进后的代码只生成了四个对象变量:STR_1,STR_2,STR_3和STR_BUF_1你可能觉得少生成一个对象不会对程序的性能有很大的提高。但下面的代码段2的执行速度将是代码段1的2倍。因为代码段1生成了八个对象,而代码段2只生成了四个对象。
代码段1:
String name= new StringBuffer("HuangWeiFeng");
name+="is my";
name+="name";
代码段2:
StringBuffer name=new StringBuffer("HuangWeiFeng");
nameappend("is my");
nameappend("name")toString();
因此,充分的利用JAVA提供的库函数来优化程序,对提高JAVA程序的性能时非常重要的其注意点主要有如下几方面;
(1) 尽可能的使用静态变量(Static Class Variables)
如果类中的变量不会随他的实例而变化,就可以定义为静态变量,从而使他所有的实例都共享这个变量。
例:
public class foo
{
SomeObject so=new SomeObject();
}
就可以定义为:
public class foo
{
static SomeObject so=new SomeObject();
}
(2) 不要对已生成的对象作过多的改变。
对于一些类(如:String类)来讲,宁愿在重新生成一个新的对象实例,而不应该修改已经生成的对象实例。
例:
String name="Huang";
name="Wei";
name="Feng";
上述代码生成了三个String类型的对象实例。而前两个马上就需要系统进行垃圾回收处理。如果要对字符串进行连接的 *** 作,性能将得更差,因为系统将不得为此生成更多得临时变量,如上例1所示。
(3) 生成对象时,要分配给它合理的空间和大小JAVA中的很多类都有它的默认的空间分配大小。对于StringBuffer类来讲,默认的分配空间大小是16个字符。如果在程序中使用StringBuffer的空间大小不是16个字符,那么就必须进行正确的初始化。
(4) 避免生成不太使用或生命周期短的对象或变量。对于这种情况,因该定义一个对象缓冲池。以为管理一个对象缓冲池的开销要比频繁的生成和回收对象的开销小的多。
(5) 只在对象作用范围内进行初始化。JAVA允许在代码的任何地方定义和初始化对象。这样,就可以只在对象作用的范围内进行初始化。从而节约系统的开销。
例:
SomeObject so=new SomeObject();
If(x==1) then
{
Foo=sogetXX();
}
可以修改为:
if(x==1) then
{
SomeObject so=new SomeObject();
Foo=sogetXX();
}
2.异常(Exceptions)
JAVA语言中提供了try/catch来发方便用户捕捉异常,进行异常的处理。但是如果使用不当,也会给JAVA程序的性能带来影响。因此,要注意以下两点:
(1) 避免对应用程序的逻辑使用try/catch
如果可以用if,while等逻辑语句来处理,那么就尽可能的不用try/catch语句。
(2) 重用异常
在必须要进行异常的处理时,要尽可能的重用已经存在的异常对象。以为在异常的处理中,生成一个异常对象要消耗掉大部分的时间。
3 线程(Threading)
一个高性能的应用程序中一般都会用到线程。因为线程能充分利用系统的资源。在其他线程因为等待硬盘或网络读写而 时,程序能继续处理和运行。但是对线程运用不当,也会影响程序的性能。
例2:正确使用Vector类
Vector主要用来保存各种类型的对象(包括相同类型和不同类型的对象)。但是在一些情况下使用会给程序带来性能上的影响。这主要是由Vector类的两个特点所决定的。第一,Vector提供了线程的安全保护功能。即使Vector类中的许多方法同步。但是如果你已经确认你的应用程序是单线程,这些方法的同步就完全不必要了。第二,在Vector查找存储的各种对象时,常常要花很多的时间进行类型的匹配。而当这些对象都是同一类型时,这些匹配就完全不必要了。因此,有必要设计一个单线程的,保存特定类型对象的类或集合来替代Vector类用来替换的程序如下(StringVectorjava):
public class StringVector
{
private String [] data;
private int count;
public StringVector()
{
this(10); // default size is 10
}
public StringVector(int initialSize)
{
data = new String[initialSize];
}
public void add(String str)
{
// ignore null strings
if(str == null) { return; }
ensureCapacity(count + 1);
data[count++] = str;
}
private void ensureCapacity(int minCapacity)
{
int oldCapacity = datalength;
if (minCapacity > oldCapacity)
{
String oldData[] = data;
int newCapacity = oldCapacity 2;
data = new String[newCapacity];
Systemarraycopy(oldData, 0, data, 0, count);
}
}
public void remove(String str)
{
if(str == null) { return; // ignore null str }
for(int i = 0; i < count; i++)
{
// check for a match
if(data[i]equals(str))
{
Systemarraycopy(data,i+1,data,i,count-1); // copy data
// allow previously valid array element be gc'd
data[--count] = null;
return;
}
}
}
public final String getStringAt(int index)
{
if(index < 0) { return null; }
else if(index > count) { return null; // index is > # strings }
else { return data[index]; // index is good }
}
}
因此,代码:
Vector Strings=new Vector();
Stringsadd("One");
Stringsadd("Two");
String Second=(String)StringselementAt(1);
可以用如下的代码替换:
StringVector Strings=new StringVector();
Stringsadd("One");
Stringsadd("Two");
String Second=StringsgetStringAt(1);
这样就可以通过优化线程来提高JAVA程序的性能。用于测试的程序如下(TestCollectionjava):
import javautilVector;
public class TestCollection
{
public static void main(String args [])
{
TestCollection collect = new TestCollection();
if(argslength == 0)
{
Systemoutprintln("Usage: java TestCollection [ vector | stringvector ]");
Systemexit(1);
}
if(args[0]equals("vector"))
{
Vector store = new Vector();
long start = SystemcurrentTimeMillis();
for(int i = 0; i < 1000000; i++)
{
storeaddElement("string");
}
long finish = SystemcurrentTimeMillis();
Systemoutprintln((finish-start));
start = SystemcurrentTimeMillis();
for(int i = 0; i < 1000000; i++)
{
String result = (String)storeelementAt(i);
}
finish = SystemcurrentTimeMillis();
Systemoutprintln((finish-start));
}
else if(args[0]equals("stringvector"))
{
StringVector store = new StringVector();
long start = SystemcurrentTimeMillis();
for(int i = 0; i < 1000000; i++) { storeadd("string"); }
long finish = SystemcurrentTimeMillis();
Systemoutprintln((finish-start));
start = SystemcurrentTimeMillis();
for(int i = 0; i < 1000000; i++) {
String result = storegetStringAt(i);
}
finish = SystemcurrentTimeMillis();
Systemoutprintln((finish-start));
}
}
}
关于线程的 *** 作,要注意如下几个方面:
(1) 防止过多的同步
如上所示,不必要的同步常常会造成程序性能的下降。因此,如果程序是单线程,则一定不要使用同步。
(2) 同步方法而不要同步整个代码段
对某个方法或函数进行同步比对整个代码段进行同步的性能要好。
(3) 对每个对象使用多”锁”的机制来增大并发。
一般每个对象都只有一个”锁”,这就表明如果两个线程执行一个对象的两个不同的同步方法时,会发生”死锁”。即使这两个方法并不共享任何资源。为了避免这个问题,可以对一个对象实行”多锁”的机制。如下所示:
class foo
{
private static int var1;
private static Object lock1=new Object();
private static int var2;
private static Object lock2=new Object();
public static void increment1()
{
synchronized(lock1)
{
var1++;
}
}
public static void increment2()
{
synchronized(lock2)
{
var2++;
}
}
}
4.输入和输出(I/O)
输入和输出包括很多方面,但涉及最多的是对硬盘,网络或数据库的读写 *** 作。对于读写 *** 作,又分为有缓存和没有缓存的;对于数据库的 *** 作,又可以有多种类型的JDBC驱动器可以选择。但无论怎样,都会给程序的性能带来影响。因此,需要注意如下几点:
(1) 使用输入输出缓冲
尽可能的多使用缓存。但如果要经常对缓存进行刷新(flush),则建议不要使用缓存。
(2) 输出流(Output Stream)和Unicode字符串
当时用Output Stream和Unicode字符串时,Write类的开销比较大。因为它要实现Unicode到字节(byte)的转换因此,如果可能的话,在使用Write类之前就实现转换或用OutputStream类代替Writer类来使用。
(3) 当需序列化时使用transient
当序列化一个类或对象时,对于那些原子类型(atomic)或可以重建的原素要表识为transient类型。这样就不用每一次都进行序列化。如果这些序列化的对象要在网络上传输,这一小小的改变对性能会有很大的提高。
(4) 使用高速缓存(Cache)
对于那些经常要使用而又不大变化的对象或数据,可以把它存储在高速缓存中。这样就可以提高访问的速度。这一点对于从数据库中返回的结果集尤其重要。
(5) 使用速度快的JDBC驱动器(Driver)
JAVA对访问数据库提供了四种方法。这其中有两种是JDBC驱动器。一种是用JAVA外包的本地驱动器;另一种是完全的JAVA驱动器。具体要使用哪一种得根据JAVA布署的环境和应用程序本身来定。
5一些其他的经验和技巧
(1) 使用局部变量。
(2) 避免在同一个类中动过调用函数或方法(get或set)来设置或调用变量。
(3) 避免在循环中生成同一个变量或调用同一个函数(参数变量也一样)。
(4) 尽可能的使用static,final,private等关键字。
(5) 当复制大量数据时,使用Systemarraycopy()命令。
当客户端访问可以打开会话功能的资源时,web服务器会创建一个与客户端对应的>
Saga 是一种特殊类型的事件监听器:管理业务事务的事件监听器。 一些事务可能会运行数天甚至数周,而其他事务则在几毫秒内完成。 在 Axon 中,每个 Saga 实例负责管理单个业务事务。 这意味着 Saga 维护管理该事务所必需的状态,继续它或采取补偿措施来回滚已经采取的任何措施。 通常,与常规事件监听器相反,saga 有起点和终点,两者都由事件触发。 虽然 saga 的起点通常很明确,但 saga 可能有多种结束方式。
在 Axon 中,saga 是定义一个或多个 @SagaEventHandler 方法的类。 与常规事件处理程序不同,一个 saga 的多个实例可能随时存在。 Sagas 由单个事件处理器(Tracking 或 Subscribing)管理,该处理器专用于处理特定 saga 类型的事件。
单个 Saga 实例负责管理单个事务。 这意味着您需要能够指示 saga 生命周期的开始和结束。
在 saga 中,事件处理程序使用 @SagaEventHandler 进行注解。 如果特定事件表示事务的开始,请向同一方法添加另一个注解: @StartSaga 。 此注解将创建一个新的 saga,并在发布匹配事件时调用其事件处理程序方法。
默认情况下,只有在找不到合适的现有 saga(相同类型)时才会启动新的 saga。 您还可以通过将 @StartSaga 注解上的 forceNew 属性设置为 true 来强制创建新的 saga 实例。
结束一个 saga 可以通过两种方式完成。 如果某个事件总是指示 saga 其生命周期的结束,请在 saga 上使用 @EndSaga 注解该事件处理程序。 saga 的生命周期将在调用处理程序后结束。 或者,您可以从 saga 内部调用 SagaLifecycleend() 来结束生命周期。 这允许您有条件地结束 saga。
saga 中的事件处理与常规事件监听器一样。 方法和参数解析的相同规则在这里有效。 但是,有一个主要区别。 虽然有一个事件监听器实例处理所有传入事件,但可能存在多个 saga 实例,每个实例对不同的事件感兴趣。 例如,围绕 Id "1" 的订单管理事务的 saga 不会对订单 "2" 的事件感兴趣,反之亦然。
Axon 不会将所有事件发布到所有 saga 实例(这将完全浪费资源),而是仅发布包含与 saga 关联的属性的事件。 这是使用 AssociationValue 完成的。 AssociationValue 由键和值组成。 键表示使用的标识符类型,例如 "orderId" 或 "order"。 该值表示对应的值,在前面的示例中为 "1" 或 "2"。
@SagaEventHandler 注解方法的评估顺序与 @EventHandler 方法的顺序相同(请参阅《注解事件处理程序》)。 如果处理程序方法的参数与传入事件匹配,并且 saga 与处理程序方法上定义的属性有关联,则方法匹配。
@SagaEventHandler 注解有两个属性,其中 associationProperty 是最重要的一个。 这是传入事件的属性名称,应该用于查找关联的 saga。 关联值的键是属性的名称。 该值是属性的 getter 方法返回的值。
例如,一个带有方法 String getOrderId() 的传入事件,它返回 “123”。 如果接受此事件的方法使用 @SagaEventHandler(associationProperty="orderId") 进行注解,则此事件将路由到已与键为 “orderId” 且值为 “123” 的 AssociationValue 关联的所有 Sagas。 这可能恰好是一个,多于一个,甚至根本没有。
有时,您要关联的属性名称不是您要使用的关联名称。 例如,您有一个将 “Sell orders” 与 “Buy orders” 匹配的 Saga,您可能有一个包含 “buyOrderId” 和 “sellOrderId” 的交易对象。 如果您希望 saga 将 “sellOrderId” 值关联为 “orderId”,您可以在 @SagaEventHandler 注解中定义不同的 keyName 。 然后它会变成 @SagaEventHandler(associationProperty="sellOrderId", keyName="orderId")
Saga 通常不仅仅基于事件维护状态。 它们与外部组件交互。 为此,他们需要访问寻址组件所需的资源。 通常,这些资源并不是 saga 及其状态的真正一部分,并且这些资源不应该这样持久化。 然而,一旦一个 saga 被重构,这些资源必须在事件被路由到那个实例之前被注入。
为此,有 ResourceInjector 。 SagaRepository 使用它向 saga 注入资源。 Axon 提供了一个 SpringResourceInjector ,它使用来自应用程序上下文的资源注入带注解的字段和方法。 Axon 还提供了一个 SimpleResourceInjector ,它将已经注册的资源注入到 @Inject 注解的方法和字段中。
SimpleResourceInjector 允许注入预先指定的资源集合。 它扫描 Saga 的(setter)方法和字段,以找到使用 @Inject 注解的方法和字段。
使用 Configuration API 时,Axon 将默认使用 ConfigurationResourceInjector 。 它将注入配置中可用的任何资源。 EventBus 、 EventStore 、 CommandBus 和 CommandGateway 等组件默认可用。 您还可以使用 configurerregisterComponent() 注册自己的组件。
SpringResourceInjector 使用 Spring 的依赖注入机制将资源注入到 Saga 中。 这意味着您可以根据需要使用 setter 注入或直接字段注入。 要注入的方法或字段需要注解,以便 Spring 将其识别为依赖项,例如使用 @Autowired 。
事件需要重定向到适当的 saga 实例。 为此,需要一些基础设施类。 最重要的组件是 SagaManager 和 SagaRepository 。
与处理事件的任何组件一样,处理由事件处理器完成。 但是,Sagas 不是处理事件的单例实例。 他们有需要管理自己的生命周期。
Axon 通过 AnnotatedSagaManager 支持生命周期管理,该管理器提供给事件处理器以执行处理程序的实际调用。 它使用要管理的 Saga 的类型以及可以存储和检索该类型的 Saga 的 SagaRepository 进行初始化。 单个 AnnotatedSagaManager 只能管理单个 Saga 类型。
SagaRepository 负责存储和检索 saga,供 SagaManager 使用。 它能够通过它们的标识符以及它们的关联值来检索特定的 saga 实例。
但是,有一些特殊要求。 由于 saga 中的并发处理是一个非常微妙的过程,Repository 必须确保对于每个概念 saga 实例(具有相同的标识符)在 JVM 中仅存在一个实例。
Axon 提供了 AnnotatedSagaRepository 实现,它允许查找 saga 实例,同时保证只能同时访问 saga 的单个实例。 它使用 SagaStore 来执行 saga 实例的实际持久化。
使用的实现选择主要取决于应用程序使用的存储引擎。 Axon 提供了 JdbcSagaStore 、 InMemorySagaStore 、 JpaSagaStore 和 MongoSagaStore 。
在某些情况下,应用程序受益于缓存 saga 实例。 在这种情况下,有一个 CachingSagaStore 包装了另一个实现以添加缓存行为。 请注意, CachingSagaStore 是直写式缓存,这意味着保存 *** 作始终会立即转发到后备存储,以确保数据安全。
JpaSagaStore 使用 JPA 来存储 sagas 的状态和关联值。 Sagas 本身不需要任何 JPA 注解; Axon 将使用 Serializer 对 sagas 进行序列化(类似于事件序列化,您可以在 XStreamSerializer 或 JacksonSerializer 之间进行选择,这可以通过在应用程序中配置默认的 Serializer 来设置。有关更多详细信息,请参阅 Serializers
JpaSagaStore 配置有 EntityManagerProvider ,它提供对要使用的 EntityManager 实例的访问。 这种抽象允许使用应用程序管理和容器管理的 EntityManager 。 或者,您可以定义序列化程序来序列化 Saga 实例。 Axon 默认为 XStreamSerializer 。
JdbcSagaStore 使用纯 JDBC 来存储阶段实例及其关联值。 与 JpaSagaStore 类似,saga 实例不需要知道它们是如何存储的。 它们使用序列化程序进行序列化。
JdbcSagaStore 使用 DataSource 或 ConnectionProvider 进行初始化。 虽然不是必需的,但在使用 ConnectionProvider 进行初始化时,建议将实现包装在 UnitOfWorkAwareConnectionProviderWrapper 中。 它将检查当前工作单元中是否存在已打开的数据库连接,以确保工作单元内的所有活动都在单个连接上完成。
与 JPA 不同, JdbcSagaRepository 使用纯 SQL 语句来存储和检索信息。 这可能意味着某些 *** 作依赖于数据库特定的 SQL 方言。 某些数据库供应商也可能提供您想使用的非标准功能。 为此,您可以提供自己的 SagaSqlSchema 。 SagaSqlSchema 是一个接口,它定义了仓储需要在底层数据库上执行的所有 *** 作。 它允许您自定义为每个 *** 作执行的 SQL 语句。 默认值为 GenericSagaSqlSchema 。 其他可用的实现是 PostgresSagaSqlSchema 、 Oracle11SagaSqlSchema 和 HsqlSagaSchema 。
MongoSagaStore 将 saga 实例及其关联存储在 MongoDB 数据库中。 MongoSagaStore 将所有 saga 存储在 MongoDB 数据库中的单个集合中。 对于每个 saga 实例,都会创建一个文档。
MongoSagaStore 还确保在任何时候,对于单个 JVM 中的任何唯一 Saga,都只存在一个 Saga 实例。 这可确保不会因并发问题而丢失状态更改。
MongoSagaStore 使用 MongoTemplate 和可选的 Serializer 初始化。 MongoTemplate 提供了对存储 sagas 的集合的引用。Axon 提供了 DefaultMongoTemplate ,它接受一个 MongoClient 实例以及存储 sagas 的数据库名称和集合名称。数据库名称 和集合名称可以省略。 在这种情况下,它们分别默认为 “axonframework” 和 “sagas” 。
如果使用数据库支持的 saga 存储,保存和加载 saga 实例可能是一项相对昂贵的 *** 作。 在短时间内多次调用同一个 saga 实例的情况下,缓存可能对应用程序的性能特别有益。
Axon 提供 CachingSagaStore 实现。 它是一个包装了另一个 SagaStore 的 SagaStore ,它负责实际存储。 加载 saga 或关联值时, CachingSagaStore 将首先查询其缓存,然后再委托给包装的仓储。 存储信息时,所有调用总是被委派以确保后备存储始终对 saga 的状态有一致的视图。
要配置缓存,只需将任何 SagaStore 包装在 CachingSagaStore 中。 CachingSagaStore 的构造函数采用三个参数: 1 要包装的 SagaStore 2 用于关联值的缓存 3 用于 saga 实例的缓存
后两个参数可能指的是同一个缓存,也可能指不同的缓存。 这取决于您的特定应用程序的驱逐要求。
尽管 Saga 需要 manager、repository /store 和连接到正确的消息总线,但配置 Saga 很简单。 使用配置 API 时,Axon 将为大多数组件使用合理的默认值。
作为一种特定类型的 Event Handling Component,Saga 的配置与 Event Processor 的配置密切相关。 因此,配置 processor 将影响 Saga 的行为,尽管是在非功能级别上。 例如,错误处理或处理器分配规则的配置因此对 Sagas 同样有效,只要在配置期间使用正确的处理器名称。
在内部,Axon 使用 SagaConfigurer 来构建 Saga、Saga Manager、Saga Repository 和 Saga Store。 一个名为 MySaga 的 Saga 的默认配置如下所示:
Axon Configuration API
作为特殊类型的事件处理程序,注册 Saga 是通过 EventProcessingConfigurer 完成的:
Spring Boot AutoConfiguration
在 Spring 环境中,应使用 @Saga 注解 Saga 实现以自动配置它:
尽管默认值将我们引导到一个工作 Saga 环境,但建议定义 SagaStore 以使用。 SagaStore 表示“物理”存储 Saga 实例的机制,为此它使用 AnnotatedSagaRepository (默认)来存储和检索 Saga 实例。 如果没有配置 SagaStore ,Axon 默认使用 InMemorySagaStore ,因此不会在关闭时保留 Saga。 要为 MySaga 配置 SagaStore ,请参考以下代码段:
Axon Configuration API
要定义自定义 SagaStore ,应通过 EventProcessingConfigurer#registerSaga(Class , Consumer)
1程序逻辑部分,新逻辑上线,注意对老逻辑的兼容,千万不要不管三七二十一暴力替换。
2数据库部分:
1)能建新表尽量建新表以避免对老数据的破坏。
2)如果老表有字段增加,千万不要做非空,唯一性的约束,否则后果自负。
3)假如需要减字段,那么请考虑临时替代的方案,比如新建一张临时表,让程序先取临时表数据,最后等新表建立后再切换过来,导入数据。
3CACHE等需要序列化,反序列化的部分。一定要兼容原先在缓存中的数据,例如SID千万不要变化,否则反序列化失败,假如有字段需要增加,那么考虑第一次读入先取数据库。
4外部接口相关的,能不要求外部接口联调,尽量就不做联调,一是麻烦,二是风险大。尽量对原接口传入和传出的数据保持兼容。假如有变化,考虑用适配器封装,实在没办法再实行下策。
5注意 *** 作的先后顺序,这个也是非常重要,例如你先发了数据库,但是程序还是老的,并且会受到影响,那么就挂了。
详情如下:
1、连接数据库,不同数据库python有对应的第三方库,用账号密码ip地址直接连接。
2、执行sql语句,可以用pandas里面的read_sql_query,也可以用数据库第三方库的fetchall。
3、获取结果,read_sql_query直接出来带列名的DataFrame,但fetchall这种还要另外处理成DataFrame,还有其他数据处理的,在这一步加上就是了。
4、保存结果,pandas里面有一个to_pickle的函数,可以把数据序列化保存在本地文件,需要用到的时候再read_pickle反序列拿出来用,比不停地执行sql要方便。
5、将以上四步所有的 *** 作代码封装成函数,作为scheduler的其中一个作业,设置执行周期和执行时间,到点就会自动获取数据保存在本地文件了
以上就是关于数据库事务正确执行的四个基本要素包括全部的内容,包括:数据库事务正确执行的四个基本要素包括、大虾请进:oracle数据库超大数据量的处理、为什么实体类要实现serializable接口序列化等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)