Previous Next

Go Programming Blueprints (Mat Ryer) (z-library.sk, 1lib.sk, z-lib.sk)

Author: Mat Ryer

GO

Go is the language of the Internet age, and the latest version of Go comes with major architectural changes. Implementation of the language, runtime, and libraries has changed significantly. The compiler and runtime are now written entirely in Go. The garbage collector is now concurrent and provides dramatically lower pause times by running in parallel with other goroutines when possible. This book will show you how to leverage all the latest features and much more. This book shows you how to build powerful systems and drops you into real-world situations. Scale, performance, and high availability lie at the heart of our projects, and the lessons learned throughout this book will arm you with everything you need to build world-class solutions. Each project could form the basis of a start-up, which means they are directly applicable to modern software markets. What you will learn Build quirky and fun projects from scratch while exploring patterns, practices, and techniques, as well as a range of different technologies Create websites and data services capable of massive scale using Gos net/h ttp package, exploring RESTful patterns as well as low-latency WebSocket APIs Interact with a variety of remote web services to consume capabilities ranging from authentication and authorization to a fully functioning thesaurus Develop high-quality command-line tools that utilize the powerful shell capabilities and perform well using Gos in-built concurrency mechanisms Build microservices for larger organizations using the Go Kit library Implement a modern document database as well as high-throughput messaging queue technology to put together an architecture that is truly ready to scale Write concurrent programs and gracefully manage the execution of them and communication by smartly using channels Get a feel for app deployment using Docker and Google App Engine

📄 File Format: PDF
💾 File Size: 10.6 MB
12
Views
0
Downloads
0.00
Total Donations

📄 Text Preview (First 20 pages)

ℹ️

Registered users can read the full content for free

Register as a Gaohf Library member to read the complete e-book online for free and enjoy a better reading experience.

