My code gets user's input which is a list of routing keys and use them to bind a queue with an exchange. I use Declarables to generate the bindings for each routing key. The problem is, the number of routing keys that the user inputs varies each time. Considering the list of routing keys are on an array, how to bind a queue to the exchange with those routing keys. Below is my current code.
#Bean
public Declarables bindings() {
return new Declarables(
BindingBuilder.bind(queue()).to(exchange()).with(routingKey[0]),
BindingBuilder.bind(queue()).to(exchange()).with(routingKey[1]),
BindingBuilder.bind(queue()).to(exchange()).with(routingKey[2])
);
}
EDIT - adding exact use case for more clarity:
I'm using rabbitmq event exchange plugin to consume internal events(https://www.rabbitmq.com/event-exchange.html). I'm creating a queue and binding it with an exchange(amq.rabbitmq.event) that is created by the plugin. Different types of events can be consumed by binding respective routing keys.
I get the list of routing keys from the user each time and bind it to the exchange amq.rabbitmq.event and consume respective events(e.g. queue.created, user.authentication.success, etc). User adds all the routing keys as a comma separated value in application.properties file. I parse it and add these values to an array and create bindings for all the elements(routing keys) in this array. The size of this list is not constant. User may input 3 routing keys or 5 or a different number of keys each time and I have to create a binding for each of the routing key.
In the code above, bindings are created for 3 routing keys(routingKey[0], routingKey[1] & routingKey[2]). What if user inputs more than 3 routing keys and how to create bindings for all of them.
the number of routing keys that the user inputs varies each time.
It's not clear from your description whether you need a variable number of routing keys statically or dynamically.
You can use RabbitAdmin.declareBinding(...) to declare bindings at runtime (dynamically).
However, this should NOT be used within a #Bean definition.
EDIT
Based on your updated description; Spring will automatically convert a comma-delimited list to a List.
routing.keys=foo,bar,baz
#Bean
public Declarables bindings(#Value("${routing.keys}") List<String> keys) {
return new Declarables(keys.stream()
.map(key -> BindingBuilder.bind(queue()).to(exchange()).with(key))
.collect(Collectors.toList()));
}
Related
I have a HashMap<u32, Sender>. Sender is a open connection object and the key is a user id. Each user can connect from multiple devices. I need to store all possible open connections for the same user id. After this I can iterate and send messages to all open connections for same user.
The above HashMap only stores each user id and connection once. I need to get one key with multiple values. How can I make the value into a list or an array, so I can see which connections exist and send to them all?
I am not talking about different value types, like enums. I am talking about the same type values but more than one. Maybe HashMap is not designed for this?
Alternative ideas are also welcomed.
To do this with a HashMap you should use a Vec as the values, so that each key can point to multiple Senders. The type then would be HashMap<u32, Vec<Sender>>.
Using this structure, just using insert() can get clunky when you need to mutate the values like this, but instead you can use the Entry API for retrieving and updating records in one go. For example:
let mut hash_map: HashMap<u32, Vec<Sender>> = HashMap::new();
hash_map.entry(3)
// If there's no entry for key 3, create a new Vec and return a mutable ref to it
.or_default()
// and insert the item onto the Vec
.push(sender);
You could also use the multimap crate, which does something similar under the hood, but adds a layer of abstraction. You might find it easier to work with:
let mut multi_map = MultiMap::new();
multi_map.insert(3, sender_1);
multi_map.insert(3, sender_2);
The method multi_map.get(key) will the first value with that key, while multi_map.get_vec(key) will retrieve all of them.
As part of our application logic, we use Kafka Streams state store for range lookups, data is loaded from Kafka topic using builder.table() method.
The problem is that source topic's key is serialised as JSON and doesn't suite well to binary key comparisons used internally in RocksDB based state store.
We were hoping to use a separate serde for keys by passing it to Materialized.as(). However, it looks like that streams implementation resets whatever is passed to the original serdes used to load from the table topic.
This is what I can see in streams builder internals:
public synchronized <K, V> KTable<K, V> table(final String topic,
final Consumed<K, V> cons,
final Materialized<K, V, KeyValueStore<Bytes, byte[]>> materialized) {
Objects.requireNonNull(topic, "topic can't be null");
Objects.requireNonNull(consumed, "consumed can't be null");
Objects.requireNonNull(materialized, "materialized can't be null");
materialized.withKeySerde(consumed.keySerde).withValueSerde(consumed.valueSerde);
return internalStreamsBuilder.table(topic,
new ConsumedInternal<>(consumed),
new MaterializedInternal<>(materialized, internalStreamsBuilder, topic + "-"));
}
Anybody knows why it's done this way, and if it's possible to use a different serde for a DSL state store?
Please don't propose using Processor API, this route is well explored. I would like to avoid writing a processor and a custom state store every time when I need to massage data before saving it into a state store.
After some digging through streams sources, I found out that I can pass a custom Materialized.as to the filter with always true predicate. But it smells a bit hackerish.
This is my code, that unfortunately doesn't work as we hoped to, because of "serdes reset" described above.
Serde<Value> valueSerde = new JSONValueSerde()
KTable<Key, Value> table = builder.table(
tableTopic,
Consumed.with(new JSONKeySerde(), valueSerde)
Materialized.as(cacheStoreName)
.withKeySerde(new BinaryComparisonsCompatibleKeySerde())
.withValueSerde(valueSerde)
)
The code works by design. From a streams point of view, there is no reason to use a different Serde for the store are for reading the data from the topic, because it's know to be the same data. Thus, if one does not use the default Serdes from the StreamsConfig, it's sufficient to specify the Serde once (in Consumed) and it's not required to specify it in Materialized again.
For you special case, you could read the topic as a stream a do a "dummy aggregation" that just return the latest value per record (instead of computing an actual aggregate). This allows you to specify a different Serde for the result type.
I have multiple MessagePostProcessors in SpringAMQP which i set them using SimpleMessageListenerContainer.setAfterReceivePostProcessors API , now my query is does these MessagePostProcessors are called in order I have mentioned.
Pseoudo code
SimpleMessageListenerContainer container = // api returing SimpleMessageListenerContainer object
container.setAfterReceivePostProcessors(new MessagePostProcessor[] {
messagePostProcessors1 , messagePostProcessors2});
So does Spring AMQP call messagePostProcessors1 followed messagePostProcessors2 in sequence or does it randomly selects the same ?
If it randomly selects is there any way that we can order the same i.e messagePostProcessors2 always gets called after messagePostProcessors1
Akshat , the order is based on the order that is set in the processor.Quoting the document here. When i look at the concrete implementation of the processors , i find there is a setOrder method (form interface ordered i think). May be setting that in your message post processor will do the trick.
public void setAfterReceivePostProcessors(MessagePostProcessor...
afterReceivePostProcessors)
Set a MessagePostProcessor that will be
invoked immediately after a Channel#basicGet() and before any message
conversion is performed. May be used for operations such as
decompression Processors are invoked in order, depending on
PriorityOrder, Order and finally unordered.
I have a Rpc Datasource providing a list of objects. I have two selectItem databound to this datasource but each showing a different attribute.
For one of these attributes I have a duplicate value which are enabled by design.
However in the SelectItem related to this field I would like to remove duplicate to simplify the user choice(reduce the list length and also it seems strange to have double values..)
I think I can arrived to do it using lodal filtering but I cannot find the point to start to write the right citeria.
So imagine I'm building a Multi User Dungeon system using a MVC web application. To describe the areas the player can explore, the system can contain a number of Maps, which will consist of Rooms and Doors - where doors connect two Rooms.
Consider the authoring part of the system. To create a Map is easy - I need URLs like:
/Author/Maps (an index of my maps)
/Author/Maps/Create (a new Map)
/Author/Maps/Detail/3 (show Map details)
/Author/Maps/Edit/3 (edit map details)
Using a Routing scheme: /Author/{controller}/{action}/{ID}
It's the URLs for the Rooms that I need help with. When creating a new Room, I need to know which Map I'm creating it for.
/Author/Rooms/CreateIn/[mapID] ?
And then for editing a room's details:
/Author/Rooms/Edit/[roomID]
/Author/Rooms/Detail/[roomID]
Would this routing scheme work? And should the view that lists all the Rooms for a Map be an "Index" action on the Rooms controller, with a MapID passed in, or a "Rooms" action on a Map controller?
Thanks.
I don't know if this is best practice, but I would do it like this:
Add a new route: /Author/Maps/{mapID}/Rooms/{action}/{roomID}
Since this is a route that I would only expect to use for the RoomsController, I wouldn't have a {controller} parameter in that route. Just set the controller to "Rooms" in the route's default object.
Then all the actions in your RoomsController would know which map they're working with.
The default Index action for RoomsController could be a list of all the Rooms for the specified map.
The Create action would look like Create(int mapID) and the Details action would look like Details(int mapID, int roomID)
Edit: regarding invalid URLs with a mismatched mapID and roomID, you could just ignore the mapID, but I think the better process would be to validate that the specified mapID is correct and show an error message if it is not.
Edit 2: (additional thoughts regarding the relationship between mapID and roomID)
You could just make roomID unique within the given map. Therefore, map 5, room 3 would be a different room than map 8, room 3.