項(xiàng)目中自定義了攔截器Filter,項(xiàng)目中使用了spring security,它也有對(duì)應(yīng)的攔截器,我想讓我自定義的Filter在spring security的攔截器前執(zhí)行。
因?yàn)槲易远x的攔截器,需要提前做一些邏輯處理;然后spring security的攔截器需要用到這部分的處理結(jié)果;所以我必須要想辦法讓我自定義的攔截器靠前執(zhí)行。
那就一起來看看spring security設(shè)置的攔截器的默認(rèn)優(yōu)先級(jí)等級(jí)是多少吧。
自定義攔截器如下:
@Slf4j
public class MyOncePerRequestFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
log.info("======== MyOncePerRequestFilter ========");
filterChain.doFilter(request, response);
}
}
@Configuration
public class Config {
@Bean
public FilterRegistrationBean<MyOncePerRequestFilter> i18nFilterRegistrationBean() {
FilterRegistrationBean<MyOncePerRequestFilter> registrationBean = new FilterRegistrationBean();
MyOncePerRequestFilter myOncePerRequestFilter = new MyOncePerRequestFilter();
registrationBean.setFilter(myOncePerRequestFilter);
registrationBean.addUrlPatterns("/*");
registrationBean.setOrder(-1);
return registrationBean;
}
}
spring security的簡(jiǎn)單配置如下:
@Slf4j
public class MyTokenStore implements TokenStore {
@Override
public OAuth2AccessToken readAccessToken(String tokenValue) {
log.info("======== readAccessToken ========");
return new DefaultOAuth2AccessToken(tokenValue);
}
@Override
public OAuth2Authentication readAuthentication(OAuth2AccessToken token) {
Authentication authentication = new AbstractAuthenticationToken(Sets.newHashSet()) {
{
super.setAuthenticated(true);
}
@Override
public Object getCredentials() {
return null;
}
@Override
public Object getPrincipal() {
return StringUtils.EMPTY;
}
};
OAuth2Request request =
new OAuth2Request(null, null, null, true,
Sets.newHashSet(), Sets.newHashSet(), null, null, null);
return new OAuth2Authentication(request, authentication);
}
@Override public OAuth2Authentication readAuthentication(String token) {
return null;
}
@Override
public void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
}
@Override public void removeAccessToken(OAuth2AccessToken token) {
}
@Override public void storeRefreshToken(OAuth2RefreshToken refreshToken, OAuth2Authentication authentication) {
}
@Override public OAuth2RefreshToken readRefreshToken(String tokenValue) {
return null;
}
@Override public OAuth2Authentication readAuthenticationForRefreshToken(OAuth2RefreshToken token) {
return null;
}
@Override public void removeRefreshToken(OAuth2RefreshToken token) {
}
@Override public void removeAccessTokenUsingRefreshToken(OAuth2RefreshToken refreshToken) {
}
@Override public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) {
return null;
}
@Override public Collection<OAuth2AccessToken> findTokensByClientIdAndUserName(String clientId, String userName) {
return null;
}
@Override public Collection<OAuth2AccessToken> findTokensByClientId(String clientId) {
return null;
}
}
@Configuration
@EnableResourceServer
@EnableWebSecurity
public class MyResourceServerConfigurerAdapter extends ResourceServerConfigurerAdapter {
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
MyTokenStore tokenStore = new MyTokenStore();
resources.tokenStore(tokenStore);
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests().anyRequest().authenticated()
.and().anonymous().key("anonymousUser")
.and().httpBasic();
}
}
啟動(dòng)類如下:
@RestController
public class MyController {
@GetMapping("/hello")
public String hello() {
return "hello,world!";
}
}
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class Starter {
public static void main(String[] args) {
SpringApplication.run(Starter.class, args);
}
}
啟動(dòng)后,訪問 http://127.0.0.1:8080/hello?access_token=123
日志打印如下:
102149 [http-nio-8080-exec-1] INFO c.e.l.s.mvc.security.MyTokenStore - ======== readAccessToken ========
102149 [http-nio-8080-exec-1] INFO c.e.l.s.m.s.MyOncePerRequestFilter - ======== MyOncePerRequestFilter ========
從結(jié)果可以看出,spring security的攔截器是比我們自定義的攔截器先執(zhí)行的,而我們自定義的攔截器的優(yōu)先級(jí)是registrationBean.setOrder(-1)
我猜應(yīng)該是這個(gè)值決定了執(zhí)行順序,那就帶著這個(gè)猜想往下看一下吧。
在之前的配置中,我們將自定義的攔截器順序置為-1
我們先在MyOncePerRequestFilter.doFilterInternal打個(gè)斷點(diǎn),看一下執(zhí)行鏈的順序:
從這條鏈中,我們猜測(cè)springSecurityFilterChain的order是-100,我們自定義的攔截器是在它后面的
那我們直接把我們的攔截器設(shè)置成-101,registrationBean.setOrder(-101);,再來嘗試一下:
從斷點(diǎn)結(jié)果可以看出,我們的設(shè)置是有效的,并且起到了作用,而且打印日志也說明了結(jié)果,如下:
11956 [http-nio-8080-exec-1] INFO c.e.l.s.m.s.MyOncePerRequestFilter - ======== MyOncePerRequestFilter ========
98419 [http-nio-8080-exec-1] INFO c.e.l.s.mvc.security.MyTokenStore - ======== readAccessToken ========
這個(gè)過程是極其枯燥的,所以就先給結(jié)果了,如下:
spring security的攔截器鏈?zhǔn)窃谙旅孢@部分創(chuàng)建的:
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(SecurityProperties.class)
@ConditionalOnClass({ AbstractSecurityWebApplicationInitializer.class, SessionCreationPolicy.class })
@AutoConfigureAfter(SecurityAutoConfiguration.class)
public class SecurityFilterAutoConfiguration {
private static final String DEFAULT_FILTER_NAME = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME;
@Bean
@ConditionalOnBean(name = DEFAULT_FILTER_NAME)
public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(
SecurityProperties securityProperties) {
DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean(
DEFAULT_FILTER_NAME);
registration.setOrder(securityProperties.getFilter().getOrder()); // 這里
registration.setDispatcherTypes(getDispatcherTypes(securityProperties));
return registration;
}
}
public abstract class AbstractSecurityWebApplicationInitializer implements WebApplicationInitializer {
public static final String DEFAULT_FILTER_NAME = "springSecurityFilterChain";
}
@ConfigurationProperties(prefix = "spring.security")
public class SecurityProperties {
public static final int DEFAULT_FILTER_ORDER = OrderedFilter.REQUEST_WRAPPER_FILTER_MAX_ORDER - 100; // 這里
private final Filter filter = new Filter();
public Filter getFilter() {
return this.filter;
}
public static class Filter {
private int order = DEFAULT_FILTER_ORDER; // 這里
public int getOrder() {
return this.order;
}
public void setOrder(int order) {
this.order = order;
}
}
}
public interface OrderedFilter extends Filter, Ordered {
int REQUEST_WRAPPER_FILTER_MAX_ORDER = 0; // 這里
}
從上面的代碼可以看出,默認(rèn)值是-100,同樣也可以使用spring.security.filter.order來自定義值。
下面是尋找此過程的歷程:
繼續(xù)從這里開始,ApplicationFilterChain.internalDoFilter如下:
可以看出所有的攔截器都是在filters中,我們可以看這個(gè)值是怎么來的,通過調(diào)試,是在ApplicationFilterChain.addFilter這個(gè)地方,如下:
它是被ApplicationFilterFactory.createFilterChain調(diào)用的,如下:
所以filters是根據(jù)filterMaps來添加的,我們?cè)賮砜匆幌耭ilterMaps是怎么來的,一共涉及到兩個(gè)地方,如下:
StandardContext.addFilterMap和StandardContext.addFilterMapBefore如下:
看一下調(diào)用鏈:
原來是被ServletWebServerApplicationContext.selfInitialize調(diào)用的,如下:
ServletWebServerApplicationContext.getServletContextInitializerBeans如下:
ServletContextInitializerBeans構(gòu)造函數(shù)如下:
所有的攔截器都是通過addServletContextInitializerBeans(beanFactory);和addAdaptableBeans(beanFactory);來把bean加進(jìn)來的
經(jīng)過一番調(diào)試,終于找到spring security這個(gè)攔截器定義順序的位置,SecurityFilterAutoConfiguration.securityFilterChainRegistration如下:
可以看到SecurityProperties securityProperties是注入進(jìn)來的,找到這個(gè)類看一下,securityProperties.filter.order如下:
@ConfigurationProperties(prefix = "spring.security")
public class SecurityProperties {
public static final int DEFAULT_FILTER_ORDER = OrderedFilter.REQUEST_WRAPPER_FILTER_MAX_ORDER - 100;
private final Filter filter = new Filter();
private final User user = new User();
public static class Filter {
/**
* Security filter chain order.
*/
private int order = DEFAULT_FILTER_ORDER;
public int getOrder() {
return this.order;
}
public void setOrder(int order) {
this.order = order;
}
}
}
public interface OrderedFilter extends Filter, Ordered {
/**
* Filters that wrap the servlet request should be ordered less than or equal to this.
*/
int REQUEST_WRAPPER_FILTER_MAX_ORDER = 0;
}
到此我們也找到了這個(gè)默認(rèn)值,是根據(jù)spring.security.filter.order來決定的,默認(rèn)值是-100
第一種就是修改自己的順序:
@Configuration
public class Config {
@Bean
public FilterRegistrationBean<MyOncePerRequestFilter> i18nFilterRegistrationBean() {
FilterRegistrationBean<MyOncePerRequestFilter> registrationBean = new FilterRegistrationBean();
MyOncePerRequestFilter myOncePerRequestFilter = new MyOncePerRequestFilter();
registrationBean.setFilter(myOncePerRequestFilter);
registrationBean.addUrlPatterns("/*");
registrationBean.setOrder(-101); // 這里
return registrationBean;
}
}
第二種就是修改spring security攔截器的順序:
spring:
security:
filter:
order: 0
大家可以自己跑跑試試看,完結(jié)撒花~~~~~~
原文地址:https://www.cnblogs.com/eaglelihh/p/15009562.html
述:本文將討論如何用最簡(jiǎn)單的術(shù)語在網(wǎng)站上運(yùn)行 C# 代碼。半技術(shù)講座我使用了 wasm-tools-net7,這是一個(gè)基于 wasm-tools 的工作負(fù)載,沒有包含任何額外的包。我的重點(diǎn)是簡(jiǎn)單性和主要主題。徹底了解該主題可提供完成所有其他任務(wù)所需的信息。如何工作?WebAssembly 工作原理:序列圖創(chuàng)建演示創(chuàng)建項(xiàng)目我用的是net7,但這取決于你。Dotnet new console -o WASM_Demo cd WASM_Demo Dotnet workload install wasm-tools-net7此時(shí),需要對(duì) csproj 文件進(jìn)行修改。Project Sdk=Mi
本文將討論如何用最簡(jiǎn)單的術(shù)語在網(wǎng)站上運(yùn)行 C# 代碼。
我使用了 wasm-tools-net7,這是一個(gè)基于 wasm-tools 的工作負(fù)載,沒有包含任何額外的包。我的重點(diǎn)是簡(jiǎn)單性和主要主題。徹底了解該主題可提供完成所有其他任務(wù)所需的信息。
WebAssembly 工作原理:序列圖
Dotnet new console -o WASM_Demo
cd WASM_Demo
Dotnet workload install wasm-tools-net7
此時(shí),需要對(duì) csproj 文件進(jìn)行修改。
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RuntimeIdentifier>browser-wasm</RuntimeIdentifier>
<WasmMainJSPath>main.js</WasmMainJSPath>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<WasmExtraFilesToDeploy Include="index.html" />
<WasmExtraFilesToDeploy Include="main.js" />
</ItemGroup>
</Project>
我們添加了什么:
返回到program.cs文件,需要考慮某些規(guī)則。
讓我們舉個(gè)例子。
using System.Runtime.InteropServices.JavaScript;
namespace WASM_Demo;
public partial class Program
{
static void Main(string[] args) { }
[JSExport]
public static string Response()
{
return """
<h1>
Hello World
</h1>
""";
}
}
沒關(guān)系,但是我們?nèi)绾卧跒g覽器中運(yùn)行此代碼?
運(yùn)行這個(gè)程序的代碼是dotnet.js的,它自帶了wasm-tools,所以沒有必要擔(dān)心它。要使用此dotnet.js,我們只需使用一個(gè)名為 main.js 的文件。
import { dotnet } from './dotnet.js'
const is_browser = typeof window != "undefined";
if (!is_browser) throw new Error(`Expected to be running in a browser`);
const { setModuleImports, getAssemblyExports, getConfig, runMainAndExit } = await dotnet
.withDiagnosticTracing(false)
.withApplicationArgumentsFromQuery()
.create();
const config = getConfig();
const exports = await getAssemblyExports(config.mainAssemblyName);
const html =
exports
.WASM_Demo // Namespace
.Program // Class Name
.Response(); // Function Name
// Regular javascript code
document.getElementById("app").innerHTML = `${html}`;
await runMainAndExit(config.mainAssemblyName, [] /* Console App Args */);
index.html頁面的模板已經(jīng)準(zhǔn)備完畢。
<!DOCTYPE html>
<html lang="en">
<head>
<title>WASM Demo</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="modulepreload" href="./dotnet.js" />
</head>
<body>
<main id="app"></main>
<script type="module" src="./main.js"></script>
</body>
</html>
現(xiàn)在,讓我們?cè)倏匆槐檫@個(gè)過程,
我們還有一件事要做,你需要打開一個(gè)名為 runtimeconfig.template.json 的文件,并將以下 JSON 數(shù)據(jù)放入其中。
{
"wasmHostProperties": {
"perHostConfig": [
{
"name": "browser",
"html-path": "index.html",
"Host": "browser"
}
]
}
}
我們已經(jīng)到了盡頭,程序現(xiàn)在可以運(yùn)行了。唯一需要的命令是:
Dotnet run -c Release
我可以托管所有文件而不是 wasm-tools 嗎?又是如何做到的呢?
當(dāng)然,但它可能會(huì)變得有點(diǎn)復(fù)雜,你用 wasm-tools 制作的項(xiàng)目不能用于任何其他目的,即控制臺(tái)應(yīng)用程序不起作用,wasm-tools 可以工作。因?yàn)槲覀冞x擇 browser-wasm 作為 RuntimeIdentifier,并且多個(gè) RuntimeIdentifiers 在 .NET 中不可用。作為替代方法,您可以打開兩個(gè)項(xiàng)目,將第一個(gè)項(xiàng)目設(shè)置為 WASM 項(xiàng)目,然后在第二個(gè)項(xiàng)目中將其設(shè)置為控制臺(tái)應(yīng)用程序,然后生成第一個(gè)項(xiàng)目并托管輸出文件夾,所有 DLL 和文件都將在那里。
這個(gè)演示只是索引文件,我可以做多頁嗎?又是如何做到的呢?
當(dāng)然,但這比你想象的要難得多,因?yàn)檫@樣做的方法是一種叫做SPA(單頁應(yīng)用程序)的方法,用戶總是在同一頁面上,只是內(nèi)容發(fā)生了變化。有多種方法可以做到這一點(diǎn)。所以它可以用你的創(chuàng)造力來完成。
我可以像計(jì)數(shù)器一樣做動(dòng)態(tài)代碼嗎?又是如何做到的呢?
_是的,我也這樣做了,你可以一遍又一遍地調(diào)用 C# 函數(shù),如果你只是將導(dǎo)出綁定到 window 對(duì)象,你可以從每個(gè) JavaScript 代碼中調(diào)用它。
由于MQ是異步處理消息的,所以MQ不適合做同步處理操作,如果需要及時(shí)的返回處理結(jié)果請(qǐng)不要用MQ;
優(yōu)點(diǎn): 解耦,利用MQ我們可以很好的給我們系統(tǒng)解耦,特別是分布式/微服系統(tǒng)!
原來的同步操作,可以用異步處理,也可以帶來更快的響應(yīng)速度;
場(chǎng)景 (1)
系統(tǒng)解耦,用戶系統(tǒng)或者其他系統(tǒng)需要發(fā)送短信可以通過 MQ 執(zhí)行;很好的將 用戶系統(tǒng) 和 短信系統(tǒng)進(jìn)行解耦;
場(chǎng)景(2)
順序執(zhí)行的任務(wù)場(chǎng)景,假設(shè) A B C 三個(gè)任務(wù),B需要等待 A完成才去執(zhí)行,C需要等待B完成才去執(zhí)行;
我見過一些同學(xué)的做法是 ,用 三個(gè)定時(shí)器 錯(cuò)開時(shí)間去執(zhí)行的,假設(shè) A定時(shí)器 9 點(diǎn)執(zhí)行, B 定時(shí)器 10 點(diǎn)執(zhí)行 , C 11 點(diǎn)執(zhí)行 , 類似這樣子;
這樣做其實(shí)是 不安全的, 因?yàn)?后一個(gè)任務(wù) 無法知道 前一個(gè)任務(wù)是否 真的執(zhí)行了! 假設(shè) A 宕機(jī)了, 到 10 點(diǎn) B 定時(shí)去 執(zhí)行,這時(shí)候 數(shù)據(jù)就會(huì)產(chǎn)生異常!
當(dāng)我們 引入 MQ 后 可以這么做, A執(zhí)行完了 發(fā)送 消息給 B ,B收到消息后 執(zhí)行,C 類似,收到 B消息后執(zhí)行;
場(chǎng)景(3)
支付網(wǎng)關(guān)的通知,我們的系統(tǒng)常常需要接入支付功能,微信或者支付寶通常會(huì)以回調(diào)的形式通知我們系統(tǒng)支付結(jié)果。
我們可以將我們的支付網(wǎng)關(guān)獨(dú)立出來,通過MQ通知我們業(yè)務(wù)系統(tǒng)進(jìn)行處理,這樣處理有利于系統(tǒng)的解耦和擴(kuò)展!
假設(shè)我們還有一個(gè)積分系統(tǒng),用戶支付成功,給用戶添加積分。只需要積分系統(tǒng)監(jiān)聽這個(gè)消息,并處理積分就好,無需去修改再去修改網(wǎng)關(guān)層代碼!
如果沒有使用MQ ,我是不是還得去修改網(wǎng)關(guān)系統(tǒng)的代碼,遠(yuǎn)程調(diào)用增加積分的接口?
這就是使用了MQ的好處,解耦和擴(kuò)展!
當(dāng)然我們的轉(zhuǎn)發(fā)規(guī)則也要保證每個(gè)感興趣的隊(duì)列能獲取到消息!
場(chǎng)景(4)
微服/分布式系統(tǒng),分布式事務(wù) - 最終一致性 處理方案!
詳情: 分布式事務(wù)處理方案,微服事務(wù)處理方案
場(chǎng)景(5)
我們以前的做法是 通常啟用一個(gè)定時(shí)器,每分鐘或者每小時(shí),去跑一次取出需要處理的訂單或其他數(shù)據(jù)進(jìn)行處理。
這種做法一個(gè)是 效率比較低,如果數(shù)據(jù)量大的話,每次都要掃庫,非常要命!
再者時(shí)效性不是很高,最差的時(shí)候可能需要等待一輪時(shí)長(zhǎng)!
還有可能出現(xiàn)重復(fù)執(zhí)行的結(jié)果,時(shí)效和輪詢的頻率難以平衡!
利用MQ(Rabbitmq),DLX (Dead Letter Exchanges)和 消息的 TTL (Time-To-Live Extensions)特性。我們可以高效的完成這個(gè)任務(wù)場(chǎng)景!不需要掃庫,時(shí)效性更好!
DLX:http://www.rabbitmq.com/dlx.html,
TTL:http://www.rabbitmq.com/ttl.html#per-message-ttl
原理:
發(fā)送到隊(duì)列的消息,可以設(shè)置一個(gè)存活時(shí)間 TTL,在存活時(shí)間內(nèi)沒有被消費(fèi),可以設(shè)置這個(gè)消息轉(zhuǎn)發(fā)到其他隊(duì)列里面去;然后我們從這個(gè)其他隊(duì)列里面消費(fèi)執(zhí)行我們的任務(wù),這樣就可以達(dá)到一個(gè)消息延時(shí)的效果!
設(shè)置過期時(shí)間:
過期時(shí)間可以統(tǒng)一設(shè)置到消息隊(duì)列里面,也可以單獨(dú)設(shè)置到某個(gè)消息!
PS 如果消息設(shè)置了過期時(shí)間,發(fā)生到了設(shè)置有過期時(shí)間的隊(duì)列,已隊(duì)列設(shè)置的過期時(shí)間為準(zhǔn)!
已 SpringBoot 為例:
配置轉(zhuǎn)發(fā)隊(duì)列和被轉(zhuǎn)發(fā)隊(duì)列:
@Component @Configuration public class RabbitMqConfig { @Bean public Queue curQueue() { Map<String, Object> args = new HashMap<String, Object>(); //超時(shí)后的轉(zhuǎn)發(fā)器 過期轉(zhuǎn)發(fā)到 delay_queue_exchange args.put("x-dead-letter-exchange", "delay_queue_exchange"); //routingKey 轉(zhuǎn)發(fā)規(guī)則 args.put("x-dead-letter-routing-key", "user.#"); //過期時(shí)間 20 秒 args.put("x-message-ttl", 20000); return new Queue("cur_queue", false, false, false, args); } @Bean public Queue delayQueue() { return new Queue("delay_queue"); } @Bean TopicExchange exchange() { //當(dāng)前隊(duì)列 return new TopicExchange("cur_queue_exchange"); } @Bean TopicExchange exchange2() { //被轉(zhuǎn)發(fā)的隊(duì)列 return new TopicExchange("delay_queue_exchange"); } @Bean Binding bindingHelloQueue(Queue curQueue, TopicExchange exchange) { //綁定隊(duì)列到轉(zhuǎn)發(fā)器 return BindingBuilder.bind(curQueue).to(exchange).with("user.#"); } @Bean Binding bindingHelloQueue2(Queue delayQueue, TopicExchange exchange2) { return BindingBuilder.bind(delayQueue).to(exchange2).with("user.#"); } }
發(fā)生消息:
@Component public class MqEventSender { Logger logger = LoggerFactory.getLogger(MqEventSender.class); @Autowired private RabbitTemplate rabbitTemplate; /** * 消息沒有設(shè)置 時(shí)間 * 發(fā)生到隊(duì)列 cur_queue_exchange * @param msg */ public void sendMsg(String msg) { logger.info("發(fā)送消息: " + msg); rabbitTemplate.convertAndSend("cur_queue_exchange", "user.ss", msg); } /** * 消息設(shè)置時(shí)間 * 發(fā)生到隊(duì)列 cur_queue_exchange * @param msg */ public void sendMsgWithTime(String msg) { logger.info("發(fā)送消息: " + msg); MessageProperties messageProperties = new MessageProperties(); //過期時(shí)間設(shè)置 10 秒 messageProperties.setExpiration("10000"); Message message = rabbitTemplate.getMessageConverter().toMessage(msg, messageProperties); rabbitTemplate.convertAndSend("cur_queue_exchange", "user.ss", message); } }
消息監(jiān)聽:
監(jiān)聽 的隊(duì)列是 delay_queue 而不是 cur_queue;
PS cur_queue 不應(yīng)該有監(jiān)聽者,否則消息被消費(fèi)達(dá)不到想要的延時(shí)消息效果!
/** * Created by linli on 2017/8/21. * 監(jiān)聽 被丟到 超時(shí)隊(duì)列內(nèi)容 */ @Component @RabbitListener(queues = "delay_queue") public class DelayQueueListener { public static Logger logger = LoggerFactory.getLogger(AddCommentsEventListener.class); @RabbitHandler public void process(@Payload String msg) { logger.info("收到消息 "+msg); } }
測(cè)試:
/** * Created by linli on 2017/8/21. */ @RestController @RequestMapping("/test") public class TestContorller { @Autowired MqEventSender sender; @RequestMapping("/mq/delay") public String test() { sender.sendMsg("隊(duì)列延時(shí)消息!"); sender.sendMsgWithTime("消息延時(shí)消息!"); return ""; } }
結(jié)果:
觀察結(jié)果發(fā)現(xiàn):發(fā)送時(shí)間 和 收到時(shí)間 間隔 20秒 ;
我們給消息設(shè)置的 10 秒 TTL 時(shí)間沒有生效!驗(yàn)證了 : 如果消息設(shè)置了過期時(shí)間,發(fā)生到了設(shè)置有過期時(shí)間的隊(duì)列,已隊(duì)列設(shè)置的過期時(shí)間為準(zhǔn)!
如果希望每個(gè)消息都要自己的存活時(shí)間,發(fā)送到隊(duì)列 不要設(shè)置
args.put(“x-message-ttl”, 20000);
消息的過期時(shí)間 設(shè)置在隊(duì)列還是消息,根據(jù)自己的業(yè)務(wù)場(chǎng)景去定!
MQ 是一個(gè)跨進(jìn)程的消息隊(duì)列,我們可以很好的利用他進(jìn)行系統(tǒng)的解耦;
引入MQ會(huì)給系統(tǒng)帶來一定的復(fù)雜度,需要評(píng)估!
MQ 適合做異步任務(wù),不適合做同步任務(wù)!
*請(qǐng)認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。