├── Ch3 ├── README.md └── RMI │ ├── MyBank.java │ ├── MyBankClient.java │ ├── MyBankServer.java │ └── README.md ├── Ch4 ├── BoundedBuffer │ ├── Buffer.java │ ├── Consumer.java │ ├── Producer.java │ └── ProducerConsumerEx.java └── DiningPhilosophers │ ├── DeadlockDiningPhilosophers.java │ ├── DiningPhilosophers.java │ └── Philosopher.java ├── Ch7 ├── rabbitmqtests │ ├── RabbitMQTests.java │ └── Recv.java └── rmqpool │ ├── RMQChannelFactory.java │ └── RMQChannelPool.java └── README.md /Ch3/README.md: -------------------------------------------------------------------------------- 1 | # BSDS-book 2 | Code example for BSDS book 3 | -------------------------------------------------------------------------------- /Ch3/RMI/MyBank.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package bsds.chapter3.rmi; 7 | 8 | /** 9 | * 10 | * @author igortn 11 | */ 12 | // Simple mybank.com server interface 13 | public interface MyBank extends java.rmi.Remote{ 14 | public double balance (String accNo) 15 | throws java.rmi.RemoteException ; 16 | public boolean statement(String month) 17 | throws java.rmi.RemoteException ; 18 | // other operations 19 | } 20 | 21 | -------------------------------------------------------------------------------- /Ch3/RMI/MyBankClient.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package bsds.chapter4.rmi; 7 | import java.rmi.*; 8 | /** 9 | * 10 | * @author igortn 11 | */ 12 | public class MyBankClient { 13 | 14 | 15 | public static void main(String args[]){ 16 | try{ 17 | MyBank bankServer=(MyBank)Naming.lookup("rmi://localhost:1099/MyBankServer"); 18 | System.out.println("connecting to server"); 19 | System.out.println(bankServer.balance("00169990")); 20 | }catch(Exception e){ 21 | e.printStackTrace(); 22 | } 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /Ch3/RMI/MyBankServer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package bsds.chapter4.rmi; 7 | 8 | import java.rmi.Naming; 9 | import java.rmi.RemoteException; 10 | import java.rmi.registry.LocateRegistry; 11 | import java.rmi.registry.Registry; 12 | import java.rmi.server.UnicastRemoteObject; 13 | 14 | /** 15 | * 16 | * @author igortn 17 | */ 18 | public class MyBankServer 19 | extends UnicastRemoteObject 20 | implements MyBank { 21 | 22 | MyBankServer() throws RemoteException { 23 | super(); 24 | } 25 | 26 | @Override 27 | public double balance (String accNo) 28 | throws java.rmi.RemoteException { 29 | return 33.3; 30 | } 31 | @Override 32 | public boolean statement(String month) 33 | throws java.rmi.RemoteException { 34 | return true; 35 | } 36 | public static void main(String args[]){ 37 | try{ 38 | MyBankServer server=new MyBankServer(); 39 | // create a registry in this JVM on default port 40 | Registry registry = LocateRegistry.createRegistry(1099); 41 | registry.bind("MyBankServer", server); 42 | System.out.println("server ready"); 43 | }catch(Exception e){System.out.println(e);} 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /Ch3/RMI/README.md: -------------------------------------------------------------------------------- 1 | # BSDS-book 2 | Code example for BSDS book 3 | -------------------------------------------------------------------------------- /Ch4/BoundedBuffer/Buffer.java: -------------------------------------------------------------------------------- 1 | 2 | package producerconsumerex; 3 | 4 | /** 5 | * 6 | * @author igorton 7 | */ 8 | public class Buffer { 9 | // Message buffer between producer to consumer. 10 | private String message; 11 | // True if consumer must wait for producer to send message, 12 | // false if producer must wait for consumer to retrieve message. 13 | private boolean empty = true; 14 | 15 | public synchronized String retrieve() { 16 | // Wait until message is available. 17 | while (empty) { 18 | try { 19 | System.out.println("Waiting for a message"); 20 | wait(); 21 | } catch (InterruptedException e) {} 22 | } 23 | // Toggle status. 24 | empty = true; 25 | // Notify producer that buffer is empty 26 | notifyAll(); 27 | return message; 28 | } 29 | 30 | public synchronized void put(String message) { 31 | // Wait until message has been retrieved. 32 | while (!empty) { 33 | try { 34 | wait(); 35 | } catch (InterruptedException e) {} 36 | } 37 | // Toggle status. 38 | empty = false; 39 | // Store message. 40 | this.message = message; 41 | // Notify consumer that message is available 42 | notifyAll(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Ch4/BoundedBuffer/Consumer.java: -------------------------------------------------------------------------------- 1 | 2 | package producerconsumerex; 3 | 4 | /** 5 | * 6 | * @author igorton 7 | */ 8 | import java.util.Random; 9 | 10 | public class Consumer implements Runnable { 11 | private Buffer buffer; 12 | 13 | public Consumer(Buffer buf) { 14 | this.buffer = buf; 15 | } 16 | 17 | public void run() { 18 | Random random = new Random(); 19 | for (String message = buffer.retrieve(); 20 | ! message.equals("DONE"); 21 | message = buffer.retrieve()) { 22 | System.out.format("MESSAGE RECEIVED: %s%n", message); 23 | try { 24 | Thread.sleep(random.nextInt(5000)); 25 | } catch (InterruptedException e) {} 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Ch4/BoundedBuffer/Producer.java: -------------------------------------------------------------------------------- 1 | package producerconsumerex; 2 | 3 | /** 4 | * 5 | * @author igorton 6 | */ 7 | import java.util.Random; 8 | 9 | public class Producer implements Runnable { 10 | private Buffer buffer; 11 | 12 | public Producer(Buffer drop) { 13 | this.buffer = drop; 14 | } 15 | 16 | public void run() { 17 | String importantInfo[] = { 18 | "Mares eat oats", 19 | "Does eat oats", 20 | "Little lambs eat ivy", 21 | "A kid will eat ivy too" 22 | }; 23 | Random random = new Random(); 24 | 25 | for (int i = 0; 26 | i < importantInfo.length; 27 | i++) { 28 | buffer.put(importantInfo[i]); 29 | try { 30 | Thread.sleep(random.nextInt(1000)); 31 | } catch (InterruptedException e) {} 32 | } 33 | buffer.put("DONE"); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Ch4/BoundedBuffer/ProducerConsumerEx.java: -------------------------------------------------------------------------------- 1 | package producerconsumerex; 2 | 3 | /** 4 | * 5 | * @author igorton 6 | */ 7 | public class ProducerConsumerEx { 8 | 9 | public static void main(String[] args) throws InterruptedException{ 10 | Buffer drop = new Buffer(); 11 | (new Thread(new Consumer(drop))).start(); 12 | (new Thread(new Consumer(drop))).start(); 13 | Thread.sleep(5000); 14 | (new Thread(new Producer(drop))).start(); 15 | (new Thread(new Producer(drop))).start(); 16 | 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Ch4/DiningPhilosophers/DeadlockDiningPhilosophers.java: -------------------------------------------------------------------------------- 1 | 2 | package dining.philosophers; 3 | 4 | // 5 | // Deadlocked implementation of Dining Philosophers 6 | // 7 | 8 | public class DeadlockDiningPhilosophers { 9 | 10 | private final static int NUMCHOPSTICKS = 5 ; 11 | private final static int NUMPHILOSOPHERS = 5; 12 | 13 | public static void main(String[] args) throws Exception { 14 | 15 | final Philosopher[] ph = new Philosopher[NUMPHILOSOPHERS]; 16 | Object[] chopSticks = new Object[NUMCHOPSTICKS]; 17 | 18 | for (int i = 0; i < NUMCHOPSTICKS; i++) { 19 | chopSticks[i] = new Object(); 20 | } 21 | 22 | for (int i = 0; i < NUMPHILOSOPHERS; i++) { 23 | Object leftChopStick = chopSticks[i]; 24 | Object rightChopStick = chopSticks[(i + 1) % chopSticks.length]; 25 | 26 | ph[i] = new Philosopher(leftChopStick, rightChopStick); 27 | 28 | Thread th = new Thread(ph[i], "Philosopher " + i); 29 | th.start(); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Ch4/DiningPhilosophers/DiningPhilosophers.java: -------------------------------------------------------------------------------- 1 | 2 | package dining.philosophers; 3 | 4 | // 5 | // Correct implementation of Dining Philosophers 6 | // 7 | 8 | public class DiningPhilosophers { 9 | 10 | private final static int NUMCHOPSTICKS = 5 ; 11 | private final static int NUMPHILOSOPHERS = 5; 12 | 13 | public static void main(String[] args) throws Exception { 14 | 15 | final Philosopher[] ph = new Philosopher[NUMPHILOSOPHERS]; 16 | Object[] chopSticks = new Object[NUMCHOPSTICKS]; 17 | 18 | for (int i = 0; i < NUMCHOPSTICKS; i++) { 19 | chopSticks[i] = new Object(); 20 | } 21 | 22 | for (int i = 0; i < NUMPHILOSOPHERS; i++) { 23 | Object leftChopStick = chopSticks[i]; 24 | Object rightChopStick = chopSticks[(i + 1) % chopSticks.length]; 25 | if (i == NUMPHILOSOPHERS - 1) { 26 | // The last philosopher picks up the right fork first 27 | ph[i] = new Philosopher(rightChopStick, leftChopStick); 28 | } else { 29 | // all others pick up the left chop stick first 30 | ph[i] = new Philosopher(leftChopStick, rightChopStick); 31 | } 32 | 33 | Thread th = new Thread(ph[i], "Philosopher " + i); 34 | th.start(); 35 | } 36 | 37 | } 38 | 39 | 40 | } 41 | -------------------------------------------------------------------------------- /Ch4/DiningPhilosophers/Philosopher.java: -------------------------------------------------------------------------------- 1 | package dining.philosophers; 2 | 3 | // 4 | // Thread to represent behavior of a single philosopher 5 | // 6 | public class Philosopher implements Runnable { 7 | 8 | private final Object leftChopStick; 9 | private final Object rightChopStick; 10 | private static final int SLEEPYTIME = 1000; 11 | 12 | Philosopher(Object leftChopStick, Object rightChopStick) { 13 | this.leftChopStick = leftChopStick; 14 | this.rightChopStick = rightChopStick; 15 | } 16 | private void LogEvent(String event) throws InterruptedException { 17 | System.out.println(Thread.currentThread().getName() + " " + event); 18 | Thread.sleep(SLEEPYTIME); 19 | } 20 | 21 | @Override 22 | public void run() { 23 | try { 24 | while (true) { 25 | LogEvent(": Thinking deeply"); 26 | synchronized (leftChopStick) { 27 | LogEvent( ": Picked up left chop stick"); 28 | synchronized (rightChopStick) { 29 | LogEvent(": Picked up right chopstick – eating"); 30 | LogEvent(": Put down right chopstick"); 31 | } 32 | LogEvent(": Put down left chopstick. Ate too much"); 33 | } 34 | } // end while 35 | } catch (InterruptedException e) { 36 | Thread.currentThread().interrupt(); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Ch7/rabbitmqtests/RabbitMQTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** RabbitMQ examples to illustrate two techniques for channel pooling. 3 | ** 1) using Apache GenericObjectPool 4 | ** 2) Using a simple channel pool using a BlockingQueue 5 | ** 6 | ** The examples supplement Chapter 7 of the Foundations of Scalable Systems, O'Reilly Media 2022 7 | */ 8 | package rabbitmqtests; 9 | import com.rabbitmq.client.Channel; 10 | import com.rabbitmq.client.Connection; 11 | import com.rabbitmq.client.ConnectionFactory; 12 | import java.io.IOException; 13 | import java.time.Duration; 14 | import java.util.Scanner; 15 | import java.util.concurrent.CountDownLatch; 16 | import java.util.concurrent.TimeUnit; 17 | import java.util.concurrent.TimeoutException; 18 | import java.util.logging.Level; 19 | import java.util.logging.Logger; 20 | import org.apache.commons.pool2.*; 21 | import rmqpool.RMQChannelFactory; 22 | import rmqpool.RMQChannelPool; 23 | import org.apache.commons.pool2.impl.*; 24 | 25 | /** 26 | * 27 | * @author Ian Gorton, Northeastern University 28 | * 29 | */ 30 | public class RabbitMQTests { 31 | // Number of threads to deploy in each test 32 | private static final int NUM_THREADS = 200; 33 | // Number of messages to publish in each thread 34 | private static final int NUM_ITERATIONS = 100; 35 | // Number of channels to add to pools 36 | private static final int NUM_CHANS = 30; 37 | // For Apache pool example, this allows the pool size to grow to ~= the same number of concurrent threads 38 | // that utilize the pool. Pass to config.setMaxWait(..) method to allow this behaviour 39 | private static final int ON_DEMAND = -1; 40 | // RMQ broker machine 41 | private static final String SERVER = "localhost"; 42 | // test queue name 43 | private static final String QUEUE_NAME = "test"; 44 | // the durtaion in seconds a client waits for a channel to be available in the pool 45 | // Tune value to meet request load and pass to config.setMaxWait(...) method 46 | private static final int WAIT_TIME_SECS = 1; 47 | 48 | 49 | 50 | /** 51 | * This method runs a multi-threaded test using a channel pool based on the 52 | * Apache GenericObjectPool class 53 | * 54 | * @param conn A connection to a RabbitMQ broker 55 | * @throws java.lang.InterruptedException 56 | */ 57 | public void ApachePoolTest(Connection conn) throws InterruptedException { 58 | 59 | final GenericObjectPool pool; 60 | 61 | // we use this object to tailor the behavior of the GenericObjectPool 62 | GenericObjectPoolConfig config = new GenericObjectPoolConfig(); 63 | // The code as is allows the cahhnel pool to grow to meet demand. 64 | // Change to config.setMaxTotal(NUM_CHANS) to limit the pool size 65 | config.setMaxTotal(ON_DEMAND); 66 | // clients will block when pool is exhausted, for a maximum duration of WAIT_TIME_SECS 67 | config.setBlockWhenExhausted(true); 68 | // tune WAIT_TIME_SECS to meet your workload/demand 69 | config.setMaxWait(Duration.ofSeconds(WAIT_TIME_SECS)); 70 | 71 | // The channel facory generates new channels on demand, as needed by the GenericObjectPool 72 | RMQChannelFactory chanFactory = new RMQChannelFactory (conn); 73 | 74 | //create the pool 75 | pool = new GenericObjectPool<>(chanFactory, config); 76 | 77 | // latch is used for the main thread to block until all test treads complete 78 | final CountDownLatch latch = new CountDownLatch(NUM_THREADS); 79 | 80 | // create N threads, each of which uses the channel pool to publish messages 81 | // TO DO refactor the loop into a method used by both tests? 82 | for (int i=0; i < NUM_THREADS; i++) { 83 | 84 | Runnable testThread = () -> { 85 | for (int j = 0; j < NUM_ITERATIONS; j++) { 86 | try { 87 | Channel channel; 88 | // get a channel from the pool 89 | channel = pool.borrowObject(); 90 | 91 | // publish a message 92 | channel.queueDeclare(QUEUE_NAME, false, false, false, null); 93 | byte[] payLoad = "test message apache pool".getBytes(); 94 | channel.basicPublish("", QUEUE_NAME, null, payLoad); 95 | 96 | // return the channel to the pool 97 | pool.returnObject(channel); 98 | 99 | } catch (IOException ex) { 100 | Logger.getLogger(RabbitMQTests.class.getName()).log(Level.INFO, null, ex); 101 | } catch (Exception ex) { 102 | Logger.getLogger(RabbitMQTests.class.getName()).log(Level.INFO, null, ex); 103 | } 104 | } 105 | // thread has finished, signal parent thread of completion 106 | latch.countDown(); 107 | 108 | }; 109 | new Thread(testThread).start(); 110 | } 111 | // block until all threads complete 112 | latch.await(); 113 | // close the channels and shutdown the pool 114 | pool.close(); 115 | System.out.println("INFO: Apache pool test finished"); 116 | } 117 | 118 | /** 119 | * This method runs a multithreaded test using a channel pool based on s 120 | * simple BlockQueue based implementation 121 | * 122 | * @param conn A valid connection to a RabbitMQ broker 123 | * @throws java.lang.InterruptedException 124 | */ 125 | public void QueuePoolTest(Connection conn) throws InterruptedException { 126 | 127 | // The channel facory generates new channels on demand, as needed by the channel pool 128 | RMQChannelFactory chanFactory = new RMQChannelFactory (conn); 129 | // create the fixed size channel pool 130 | RMQChannelPool pool = new RMQChannelPool(NUM_CHANS, chanFactory); 131 | 132 | // latch is used for the main thread to block until all test treads complete 133 | final CountDownLatch latch = new CountDownLatch(NUM_THREADS); 134 | 135 | // create N threads, each of which uses the channel pool to publish messages 136 | // TO DO refactor loop into a method for use by both tests 137 | for (int i=0; i < NUM_THREADS; i++) { 138 | 139 | Runnable testThread = () -> { 140 | for (int j = 0; j < NUM_ITERATIONS; j++) { 141 | try { 142 | Channel channel; 143 | // get a channel from the pool 144 | channel = pool.borrowObject(); 145 | 146 | // publish message 147 | channel.queueDeclare(QUEUE_NAME, false, false, false, null); 148 | byte[] payLoad = "queue test message".getBytes(); 149 | channel.basicPublish("", QUEUE_NAME, null, payLoad); 150 | 151 | // return channel to the pool 152 | pool.returnObject(channel); 153 | 154 | } catch (IOException ex) { 155 | Logger.getLogger(RabbitMQTests.class.getName()).log(Level.INFO, null, ex); 156 | } catch (Exception ex) { 157 | Logger.getLogger(RabbitMQTests.class.getName()).log(Level.INFO, null, ex); 158 | } 159 | } 160 | // I've finished - inform parent thread 161 | latch.countDown(); 162 | 163 | }; 164 | new Thread(testThread).start(); 165 | } 166 | // Block until all threads complete 167 | latch.await(); 168 | System.out.println("INFO: Queue based pool test finished"); 169 | 170 | } 171 | 172 | private void showTestConfig() { 173 | System.out.println("INFO: RabbitMQ Channel Pool Examples"); 174 | System.out.println("INFO: Test Configuration"); 175 | System.out.println("INFO: =================="); 176 | System.out.println(" "); 177 | System.out.println("INFO: Number of Threads per Test: " + NUM_THREADS); 178 | System.out.println("INFO: Number of messges to publish per thread: " + NUM_ITERATIONS); 179 | System.out.println("INFO: Channel Pool Size: " + NUM_CHANS); 180 | System.out.println("INFO: Queue name: " + QUEUE_NAME); 181 | System.out.println("INFO: RMQ Broker: " + SERVER); 182 | System.out.println(" "); 183 | System.out.println("INFO: =============================="); 184 | System.out.println(" "); 185 | } 186 | 187 | /** 188 | * Run this method to execute two multi threaded tests using (1) an Apache GenericObjectPool based implementation 189 | * and (2) a channel pool based on s simple BlockQueue based implementation 190 | * 191 | */ 192 | public static void main(String[] args) throws IOException, TimeoutException { 193 | // create object to run tests 194 | RabbitMQTests test = new RabbitMQTests(); 195 | test.showTestConfig(); 196 | 197 | // connect to local for testing 198 | // TO DO refactor to use constants or config file 199 | ConnectionFactory factory = new ConnectionFactory(); 200 | factory.setHost(SERVER); 201 | factory.setUsername("guest"); 202 | factory.setPassword("guest"); 203 | 204 | final Connection RMQconn = factory.newConnection(); 205 | System.out.println("INFO: RabbitMQ connection established"); 206 | 207 | 208 | // run ApachePoolTest and calculate the duration it takes 209 | long start = System.nanoTime(); 210 | try { 211 | test.ApachePoolTest(RMQconn); 212 | } catch (InterruptedException ex) { 213 | Logger.getLogger(RabbitMQTests.class.getName()).log(Level.SEVERE, null, ex); 214 | } 215 | long duration = System.nanoTime() - start; 216 | long convert = TimeUnit.MILLISECONDS.convert(duration, TimeUnit.NANOSECONDS); 217 | 218 | System.out.println("INFO: Test Duration = " + convert + " milliseconds"); 219 | System.out.println("INFO: Apache Pool Test Complete - hit any key to continue"); 220 | 221 | // read keybord input to continue 222 | Scanner scan = new Scanner(System.in); 223 | String text = scan.nextLine(); 224 | 225 | // run Blocking Queue based pool test and calculate the duration it takes 226 | long QueueTestStart = System.nanoTime(); 227 | try { 228 | test.QueuePoolTest(RMQconn); 229 | } catch (InterruptedException ex) { 230 | Logger.getLogger(RabbitMQTests.class.getName()).log(Level.SEVERE, null, ex); 231 | } 232 | duration = System.nanoTime() - QueueTestStart; 233 | convert = TimeUnit.MILLISECONDS.convert(duration, TimeUnit.NANOSECONDS); 234 | 235 | System.out.println("INFO: Test Duration = " + convert + " milliseconds"); 236 | System.out.println("INFO: All tests complete"); 237 | 238 | // clsoe RMQ connection and terminate 239 | RMQconn.close(); 240 | 241 | } 242 | 243 | } 244 | -------------------------------------------------------------------------------- /Ch7/rabbitmqtests/Recv.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package rabbitmqtests; 7 | 8 | import com.rabbitmq.client.Channel; 9 | import com.rabbitmq.client.Connection; 10 | import com.rabbitmq.client.ConnectionFactory; 11 | import com.rabbitmq.client.DeliverCallback; 12 | 13 | public class Recv { 14 | 15 | // private final static String QUEUE_NAME = "threadExQ"; 16 | private final static String QUEUE_NAME = "test"; 17 | 18 | public static void main(String[] argv) throws Exception { 19 | ConnectionFactory factory = new ConnectionFactory(); 20 | factory.setHost("localhost"); 21 | Connection connection = factory.newConnection(); 22 | Channel channel = connection.createChannel(); 23 | 24 | channel.queueDeclare(QUEUE_NAME, false, false, false, null); 25 | System.out.println(" [*] Waiting for messages. To exit press CTRL+C"); 26 | 27 | // accept only 1 unacknowledged message 28 | channel.basicQos(1); 29 | 30 | DeliverCallback deliverCallback = (consumerTag, delivery) -> { 31 | String message = new String(delivery.getBody(), "UTF-8"); 32 | channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false); 33 | System.out.println(" [x] Received '" + message + "'"); 34 | }; 35 | channel.basicConsume(QUEUE_NAME, false, deliverCallback, consumerTag -> { }); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Ch7/rmqpool/RMQChannelFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package rmqpool; 7 | 8 | /** 9 | * 10 | * @author Ian Gorton, Northeastern University 11 | * The examples supplement Chapter 7 of the Foundations of Scalable Systems, O'Reilly Media 2022 12 | */ 13 | 14 | 15 | import com.rabbitmq.client.Channel; 16 | import com.rabbitmq.client.Connection; 17 | 18 | import org.apache.commons.pool2.BasePooledObjectFactory; 19 | import org.apache.commons.pool2.PooledObject; 20 | import org.apache.commons.pool2.impl.DefaultPooledObject; 21 | 22 | import java.io.IOException; 23 | 24 | /** 25 | * A simple RabbitMQ channel factory based on the APche pooling libraries 26 | */ 27 | public class RMQChannelFactory extends BasePooledObjectFactory { 28 | 29 | // Valid RMQ connection 30 | private final Connection connection; 31 | // used to count created channels for debugging 32 | private int count; 33 | 34 | public RMQChannelFactory(Connection connection) { 35 | this.connection = connection; 36 | count = 0; 37 | } 38 | 39 | @Override 40 | synchronized public Channel create() throws IOException { 41 | count ++; 42 | Channel chan = connection.createChannel(); 43 | // Uncomment the line below to validate the expected number of channels are being created 44 | // System.out.println("Channel created: " + count); 45 | return chan; 46 | 47 | } 48 | 49 | @Override 50 | public PooledObject wrap(Channel channel) { 51 | //System.out.println("Wrapping channel"); 52 | return new DefaultPooledObject<>(channel); 53 | } 54 | 55 | public int getChannelCount() { 56 | return count; 57 | } 58 | 59 | // for all other methods, the no-op implementation 60 | // in BasePooledObjectFactory will suffice 61 | } 62 | -------------------------------------------------------------------------------- /Ch7/rmqpool/RMQChannelPool.java: -------------------------------------------------------------------------------- 1 | package rmqpool; 2 | 3 | /** 4 | * 5 | * @author Ian Gorton, Northeastern University 6 | * The examples supplement Chapter 7 of the Foundations of Scalable Systems, O'Reilly Media 2022 7 | */ 8 | 9 | import com.rabbitmq.client.Channel; 10 | import org.apache.commons.pool2.ObjectPool; 11 | import java.io.IOException; 12 | import java.util.concurrent.BlockingQueue; 13 | import java.util.concurrent.LinkedBlockingQueue; 14 | import java.util.logging.Level; 15 | import java.util.logging.Logger; 16 | import org.apache.commons.pool2.impl.GenericObjectPool; 17 | 18 | 19 | /** 20 | * A simple RabbitMQ channel pool based on a BlockingQueue implementation 21 | * 22 | */ 23 | public class RMQChannelPool { 24 | 25 | // used to store and distribute channels 26 | private final BlockingQueue pool; 27 | // fixed size pool 28 | private int capacity; 29 | // used to ceate channels 30 | private RMQChannelFactory factory; 31 | 32 | 33 | public RMQChannelPool(int maxSize, RMQChannelFactory factory) { 34 | this.capacity = maxSize; 35 | pool = new LinkedBlockingQueue<>(capacity); 36 | this.factory = factory; 37 | for (int i = 0; i < capacity; i++) { 38 | Channel chan; 39 | try { 40 | chan = factory.create(); 41 | pool.put(chan); 42 | } catch (IOException | InterruptedException ex) { 43 | Logger.getLogger(RMQChannelPool.class.getName()).log(Level.SEVERE, null, ex); 44 | } 45 | 46 | } 47 | } 48 | 49 | public Channel borrowObject() throws IOException { 50 | 51 | try { 52 | return pool.take(); 53 | } catch (InterruptedException e) { 54 | throw new RuntimeException("Error: no channels available" + e.toString()); 55 | } 56 | } 57 | 58 | public void returnObject(Channel channel) throws Exception { 59 | if (channel != null) { 60 | pool.add(channel); 61 | } 62 | } 63 | 64 | public void close() { 65 | // pool.close(); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # foundations-of-scalable-systems 2 | Code examples for the Foundations of Scalable Systems book 3 | --------------------------------------------------------------------------------