ActiveMQ持久化方式
消息持久性对于可靠消息传递来说应该是一种比较好的方法,有了消息持久化,即使发送者和接受者不是同时在线或者消息中心在发送者发送消息后宕机了,在消息 中心重新启动后仍然可以将消息发送出去,如果把这种持久化和ReliableMessaging结合起来应该是很好的保证了消息的可靠传送。
消息持久性的原理很简单,就是在发送者将消息发送出去后,消息中心首先将消息存储到本地数据文件、内存数据库或者远程数据库等,然后试图将消息发送 给接收者,发送成功则将消息从存储中删除,失败则继续尝试。消息中心启动以后首先要检查制定的存储位置,如果有未发送成功的消息,则需要把消息发送出去。
ActiveMQ持久化方式:AMQ、KahaDB、JDBC、LevelDB。
1、AMQ
AMQ是一种文件存储形式,它具有写入速度快和容易恢复的特点。消息存储在一个个文件中,文件的默认大小为32M,如果一条消息的大小超过了 32M,那么这个值必须设置大一点。当一个存储文件中的消息已经全部被消费,那么这个文件将被标识为可删除,在下一个清除阶段,这个文件被删除。AMQ适用于ActiveMQ5.3之前的版本。默认配置如下:
1
2
3
|
< persistenceAdapter > < amqPersistenceAdapter directory = "activemq-data" maxFileLength = "32mb" /> </ persistenceAdapter > |
属性如下:
属性名称 |
默认值 |
描述 |
directory |
activemq-data |
消息文件和日志的存储目录 |
useNIO |
true |
使用NIO协议存储消息 |
syncOnWrite |
false |
同步写到磁盘,这个选项对性能影响非常大 |
maxFileLength |
32Mb |
一个消息文件的大小 |
persistentIndex |
true |
消息索引的持久化,如果为false,那么索引保存在内存中 |
maxCheckpointMessageAddSize |
4kb |
一个事务允许的最大消息量 |
cleanupInterval |
30000 |
清除操作周期,单位ms |
indexBinSize |
1024 |
索引文件缓存页面数,缺省为1024,当amq扩充或者缩减存储时,会锁定整个broker,导致一定时间的阻塞,所以这个值应该调整到比较大,但是代码中实现会动态伸缩,调整效果并不理想。 |
indexKeySize |
96 |
索引key的大小,key是消息ID |
indexPageSize |
16kb |
索引的页大小 |
directoryArchive |
archive |
存储被归档的消息文件目录 |
archiveDataLogs |
false |
当为true时,归档的消息文件被移到directoryArchive,而不是直接删除 |
2、KahaDB
KahaDB是基于文件的本地数据库储存形式,虽然没有AMQ的速度快,但是它具有强扩展性,恢复的时间比AMQ短,从5.4版本之后KahaDB做为默认的持久化方式。默认配置如下:
KahaDB的属性如下:
属性名称 |
默认值 |
描述 |
directory |
activemq-data |
消息文件和日志的存储目录 |
indexWriteBatchSize |
1000 |
一批索引的大小,当要更新的索引量到达这个值时,更新到消息文件中 |
indexCacheSize |
10000 |
内存中,索引的页大小 |
enableIndexWriteAsync |
false |
索引是否异步写到消息文件中 |
journalMaxFileLength |
32mb |
一个消息文件的大小 |
enableJournalDiskSyncs |
true |
是否讲非事务的消息同步写入到磁盘 |
cleanupInterval |
30000 |
清除操作周期,单位ms |
checkpointInterval |
5000 |
索引写入到消息文件的周期,单位ms |
ignoreMissingJournalfiles |
false |
忽略丢失的消息文件,false,当丢失了消息文件,启动异常 |
checkForCorruptJournalFiles |
false |
检查消息文件是否损坏,true,检查发现损坏会尝试修复 |
checksumJournalFiles |
false |
产生一个checksum,以便能够检测journal文件是否损坏。 |
5.4版本之后有效的属性: |
||
archiveDataLogs |
false |
当为true时,归档的消息文件被移到directoryArchive,而不是直接删除 |
directoryArchive |
null |
存储被归档的消息文件目录 |
databaseLockedWaitDelay |
10000 |
在使用负载时,等待获得文件锁的延迟时间,单位ms |
maxAsyncJobs |
10000 |
同个生产者产生等待写入的异步消息最大量 |
concurrentStoreAndDispatchTopics |
false |
当写入消息的时候,是否转发主题消息 |
concurrentStoreAndDispatchQueues |
true |
当写入消息的时候,是否转发队列消息 |
5.6版本之后有效的属性: |
||
archiveCorruptedIndex |
false |
是否归档错误的索引 |
每个KahaDB的实例都可以配置单独的适配器,如果没有目标队列提交给filteredKahaDB,那么意味着对所有的队列有效。如果一个队列没有对应的适配器,那么将会抛出一个异常。配置如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
< persistenceAdapter > < mKahaDBdirectory = "${activemq.base}/data/kahadb" > < filteredPersistenceAdapters > <!-- match all queues --> < filteredKahaDBqueue =">"> < persistenceAdapter > < kahaDBjournalMaxFileLength = "32mb" /> </ persistenceAdapter > </ filteredKahaDB > <!-- match all destinations --> < filteredKahaDB > < persistenceAdapter > < kahaDBenableJournalDiskSyncs = "false" /> </ persistenceAdapter > </ filteredKahaDB > </ filteredPersistenceAdapters > </ mKahaDB > </ persistenceAdapter > |
如果filteredKahaDB的perDestination属性设置为true,那么匹配的目标队列将会得到自己对应的KahaDB实例。配置如下:
1
2
3
4
5
6
7
8
9
10
11
12
|
< persistenceAdapter > < mKahaDBdirectory = "${activemq.base}/data/kahadb" > < filteredPersistenceAdapters > <!-- kahaDB per destinations --> < filteredKahaDB perDestination = "true" > < persistenceAdapter > < kahaDBjournalMaxFileLength = "32mb" /> </ persistenceAdapter > </ filteredKahaDB > </ filteredPersistenceAdapters > </ mKahaDB > </ persistenceAdapter > |
3、JDBC
可以将消息存储到数据库中,例如:Mysql、SQL Server、Oracle、DB2。
配置JDBC适配器:
1
2
3
|
< persistenceAdapter > < jdbcPersistenceAdapterdataSource = "#mysql-ds" createTablesOnStartup = "false" /> </ persistenceAdapter > |
dataSource指定持久化数据库的bean,createTablesOnStartup是否在启动的时候创建数据表,默认值是true,这样每次启动都会去创建数据表了,一般是第一次启动的时候设置为true,之后改成false。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
Mysql持久化bean: < bean id = "mysql-ds" class = "org.apache.commons.dbcp.BasicDataSource" destroy-method = "close" > < property name = "driverClassName" value = "com.mysql.jdbc.Driver" /> < property name = "url" value = "jdbc:mysql://localhost/activemq?relaxAutoCommit=true" /> < property name = "username" value = "activemq" /> < property name = "password" value = "activemq" /> < property name = "poolPreparedStatements" value = "true" /> </ bean > SQL Server持久化bean: < bean id = "mssql-ds" class = "net.sourceforge.jtds.jdbcx.JtdsDataSource" destroy-method = "close" > < property name = "serverName" value = "SERVERNAME" /> < property name = "portNumber" value = "PORTNUMBER" /> < property name = "databaseName" value = "DATABASENAME" /> < property name = "user" value = "USER" /> < property name = "password" value = "PASSWORD" /> </ bean > Oracle持久化bean: < bean id = "oracle-ds" class = "org.apache.commons.dbcp.BasicDataSource" destroy-method = "close" > < property name = "driverClassName" value = "oracle.jdbc.driver.OracleDriver" /> < property name = "url" value = "jdbc:oracle:thin:@10.53.132.47:1521:activemq" /> < property name = "username" value = "activemq" /> < property name = "password" value = "activemq" /> < property name = "maxActive" value = "200" /> < property name = "poolPreparedStatements" value = "true" /> </ bean > DB2持久化bean: < bean id = "db2-ds" class = "org.apache.commons.dbcp.BasicDataSource" destroy-method = "close" > < property name = "driverClassName" value = "com.ibm.db2.jcc.DB2Driver" /> < property name = "url" value = "jdbc:db2://hndb02.bf.ctc.com:50002/activemq" /> < property name = "username" value = "activemq" /> < property name = "password" value = "activemq" /> < property name = "maxActive" value = "200" /> < property name = "poolPreparedStatements" value = "true" /> </ bean > |
4、LevelDB
这种文件系统是从ActiveMQ5.8之后引进的,它和KahaDB非常相似,也是基于文件的本地数据库储存形式,但是它提供比KahaDB更快的持久性。与KahaDB不同的是,它不是使用传统的B-树来实现对日志数据的提前写,而是使用基于索引的LevelDB。
默认配置如下:
1
2
3
|
< persistenceAdapter > < levelDBdirectory = "activemq-data" /> </ persistenceAdapter > |
属性如下:
属性名称 |
默认值 |
描述 |
directory |
"LevelDB" |
数据文件的存储目录 |
readThreads |
10 |
系统允许的并发读线程数量 |
sync |
true |
同步写到磁盘 |
logSize |
104857600 (100 MB) |
日志文件大小的最大值 |
logWriteBufferSize |
4194304 (4 MB) |
日志数据写入文件系统的最大缓存值 |
verifyChecksums |
false |
是否对从文件系统中读取的数据进行校验 |
paranoidChecks |
false |
尽快对系统内部发生的存储错误进行标记 |
indexFactory |
org.fusesource.leveldbjni.JniDBFactory, org.iq80.leveldb.impl.Iq80DBFactory |
在创建LevelDB索引时使用 |
indexMaxOpenFiles |
1000 |
可供索引使用的打开文件的数量 |
indexBlockRestartInterval |
16 |
Number keys between restart points for delta encoding of keys. |
indexWriteBufferSize |
6291456 (6 MB) |
内存中索引数据的最大值 |
indexBlockSize |
4096 (4 K) |
每个数据块的索引数据大小 |
indexCacheSize |
268435456 (256 MB) |
使用缓存索引块允许的最大内存 |
indexCompression |
snappy |
适用于索引块的压缩类型 |
logCompression |
none |
适用于日志记录的压缩类型 |
5、 下面详细介绍一下如何将消息持久化到Mysql数据库中
Ø 需要将mysql的驱动包放置到ActiveMQ的lib目录下
Ø 修改activeMQ的配置文件:
1
2
3
|
< persistenceAdapter > < jdbcPersistenceAdapter dataDirectory = "${activemq.base}/data" dataSource = "#mysql-ds" createTablesOnStartup = "false" /> </ persistenceAdapter > |
在配置文件中的broker节点外增加:
1
2
3
4
5
6
7
8
|
< beanid = "mysql-ds" class = "org.apache.commons.dbcp.BasicDataSource" destroy-method = "close" > < propertyname = "driverClassName" value = "com.mysql.jdbc.Driver" /> < property name = "url" value = "jdbc:mysql://localhost:3306/activemq?relaxAutoCommit=true" /> < property name = "username" value = "root" /> < property name = "password" value = "root" /> < property name = "maxActive" value = "200" /> < propertyname = "poolPreparedStatements" value = "true" /> </ bean > |
从配置中可以看出数据库的名称是activemq,需要手动在MySql中建立这个数据库。
然后重新启动activeMQ,会发现activemq多了三张表:
1:activemq_acks
2:activemq_lock
3:activemq_msgs
Ø 点到点类型
Sender类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
import javax.jms.Connection; import javax.jms.ConnectionFactory; import javax.jms.DeliveryMode; import javax.jms.Destination; import javax.jms.JMSException; import javax.jms.MessageProducer; import javax.jms.Session; import javax.jms.TextMessage; import org.apache.activemq.ActiveMQConnection; import org.apache.activemq.ActiveMQConnectionFactory; public class Sender { private static final int SEND_NUMBER = 2000 ; public static void main(String[] args) { // ConnectionFactory :连接工厂,JMS用它创建连接 ConnectionFactory connectionFactory; // Connection :JMS客户端到JMS Provider的连接 Connection connection = null ; // Session:一个发送或接收消息的线程 Session session; // Destination :消息的目的地;消息发送给谁. Destination destination; // MessageProducer:消息发送者 MessageProducer producer; // TextMessage message; // 构造ConnectionFactory实例对象,此处采用ActiveMq的实现 connectionFactory = new ActiveMQConnectionFactory( ActiveMQConnection.DEFAULT_USER, ActiveMQConnection.DEFAULT_PASSWORD, "tcp://localhost:61616" ); try { // 构造从工厂得到连接对象 connection = connectionFactory.createConnection(); //启动 connection.start(); //获取操作连接 session = connection.createSession( false , Session.AUTO_ACKNOWLEDGE); //获取session,FirstQueue是一个服务器的queue destination = session.createQueue("FirstQueue"); // 得到消息生成者【发送者】 producer = session.createProducer(destination); //设置不持久化 producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT); //构造消息 sendMessage(session, producer); //session.commit(); connection.close(); } catch (Exception e){ e.printStackTrace(); } finally { if ( null != connection){ try { connection.close(); } catch (JMSException e) { // TODO Auto-generatedcatch block e.printStackTrace(); } } } } public static void sendMessage(Session session, MessageProducer producer) throws Exception{ for ( int i= 1 ; i<=SEND_NUMBER; i++){ TextMessage message = session.createTextMessage( "ActiveMQ发送消息" +i); System.out.println( "发送消息:ActiveMQ发送的消息" +i); producer.send(message); } } } |
Receiver类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
import javax.jms.Connection; import javax.jms.ConnectionFactory; import javax.jms.Destination; import javax.jms.MessageConsumer; import javax.jms.Session; import javax.jms.TextMessage; import org.apache.activemq.ActiveMQConnection; import org.apache.activemq.ActiveMQConnectionFactory; public class Receiver { public static void main(String[] args) { // ConnectionFactory :连接工厂,JMS用它创建连接 ConnectionFactory connectionFactory; // Connection :JMS客户端到JMS Provider的连接 Connection connection = null ; // Session:一个发送或接收消息的线程 Session session; // Destination :消息的目的地;消息发送给谁. Destination destination; // 消费者,消息接收者 MessageConsumer consumer; connectionFactory = newActiveMQConnectionFactory( ActiveMQConnection.DEFAULT_USER, ActiveMQConnection.DEFAULT_PASSWORD, "tcp://localhost:61616" ); try { //得到连接对象 connection =connectionFactory.createConnection(); // 启动 connection.start(); // 获取操作连接 session = connection.createSession( false , Session.AUTO_ACKNOWLEDGE); // 创建Queue destination = session.createQueue( "FirstQueue" ); consumer =session.createConsumer(destination); while ( true ){ //设置接收者接收消息的时间,为了便于测试,这里定为100s TextMessagemessage = (TextMessage)consumer.receive( 100000 ); if ( null != message){ System.out.println( "收到消息" +message.getText()); } else break ; } } catch (Exception e){ e.printStackTrace(); } finally { try { if ( null != connection) connection.close(); } catch (Throwable ignore) { } } } } |
测试:
测试一:
A、 先运行Sender类,待运行完毕后,运行Receiver类
B、 在此过程中activemq数据库的activemq_msgs表中没有数据
C、 再次运行Receiver,消费不到任何信息
测试二:
A、 先运行Sender类
B、 重启电脑
C、 运行Receiver类,无任何信息被消费
测试三:
A、 把Sender类中的producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);改为producer.setDeliveryMode(DeliveryMode.PERSISTENT);
B、 先运行Sender类,待运行完毕后,运行Receiver类
C、 在此过程中activemq数据库的activemq_msgs表中有数据生成,运行完Receiver类后,数据清除
测试四:
A、 把Sender类中的producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);改为producer.setDeliveryMode(DeliveryMode.PERSISTENT);
B、 运行Sender类
C、 重启电脑
D、 运行Receiver类,有消息被消费
结论:
通过以上测试,可以发现,在P2P类型中当DeliveryMode设置为NON_PERSISTENCE时,消息被保存在内存中,而当 DeliveryMode设置为PERSISTENCE时,消息保存在broker的相应的文件或者数据库中。而且P2P中消息一旦被Consumer消 费就从broker中删除。
Ø 发布/订阅类型
Sender类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
import javax.jms.Connection; import javax.jms.ConnectionFactory; import javax.jms.DeliveryMode; import javax.jms.Destination; import javax.jms.JMSException; import javax.jms.MessageProducer; import javax.jms.Session; import javax.jms.TextMessage; import javax.jms.Topic; import org.apache.activemq.ActiveMQConnection; import org.apache.activemq.ActiveMQConnectionFactory; public class Sender { private static final int SEND_NUMBER = 100 ; public static void main(String[] args) { // ConnectionFactory :连接工厂,JMS用它创建连接 ConnectionFactory connectionFactory; // Connection :JMS客户端到JMS Provider的连接 Connection connection = null ; // Session:一个发送或接收消息的线程 Session session; // MessageProducer:消息发送者 MessageProducer producer; // TextMessage message; // 构造ConnectionFactory实例对象,此处采用ActiveMq的实现 connectionFactory = new ActiveMQConnectionFactory( ActiveMQConnection.DEFAULT_USER, ActiveMQConnection.DEFAULT_PASSWORD, "tcp://localhost:61616" ); try { //得到连接对象 connection = connectionFactory.createConnection(); //启动 connection.start(); //获取操作连接 session = connection.createSession( false , Session.AUTO_ACKNOWLEDGE); Topic topic = session.createTopic( "MQ_test" ); // 得到消息生成者【发送者】 producer = session.createProducer(topic); //设置持久化 producer.setDeliveryMode(DeliveryMode.PERSISTENT); //构造消息 sendMessage(session, producer); //session.commit(); connection.close(); } catch (Exception e){ e.printStackTrace(); } finally { if ( null != connection){ try { connection.close(); } catch (JMSException e) { // TODO Auto-generatedcatch block e.printStackTrace(); } } } } public static void sendMessage(Session session, MessageProducer producer) throws Exception{ for ( int i= 1 ; i<=SEND_NUMBER; i++){ TextMessage message = session.createTextMessage( "ActiveMQ发送消息" +i); System.out.println( "发送消息:ActiveMQ发送的消息" +i); producer.send(message); } } } |
Receiver类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
import javax.jms.Connection; import javax.jms.ConnectionFactory; import javax.jms.Destination; import javax.jms.MessageConsumer; import javax.jms.Session; import javax.jms.TextMessage; import javax.jms.Topic; import org.apache.activemq.ActiveMQConnection; import org.apache.activemq.ActiveMQConnectionFactory; public class Receiver { public static void main(String[] args) { // ConnectionFactory :连接工厂,JMS用它创建连接 ConnectionFactory connectionFactory; // Connection :JMS客户端到JMS Provider的连接 Connection connection = null ; // Session:一个发送或接收消息的线程 Session session; // 消费者,消息接收者 MessageConsumer consumer; connectionFactory = newActiveMQConnectionFactory( ActiveMQConnection.DEFAULT_USER, ActiveMQConnection.DEFAULT_PASSWORD, "tcp://localhost:61616" ); try { // 构造从工厂得到连接对象 connection =connectionFactory.createConnection(); connection.setClientID( "clientID001" ); // 启动 connection.start(); // 获取操作连接 session = connection.createSession( false , Session.AUTO_ACKNOWLEDGE); // 获取session Topic topic = session.createTopic( "MQ_test" ); // 得到消息生成者【发送者】 consumer = session.createDurableSubscriber(topic, "MQ_sub" ); while ( true ){ //设置接收者接收消息的时间,为了便于测试,这里谁定为100s TextMessagemessage = (TextMessage)consumer.receive( 100000 ); if ( null != message){ System.out.println( "收到消息" +message.getText()); } else break ; } } catch (Exception e){ e.printStackTrace(); } finally { try { if ( null != connection) connection.close(); } catch (Throwable ignore) { } } } } |
测试:
测试一:
A、先启动Sender类
B、再启动Receiver类
C、结果无任何记录被订阅
测试二:
A、先启动Receiver类,让Receiver在相关主题上进行订阅
B、停止Receiver类,再启动Sender类
C、待Sender类运行完成后,再启动Receiver类
D、结果发现相应主题的信息被订阅
Ø
ActiveMQ持久化方式的更多相关文章
- ActiveMQ持久化方式(转)
消息持久性对于可靠消息传递来说应该是一种比较好的方法,有了消息持久化,即使发送者和接受者不是同时在线或者消息中心在发送者发送消息后宕机了,在消息 中心重新启动后仍然可以将消息发送出去,如果把这种持久化 ...
- ActiveMQ入门之四--ActiveMQ持久化方式
消息持久性对于可靠消息传递来说应该是一种比较好的方法,有了消息持久化,即使发送者和接受者不是同时在线或者消息中心在发送者发送消息后宕机了,在消息中心重新启动后仍然可以将消息发送出去,如果把这种持久化和 ...
- ActiveMQ持久化及测试(转)
转:http://blog.csdn.net/xyw_blog/article/details/9128219 ActiveMQ持久化 消息持久性对于可靠消息传递来说应该是一种比较好的方法,有了消息持 ...
- ActiveMQ持久化消息
ActiveMQ的另一个问题就是只要是软件就有可能挂掉,挂掉不可怕,怕的是挂掉之后把信息给丢了,所以本节分析一下几种持久化方式: 一.持久化为文件 ActiveMQ默认就支持这种方式,只要在发消息时设 ...
- ActiveMQ持久化消息(转)
ActiveMQ的另一个问题就是只要是软件就有可能挂掉,挂掉不可怕,怕的是挂掉之后把信息给丢了,所以本节分析一下几种持久化方式: 一.持久化为文件 ActiveMQ默认就支持这种方式,只要在发消息时设 ...
- ActiveMQ持久化到MySQL以及使用SSL协议通讯
最近公司事情稍微少了点,研究下怎么优化几个系统的交互,因为我们目前使用的是长链接的同步接口,就考虑用下MQ来处理下.由于公司对安全有要求且和CA业务有关,则使用了SSL协议.此文使用的是Activem ...
- ActiveMQ持久化
ActiveMQ中,持久化是值对消息数据的持久化.在ActiveMQ中,默认的消息是保存在内存中的.当内存容量不足的时候,或ActiveMQ正常关闭的时候,会将内存中的未处理的消息持久化到磁盘中.具体 ...
- Activemq持久化之kahadb特性
介绍数据的持久化是很多系统都会涉及到的一个问题,尤其是redis,activemq这些数据主要是存储在内存中的.既然存在内存中,就会面临宕机时数据丢失的风险.这一问题的解决方案就是通过某种方式将数据写 ...
- Redis两种持久化方式(RDB&AOF)
爬虫和转载请注明原文地址;博客园蜗牛:http://www.cnblogs.com/tdws/p/5754706.html Redis所需内存 超过可用内存怎么办 Redis修改数据多线程并发—Red ...
随机推荐
- socket shutdown和close的区别
http://www.jianshu.com/p/eecab8d50697 shutdown() doesn't actually close the file descriptor—it just ...
- [转]Twemproxy 介绍与使用
Twemproxy是一种代理分片机制,由Twitter开源.Twemproxy作为代理,可接受来自多个程序的访问,按照路由规则,转发给后台的各个Redis服务器,再原路返回.该方案很好的解决了单个Re ...
- chrome浏览器调试报错:Failed to load resource: the server responsed width a status of 404 (Not Found)…http://127.0.0.1:5099/favicon.ico
chrome浏览器在调试的时候默认会查找根目录下的favicon.ico文件,如果不存在就会报错. 解决办法:F12,点击<top frame>左侧漏斗形状的filter,勾选上" ...
- 在ASP.NET MVC下有关上传图片脏数据的解决方案
在"在ASP.NET MVC下实现单个图片上传, 客户端服务端双重限制图片大小和格式, 服务端裁剪图片"中,已经实现了在客户端和服务端限制图片大小和格式,以及在服务端裁剪图片.但还 ...
- 在ASP.NET MVC中实现一种不同于平常的三级联动、级联方式, 可用于城市、车型选择等多层级联场景
三级或多级联动的场景经常会碰到,比如省.市.区,比如品牌.车系.车型,比如类别的多级联动......我们首先想到的是用三个select来展示,这是最通常的做法.但在另外一些场景中,比如确定搜索条件的时 ...
- 委托、Lambda表达式、事件系列06,使用Action实现观察者模式,体验委托和事件的区别
在"实现观察者模式(Observer Pattern)的2种方式"中,曾经通过接口的方式.委托与事件的方式实现过观察者模式.本篇体验使用Action实现此模式,并从中体验委托与事件 ...
- 将 nginx 安装成 windows 的方法
服务器这几天不稳定,经常性的重启(硬件问题),而且是windows环境,在其上跑了nginx,每次重启后需要手动启动nginx方能是整个系统正常. 所以就查找了下一种方法,能否将nginx做成wind ...
- 猫都能学会的Unity3D Shader入门指南(一)
动机 自己使用Unity3D也有一段时间了,但是很多时候是流于表面,更多地是把这个引擎简单地用作脚本控制,而对更深入一些的层次几乎没有了解.虽然说Unity引擎设计的初衷就是创建简单的不需要开发者操心 ...
- SharePoint Online 创建和使用栏
前言 本文介绍如何在Office 365中创建和使用栏. 正文 通过登录地址登录到Office 365的SharePoint Online站点中,我们可以在右上角的设置菜单中,进入网站内容: 找到我们 ...
- insert into select 多个表
INSERT INTO user_auth(userid, auth_plane_id) select user.user_id AS userid, plane.id AS auth_plane_i ...