leetcode.201 Word Search II

*Check whether a word in a board

  • Solution1: backtracking || dfs
  • Solution2: Trie
    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
    class solution1{
    Set<String> res = new HashSet<>();
    public List<String> findWords(char[][] board, String[] words){
    if(!res.contains(word) && exist(board, word)){
    res.add(word);
    }
    return new ArrayList<>(res);
    }

    private boolean exist(char[][] board, String word){
    int m = board.length;
    int n = board[0].length;
    boolean[][] visisted = new boolean[m][n];
    for(int i = 0; i < m; i++){
    for(int j = 0; j < n; j++){
    if(dfs(board, i, j, word, 0, visited)) return true;
    }
    return false;
    }
    }
    private boolean dfs(char[][] board, int i, int j, String word, int index, boolean[][] visited){
    if(index == word.length()){ return true;
    boolean ret = false;
    if(dfs(board, i+1, j, word, index+1, visited) || dfs(board, i-1, j, index +1, visited) ||
    dfs(board, i, j+1, word, index+1, visited) || dfs(board, i, j-1, index + 1, visited) ||
    dfs(board, i, j-1, word, index+1, visited)) ret = true;
    visited[i][j] = false;
    }
    return ret;
    }
    private boolean inboard(char[][] board, int i, int j){
    return i >= 0 && i < board.length && j >= 0 && j <board[0].length;
    }
    }

Solution2: trie + dfs + backtracking

  • Trie implementation
    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
    	class TrieNode {
    public TrieNode[] children = new TrieNode[26];
    public String item = "";
    public TrieNode(){
    }
    }

    class Trie(){
    private TrieNode root;
    public Trie(){
    root = new TrieNode();
    }

    //insert method -> like data preprocess
    public void insert(String word){
    TrieNode node = root;
    for(char c: word.toCharArray()){
    if(node.children[c-'a']==null){
    node.children[c-'a'] = new TrieNode();
    }
    node = node.children[c-'a'];
    }
    node.item = word; // the last node with the word
    }

    public boolean search(String word){
    TrieNode node = root;
    for(char c: word.toCharArray()){
    if(node.children[c-'a']==null) return false;
    node = node.children[c-'a'];
    }
    //at the end, check the item == word
    return node.item.equals(word);
    }
    //check whether there is any word in the trie which start with the given prefix
    public boolean startsWith(String prefix){
    TrieNode node =root;
    for(char c: prefix.toCharArray()){
    if(node.children[c-'a']==null) return false;
    node = node.children[c-'a'];
    }
    return true;
    }
    }

solution II

using trie, the time complexity got decreased while the space will be cratically increased!
so, when the words is big, the memory space limited will be hit!
Trie can be used in small dataset with a high performance!

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
class solutionII{
Set<String> res = new HashSet<String>();
public List<String> findwords(char[][] board, String[] words){
Trie trie = new Trie();
for(String word: words){
trie.insert(word);
}
int m = board.length;
int n = board[0].length;
boolean[][] visited = new boolean[m][n];
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
dfs(board, visited, "", i, j, trie);
}
}
return new ArrayList<String>(res);
}
private void dfs(char[][] board, boolean[][] visited, String str, int x, int y, Trie trie){
if (x < 0 || x >= board.length || y < 0 || y >= board[0].length) return;
if (visited[x][y]) return;

str += board[x][y];
if(!trie.startsWith(str)) return;
if(trie.search(str)){
res.add(str);
}
//still backtrack
visited[x][y] = true;
dfs(board, visited, str, x - 1, y, trie);
dfs(board, visited, str, x + 1, y, trie);
dfs(board, visited, str, x, y - 1, trie);
dfs(board, visited, str, x, y + 1, trie);
visited[x][y] = false;
}
}

leetcode1033.Move Stones Until Consecutive

Catogory: Brainteaser

-Edge case 1: all three stones are next to each other (z - x == 2). Return {0, 0}.
-Edge case 2: two stones are next to each other, or there is only one space in between. Minimum moves is 1.

-Otherwise; minimum moves are 2, maximum - z - x - 2.

