As a PHP engineer, have you ever asked yourself if you need to know other programming languages?
PHP has been the preferred language for building full-stack projects for many years. In addition, frameworks (Symfony, Laravel, Zend), tools (Composer, Monolog), and rapidly growing communities (PHP-FIG) have helped many engineers build enterprise-level software applications over the past five years. Many companies, such as Facebook, Yahoo! Wikipedia, WordPress, and Tumblr used native PHP in their early days, and that hasn’t stopped them from succeeding over the years.
However, as a successful business grows, so does the number of engineers it needs. The organizational structure of the company suggests that the existing whole is best separated. To some extent, the strategy has stabilized and the team is now focusing on standalone services.
In this article, we’ll try to assess how far PHP alone can take us, and where Go can step in and solve the problems we’re about to face.
PHP and microservices
As a PHP engineer, if you manage initialization requests within 30 milliseconds each time in a large application, great! Add another 50-100 milliseconds to the actual request processing, and you have an amazing overall response time.
Now that we plan to break up our original project as a whole and stick to the same setup for our service, what happens? A simple calculation shows that if the services interact with each other 10 times in the same request, we have 300 milliseconds of boot time. What a terrible user experience!
While PHP shines in WEB development, its capabilities in microservice architectures are still fairly immature. Basic terms such as timeout handling, health check, metric collection, next-door, circuit breaker, etc., are often unfamiliar to PHP engineers. Most of these can be found in various frameworks in many other languages. They are poorly supported, if at all, by the PHP ecosystem.
Meet the Go
From Wikipedia:
Go (often referred to as golang) is an open source programming language developed at Google in 2007 by robertgriesemer, robpike, and kenthompson. Primarily designed for system programming, it is a compiled, statically typed language that features garbage collection, various security features, and CSP-style concurrent programming in traditional C and C++.
Go was developed by Google. This is not a fun 20% project, nor is it about achieving something that others cannot. It is simply built on the frustration of dealing with the complexity created by very large teams dealing with very large software, written in languages with large feature sets.
While it has some nice features, such as easy concurrency or fast compilation, its main feature is simplicity. On a related note, there are 25 keywords in PHP and 67 in PHP.
Go appeared on the market around the same time PHP gained namespace support (2009). In just 6 years, there have been dozens of great projects, including Docker, Kubernetes, ETCD, InfluxDB, And companies like Cloudflare, Google, Dropbox, SoundCloud, Twitter, PayPal, who built their back-end systems into Go.
Back to microservices
Let’s take a look at some of the issues discussed earlier and see what the benefits of using Go are.
Go applications quickly compile into machine code. Many people claim that the compile time is so fast that by the time they switch to the browser and hit “Refresh,” the application has already been recompiled, restarted, and served to the request. Even with a large code base, there is a sense that you are using an interpreted language, just like PHP.
The traditional “Hello World” API is written in 10 lines of code with a response time of less than a millisecond. Go’s multi-core capabilities allow engineers to run their software in parallel. The ecosystem allows you to choose any transport protocol, such as JSON over HTTP, gRPC, Protocol Buffers, or Thrift. It’s easy to track requests in Zipkin. Indicators are exported from Statsd to Prometheus. Use rate limiters to enforce incoming or outgoing request throughput and protect client service communication with the breaker.
On the Go
language
Go is a strictly typed language that requires you to specify the type of a variable during initialization:
var name string = "sobit"
Copy the code
Variables can infer the type of the value on the right. The above code is equivalent to the abbreviated version:
var name = "sobit"
// or even better:
name := "sobit"
Copy the code
Now, let’s remember how to swap the values of two variables in PHP:
$buffer = $first;
$first = $second;
$second = $buffer;
Copy the code
Go
first, second = second, first
Copy the code
Because Go has the ability to return multiple values. Here is an example of a multi-return function:
func getName(a) (string.string) {
return "sobit"."akhmedov"
}
first, last = getName()
Copy the code
You won’t see foreach, while, and do-while used so often in Go code. They are unified into a single for statement:
// foreach ($bookings as $key => $booking) {}
for key, booking := range bookings {}
// for ($i = 0; $i < count($bookings); $i++) {}
for i := 0; i < len(bookings); i++ {}
// while ($i < count($bookings)) {}
for i < len(bookings) {}
// do {} while (true);
for {}
Copy the code
While Go has no such thing as a “class” or “object,” its type matches the same definition of a data structure that integrates code and behavior. In Go, this is called a “structure.” Let’s use an example to illustrate:
type rect struct { // define a struct
width int
height int
}
func (r *rect) area(a) int { // define a function on struct
return r.width * r.height
}
r := rect{width: 10, height: 15} // initialize
fmt.Print(r.area())
Copy the code
Sometimes it is useful to define a complex structure. Here’s an example:
type Employee struct {
Name string
Job Job
}
type Job struct {
Employer string
Position string
}
// and to structure it
e := Employee{
Name: "Sobit",
Job: {
Employer: "GetYourGuide",
Position: "Software Engineer",}}Copy the code
There are plenty of other features I’d love to talk about, but that would allow me to copy an Effective Go document from the official website and produce a never-ending article. But I want to take a moment to tell you about concurrency in Go, which I find to be one of the most interesting topics about the language. Before we begin, there are two things you need to know about concurrency in Go-Goroutines and channels.
Goroutine has a simple model: it is a function that executes concurrently with other Goroutines in the same address space. When the call completes, the Goroutine will exist silently. The simplest example is to create a heartbeat function that prints I’m alive messages to a terminal every second:
func heartbeat(a) {
for {
time.Sleep(time.Second)
fmt.Println("I'm still running...")}}Copy the code
Now, how do we run it in the background and allow us to do other things in parallel? The answer is simpler than you might think, just add go in front of Channel:
go heartbeat()
// keep doing other stuff
Copy the code
This approach is similar to fire-and-forget events. But what if we don’t need to forget and are interested in the results of the function? This is where Channel comes in:
func getName(id int, c chan string) {
c <- someHeavyOperationToFindTheName(id)
}
func main(a) {
c := make(chan string.2) // allocate a channel
go getName(1, c) // trigger
go getName(2, c) // don't wait and trigger again
// keep doing other stuff
fmt.Printf("Employees: %s, %s", <-c, <-c) // combine
}
Copy the code
In short, we’re just firing the same function twice, letting the application execute them in parallel, and demanding that the result be returned when it’s actually needed.
tool
Go was designed, again, with simplicity in mind. The authors take a rather radical approach to a common activity that engineers do all day. Say Goodbye to the endless space-VS-Tab debate, a code standard from a community group that is trying to somehow simplify the way engineers share code. Go provides the Go FMT tool, which is responsible for styling code. No more sharing IDE configuration files, and no more trying to remember whether the opening brace should be on the same line or the next.
Documentation of the package source code can be read using Go Doc, and Go Vet will help find potential problems in the code. To install dependencies, run go get github.com/[vendor]/[package] to trigger the go test[package] command. As you can see, most of the tools that engineers provide for every application in every language are already here.
Project deployment
No matter what language you choose or what you build, deployment is mandatory. How many Capistrano or Envoy profiles have you written in your career as a PHP engineer? How many times in the past few days have you manually transferred the changed files to the server via FTP?
The most common and simplest PHP deployment process is as follows:
- Put the latest code on the target server into the new distribution folder
- Copy cached dependencies and install updates
- Copy environment-specific configuration files
- Run all scripts to warm up the application
- Points the current version symlink to the new version folder
- Restart php-fpm
Some teams can take this process to a higher level, such as:
- Pull the latest code from the server
- “Build” projects (install dependencies, warm up caches, etc.)
- Create a distributable “artifact” (a compressed
tar.gz
File) - Transfer the artifact to the target server
- Unzip to the new distribution folder
- Points the current version symlink to the new version folder
- Restart php-fpm
Beyond that, of course, you need to make sure that your target server and build server have at least PHP-FPM and PHP installed, and that the versions better match each other and the versions engineers use for local development.
Now let me walk you through the deployment process for a regular Go application:
- Pull the latest code from the server
- Build it (note no quotes)
- Transfer the artifact (again without quotes) to the target server
- Restart the running application
That’s all. The only difference between a potentially simple version and an advanced version is whether there is a separate build server.
The beauty is that you don’t even need to install Go on the target servers, and even if they run different operating systems or are based on different CPU architectures, you can still prepare artifacts for all of those servers (even for Windows) from a single build server or any other machine.
conclusion
No one ever suggested breaking up the system into microservices when the system was immature. Small as they are, companies must be flexible to respond to market opportunities. This is not an ideal environment for building microservices, but rather a whole, with more benefits to be gained. PHP and its ecosystem are well suited to this strategy.
When an organization is stable and its bounded context is easily recognized, building microservices in PHP to supplement them can be painful. Tools and techniques exist in many other languages to support this architecture. You may have to take the risk of either building your own tools or rejecting them altogether — both of which are expensive for your organization.
On the other hand, building microservices in Go is worth considering. The language is easy to write and understand, has a less steep learning curve than Java or Scala, and performance is not far behind C. Compilation times keep engineers productive, while support for multicore or networked machines makes it possible to write powerful applications.
Other resources
For a beginner, the best place to start is with the learning section of the official site documentation. It includes an introduction to the Go syntax page that can be opened in the browser, the Go code specification, which will help you understand how to write clear, idiomatic Go code.
If that’s not enough for you, the Go Learning Resources directory can help you improve your newly acquired skills.
If you’re a fan of JetBrains IDE like me, check out the Go Plugin for IntelliJ, which was developed primarily by the same JetBrains developer. It can be installed on almost any IDE, including PhpStorm. For other ides and text editors, visit the Wiki page.
When you want to build your first production-ready microservice in Go, I encourage you to check out the Go Kit. It is a programming toolkit dedicated to solving common problems in distributed systems, allowing engineers to focus on business logic.
Least important, but worth mentioning, is that you can build client-side JavaScript applications and SDK applications using GopherJS, and you can build iOS and Android applications.
Sobit.me /2016/02/25/…