1"use strict";(self.webpackChunkrabbitmq_website=self.webpackChunkrabbitmq_website||[]).push([["19525"],{85181(e,n,a){a.r(n),a.d(n,{metadata:()=>t,default:()=>p,frontMatter:()=>o,contentTitle:()=>c,toc:()=>u,assets:()=>d});var t=JSON.parse('{"id":"ae","title":"Alternate Exchanges","description":"\x3c!--","source":"@site/versioned_docs/version-4.2/ae.md","sourceDirName":".","slug":"/ae","permalink":"/docs/4.2/ae","draft":false,"unlisted":false,"editUrl":"https://github.com/rabbitmq/rabbitmq-website/tree/main/versioned_docs/version-4.2/ae.md","tags":[],"version":"4.2","frontMatter":{"title":"Alternate Exchanges"},"sidebar":"docsSidebar","previous":{"title":"Exchange to Exchange Bindings","permalink":"/docs/4.2/e2e"},"next":{"title":"Sender-selected Distribution","permalink":"/docs/4.2/sender-selected"}}'),r=a(74848),i=a(28453),s=a(50773),l=a(57250);let o={title:"Alternate Exchanges"},c="Alternate Exchanges",d={},u=[{value:"Overview",id:"overview",level:2},{value:"How to Define an Alternate Exchange",id:"define",level:2},{value:"Configuration Using a Policy",id:"define-using-policy",level:3},{value:"Configuration Using Client-provided Arguments",id:"define-using-arguments",level:3},{value:"How Alternate Exchanges Work",id:"how-it-works",level:2}];function h(e){let n={a:"a",code:"code",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",p:"p",pre:"pre",ul:"ul",...(0,i.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n.header,{children:(0,r.jsx)(n.h1,{id:"alternate-exchanges",children:"Alternate Exchanges"})}),"\n",(0,r.jsx)(n.h2,{id:"overview",children:"Overview"}),"\n",(0,r.jsx)(n.p,{children:"It is sometimes desirable to let clients handle messages\nthat an exchange was unable to route (i.e. either because\nthere were no bound queues or no matching\nbindings). Typical examples of this are"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"detecting when clients accidentally or maliciously publish messages that cannot be routed"}),"\n",(0,r.jsx)(n.li,{children:'"or else" routing semantics where some messages are handled specially and the rest by a generic handler'}),"\n"]}),"\n",(0,r.jsx)(n.p,{children:'Alternate Exchange ("AE") is a feature that addresses these use cases.'}),"\n",(0,r.jsx)(n.h2,{id:"define",children:"How to Define an Alternate Exchange"}),"\n",(0,r.jsxs)(n.p,{children:["For any given exchange except for the default one, an AE can be defined by clients using\n",(0,r.jsx)(n.a,{href:"./policies",children:"policies"}),". This is the recommended option\nas policies significantly simplify changes to such options (e.g. during\nupgrades)."]}),"\n",(0,r.jsx)(n.p,{children:'In modern RabbitMQ versions, the default exchange is a special-cased convention in the code\nand not a "real" exchange. Therefore it does not support the alternate exchange feature.'}),"\n",(0,r.jsx)(n.p,{children:"Alternatively, optional exchange arguments can be provided by client\nat exchange declaration time.\nIn the case where both policy and arguments specify an AE, the one\nspecified in arguments overrules the one specified in policy."}),"\n",(0,r.jsx)(n.h3,{id:"define-using-policy",children:"Configuration Using a Policy"}),"\n",(0,r.jsx)(n.p,{children:"This is the recommended way of defining alternate exchanges."}),"\n",(0,r.jsx)(n.p,{children:"To specify an AE using policy, add the key 'alternate-exchange'\nto a policy definition and make sure that the policy matches the exchange(s)\nthat need the AE defined. For example:"}),"\n",(0,r.jsxs)(s.A,{groupId:"examples",children:[(0,r.jsx)(l.A,{value:"bash",label:"rabbitmqctl with bash",default:!0,children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:'rabbitmqctl set_policy AE "^my-direct$" \'{"alternate-exchange":"my-ae"}\' --apply-to exchanges\n'})})}),(0,r.jsx)(l.A,{value:"rabbitmqadmin",label:"rabbitmqadmin with bash",children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:'rabbitmqadmin policies declare \\\n --name "AE" \\\n --pattern "^my-direct$" \\\n --definition \'{"alternate-exchange":"my-ae"}\' \\\n --apply-to "exchanges"\n'})})}),(0,r.jsx)(l.A,{value:"PowerShell",label:"rabbitmqctl with PowerShell",children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-PowerShell",children:'rabbitmqctl.bat set_policy AE "^my-direct$" "{""alternate-exchange"":""my-ae""}" --apply-to exchanges\n'})})}),(0,r.jsx)(l.A,{value:"rabbitmqadmin-PowerShell",label:"rabbitmqadmin with PowerShell",children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-PowerShell",children:'rabbitmqadmin.exe policies declare ^\n --name "AE" ^\n --pattern "^my-direct$" ^\n --definition "{""alternate-exchange"":""my-ae""}" ^\n --apply-to "exchanges"\n'})})})]}),"\n",(0,r.jsxs)(n.p,{children:['This will apply an AE of "my-ae" to the exchange c
1alled\n"my-direct". Policies can also be defined using the management\nplugin, see the ',(0,r.jsx)(n.a,{href:"./policies",children:"policy documentation"})," for more details."]}),"\n",(0,r.jsx)(n.h3,{id:"define-using-arguments",children:"Configuration Using Client-provided Arguments"}),"\n",(0,r.jsx)(n.p,{children:"This way of defining an alternate exchange is discouraged. Consider\nusing a policy instead (see above)."}),"\n",(0,r.jsxs)(n.p,{children:["When creating an exchange the name of an AE can be\noptionally supplied in the ",(0,r.jsx)(n.code,{children:"exchange.declare"}),"\nmethod's ",(0,r.jsx)(n.code,{children:"arguments"})," table by specifying a key\nof 'alternate-exchange' and a string value containing the name."]}),"\n",(0,r.jsxs)(n.p,{children:["When an AE has been specified, in addition to the usual\n",(0,r.jsx)(n.code,{children:"configure"})," permission on the declared exchange,\nthe user needs to have ",(0,r.jsx)(n.code,{children:"read"})," permissions on that\nexchange and ",(0,r.jsx)(n.code,{children:"write"})," permissions on the AE."]}),"\n",(0,r.jsx)(n.p,{children:"For example:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-java",children:'Map<String, Object> args = new HashMap<String, Object>();\nargs.put("alternate-exchange", "my-ae");\nchannel.exchangeDeclare("my-direct", "direct", false, false, args);\nchannel.exchangeDeclare("my-ae", "fanout");\nchannel.queueDeclare("routed");\nchannel.queueBind("routed", "my-direct", "key1");\nchannel.queueDeclare("unrouted");\nchannel.queueBind("unrouted", "my-ae", "");\n'})}),"\n",(0,r.jsx)(n.p,{children:"In the above fragment of Java code we create a direct\nexchange 'my-direct' that is configured with an AE called\n'my-ae'. The latter is declared as a fanout exchange. We\nbind one queue 'routed' to 'my-direct' with a binding key\nof 'key1', and a queue 'unrouted' to 'my-ae'."}),"\n",(0,r.jsx)(n.h2,{id:"how-it-works",children:"How Alternate Exchanges Work"}),"\n",(0,r.jsx)(n.p,{children:"Whenever a message published to an exchange with a configured AE cannot be routed\nto any queue, the channel re-publishes the message to the specified AE\ninstead. If that AE does not exist then a warning is logged. If\nan AE cannot route a message, it in turn publishes the message\nto its AE, if it has one configured. This process continues\nuntil either the message is successfully routed, the end of the\nchain of AEs is reached, or an AE is encountered which has\nalready attempted to route the message."}),"\n",(0,r.jsx)(n.p,{children:"For example if we publish a message to 'my-direct' with a\nrouting key of 'key1' then that message is routed to the\n'routed' queue, in accordance with the standard AMQP\nbehaviour. However, when publishing a message to\n'my-direct' with a routing key of 'key2', rather than\nbeing discarded the message is routed via our configured\nAE to the 'unrouted' queue."}),"\n",(0,r.jsx)(n.p,{children:"The behaviour of an AE purely pertains to routing. If a message\nis routed via an AE it still counts as routed for the purpose of\nthe 'mandatory' flag, and the message is otherwise unchanged."})]})}function p(e={}){let{wrapper:n}={...(0,i.R)(),...e.components};return n?(0,r.jsx)(n,{...e,children:(0,r.jsx)(h,{...e})}):h(e)}},57250(e,n,a){a.d(n,{A:()=>l});var t=a(74848);a(96540);var r=a(34164),i=a(57663);function s({children:e,className:n,hidden:a}){return(0,t.jsx)("div",{role:"tabpanel",className:(0,r.A)("tabItem_Ymn6",n),hidden:a,children:e})}function l({children:e,className:n,value:a}){let{selectedValue:r,lazy:o}=(0,i.uc)(),c=a===r;return!c&&o?null:(0,t.jsx)(s,{className:n,hidden:!c,children:e})}},50773(e,n,a){a.d(n,{A:()=>h});var t=a(74848);a(96540);var r=a(34164),i=a(88287),s=a(57663),l=a(28584),o=a(19863);function c({className:e}){let{selectedValue:n,selectValue:a,tabValues:i,block:o}=(0,s.uc)(),d=[],{blockElementScrollPositionUntilNextRender:u}=(0,l.a_)(),h=e=>{let t=e.currentTarget,r=i[d.indexOf(t)].value;r!==n&&(u(t),a(r))},p=e=>{let n=null;switch(e.key){case"Enter":h(e);break;case"ArrowRight":{let a=d.indexOf(e.currentTarget)+1;n=d[a]??d[0];break}case"ArrowLeft":{let a=d.indexOf(e.currentTarget)-1;n=d[a]??d[d.length-1]}}n?.focus()};return(0,t.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,r.A)("tabs",{"tabs--block":o},e),children:i.map(({value:e,label:a,attributes:i})=>(0,t.jsx)("li",{role:"tab",tabIndex:n===e?0:-1,"aria-selected":n===e,ref:e=>{d.push(e)},onKeyDown:p,onClick:h,...i,className:(0,r.A)("tabs__item","tabItem_LNqP",i?.className,{"tabs__item--active":n===e}),children:a??e},e))})}function d({children:e}){return(0,t.jsx)("div",{className:"margin-top--md",children:e})}function u({className:e,children:n}){return(0,t.jsxs)("div",{className:(0,r.A)(i.G.tabs.container,"tabs-container","tabList__CuJ"),children:[(0,t.jsx)(c,{className:e}),(0,t.jsx)(d,{children:n})]})}function h(e){let n=(0,o.A)(),a=(0,s.OC)(e);return(0,t.jsx)(s.O_,{value:a,children:(0,t.jsx)(u,{className:e.className,children:(0,s.vT)(e.children)})},String(n))}},57663(e,n,a){a.d(n,{OC:()=>h,O_:()=>g,uc:()=>m,vT:()=>d});var t=a(74848),r=a(96540),i=a(56347),s=a(99989),l=a(96629),o=a(80618),c=a(41367);function d(e){return r.Children.toArray(e).filter(e=>"\n"!==e)}function u({value:e,tabValues:n}){return n.some(n=>n.value===e)}function h(e){let n,{defaultValue:a,queryString:t=!1,groupId:d}=e,h=function(e){let{values:n,children:a}=e;return(0,r.useMemo)(()=>{let e=n??r.Children.toArray(a).flatMap(e=>{if(!e)return[];if((0,r.isValidElement)(e)&&function(e){let{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return[e];
1let n="string"==typeof e.type?e.type:e.type.name;throw Error(`Docusaurus error: Bad <Tabs> child <${n}>: all children of the <Tabs> component should be <TabItem>, and every <TabItem> should have a unique "value" prop. 2If you do not want to pass on a "value" prop to the direct children of <Tabs>, you can also pass an explicit <Tabs values={...}> prop.`)}).map(({props:{value:e,label:n,attributes:a,default:t}})=>({value:e,label:n,attributes:a,default:t})),t=(0,o.XI)(e,(e,n)=>e.value===n.value);if(t.length>0)throw Error(`Docusaurus error: Duplicate values "${t.map(e=>`'${e.value}'`).join(", ")}" found in <Tabs>. Every value needs to be unique.`);return e},[n,a])}(e),[p,m]=(0,r.useState)(()=>(function({defaultValue:e,tabValues:n}){if(0===n.length)throw Error("Docusaurus error: the <Tabs> component requires at least one <TabItem> children component");if(e){if(!u({value:e,tabValues:n}))throw Error(`Docusaurus error: The <Tabs> has a defaultValue "${e}" but none of its children has the corresponding value. Available values are: ${n.map(e=>e.value).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return e}let a=n.find(e=>e.default)??n[0];if(!a)throw Error("Unexpected error: 0 tabValues");return a.value})({defaultValue:a,tabValues:h})),[g,f]=function({queryString:e=!1,groupId:n}){let a=(0,i.W6)(),t=function({queryString:e=!1,groupId:n}){if("string"==typeof e)return e;if(!1===e)return null;if(!0===e&&!n)throw Error('Docusaurus error: The <Tabs> component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return n??null}({queryString:e,groupId:n});return[(0,l.aZ)(t),(0,r.useCallback)(e=>{if(!t)return;let n=new URLSearchParams(a.location.search);n.set(t,e),a.replace({...a.location,search:n.toString()})},[t,a])]}({queryString:t,groupId:d}),[x,b]=function({groupId:e}){let n=e?`docusaurus.tab.${e}`:null,[a,t]=(0,c.Dv)(n);return[a,(0,r.useCallback)(e=>{n&&t.set(e)},[n,t])]}({groupId:d}),v=u({value:n=g??x,tabValues:h})?n:null;return(0,s.A)(()=>{v&&m(v)},[v]),{selectedValue:p,selectValue:(0,r.useCallback)(e=>{if(!u({value:e,tabValues:h}))throw Error(`Can't select invalid tab value=${e}`);m(e),f(e),b(e)},[f,b,h]),tabValues:h,lazy:e.lazy??!1,block:e.block??!1}}let p=(0,r.createContext)(null);function m(){let e=r.useContext(p);if(!e)throw Error("useTabsContext() must be used within a Tabs component");return e}function g(e){return(0,t.jsx)(p.Provider,{value:e.value,children:e.children})}},28453(e,n,a){a.d(n,{R:()=>s,x:()=>l});var t=a(96540);let r={},i=t.createContext(r);function s(e){let n=t.useContext(i);return t.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function l(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:s(e.components),t.createElement(i.Provider,{value:n},e.children)}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.