1
2
3
4
5
6
vector<int> numMovesStones(int a, int b, int c) {
vector<int> s = { a, b, c };
sort(begin(s), end(s));
if (s[2] - s[0] == 2) return { 0, 0 };
return { min(s[1] - s[0], s[2] - s[1]) <= 2 ? 1 : 2, s[2] - s[0] - 2 };
}

ACM/ICPC Road Map

The necessary Topics and Algorithms.

image

Elementary Data structure

  • array
  • stack
  • queue
  • string
  • heap
  • hash

Advanced Data Structrue

  • Priority Queue
  • Binary Indexed Tree or Fenwick Tree
  • Segment Tree(RMQ, Range Sum and Lazy Propagation)
  • K-D tree(insert, minimum, delete)
  • Union Find Disjoint Set(cycle Detection and by range or path compression)
  • Tries
  • Interval Tree
  • binary Search
  • Qucik Sort(Quick Select)
  • Merge Sort
  • Order Statistics

String Manipulation

  • KMP algorithm
  • Rabin Karp
  • Z’s algorithm
  • Aho Corasick String Matching

C++ skillset

Dynamic Programming

1
2
3
4
5
6
7
8
9
10
11
Longest Common Subsequence
Longest Increasing Subsequence
Edit Distance
Minimum Partition
Ways to Cover a Distance
Longest Path In Matrix
Subset Sum Problem
Optimal Strategy for a Game
0-1 Knapsack Problem
Assembly Line Scheduling
Optimal Binary Search Tree

backtracking

1
2
3
4
5
Rat in a Maze
N Queen Problem
Subset Sum
m Coloring Problem
Hamiltonian Cycle

Graph Algorithms

One of the most important topic which you can not ignore if preparing for ACM – ICPC.

1
2
3
4
5
6
7
8
9
10
Breadth First Search (BFS)
Depth First Search (DFS)
Shortest Path from source to all vertices **Dijkstra**
Shortest Path from every vertex to every other vertex **Floyd Warshall**
Minimum Spanning tree **Prim**
Minimum Spanning tree **Kruskal**
Topological Sort
Johnson’s algorithm
Articulation Points (or Cut Vertices) in a Graph
Bridges in a graph

stepsToBuildSpringbootRESTAPI

  • Using http://start.spring.io to initialize: web, jpa, mysql, devtools for basic dependency

  • Configure application.properties file

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    ## Spring DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties)
    spring.datasource.url = jdbc:mysql://localhost:3306/notes_app?useSSL=false
    spring.datasource.username = root
    spring.datasource.password = root


    ## Hibernate Properties
    # The SQL dialect makes Hibernate generate better SQL for the chosen database
    spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect

    # Hibernate ddl auto (create, create-drop, validate, update)
    spring.jpa.hibernate.ddl-auto = update

The last two properties are for hibernate. Spring Boot uses Hibernate as the default JPA implementation.

The property spring.jpa.hibernate.ddl-auto is used for database initialization. I’ve used the value “update” for this property.

It does two things -

  • When you define a domain model, a table will automatically be created in the database and the fields of the domain model will be mapped to the corresponding columns in the table.

  • Any change to the domain model will also trigger an update to the table. For example, If you change the name or type of a field, or add another field to the model, then all these changes will be reflected in the mapped table as well.

Using update for spring.jpa.hibernate.ddl-auto property is fine for development. But, For production, You should keep the value of this property to “validate”, and use a database migration tool like Flyway for managing changes in the database schema.

  • Set up data model mapping to table
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
package com.example.easynotes.model;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.*;
import javax.validation.constraints.NotBlank;
import java.util.Date;

@Entity
@Table(name = "notes")
@EntityListeners(AuditingEntityListener.class)
@JsonIgnoreProperties(value = {"createdAt", "updatedAt"},
allowGetters = true)
public class Note implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@NotBlank
private String title;

@NotBlank
private String content;

@Column(nullable = false, updatable = false)
@Temporal(TemporalType.TIMESTAMP)
@CreatedDate
private Date createdAt;

@Column(nullable = false)
@Temporal(TemporalType.TIMESTAMP)
@LastModifiedDate
private Date updatedAt;

