Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 久操视频在线播放,国产成人精品曰本亚洲,久久免费观看国产精品

          整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          我自定義的攔截器為什么會靠后執行?

          項目中自定義了攔截器Filter,項目中使用了spring security,它也有對應的攔截器,我想讓我自定義的Filter在spring security的攔截器前執行。

          因為我自定義的攔截器,需要提前做一些邏輯處理;然后spring security的攔截器需要用到這部分的處理結果;所以我必須要想辦法讓我自定義的攔截器靠前執行。

          那就一起來看看spring security設置的攔截器的默認優先級等級是多少吧。

          模擬場景

          自定義攔截器如下:

          @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的簡單配置如下:

          @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();
              }
          }
          

          啟動類如下:

          @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);
              }
          }
          

          啟動后,訪問 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 ======== 
          

          從結果可以看出,spring security的攔截器是比我們自定義的攔截器先執行的,而我們自定義的攔截器的優先級是registrationBean.setOrder(-1)

          我猜應該是這個值決定了執行順序,那就帶著這個猜想往下看一下吧。

          是不是因為order的值

          在之前的配置中,我們將自定義的攔截器順序置為-1

          我們先在MyOncePerRequestFilter.doFilterInternal打個斷點,看一下執行鏈的順序:

          從這條鏈中,我們猜測springSecurityFilterChain的order是-100,我們自定義的攔截器是在它后面的

          那我們直接把我們的攔截器設置成-101,registrationBean.setOrder(-101);,再來嘗試一下:

          從斷點結果可以看出,我們的設置是有效的,并且起到了作用,而且打印日志也說明了結果,如下:

          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 ======== 
          

          找出在哪里賦予的order值

          這個過程是極其枯燥的,所以就先給結果了,如下:

          spring security的攔截器鏈是在下面這部分創建的:

          @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; // 這里
          }
          

          從上面的代碼可以看出,默認值是-100,同樣也可以使用spring.security.filter.order來自定義值。

          下面是尋找此過程的歷程:

          繼續從這里開始,ApplicationFilterChain.internalDoFilter如下:

          可以看出所有的攔截器都是在filters中,我們可以看這個值是怎么來的,通過調試,是在ApplicationFilterChain.addFilter這個地方,如下:

          它是被ApplicationFilterFactory.createFilterChain調用的,如下:

          所以filters是根據filterMaps來添加的,我們再來看一下filterMaps是怎么來的,一共涉及到兩個地方,如下:

          StandardContext.addFilterMap和StandardContext.addFilterMapBefore如下:

          看一下調用鏈:

          原來是被ServletWebServerApplicationContext.selfInitialize調用的,如下:

          ServletWebServerApplicationContext.getServletContextInitializerBeans如下:

          ServletContextInitializerBeans構造函數如下:

          所有的攔截器都是通過addServletContextInitializerBeans(beanFactory);和addAdaptableBeans(beanFactory);來把bean加進來的

          經過一番調試,終于找到spring security這個攔截器定義順序的位置,SecurityFilterAutoConfiguration.securityFilterChainRegistration如下:

          可以看到SecurityProperties securityProperties是注入進來的,找到這個類看一下,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;
          }
          

          到此我們也找到了這個默認值,是根據spring.security.filter.order來決定的,默認值是-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
          

          大家可以自己跑跑試試看,完結撒花~~~~~~

          原文地址:https://www.cnblogs.com/eaglelihh/p/15009562.html

          述:本文將討論如何用最簡單的術語在網站上運行 C# 代碼。半技術講座我使用了 wasm-tools-net7,這是一個基于 wasm-tools 的工作負載,沒有包含任何額外的包。我的重點是簡單性和主要主題。徹底了解該主題可提供完成所有其他任務所需的信息。如何工作?WebAssembly 工作原理:序列圖創建演示創建項目我用的是net7,但這取決于你。Dotnet new console -o WASM_Demo cd WASM_Demo Dotnet workload install wasm-tools-net7此時,需要對 csproj 文件進行修改。Project Sdk=Mi

          本文將討論如何用最簡單的術語在網站上運行 C# 代碼。

          半技術講座

          我使用了 wasm-tools-net7,這是一個基于 wasm-tools 的工作負載,沒有包含任何額外的包。我的重點是簡單性和主要主題。徹底了解該主題可提供完成所有其他任務所需的信息。

          如何工作?

          WebAssembly 工作原理:序列圖

          創建演示

          創建項目

          • 我用的是net7,但這取決于你。
          Dotnet new console -o WASM_Demo  
            
          cd WASM_Demo  
            
          Dotnet workload install wasm-tools-net7
          

          此時,需要對 csproj 文件進行修改。

          <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>

          我們添加了什么:

          • RuntimeIdentifier (wasm-tools 需要)
          • WasmMainJSPath (wasm-tools 需要)
          • AllowUnsafeBlocks(JSExportAttribute 需要不安全的代碼)
          • ItemGroup (Include as resource)導入 index.html導入main.js

          返回到program.cs文件,需要考慮某些規則。

          • 類必須是公共的和部分的。
          • 函數必須是公共的和靜態的,并且必須使用 [JSExport] 進行屬性化。

          讓我們舉個例子。

          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>
                         """;
              }
          }

          沒關系,但是我們如何在瀏覽器中運行此代碼?

          運行這個程序的代碼是dotnet.js的,它自帶了wasm-tools,所以沒有必要擔心它。要使用此dotnet.js,我們只需使用一個名為 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頁面的模板已經準備完畢。

          <!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>

          現在,讓我們再看一遍這個過程,

          • HTTP 請求傳入
          • WASM-Tools 處理此問題并發送index.html文件。
          • index.html文件請求dotnet.js和main.js文件,dotnet.js由 WASM-Tools 發送,main.js是我們的自定義代碼。
          • 通過在 main.js 中使用 dotnet.js,我們可以進入 C# 代碼中的 Program 類,調用 Response 函數并在 main.js 中進行我們想要的任何客戶端更改。

          我們還有一件事要做,你需要打開一個名為 runtimeconfig.template.json 的文件,并將以下 JSON 數據放入其中。

          {
            "wasmHostProperties": {
              "perHostConfig": [
                {
                  "name": "browser",
                  "html-path": "index.html",
                  "Host": "browser"
                }
              ]
            }
          }

          我們已經到了盡頭,程序現在可以運行了。唯一需要的命令是:

          Dotnet run -c Release
          

          常見問題

          我可以托管所有文件而不是 wasm-tools 嗎?又是如何做到的呢?

          當然,但它可能會變得有點復雜,你用 wasm-tools 制作的項目不能用于任何其他目的,即控制臺應用程序不起作用,wasm-tools 可以工作。因為我們選擇 browser-wasm 作為 RuntimeIdentifier,并且多個 RuntimeIdentifiers 在 .NET 中不可用。作為替代方法,您可以打開兩個項目,將第一個項目設置為 WASM 項目,然后在第二個項目中將其設置為控制臺應用程序,然后生成第一個項目并托管輸出文件夾,所有 DLL 和文件都將在那里。

          這個演示只是索引文件,我可以做多頁嗎?又是如何做到的呢?

          當然,但這比你想象的要難得多,因為這樣做的方法是一種叫做SPA(單頁應用程序)的方法,用戶總是在同一頁面上,只是內容發生了變化。有多種方法可以做到這一點。所以它可以用你的創造力來完成。

          我可以像計數器一樣做動態代碼嗎?又是如何做到的呢?

          _是的,我也這樣做了,你可以一遍又一遍地調用 C# 函數,如果你只是將導出綁定到 window 對象,你可以從每個 JavaScript 代碼中調用它。

          Q簡介

          • 什么是MQ
          • 跨進程的消息隊列,主要角色包括生產者與消費者。
          • 生產者只負責生產信息,無法感知消費者是誰,消息怎么處理,處理結果是什么。
          • 消費者負責接收及處理消息,無法感知生產者是誰,怎么產生的。
          • Mq能做什么?
          • MQ 特性一般有異步,吞吐量大 ,延時低;
          • 適合做:
          1. 投遞異步通知。
          2. 限流,削峰谷。
          3. 可靠事件,處理數據一致性。
          4. 利用一些特性,可以做定時任務。
          5. 等….

          由于MQ是異步處理消息的,所以MQ不適合做同步處理操作,如果需要及時的返回處理結果請不要用MQ;

          • MQ 個系統帶來了什么?
          • 缺點:增加了系統的復雜性,除了代碼組件接入以外還需要考慮,高可用,集群,消息的可靠性等問題!
          • 生產者:消息發送怎么保證可靠性,怎么保證不重復!
          • 消費者:怎么保證冪等性,接收到重復消息怎么處理!
          • 還有會帶來的處理延時等問題!

          優點: 解耦,利用MQ我們可以很好的給我們系統解耦,特別是分布式/微服系統!

          原來的同步操作,可以用異步處理,也可以帶來更快的響應速度;

          • 哪些場景可以使用MQ

          場景 (1)

          系統解耦,用戶系統或者其他系統需要發送短信可以通過 MQ 執行;很好的將 用戶系統 和 短信系統進行解耦;

          場景(2)

          順序執行的任務場景,假設 A B C 三個任務,B需要等待 A完成才去執行,C需要等待B完成才去執行;

          我見過一些同學的做法是 ,用 三個定時器 錯開時間去執行的,假設 A定時器 9 點執行, B 定時器 10 點執行 , C 11 點執行 , 類似這樣子;

          這樣做其實是 不安全的, 因為 后一個任務 無法知道 前一個任務是否 真的執行了! 假設 A 宕機了, 到 10 點 B 定時去 執行,這時候 數據就會產生異常!

          當我們 引入 MQ 后 可以這么做, A執行完了 發送 消息給 B ,B收到消息后 執行,C 類似,收到 B消息后執行;

          場景(3)

          支付網關的通知,我們的系統常常需要接入支付功能,微信或者支付寶通常會以回調的形式通知我們系統支付結果。

          我們可以將我們的支付網關獨立出來,通過MQ通知我們業務系統進行處理,這樣處理有利于系統的解耦和擴展!

          假設我們還有一個積分系統,用戶支付成功,給用戶添加積分。只需要積分系統監聽這個消息,并處理積分就好,無需去修改再去修改網關層代碼!

          如果沒有使用MQ ,我是不是還得去修改網關系統的代碼,遠程調用增加積分的接口?

          這就是使用了MQ的好處,解耦和擴展!

          當然我們的轉發規則也要保證每個感興趣的隊列能獲取到消息!



          場景(4)

          微服/分布式系統,分布式事務 - 最終一致性 處理方案!

          詳情: 分布式事務處理方案,微服事務處理方案

          場景(5)

          • 消息延時隊列,可做些定時任務,不固定時間執行的定時任務。
          • 例如:用戶下單后如果24小時未支付訂單取消;
          • 確認收貨后2天后沒有評價自動好評;
          • 等...

          我們以前的做法是 通常啟用一個定時器,每分鐘或者每小時,去跑一次取出需要處理的訂單或其他數據進行處理。

          這種做法一個是 效率比較低,如果數據量大的話,每次都要掃庫,非常要命!

          再者時效性不是很高,最差的時候可能需要等待一輪時長!

          還有可能出現重復執行的結果,時效和輪詢的頻率難以平衡!

          利用MQ(Rabbitmq),DLX (Dead Letter Exchanges)和 消息的 TTL (Time-To-Live Extensions)特性。我們可以高效的完成這個任務場景!不需要掃庫,時效性更好!

          DLX:http://www.rabbitmq.com/dlx.html,

          TTL:http://www.rabbitmq.com/ttl.html#per-message-ttl

          原理:

          發送到隊列的消息,可以設置一個存活時間 TTL,在存活時間內沒有被消費,可以設置這個消息轉發到其他隊列里面去;然后我們從這個其他隊列里面消費執行我們的任務,這樣就可以達到一個消息延時的效果!



          設置過期時間:

          過期時間可以統一設置到消息隊列里面,也可以單獨設置到某個消息!

          PS 如果消息設置了過期時間,發生到了設置有過期時間的隊列,已隊列設置的過期時間為準!

          已 SpringBoot 為例:

          配置轉發隊列和被轉發隊列:

          @Component
          @Configuration
          public class RabbitMqConfig {
           @Bean
           public Queue curQueue() {
           Map<String, Object> args = new HashMap<String, Object>();
           //超時后的轉發器 過期轉發到 delay_queue_exchange
           args.put("x-dead-letter-exchange", "delay_queue_exchange");
           //routingKey 轉發規則
           args.put("x-dead-letter-routing-key", "user.#");
           //過期時間 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() {
           //當前隊列
           return new TopicExchange("cur_queue_exchange");
           }
           @Bean
           TopicExchange exchange2() {
           //被轉發的隊列
           return new TopicExchange("delay_queue_exchange");
           }
           @Bean
           Binding bindingHelloQueue(Queue curQueue, TopicExchange exchange) {
           //綁定隊列到轉發器
           return BindingBuilder.bind(curQueue).to(exchange).with("user.#");
           }
           @Bean
           Binding bindingHelloQueue2(Queue delayQueue, TopicExchange exchange2) {
           return BindingBuilder.bind(delayQueue).to(exchange2).with("user.#");
           }
          }
          

          發生消息:

          @Component
          public class MqEventSender {
           Logger logger = LoggerFactory.getLogger(MqEventSender.class);
           @Autowired
           private RabbitTemplate rabbitTemplate;
           /**
           * 消息沒有設置 時間
           * 發生到隊列 cur_queue_exchange
           * @param msg
           */
           public void sendMsg(String msg) {
           logger.info("發送消息: " + msg);
           rabbitTemplate.convertAndSend("cur_queue_exchange", "user.ss", msg);
           }
           /**
           * 消息設置時間
           * 發生到隊列 cur_queue_exchange
           * @param msg
           */
           public void sendMsgWithTime(String msg) {
           logger.info("發送消息: " + msg);
           MessageProperties messageProperties = new MessageProperties();
           //過期時間設置 10 秒
           messageProperties.setExpiration("10000");
           Message message = rabbitTemplate.getMessageConverter().toMessage(msg, messageProperties);
           rabbitTemplate.convertAndSend("cur_queue_exchange", "user.ss", message);
           }
          }
          

          消息監聽:

          監聽 的隊列是 delay_queue 而不是 cur_queue;

          PS cur_queue 不應該有監聽者,否則消息被消費達不到想要的延時消息效果!

          /**
           * Created by linli on 2017/8/21.
           * 監聽 被丟到 超時隊列內容
           */
          @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);
           }
          }
          

          測試:

          /**
           * Created by linli on 2017/8/21.
           */
          @RestController
          @RequestMapping("/test")
          public class TestContorller {
           @Autowired
           MqEventSender sender;
           @RequestMapping("/mq/delay")
           public String test() {
           sender.sendMsg("隊列延時消息!");
           sender.sendMsgWithTime("消息延時消息!");
           return "";
           }
          }
          

          結果:



          觀察結果發現:發送時間 和 收到時間 間隔 20秒 ;

          我們給消息設置的 10 秒 TTL 時間沒有生效!驗證了 : 如果消息設置了過期時間,發生到了設置有過期時間的隊列,已隊列設置的過期時間為準!

          如果希望每個消息都要自己的存活時間,發送到隊列 不要設置

          args.put(“x-message-ttl”, 20000);

          消息的過期時間 設置在隊列還是消息,根據自己的業務場景去定!

          • 總結

          MQ 是一個跨進程的消息隊列,我們可以很好的利用他進行系統的解耦;

          引入MQ會給系統帶來一定的復雜度,需要評估!

          MQ 適合做異步任務,不適合做同步任務!


          主站蜘蛛池模板: 国产成人一区二区三区电影网站| 亚洲毛片αv无线播放一区| 国产成人一区二区三区高清| 久久一本一区二区三区| 亚洲熟妇无码一区二区三区导航| 精品视频在线观看你懂的一区| 亚洲国产视频一区| 中文字幕一区在线| 国产无吗一区二区三区在线欢| 人妖在线精品一区二区三区| 国产精品特级毛片一区二区三区| 国产免费一区二区三区免费视频| 亚洲一区二区三区久久久久| 久久4k岛国高清一区二区| 久久久久国产一区二区| 免费视频精品一区二区| 日本福利一区二区| 夜夜高潮夜夜爽夜夜爱爱一区| 91精品乱码一区二区三区| 无码乱人伦一区二区亚洲一 | 国产日韩AV免费无码一区二区三区| 国产精品一区在线麻豆 | 国产品无码一区二区三区在线| 精品人妻中文av一区二区三区| 秋霞午夜一区二区| 国产午夜精品一区理论片| 国产波霸爆乳一区二区| 国产人妖视频一区二区| 久久久久久人妻一区精品| 亚洲一区二区在线免费观看| 久久久精品人妻一区亚美研究所 | 国产精品一区视频| 中文字幕一区日韩精品| 97精品国产一区二区三区| 国产香蕉一区二区三区在线视频 | 国产乱码一区二区三区爽爽爽| 国产精华液一区二区区别大吗| 亚洲av午夜福利精品一区| 日韩精品视频一区二区三区| 国产一区二区三区韩国女主播 | 久久久久人妻精品一区二区三区|