📄 Page 1
(This page has no text content)
📄 Page 2
Go Programming Blueprints Second Edition Build real-world, production-ready solutions in Go using cutting-edge technology and techniques Mat Ryer BIRMINGHAM - MUMBAI
📄 Page 3
Go Programming Blueprints Second Edition Copyright © 2016 Packt Publishing All rights reserved. No part of this book may be reproduced, stored in a retrieval system, or transmitted in any form or by any means, without the prior written permission of the publisher, except in the case of brief quotations embedded in critical articles or reviews. Every effort has been made in the preparation of this book to ensure the accuracy of the information presented. However, the information contained in this book is sold without warranty, either express or implied. Neither the author, nor Packt Publishing, and its dealers and distributors will be held liable for any damages caused or alleged to be caused directly or indirectly by this book. Packt Publishing has endeavored to provide trademark information about all of the companies and products mentioned in this book by the appropriate use of capitals. However, Packt Publishing cannot guarantee the accuracy of this information. First published: January 2015 Second edition: October 2016 Production reference: 1211016 Published by Packt Publishing Ltd. Livery Place 35 Livery Street Birmingham B3 2PB, UK. ISBN 978-1-78646-894-9 www.packtpub.com
📄 Page 4
Credits Author Mat Ryer Copy Editor Stuti Srivastava Reviewers Michael Hamrah David Hernandez Project Coordinator Suzanne Coutinho Commissioning Editor Kunal Parikh Proofreader SAFIS Editing Acquisition Editor Sonali Vernekar Indexer Tejal Daruwale Soni Content Development Editor Siddhi Chavan Graphics Abhinash Sahu Technical Editors Bhavin Savalia Dhiraj Chandanshive Production Coordinator Aparna Bhagat
📄 Page 5
About the Author Mat Ryer has been programming computers since he was 6 years old; he and his father would build games and programs, first in BASIC on a ZX Spectrum and then in AmigaBASIC and AMOS on Commodore Amiga. Many hours were spent on manually copying the code from Amiga Format magazine and tweaking variables or moving GOTO statements around to see what might happen. The same spirit of exploration and obsession with programming led Mat to start work with a local agency in Mansfield, England, when he was 18, where he started to build websites and services. In 2006, Mat left rural Nottinghamshire for London, where he took a job at BT. It was here that he worked with a talented group of developers and managers on honing his agile development skills and developing the light flavor that he still uses today. After being contracted around London for a few years, coding everything from C# and Objective-C to Ruby and JavaScript, Mat noticed a new systems language called Go that Google was pioneering. Since it addressed very pertinent and relevant modern technical challenges, Mat started using it to solve problems while the language was still in the beta stage and he has used it ever since. In 2012, Mat moved to Boulder, Colorado, where he worked on a variety of projects, from big data web services and highly available systems to small side projects and charitable endeavors. He returned home, to London, in 2015 after the company he was working in was sold. Mat, to this day, continues to use Go to build a variety of products, services, and open- source projects. He writes articles about Go on his blog at matryer.com and tweets about Go with the handle @matryer. Mat is a regular speaker at Go conferences around the world and encourages people to come up and introduce themselves if their paths ever cross.
📄 Page 6
Acknowledgments I wouldn't have been able to write this book, or the second edition, without the help of the wonderful Laurie Edwards, who, while working on her own projects took the time to keep me organized and focused. Without her continuous and undying support, I dare say this book (along with every other project I have embarked on) would never have happened. Development heroes of mine include David Hernández (@dahernan on GitHub), who delights in telling me that my ideas are "terrible" before later falling in love with them; Ernesto Jiménez, who works extremely hard and extremely effectively on private and public projects alike; Tyler Bunnell (@tylerb on GitHub), who I learned Go with; and Ryan Quinn (@mazondo on GitHub), who seems to build an app a day and is living proof of how building something, however simple, is always better than building nothing. Thanks also goes out to Tim Schreiner for engaging in debates with me over the good and bad bits of Go as well as being my go-to guy on matters close to and beyond the fringes of computer science. Thanks go to the core Go team for building such a fun language and to the entire Go community, who have saved me months of development with their contributions. A special shout out to the Women Who Go and Go Bridge (@golangbridge on Twitter) groups, who are working increasingly hard to help us reach and maintain a rich and diversely populated community. Special thanks also goes to everyone who has supported me in my life and helped me in developing what I love into a career, including, but not limited to, Nick and Maggie Ryer, Chris Ryer, Glenn and Tracey Wilson, Phil Jackson, Aaron Edell, Sek Chai, David Hernández, Ernesto Jiménez, Blaine Garst, Tim and Stacey Stockhaus, Tom Szabo, Steve Davis, Mark Gray, John Motz, Rory Donnelly, Piotr Rojek, Corey Prak, Peter Bourgon, Andrew Gerrand, Dave Cheney, William (Bill) Kennedy, Matt Heath, Carlisia Campos, Tiffany Jernigan, Natalie Pistunovich, Simon Howard, Sean Thompson, Jeff Cavins, Edd Grant, Alan Meade, Steve Cart, Andy Jackson, Aditya Pradana, Andy Joslin, Kal Chottai, Tim Ryer, Emma Payne, Corey and Ashton Ryer, Clair Ryer, Gareth and Dylan Evans, Holly Smitheman, Phil Edwards, Tracey Edwards, Kirsten, Megan and Paul Krawczyk, Alex, Adriénne and Ethan Edwards, Chantelle and Greg Rosson, and all my other great friends and family. In the loving memory of Maggie Ryer, 1961 - 2015.
📄 Page 7
About the Reviewer Michael Hamrah is a software engineer from Brooklyn, New York, specializing in scalable and distributed systems with more than a decade of development experience. He is currently working as a Senior Software Engineer at Uber focusing on metrics and monitoring systems, which handles billions of low-latency events per day across multiple data centers. He works primarily with Go and has an extensive experience with all levels of the software stack . He can be reached on LinkedIn at h t t p s : / / w w w . l i n k e d i n . c o m / i n / h a m r a h. David Hernandez is an independent software engineer from London. He helps companies improve their software. He has worked in different countries, such as Spain, UK, and Australia. He has participated in projects such as the BBC London Olympics 2012. Additionally, he has also helped to achieve Continuous Delivery at Atlassian, and he has delivered services to citizens at UK Government Digital Services. You can find David speaking and collaborating at the Go London User Group, as Go is his favorite programming language.
📄 Page 8
www.PacktPub.com For support files and downloads related to your book, please visit www.PacktPub.com. For support files and downloads related to your book, please visit www.PacktPub.com. Did you know that Packt offers eBook versions of every book published, with PDF and ePub files available? You can upgrade to the eBook version at www.PacktPub.com and as a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at service@packtpub.com for more details. At www.PacktPub.com, you can also read a collection of free technical articles, sign up for a range of free newsletters and receive exclusive discounts and offers on Packt books and eBooks. h t t p s : / / w w w . p a c k t p u b . c o m / m a p t Get the most in-demand software skills with Mapt. Mapt gives you full access to all Packt books and video courses, as well as industry-leading tools to help you plan your personal development and advance your career. Why subscribe? Fully searchable across every book published by Packt Copy and paste, print, and bookmark content On demand and accessible via a web browser
📄 Page 9
Table of Contents Preface 1 Chapter 1: Chat Application with Web Sockets 9 A simple web server 10 Separating views from logic using templates 12 Doing things once 14 Using your own handlers 14 Properly building and executing Go programs 15 Modeling a chat room and clients on the server 15 Modeling the client 16 Modeling a room 19 Concurrency programming using idiomatic Go 19 Turning a room into an HTTP handler 20 Using helper functions to remove complexity 22 Creating and using rooms 23 Building an HTML and JavaScript chat client 23 Getting more out of templates 25 Tracing code to get a look under the hood 28 Writing a package using TDD 28 Interfaces 29 Unit tests 30 Red-green testing 32 Implementing the interface 34 Unexported types being returned to users 35 Using our new trace package 36 Making tracing optional 38 Clean package APIs 39 Summary 40 Chapter 2: Adding User Accounts 41 Handlers all the way down 42 Making a pretty social sign-in page 45 Endpoints with dynamic paths 47 Getting started with OAuth2 50 Open source OAuth2 packages 50 Tell the authorization providers about your app 51
📄 Page 10
[ ii ] Implementing external logging in 52 Logging in 53 Handling the response from the provider 56 Presenting the user data 58 Augmenting messages with additional data 59 Summary 64 Chapter 3: Three Ways to Implement Profile Pictures 65 Avatars from the OAuth2 server 66 Getting the avatar URL 66 Transmitting the avatar URL 67 Adding the avatar to the user interface 68 Logging out 69 Making things prettier 71 Implementing Gravatar 73 Abstracting the avatar URL process 73 The auth service and the avatar's implementation 74 Using an implementation 76 The Gravatar implementation 78 Uploading an avatar picture 81 User identification 82 An upload form 83 Handling the upload 84 Serving the images 86 The Avatar implementation for local files 87 Supporting different file types 89 Refactoring and optimizing our code 90 Replacing concrete types with interfaces 91 Changing interfaces in a test-driven way 92 Fixing the existing implementations 94 Global variables versus fields 95 Implementing our new design 96 Tidying up and testing 97 Combining all three implementations 98 Summary 100 Chapter 4: Command-Line Tools to Find Domain Names 101 Pipe design for command-line tools 102 Five simple programs 102 Sprinkle 103 Domainify 107 Coolify 109
📄 Page 11
[ iii ] Synonyms 112 Using environment variables for configuration 113 Consuming a web API 113 Getting domain suggestions 117 Available 118 Composing all five programs 122 One program to rule them all 123 Summary 127 Chapter 5: Building Distributed Systems and Working with Flexible Data 128 The system design 129 The database design 130 Installing the environment 131 Introducing NSQ 131 NSQ driver for Go 133 Introducing MongoDB 133 MongoDB driver for Go 134 Starting the environment 134 Reading votes from Twitter 135 Authorization with Twitter 135 Extracting the connection 137 Reading environment variables 138 Reading from MongoDB 140 Reading from Twitter 142 Signal channels 144 Publishing to NSQ 146 Gracefully starting and stopping programs 148 Testing 150 Counting votes 151 Connecting to the database 152 Consuming messages in NSQ 153 Keeping the database updated 155 Responding to Ctrl + C 157 Running our solution 158 Summary 159 Chapter 6: Exposing Data and Functionality through a RESTful Data Web Service API 161 RESTful API design 162 Sharing data between handlers 163 Context keys 163
📄 Page 12
[ iv ] Wrapping handler functions 165 API keys 165 Cross-origin resource sharing 166 Injecting dependencies 167 Responding 167 Understanding the request 169 Serving our API with one function 171 Using handler function wrappers 173 Handling endpoints 173 Using tags to add metadata to structs 174 Many operations with a single handler 174 Reading polls 175 Creating a poll 178 Deleting a poll 179 CORS support 180 Testing our API using curl 180 A web client that consumes the API 182 Index page showing a list of polls 183 Creating a new poll 185 Showing the details of a poll 186 Running the solution 189 Summary 191 Chapter 7: Random Recommendations Web Service 193 The project overview 194 Project design specifics 195 Representing data in code 197 Public views of Go structs 200 Generating random recommendations 201 The Google Places API key 203 Enumerators in Go 203 Test-driven enumerator 205 Querying the Google Places API 209 Building recommendations 210 Handlers that use query parameters 212 CORS 213 Testing our API 214 Web application 216 Summary 216 Chapter 8: Filesystem Backup 218
📄 Page 13
[ v ] Solution design 219 The project structure 219 The backup package 220 Considering obvious interfaces first 220 Testing interfaces by implementing them 221 Has the filesystem changed? 224 Checking for changes and initiating a backup 226 Hardcoding is OK for a short while 228 The user command-line tool 229 Persisting small data 230 Parsing arguments 231 Listing the paths 232 String representations for your own types 232 Adding paths 233 Removing paths 233 Using our new tool 234 The daemon backup tool 235 Duplicated structures 237 Caching data 237 Infinite loops 238 Updating filedb records 239 Testing our solution 240 Summary 242 Chapter 9: Building a Q&A Application for Google App Engine 243 The Google App Engine SDK for Go 244 Creating your application 245 App Engine applications are Go packages 246 The app.yaml file 246 Running simple applications locally 247 Deploying simple applications to Google App Engine 249 Modules in Google App Engine 250 Specifying modules 251 Routing to modules with dispatch.yaml 252 Google Cloud Datastore 252 Denormalizing data 253 Entities and data access 255 Keys in Google Cloud Datastore 256 Putting data into Google Cloud Datastore 257 Reading data from Google Cloud Datastore 259 Google App Engine users 259
📄 Page 14
[ vi ] Embedding denormalized data 261 Transactions in Google Cloud Datastore 262 Using transactions to maintain counters 263 Avoiding early abstraction 267 Querying in Google Cloud Datastore 267 Votes 269 Indexing 270 Embedding a different view of entities 271 Casting a vote 273 Accessing parents via datastore.Key 274 Line of sight in code 274 Exposing data operations over HTTP 277 Optional features with type assertions 277 Response helpers 278 Parsing path parameters 279 Exposing functionality via an HTTP API 281 HTTP routing in Go 281 Context in Google App Engine 282 Decoding key strings 283 Using query parameters 285 Anonymous structs for request data 286 Writing self-similar code 287 Validation methods that return an error 288 Mapping the router handlers 289 Running apps with multiple modules 290 Testing locally 290 Using the admin console 291 Automatically generated indexes 292 Deploying apps with multiple modules 292 Summary 293 Chapter 10: Micro-services in Go with the Go kit Framework 294 Introducing gRPC 296 Protocol buffers 297 Installing protocol buffers 298 Protocol buffers language 298 Generating Go code 300 Building the service 301 Starting with tests 302 Constructors in Go 303 Hashing and validating passwords with bcrypt 304
📄 Page 15
[ vii ] Modeling method calls with requests and responses 305 Endpoints in Go kit 307 Making endpoints for service methods 308 Different levels of error 309 Wrapping endpoints into a Service implementation 309 An HTTP server in Go kit 311 A gRPC server in Go kit 312 Translating from protocol buffer types to our types 313 Creating a server command 315 Using Go kit endpoints 318 Running the HTTP server 318 Running the gRPC server 319 Preventing a main function from terminating immediately 320 Consuming the service over HTTP 320 Building a gRPC client 321 A command-line tool to consume the service 323 Parsing arguments in CLIs 324 Maintaining good line of sight by extracting case bodies 325 Installing tools from the Go source code 326 Rate limiting with service middleware 327 Middleware in Go kit 328 Manually testing the rate limiter 330 Graceful rate limiting 331 Summary 332 Chapter 11: Deploying Go Applications Using Docker 333 Using Docker locally 334 Installing Docker tools 334 Dockerfile 334 Building Go binaries for different architectures 335 Building a Docker image 336 Running a Docker image locally 337 Inspecting Docker processes 338 Stopping a Docker instance 339 Deploying Docker images 339 Deploying to Docker Hub 339 Deploying to Digital Ocean 341 Creating a droplet 341 Accessing the droplet's console 344 Pulling Docker images 346
📄 Page 16
[ viii ] Running Docker images in the cloud 348 Accessing Docker images in the cloud 348 Summary 349 Appendix: Good Practices for a Stable Go Environment 350 Installing Go 350 Configuring Go 351 Getting GOPATH right 352 Go tools 353 Cleaning up, building, and running tests on save 355 Integrated developer environments 356 Sublime Text 3 356 Visual Studio Code 359 Summary 362 Index 363
📄 Page 17
Preface I have been blown away by the response Go Programming Blueprints has received, both from newcomers to Go, as well as well-respected titans of the community. The positive feedback has inspired me to do this second edition, where the code has been updated to the latest thinking and three new chapters have been added. Thanks to the contributions and questions from readers on the GitHub repository ( h t t p s : / / g i t h u b . c o m / m a t r y e r / g o b l u e p r i n t s), I have been able to address some errors, fix some bugs, and clear some things up. See the README file on GitHub for a complete list of their names. I decided to write Go Programming Blueprints because I wanted to expel the myth that Go, being a relatively young language and community, is a bad choice to write and iterate on software quickly. I have a friend who knocks out complete Ruby on Rails apps in a weekend by mashing up pre-existing gems and libraries; Rails as a platform has become known for enabling rapid development. As I do the same with Go and the ever-growing buffet of open source packages, I wanted to share some real-world examples of how we can quickly build and release software that performs well from day one and is ready to scale when our projects take off in a way that Rails cannot compete with. Of course, most scalability happens outside the language, but features such as Go's built-in concurrency mean you can get some very impressive results from even the most basic hardware, giving you a head start when things start to get real. This book explores some very different projects, any of which can form the basis of a genuine startup. Whether it's a low-latency chat application, a domain name suggestion tool, a social polling and election service built on Twitter, or a random night out generator powered by Google Places, each chapter touches upon a variety of problems that most products or services written in Go will need to address. The solutions I present in this book are just one of many ways to tackle each project, and I will encourage you to make up your own mind about how I approached them. The concepts are more important than the code itself, but you'll hopefully pick up a few tips and tricks here and there that can go into your Go toolbelt. New to this second edition, we will explore some practical modern architectural thinking, such as how to build for Google App Engine, what a microservice looks like, and how to package up our code with Docker and deploy to anywhere.
📄 Page 18
Preface [ 2 ] The process by which I wrote this book may be interesting because it represents something about the philosophies adopted by many agile developers. I started by giving myself the challenge of building a real deployable product (albeit a simple one; a minimum viable product, if you will) before getting stuck into it and writing a version 1. Once I got it working, I would rewrite it from scratch. It is said many times by novelists and journalists that the art of writing is rewriting; I have found this to be true for software as well. The first time we write a piece of code, all we are really doing is learning about the problem and how it might be tackled, as well as getting some of our thinking out of our heads and onto paper, or into a text editor. The second time we write it, we are applying our new knowledge to actually solve the problem. If you've never tried this, give it a shot—you might find that the quality of your code shoots up quite dramatically as I did. It doesn't mean the second time will be the last time—software evolves and we should try to keep it as cheap and disposable as possible so we don't mind throwing pieces away if they go stale or start to get in the way. I write all of my code following Test-driven development (TDD) practices, some of which we will do together throughout the book and some you'll just see the result of in the final code. All of the test code can be found in the GitHub repositories for this book, even if it's not included in print. Once I had my test-driven second versions completed, I started writing the chapter describing how and why I did what I did. In most cases, the iterative approach I took is left out of the book because it would just add pages of tweaks and edits, which would probably just become frustrating for the reader. However, on a couple of occasions, we will iterate together to get a feel of how a process of gradual improvements and small iterations (starting and keeping it simple and introducing complexity only when absolutely necessary) can be applied when writing Go packages and programs. I moved to the United States from England in 2012, but that is not why the chapters are authored in American English; it was a requirement from the publisher. I suppose this book is aimed at an American audience, or perhaps it's because American English is the standard language of computing (in British code, properties that deal with color are spelled without the U). Either way, I apologize in advance for any trans-Atlantic slips; I know how pedantic programmers can be. Any questions, improvements, suggestions, or debates (I love how opinionated the Go community—as well as the core team and the language itself—is) are more than welcome. These should probably take place in the GitHub issues for the book setup, specifically at h t t p s : / / g i t h u b . c o m / m a t r y e r / g o b l u e p r i n t s, so that everybody can take part.
📄 Page 19
Preface [ 3 ] Finally, I would be thrilled if somebody forms a start-up based on any of these projects, or makes use of them in other places. I would love to hear about it; you can tweet me at @matryer. What this book covers Chapter 1, Chat Application with Web Sockets, shows how to build a complete web application that allows multiple people to have a real-time conversation right in their web browser. We will see how the NET/HTTP package let us serve HTML pages as well as connect to the client's browser with web sockets. Chapter 2, Adding User Accounts, shows how to add OAuth to our chat application so that we can keep track of who is saying what, but let them log in using Google, Facebook, or GitHub. Chapter 3, Three Ways to Implement Profile Pictures, explains how to add profile pictures to the chat application taken from either the authentication service, the Gravitar.com web service, or by allowing users to upload their own picture from their hard drive. Chapter 4, Command-Line Tools to Find Domain Names, explores how easy building command-line tools is in Go and puts those skills to use to tackle the problem of finding the perfect domain name for our chat application. It also explores how easy Go makes it to utilize the standard-in and standard-out pipes to produce some pretty powerful composable tools. Chapter 5, Building Distributed Systems and Working with Flexible Data, explains how to prepare for the future of democracy by building a highly-scalable Twitter polling and vote counting engine powered by NSQ and MongoDB. Chapter 6, Exposing Data and Functionality through a RESTful Data Web Service API, looks at how to expose the capabilities we built in Chapter 5, Building Distributed Systems and Working with Flexible Data, through a JSON web service, specifically how the wrapping http.HandlerFunc functions give us a powerful pipeline pattern. Chapter 7, Random Recommendations Web Service, shows how to consume the Google Places API to generate a location-based random recommendations API that represents a fun way to explore any area. It also explores why it's important to keep internal data structures private, controlling the public view into the same data, as well as how to implement enumerators in Go.
📄 Page 20
Preface [ 4 ] Chapter 8, Filesystem Backup, helps to build a simple but powerful filesystem backup tool for our code projects and explore interacting with the filesystem using the OS package from the Go standard library. It also looks at how Go's interfaces allow simple abstractions to yield powerful results. Chapter 9, Building a Q&A Application for Google App Engine, shows how to build applications that can be deployed to Google's infrastructure and run at high scale with little to no operational duties for us. The project we build utilizes some of the cloud services available on Google App Engine, including the Google Cloud Datastore—a highly available and extremely fast schema-less data storage option. Chapter 10, Micro-services in Go with the Go Kit Framework, explores a modern software architecture paradigm whereby large monolithic applications are broken down into discrete services with a singular focus. The services run independently of each other, allowing them to be individually scaled to meet demand. Go Kit is a software framework that addresses some of the challenges of a microservice architecture while remaining agnostic to the implementation details. Chapter 11, Deploying Go Applications Using Docker, looks at how simple it is to build Docker images to package and deploy the application we built in Chapter 9, Building a Q&A Application for Google App Engine. We will write a Dockerfile that describes the image, and use the Docker tools to build the image, which we will then deploy to the Digital Ocean cloud. Appendix, Good Practices for a Stable Go Environment, shows how to install Go from scratch on a new machine and discusses some of the environmental options we have and the impact they might have in the future. We will look at a few code editor (or IDE—Integrated Developer Environment) options and also consider how collaboration might influence some of our decisions as well as the impact open sourcing our packages might have. What you need for this book To compile and run the code from this book, you will need a computer capable of running an operating system that supports the Go toolset, a list of which can be found at h t t p s : / / g o l a n g . o r g / d o c / i n s t a l l # r e q u i r e m e n t s. Appendix, Good Practices for a Stable Go Environment, has some useful tips to install Go and set up your development environment, including how to work with the GOPATH environment variable.
The above is a preview of the first 20 pages. Register to read the complete e-book.

💝 Support Author

0.00
Total Amount (¥)
0
Donation Count

Login to support the author

Login Now

Recommended for You

Loading recommended books...
Failed to load, please try again later
Back to List