// Getters and Setters ... (Omitted for brevity)
}

annotated createdAt and updatedAt fields with @CreatedDate and @LastModifiedDate annotations respectively.

what we want is that these fields should automatically get populated whenever we create or update an entity.

To achieve this, we need to do two things -

  1. Add Spring Data JPA’s AuditingEntityListener to the domain model.

    1
    @EntityListeners(AuditingEntityListener.class)
  2. Enable JPA Auditing in the main application.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.example.easynotes;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@SpringBootApplication
@EnableJpaAuditing
public class EasyNotesApplication {

public static void main(String[] args) {
SpringApplication.run(EasyNotesApplication.class, args);
}
}

Creating NoteRepository to access data from the database

  1. Spring Data JPA with paRepository interface with methods for CRUD operations, First, Create a new package called repository inside the base package com.example.easynotes. Then, create an interface called NoteRepository and extend it from JpaRepository.
    This is different from nodejs or golang. but why we need this interface. apparently it give us the method, which manipulate the java object in a collection. in mongodb, it is the schema!
1
2
3
4
5
6
7
8
9
package com.example.easynotes.repository;

import com.example.easynotes.model.Note;
import org.springframework.data.jpa.repository.JpaRepository;

@Repository
public interface NoteRepository extends JpaRepository<Note, Long> {

}

Spring Data JPA has a bunch of other interesting features like Query methods (dynamically creating queries based on method names), Criteria API, Specifications, QueryDsl etc.
strongly recommend to checkout the Spring Data JPA’s documentation to learn more.

Creating Custom Business Exception

The APIs will throw a ResourceNotFoundException whenever a Note with a given id is not found in the database.

Following is the definition of ResourceNotFoundException. (I’ve created a package named exception inside com.example.easynotes to store this exception class) -

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

package com.example.easynotes.exception;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
private String resourceName;
private String fieldName;
private Object fieldValue;

public ResourceNotFoundException( String resourceName, String fieldName, Object fieldValue) {
super(String.format("%s not found with %s : '%s'", resourceName, fieldName, fieldValue));
this.resourceName = resourceName;
this.fieldName = fieldName;
this.fieldValue = fieldValue;
}

public String getResourceName() {
return resourceName;
}

public String getFieldName() {
return fieldName;
}

public Object getFieldValue() {
return fieldValue;
}
}

Notice the use of @ResponseStatus annotation in the above exception class. This will cause Spring boot to respond with the specified HTTP status code whenever this exception is thrown from your controller.

Creating NoteController

This is final step as router will wrap inside and mapping with the controller.
which is almost the same as nodejs rest api or golang api. but using anotation.

First, create a new package controller inside com.example.easynotes. Then, create a new class NoteController.java with the following contents.

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
package com.example.easynotes.controller;

import com.example.easynotes.exception.ResourceNotFoundException;
import com.example.easynotes.model.Note;
import com.example.easynotes.repository.NoteRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.List;

@RestController
@RequestMapping("/api")
public class NoteController {

@Autowired
NoteRepository noteRepository;

// Get All Notes

// Create a new Note

// Get a Single Note

// Update a Note

// Delete a Note
}

@RestController annotation is a combination of Spring’s @Controller and @ResponseBody annotations.

The @Controller annotation is used to define a controller and the @ResponseBody annotation is used to indicate that the return value of a method should be used as the response body of the request.

@RequestMapping(“/api”) declares that the url for all the apis in this controller will start with /api.

Let’s now look at the implementation of all the apis one by one.

1. Get All Notes (GET /api/notes)

1
2
3
4
5
// Get All Notes
@GetMapping("/notes")
public List<Note> getAllNotes() {
return noteRepository.findAll();
}

The above method is pretty straightforward. It calls JpaRepository’s findAll() method to retrieve all the notes from the database and returns the entire list.

Also, The @GetMapping(“/notes”) annotation is a short form of @RequestMapping(value=”/notes”, method=RequestMethod.GET).

2. Create a new Note (POST /api/notes)

1
2
3
4
@PostMapping("/notes")
public Note createNote(@Valid @RequestBody Note note){
return nodeRepository.save(note);
}

