Microservices AntiPatterns and Pitfalls Mark Richards Microservices AntiPatterns and Pitfalls Mark Richards Beijing Boston Farnham Sebastopol Tokyo Microservices Antipatterns and Pitfalls by Mark Richards Copyright © 2016 O’Reilly Media, Inc All rights reserved Printed in the United States of America Published by O’Reilly Media, Inc., 1005 Gravenstein Highway North, Sebastopol, CA 95472 O’Reilly books may be purchased for educational, business, or sales promotional use Online editions are also available for most titles (http://safaribooksonline.com) For more information, contact our corporate/institutional sales department: 800-998-9938 or corporate@oreilly.com Editor: Brian Foster Production Editor: Melanie Yarbrough Copyeditor: Christina Edwards Proofreader: Amanda Kersey July 2016: Interior Designer: David Futato Cover Designer: Karen Montgomery Illustrator: Rebecca Demarest First Edition Revision History for the First Edition 2016-07-06: First Release The O’Reilly logo is a registered trademark of O’Reilly Media, Inc Microservices AntiPatterns and Pitfalls, the cover image, and related trade dress are trademarks of O’Reilly Media, Inc While the publisher and the author have used good faith efforts to ensure that the information and instructions contained in this work are accurate, the publisher and the author disclaim all responsibility for errors or omissions, including without limi‐ tation responsibility for damages resulting from the use of or reliance on this work Use of the information and instructions contained in this work is at your own risk If any code samples or other technology this work contains or describes is subject to open source licenses or the intellectual property rights of others, it is your responsi‐ bility to ensure that your use thereof complies with such licenses and/or rights 978-1-491-96331-9 [LSI] Table of Contents Preface v Data-Driven Migration AntiPattern Too Many Data Migrations Functionality First, Data Last The Timeout AntiPattern Using Timeout Values Using the Circuit Breaker Pattern The “I Was Taught to Share” AntiPattern 13 Too Many Dependencies Techniques for Sharing Code 14 15 Reach-in Reporting AntiPattern 19 Issues with Microservices Reporting Asynchronous Event Pushing 19 22 Grains of Sand Pitfall 25 Analyzing Service Scope and Function Analyzing Database Transactions Analyzing Service Choreography 26 28 29 Developer Without a Cause Pitfall 33 Making the Wrong Decisions Understanding Business Drivers 33 35 iii Jump on the Bandwagon Pitfall 37 Advantages and Disadvantages Matching Business Needs Other Architecture Patterns 37 40 41 The Static Contract Pitfall 43 Changing a Contract Header Versioning Schema Versioning 44 45 46 Are We There Yet Pitfall 49 Measuring Latency Comparing Protocols 49 50 10 Give It a Rest Pitfall 51 Asynchronous Requests Broadcast Capabilities Transacted Requests iv | Table of Contents 52 53 54 Preface In late 2006 service-oriented architecture (SOA) was all the craze Companies were jumping on the bandwagon and embracing SOA before fully understanding the advantages and disadvantages of this very complex architecture style Those companies that embarked on SOA projects often found constant struggles with service granular‐ ity, performance, data migrations, and in particular the organiza‐ tional change that comes about with SOA As a result, many companies either abandoned their SOA efforts or built hybrid archi‐ tectures that did not fulfill all of the promises of SOA Today we are poised to repeat this same experience with a relatively new architecture style known as microservices Microservices is a current trend in the industry right now, and like SOA back in the mid 2000s, is all the craze As a result, many companies are looking toward this architecture style to leverage the benefits provided by microservices such as ease of testing, fast and easy deployments, fine-grained scalability, modularity, and overall agility However, like SOA, companies developing microservices are finding themselves struggling with things like service granularity, data migration, organizational change, and distributed processing challenges As with any new technology, architecture style, or practice, antipat‐ terns, and pitfalls usually emerge as you learn more about it and experience the many “lessons learned” during the process While antipatterns and pitfalls may seem like the same thing, there is a subtle difference between them Andrew Koenig defines an antipat‐ tern as something that seems like a good idea when you begin, but leads you into trouble, whereas my friend Neal Ford defines a pitfall as something that was never a good idea, even from the start This is v an important distinction because you may not experience the nega‐ tive results from an antipattern until you are well into the develop‐ ment lifecycle or even well into production, whereas with a pitfall you usually find out you are headed down the wrong path relatively quickly This report will introduce several of the more common antipatterns and pitfalls that emerge when using microservices My goal with this report is to help you avoid costly mistakes by not only helping you understand when an antipattern or pitfall is occurring, but more importantly helping you understand the techniques and practices for avoiding these antipatterns and pitfalls While I don’t have time in this report to cover the details of all of the various antipatterns and pitfalls you might encounter with micro‐ services, I cover some of the more common ones These include antipatterns and pitfalls related to service granularity (Chapter 5, Grains of Sand Pitfall), data migration (Chapter 1, Data-Driven Migration AntiPattern), remote access latency (Chapter 9, Are We There Yet Pitfall), reporting (Chapter 4, Reach-in Reporting AntiPat‐ tern), contract versioning (Chapter 8, The Static Contract Pitfall), service responsiveness (Chapter 2, The Timeout AntiPattern), and many others I recently recorded a video for O’Reilly called Microservices AntiPat‐ terns and Pitfalls: Learning to Avoid Costly Mistakes that contains the complete set of antipatterns and pitfalls you may encounter when using microservices, as well as a more in-depth look into each one Included in the video is a self-assessment workbook containing analysis tasks and goals oriented around analyzing your current application You can use this assessment workbook to determine whether you are experiencing any of the antipatterns and pitfalls introduced in the video and ways to avoid them Conventions Used in This Book The following typographical conventions are used in this book: Italic Indicates new terms, URLs, email addresses, filenames, and file extensions vi | Preface Constant width Used for program listings, as well as within paragraphs to refer to program elements such as variable or function names, data‐ bases, data types, environment variables, statements, and key‐ words Constant width bold Shows commands or other text that should be typed literally by the user Constant width italic Shows text that should be replaced with user-supplied values or by values determined by context Safari® Books Online Safari Books Online is an on-demand digital library that delivers expert content in both book and video form from the world’s lead‐ ing authors in technology and business Technology professionals, software developers, web designers, and business and creative professionals use Safari Books Online as their primary resource for research, problem solving, learning, and certif‐ ication training Safari Books Online offers a range of plans and pricing for enter‐ prise, government, education, and individuals Members have access to thousands of books, training videos, and prepublication manuscripts in one fully searchable database from publishers like O’Reilly Media, Prentice Hall Professional, AddisonWesley Professional, Microsoft Press, Sams, Que, Peachpit Press, Focal Press, Cisco Press, John Wiley & Sons, Syngress, Morgan Kaufmann, IBM Redbooks, Packt, Adobe Press, FT Press, Apress, Manning, New Riders, McGraw-Hill, Jones & Bartlett, Course Tech‐ nology, and hundreds more For more information about Safari Books Online, please visit us online Preface | vii Other Architecture Patterns The microservices architecture style is a very powerful one that car‐ ries with it many advantages, but it isn’t the only architecture style out there Another thing you can to avoid this pitfall is to under‐ stand and analyze other architecture patterns to determine if one of those might be a better fit for your situation Besides microservices there are seven other common architecture patterns you might want to consider for your application or system: • • • • • • • Service-Based Architecture Service-Oriented Architecture Layered Architecture Microkernel Architecture Space-Based Architecture Event-Driven Architecture Pipeline Architecture Of course, you don’t have to select one single architecture pattern for your application You can certainly combine patterns to create an effective solution Some examples are event-driven microservices, event-based microkernel, layered space-based architecture, and pipeline microkernel Use the following resources to learn more about other architecture patterns: • Software Architecture Fundamentals: Understanding the Basics • Software Architecture Fundamentals: Beyond the Basics • Software Architecture Fundamentals: Service-Based Architecture • Software Architecture Patterns) • Microservices vs Service-Oriented Architecture Other Architecture Patterns | 41 CHAPTER The Static Contract Pitfall All microservices have contracts between the service consumers and the microservice A contract usually contains a schema specifying the expected input and output data, and sometimes the name of the operation (depending on how you are implementing your service) Contracts are usually owned by the service, and can be represented through formats like XML, JSON, or even a Java or C# object And of course, those contracts never change, right? Wrong The static contract pitfall occurs when you fail to version your ser‐ vice contracts from the very start, or even not at all Contract ver‐ sioning is absolutely critical for not only avoiding breaking changes (changing a contract and breaking all consumers using that con‐ tract), but also to maintain agility by supporting backward compati‐ bility Here’s an example that illustrates how you can get into trouble by not versioning your contracts Assume you have a microservice that is accessed by three different clients (client 1, client 2, and client 3) Client would like to make a change to the service contract right away You check with client and client to see if they can accom‐ modate the change, and both clients inform you that it will take weeks to implement that change due to other things going on with those clients Now you must inform client that it will take weeks to make that change because you need to coordinate the update with clients and However, client cannot wait weeks By providing versioning in your contracts, and hence providing backward compatibility, you can now be more agile in terms of cli‐ 43 ent 1’s request Agility is defined as how fast you can respond to change If you properly versioned your contracts from the very start, you could immediately respond to client 1’s request for the contract change by simply creating a new version of the contract, say version 1.1 Clients and are both using version 1.0 of the contract, so now you can implement the change right away without having to wait for client or client to respond In addition, you can make the change without making what is called a “breaking change.” There are two basic techniques for contract versioning: versioning at the header level and versioning in the contract schema itself In this chapter I will cover each of these techniques in detail, but first let’s look at an example Changing a Contract To illustrate the problem with not versioning a contract I will use an example of buying a certain number of shares of Apple common stock (AAPL) The schema for this request might look something like this: { "$schema": "http://json-schema.org/draft-04/schema#", "properties": { "acct": {"type": "number"}, "cusip": {"type": "string"}, "shares": {"type": "number", "minimum": 100} }, "required": ["acct", "cusip", "shares"] } In this case to buy stock you must specify the brokerage account (acct), the stock you wish to purchase in CUSIP (Committee on Uniform Security Identification Procedures) format (cusip), and finally the number of shares (shares), which must be greater than 100 All three fields are required The code to make a request to purchase 1000 shares of Apple stock (CUSIP 037833100) for brokerage account 12345 using REST would look like this: POST /trade/buy Accept: application/json { "acct": "12345", "cusip": "037833100", "shares": "1000" } 44 | Chapter 8: The Static Contract Pitfall Now let’s say that the service changes its contract to accept a SEDOL (Stock Exchange Daily Official List) rather than a CUSIP, which is another industry standard way of identifying a particular instru‐ ment to be traded Now the contract looks like this: { "$schema": "http://json-schema.org/draft-04/schema#", "properties": { "acct": {"type": "number"}, "sedol": {"type": "string"}, "shares": {"type": "number", "minimum": 100} }, "required": ["acct", "sedol", "shares"] } This would be considered a breaking change in that the prior client code will now fail because it is still using a CUSIP What you need to is use versioning so that version uses a CUSIP and version uses a SEDOL to identify the stock being traded Header Versioning The first technique for contract versioning is to put the contract ver‐ sion number in the header of the remote access protocol as illustra‐ ted in Figure 8-1 I like to refer to this as protocol-aware contract versioning because the information about the version of the contract you are using is contained within the header of the remote access protocol (e.g., REST, SOAP, AMQP, JMS, MSMQ, etc.) Figure 8-1 Header contract versioning When using REST you can use what is called a vendor mime type to specify the version of the contract you wish to use in the accept header of the request: POST /trade/buy Accept: application/vnd.svc.trade.v2+json Header Versioning | 45 By using the vendor mime type (vnd) in the accept header of the URI you can specify the version number of the contract, thereby directing the service to perform processing based on that contract version number Correspondingly, the service will need to parse the accept header to determine the version number One example of this would be to use a regular expression to find the version as illustrated below: def version request.headers ["Accept"][/^application/vnd.svc.trade.v(d)/, 1].to_i end Unfortunately that is the easy part; the hard part is coding all of the cyclomatic complexity into the service to provide conditional pro‐ cessing based on the contract version (e.g., if version then else if version then ) For this reason, we need some sort of versiondeprecation policy to control the level of cyclomatic complexity you introduce into each service Using messaging you will need to supply the version number in the property section of the message header For JMS 2.0 that would look something like this: String msg = createJSON( "acct","12345", "sedol","2046251", "shares","1000")}; jmsContext.createProducer() setProperty("version", 2) send(queue, msg); Each messaging standard will have its own way of setting this header The important thing to remember here is that regardless of the messaging standard, the version property is a string value that needs to match exactly with what the service is expecting, including being case-sensitive For this reason it’s generally not a good idea to supply a default version if the version number cannot be found in the header Schema Versioning Another contract-versioning technique is adding the version num‐ ber to the actual schema itself This technique is illustrated in Figure 8-2 I usually refer to this technique as protocol-agnostic con‐ 46 | Chapter 8: The Static Contract Pitfall tract versioning because the version identification is completely inde‐ pendent of the remote access protocol Nothing needs to be specified in the headers of the remote access protocol in order to use versioning Figure 8-2 Schema-based contract versioning By using schema-based versioning, the schema used in the previous example would look like this: { "$schema": "http://json-schema.org/draft-04/schema#", "properties": { "version": {"type": "integer"}, "acct": {"type": "number"}, "cusip": {"type": "string"}, "sedol": {"type": "string"}, "shares": {"type": "number", "minimum": 100} }, "required": ["version", "acct", "shares"] } Notice that the the schema actually contains the version number field (version) as an integer value Because you only have one schema now, you will need to add all of the combinations of possi‐ bilities to the schema In the example above both the CUSIP and SEDOL are added to the schema because that is what varies between the versions The big advantage of this technique is that the schema (including the version) is independent of the remote access protocol This means that the same exact schema can be used by multiple proto‐ cols For example, the same schema can be used by REST and JMS 2.0 without any modifications to the remote access protocol headers: POST /trade/buy Accept: application/json { "version": "2", "acct": "12345", Schema Versioning | 47 "sedol": "2046251", "shares": "1000" } String msg = createJSON( "version","2", "acct","12345", "sedol","2046251", "shares","1000")}; jmsContext.createProducer().send(queue, msg); Unfortunately this technique has a lot of disadvantages associated with it First, you must parse the actual payload of the message to extract the version number This precludes using things like XML appliances (e.g., DataPower) to routing, and also might present issues when trying to parse the schema (particularly with XML) Secondly, the schemas can get quite complex, making it difficult to automated conversions of the schema (e.g., JSON to Java object) Finally, custom validations may be required in the service to validate the schema In the example above, the service would have to validate that either the CUSIP or SEDOL is filled in based on the version number 48 | Chapter 8: The Static Contract Pitfall CHAPTER Are We There Yet Pitfall With the microservices architecture every service is deployed as a separate application, meaning all of the communication to a micro‐ service from the client or API layer, as well as communication between services, requires a remote call This pitfall occurs when you don’t know how long the remote access call takes You might assume the latency it around 50 milliseconds, but have you ever measured it? Do you know what the average latency is for your particular environment? Do you know what the “long tail” latency is (e.g., 95, 99, 99.5 percentiles) for your environ‐ ment? Measuring both of these metrics is important, because even with good average latency, bad long-tail latency can destroy you Measuring Latency Measuring the remote access latency under load in your production environment (or production-like environment) is critical for under‐ standing the performance profile of your application For example, let’s say a particular business request requires the coordination of four microservices Assuming that your remote access latency is 100 milliseconds, that particular business request would consume 500 milliseconds just in remote access latency alone (the initial request plus four remote calls between services) That is a half a second of request time without one single line of source code being executed for the actual business request processing Most applications simply cannot absorb that sort of latency 49 You might think the way to avoid this pitfall is to simply measure and determine your average latency for your chosen remote access protocol (e.g., REST) However, that only provides you with one piece of information—the average latency of the particular remote access protocol you are using The other task is to investigate the comparative latency using other remote access protocols such as Java Message Service (JMS), Advanced Message Queuing Protocol (AMQP), and Microsoft Message Queue (MSMQ) Comparing Protocols The comparative latency will vary greatly based on both your envi‐ ronment and the nature of the business request, so it is important to establish these benchmarks on a variety of business requests with different load profiles Figure 9-1 Comparing remote access latency In looking at the hypothetical example in Figure 9-1 you notice that AMQP is in fact almost twice as fast as REST You can now leverage this information to make intelligent choices as to which requests should use which remote access protocol For example, you may choose to use REST for all communications from client requests to your microservices and AMQP for all interservice communication in order to increase performance within your application Performance is not the only consideration when selecting your remote access protocol As you will see in Chapter 10, you may want to leverage messaging to provide additional capabilities to your application as well 50 | Chapter 9: Are We There Yet Pitfall CHAPTER 10 Give It a Rest Pitfall Using REST is by far the most popular choice for accessing micro‐ services and communicating between services It’s so common a choice that most of the popular template frameworks (e.g., DropWi‐ zard, Spring Boot, etc.) have REST access already built into the ser‐ vice templates If REST is such a popular choice, then why is it a pitfall? The give it a rest pitfall is about using REST as the only com‐ munication protocol and ignoring the power of messaging to enhance your microservices architecture For example, in a RESTful microservices architecture, how would you handle asynchronous communications? What about the need for broadcast capabilities? What you if you need to manage multiple remote RESTful calls within a transactional unit of work? There are two types of messaging standards you should be aware of when considering using messaging for your microservices architec‐ ture—platform-specific standards and platform-independent stand‐ ards Platform-specific standards include the JMS for the Java platform and MSMQ for the NET platform Both describe a stan‐ dard API used within the platform, independent of the messaging provider (vendor) you are using For example, in the Java platform you can swap out brokers (e.g., ActiveMQ, HornetQ, etc.) with no API changes While the API is standard and remains the same, it’s the underlying proprietary protocol between these brokers that is different (which is why you need to have the same client JAR and server JAR for the same vendor) With platform-standard messaging protocols you are more concerned about portability via a common 51 API rather than the actual vendor product you are using or the wirelevel protocols used The current platform-independent standard is AMQP AMQP, which standardizes on the wire-level protocol, not the API This allows heterogeneous platforms to communicate with one another, regardless of the vendor product you are using A client using Rab‐ bitMQ, for example, can easily communicate with a StormMQ server (assuming they are using the same protocol version) AMQP using RabbitMQ is currently the most popular choice for messaging within a microservices architecture, mostly because of its platformindependent nature Asynchronous Requests The first consideration for using messaging within your microservi‐ ces architecture is asynchronous communication With asynchro‐ nous requests the service caller does not need to wait for a response from the service when making a request, as illustrated in Figure 10-1 This is sometimes referred to as “fire-and-forget” pro‐ cessing Figure 10-1 Asynchronous communications using messaging Not only does asynchronous processing increase overall perfor‐ mance, but it also adds an element of reliability to your system Per‐ formance is increased because callers don’t have to wait for a response if none is needed Through guaranteed delivery, the mes‐ sage broker ensures that the service will eventually receive the mes‐ sage Reliability is increased because the caller doesn’t need to worry about setting timeout values or using the circuit breaker pattern when communicating with a service (see Chapter 2) 52 | Chapter 10: Give It a Rest Pitfall Broadcast Capabilities Another very powerful feature of messaging that is not available within REST is the capability to broadcast a message to multiple services This is known in messaging as “publish-and-subscribe” messaging, and usually involves topics and subscribers (depending on the messaging standard you are using) Figure 10-2 illustrates the basic behavior of broadcast messaging Figure 10-2 Broadcast capabilities using messaging Broadcast messaging involves a message producer sending out the same message to multiple message receivers (i.e., services) The mes‐ sage producer generally doesn’t know who is accepting the message or what they are going to with it For example, a message pro‐ ducer may broadcast a message informing consumers about a stock split for Apple stock (AAPL) The message producer only has the responsibility of publishing a message to a topic (JMS), a fanout or topic exchange (AMQP), or a multicast queue (MSMQ) The stock split message may be picked up by any number of consumers, or no consumers at all Broadcast Capabilities | 53 Transacted Requests Messaging systems support the notion of transacted messages, meaning that if messages are sent to multiple queues or topics within the context of a transaction, the messages are not actually received by the services until the sender does a commit on that transaction The service consumer sends a message to the first ser‐ vice and then sends another message to the second service, as illus‐ trated in Figure 10-3 Until the service consumer performs a commit, those messages are held in the queues Once the service consumer performs a commit, both messages are then released Figure 10-3 Transaction capabilities of messaging If the service consumer in Figure 10-3 sends a message to the first queue, but then experiences some sort of error, the service con‐ sumer can perform a rollback on the messaging transaction, which would effectively remove the message from the first queue Implementing this sort of transaction capability using REST would be very difficult, essentially requiring the service consumer to issue compensating requests to reverse the updates made by each request Therefore, it is a good idea to consider using transacted messaging any time a service consumer needs to orchestrate multiple remote requests 54 | Chapter 10: Give It a Rest Pitfall About the Author Mark Richards is an experienced, hands-on software architect involved in the architecture, design, and implementation of micro‐ services architectures, service-oriented architectures, and dis‐ tributed systems in J2EE and other technologies He has been in the software industry since 1983 and has significant experience and expertise in application, integration, and enterprise architecture Mark served as the president of the New England Java Users Group from 1999 through 2003 He is the author of numerous technical books and videos, including the Software Architecture Fundamentals Video Series (O’Reilly video), Enterprise Messaging (O’Reilly video), Java Message Service, 2nd Edition (O’Reilly), and a contributing author to 97 Things Every Software Architect Should Know (O’Reilly) Mark has a master’s degree in computer science and numerous architect and developer certifications from IBM, Sun, The Open Group, and BEA He is a regular conference speaker at the No Fluff Just Stuff (NFJS) Symposium Series and has spoken at more than 100 conferences and user groups around the world on a variety of enterprise-related technical topics When he is not working, Mark can usually be found teaching architecture fundamentals classes and hiking in the White Mountains of New Hampshire and along the Appalachian Trail ... friend and partner-in-crime Neal Ford for helping me understand the dif‐ ferences between pitfalls and antipatterns and for also helping me properly classify each of the antipatterns and pitfalls. .. architecture style, or practice, antipat‐ terns, and pitfalls usually emerge as you learn more about it and experience the many “lessons learned” during the process While antipatterns and pitfalls. .. understand the techniques and practices for avoiding these antipatterns and pitfalls While I don’t have time in this report to cover the details of all of the various antipatterns and pitfalls