The @RequestBody annotation is used to bind the request body with a method parameter.
The @Valid annotation makes sure that the request body is valid. Remember, we had marked Note’s title and content with @NotBlank annotation in the Note model?
so if the req body does not have a title or content, then spring will return a 400 badrequest error to client.

3. get a single Note(get/api/note/{id})

@GetMapping(“/notes/{id}”)
public Note getNoteById(@PathVarible(value = “id”) Long nodeId){
return noteRespository.findById(noteId).
orElseThrow(() -> new ResourceNotFoundException(“Note”, “id”, noteId));
}

The @PathVariable annotation, as the name suggests, is used to bind a path variable with a method parameter.

we are throwing a ResourceNotFoundException whenever a Note with the given id is not found in the above method.

This will cause Spring Boot to return a 404 Not Found error to the client (Remember, we had added a @ResponseStatus(value = HttpStatus.NOT_FOUND) annotation to the ResourceNotFoundException class).

4. Update a item(PUT /api/notes/{id})

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Update a Note
@PutMapping("/notes/{id}")
public Note updateNote(@PathVariable(value = "id") Long noteId,
@Valid @RequestBody Note noteDetails) {

Note note = noteRepository.findById(noteId)
.orElseThrow(() -> new ResourceNotFoundException("Note", "id", noteId));

note.setTitle(noteDetails.getTitle());
note.setContent(noteDetails.getContent());

Note updatedNote = noteRepository.save(note);
return updatedNote;
}

5. Delete a Note (DELETE /api/notes/{noteId})

1
2
3
4
5
6
7
8
@DeleteMapping("notes/{id}")
public Repository<?> deleteNote(@PathVariable(value = "id") Long noteId){
Note note noteRepository.findById(noteId)
.orElseThrow(() -> new ResourceNotFoundException("Note", "id", noteId));
noteRepository.delete(note);

return ResponseEntity.ok().build();
}

Running the app

Command:

1
$ mvn spring-boot:run

Test the api with postman, which is same with golang as well as nodejs and python.

Still, having query about tranction! what if the sql operation involve in transaction
and need to rollback! that will be next….

reference link

systemDesignInterview

My question

  1. Design Pastebin, a website where you can store and share text online for a set period of time.

Note: Bit.ly is a similar service, with the distinction that Pastebin requires storing the paste contents instead of the original unshortened URL.

  • note: Use it to share thoughts and resources, such as:
  • Features scope
  • API design
  • Pseudo code for specific components
  • Data model/schema
  • Back-of-the-envelope calculations
  • Reference links
  • Link to whiteboard or diagram such as https://sketchboard.me/new
  • hints: interview structure
  • Establish the feature scope and constraints
  • Describe high-level architecture
  • Drill Down: design and scale core components
  • Scaling the design
  • Tradeoffs and future improvements

Note and Solution

text -> pastebin no expire time

build a restful api with post method store text in the dabase

api and then when hit api return text in brower.

txt file, service parse the text file, get string, and store in the backend.

txt -> api -> user want the text, url get list of text or one recent text

front end -> save txt file -> backend -> id-> bit.ly service -> url for text

  1. user table
    user id

2 text table
text id
text content
date

solution1: url for txt -> backend -> response with txt file

  • solution2: optimized high level design
    1
    2
    3
    4
    5
    6
    7
    client ->(LB) server -> readApi-> cache  duplicate
    server readApi DB
    DB
    ...
    ...
    server -> writeApi-> (master and slave DB)
    nosql

Pasterbin use case
Review this article to learn about the use cases, high level design, core components and scale of a system like Pastebin.

leetcode671SecondMinNodeInBinaryTree

Leetcode671. Second Minimum Node In a Binary Tree

//implementing in sort of bussiness logic in control component in
//flexibly used in front end or backend!!!

Analysis:

  1. using dfs or bfs, search the val in range (min1, ans), update ans = val if val in (min1, ans);
  2. using dfs with divide and conque, dfs to get left and right of the subtree, and only recursion on the condition of root.val == root.left.val or root.val == root.right.val;
  • solution one

    dfs

solution{
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
	int min1;
long ans = Long.MAX_VALUE;
public int findSecondMinimumValue(TreeNode root){
if(root==null) return -1;
dfs(root);
return ans ==Long.MAX_VALUE?-1:(int)ans;
}
//once root.val > min1, we never drill deeply
//only root.val == min1, we dfs left and right
private void dfs(TreeNode root){
if(root!=null){
if(root.val>min1 && root.val < ans){
ans = root.val;
}else if(root.val == min1){
dfs(root.left);
dfs(root.right);
}
}
}
}

bfs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class solution{
public int findSecondMinimumValue(TreeNode root){
if(root==null) return -1;
int rootval = root.val;
long ans = Long.MAX_VALUE;
Queue<Integer> queue = new LinkedList<>();
queue.add(root);
while(!queue.isEmpty()){
TreeNode tmp = queue.poll();
if(tmp.left != rootval && head.val < ans){
ans = head.val;
}
if(tmp.left != null){
queue.add(tmp.left);
queue.add(tmp.right);
}
}
return ans == Long.MAX_VALUE ? -1: ans;
}
}
  • solution two

    divide and conquer with dfs

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    //only left.val == root.val or right.val == root.val will be dfs
    //be familiar to implement condition returnning in order.
    class solution{
    public int findSecondMinimumValue(TreeNode root){
    if(root==null) return -1;
    if(root.left==null && root.right==null) return -1;
    int left = root.left.val;
    int right = root.right.val;
    if(root.left.val == root.val){
    left = findSecondMinimumValue(root.left);
    }
    if(root.right.val == root.val){
    right = findSecondMinimumValue(root.right);
    }
    if(left!=-1 && right != -1){
    return Math.min(left, rigth);
    }
    if(left != -1){
    return left;
    }else{
    return right;
    }
    }
    }

leetcodeContest138

    1. Height Checker – easy
    1. Grumpy BookStore owner –medium
    1. Previous Permutation with One Step –medium
    1. Distant Barcodes –medium

1. Height Checker

  • solution
    • sort and check differ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int heightChecker(vector<int>& heights) {
vector<int> tmp;
for(auto it: heights){
tmp.push_back(it);
}
sort(heights.begin(), heights.end());
int res = 0;
for(int i = 0; i < heights.size(); ++i){
if(heights[i]!=tmp[i]){
cout<<heights[i] <<endl;
res++;
}
}
return res;
}

2. Grumpy Bookstore owner

solution: sliding window
when ungrumpy, all is set setisfied, otherwise we sliding window to count the max setSatisfied.
code style similar to TwoSum, which is on-fly process!

1
2
3
4
5
6
7
8
9
10
maxSatisfied(vector<int>& cs, vector<int> grumpy){
auto satisfied = 0, totalAddSatisfied = 0, addSatisfied = 0;
for(auto i = 0; i < cs.size(); i++){
satisfied += grumpy[i] ? 0 : cs[i];
addSatisfied += grumpy[i]? cs[i] : 0;
if(i>=X) addSatisfied -= grumpy[i-X]?cs[i-X]:0;
totalAddSatisfied = max(addSatisfied, totalAddSatisfied);
}
return satisfied + totalAddSatisfied;
}

My realtime solution during test, in which I pick out all the grumpy[i]==1 into a vector, and
process to max addSum. So the space complexity become bigger!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public:
int maxSatisfied(vector<int>& customers, vector<int>& grumpy, int X) {
vector<pair<int,int>> mp;
int res=0;
for(int i = 0; i < grumpy.size(); i++){
if(grumpy[i]==1){
mp.push_back(make_pair(i,customers[i]));
}
res += grumpy[i]?0:customers[i];
}
int tmp_res = res;
for(int i = 0; i < mp.size(); ++i){
int j = i;
int tmp = tmp_res;
cout<<tmp<<endl;
while(j<mp.size()&&mp[j].first-mp[i].first+1<=X){
tmp += mp[j].second;
j++;
}
res = max(res, tmp);
}
return res;
}
};

3. Previous Permutation with One Step

RabbitMQforMicroServicesCommunication

Using RabbitMQ for MicroSerices Communication

Decomposing your monolith app into multiple, totally independently deployable and scalable services.

The smaller apps are more modular with well-defined boundaries, can be written using different languages/frameworks, fail in isolation so that the entire app doesn’t go down (no SPOF).
Take a Cinema ticketing example:
image

microservices-based architecture

bad practce to have clients to communicate with each of the service instance separately.

Each service has its own storage

Enables decoupling
Some queries will then need to join data that is owned by multiple services. To avoid this major performance hit, data may be replicated and sharded. This principle is not just tolerated in microservices, but encouraged.

a single request should, ideally, only invoke one service to fetch the response

not always possible, mechanisms like gRPC, Thrift or even simple HTTP (as in our example) are commonly employed when necessary. As you may have guessed, this implies that data will have to be replicated across our services. Say, the GET /catalog/<> endpoint is also supposed to return the premieres at each cinema in the city at that time. With our new strategy, the premieres will have to be stored in the database for the Cinema Catalog service as well. Hence, point iii).

REST calls made to our API gateway

request ==> gateway ===> complies result || response to clients

Communication among services like this on one client request to the app should not happen. Otherwise we’ll be sacrificing performance, on account of another HTTP round-trip, for the newly introduced modularity.

Communicating Asynchronously Between The Services

the premieres change as a result of some CRUD operation on the Movies service. To keep the data in sync, that update event will have to be emitted and applied to the Cinema Catalog service as well

Try to picture our microservices as a cluster of state machines where updates in states may have to be communicated across the cluster to achieve eventual consistency.

Of course, we should never expect our end-users to have to wait longer for requests to finish and sacrifice their time for modularity to our benefit. Thereby, all of this communication has to be non-blocking. ### And that’s where RabbitMQ comes in. ###

RabbitMQ: implements the AMQP messaging protocol. first, you install a RabbitMQ server instance (broker) on a system. Then a publisher/producer program connects to this server and sends out a message. RabbitMQ queues that message and siphons it off to a single or multiple subscriber/consumer programs that are out there listening on the RabbitMQ server.

microservices are way more complex and we won’t be covering critical topics like fault tolerance because of the intricacy of distributed systems, the full role of the API gateway, Service Discovery, data consistency patterns like Sagas, preventing service failure cascading using Circuit Breakers, health checks and architectural patterns like CQRS. Not to mention how to decide whether microservices will work for you or not.

How RabbitMQ Works

Massages published to an exchange inside the RabbitMQ broker, then exchange copies of that message to queues on basis f certain developer-defined rules called binding. This journey called Routing.

the direction is non-blocking message transmissions. Not exactly. There are four different types of exchanges and each, along with the bindings, defines a routing algorithm. “Routing algorithm” means, essentially, how the messages are distributed among the queues. Going into the details about each type might be an overkill here, so I’ll just expand on the one we’ll be using: the topic exchange.

For an exchange to push a message onto a queue, that queue must be bound to the exchange. We can create multiple exchanges with unique names, explicitly. However, when you deploy RabbitMQ it comes with a default, nameless exchange. The exact way a binding key works, again, depends on the type of the exchange. Here’s how it works with a topic exchange:

  1. A queue is bound to an exchange using a string pattern (the binding key)
    The published message is delivered to the exchange along with a routing key.

  2. The exchange checks which queues match the routing key based on the binding key pattern defined before.

image
source:

Any message with a routing key ”quick.orange.rabbit” will be delivered to both queues. However, messages with ”lazy.brown.fox” will only reach Q2. Those with a routing key not matching any pattern will be lost.

For some perspective, let’s just skim over two other exchange types:

  • Fanout exchange: Messages sent to this kind of exchange will be sent to ALL the queues bound to it. The routing key, if provided, will be completely ignored. This can be used, for example, for broadcasting global configuration updates across a distributed system.
  • Direct exchange (simplest): Sends the message to the queue whose binding key is exactly equal to the given routing key. If there are multiple consumers listening on the queue, then the messages will be load-balanced among them, hence, it is commonly used to distribute tasks between multiple workers in a round robin manner.

//still need to follow up docker tutorial! and Circle.ci

https://codeburst.io/using-rabbitmq-for-microservices-communication-on-docker-a43840401819