How to configure a GCP App Engine properly - google-app-engine

I am struggling to understand the difference and benefits of a few terms which I use while I setup my App Engine instance:
I am wondering how does server handle more than one request? Does it need to create a new instance of my server code for every request, sounds unlikely but I want to clarify. OR does it create a copy of my function handling the request? A typical function of mine looks like the following:
app.get("/", limiter, async function(req , res){
try {
my code
} catch (err){
console.log(err);
res.status(400).send('Something went wrong')
}
})
What is the relation of a flex environment vs standard environment?
When does a new instance is required?
My app.yaml looks like the following:
runtime: nodejs12
env: standard
manual_scaling:
instances: 1 // Is this a bad idea in production?
resources:
cpu: 1
memory_gb: 0.5
disk_size_gb: 10
Please I need help in understanding this so I can setup my instance properly.

Neither a new server nor a new function instance is needed. Instead, the same function (async function(req , res) in your case) is called several times concurrently. Each call will have a separate request and response instance (req and res in your case).
Standard and Flexible are two different App Engine offerings from Google with different features and different pricing. Standard is more dynamic (quicker start-up, quicker deployment, scales down to 0 instances). Flexible offers more options (any programming language, support for web sockets etc.). For more details, see Choosing an App Engine environment
.
Unless you explicitly specify the number of instances, Google App Engine monitors request latency and probably CPU load and will increase and decrease the number of running instances depending on the actual load (possibly within the limits you have set). If a single instance is sufficient to handle the load of your application, you can fix the instance count at 1. That's a valid setting.

Related

Running min 1 instance of Google-App-Engine in standard environment

Looking at the Google-App-Engine's two environments, standard and flex, most of the features offered by standard seem more appropriate for my use case.
According to https://cloud.google.com/appengine/docs/the-appengine-environments, both standard and flex environment support automatic scaling while standard can scale to 0 instances and flex can scale to 1 instance.
According to https://cloud.google.com/appengine/docs/standard/nodejs/config/appref, an option for automatic scaling is specifying the min/max number of instances running at any given moment. I would have thought that this would 'override' standard environment's ability to scale to zero, but after my service had seen no traffic in 15 hours, it still closed the last remaining instance.
I have the following config-settings in my app.yaml file.
runtime: nodejs10
automatic_scaling:
min_instances: 1
max_instances: 1 # Increase in production
target_cpu_utilization: 0.95
I was trying to force GAE to have 1 running instance at any time while in testing. I realize that having a static number of instances running is not the point of automatic scaling, but I plan to increase the maximum number of instances when moving to production. I have also tried adding min_idle_instances: 1 to the settings without any difference.
Can standard environment be forced to have a minimum of 1 running instance at any time?
A way to ensure that your instance is ready to serve is to configure warm up request.
Bear in mind that even with Warm up request, you might encounter loading request. If your app has no traffic, the first request will always be a loading request and not a warm up. Thus, in my opinion the best way to approach a situation like this is to set 2 min_instances.
Example of an express.js handler:
js
const express = require('express');
const app = express();
app.get('/_ah/warmup', (req, res) => {
// Handle your warmup logic. Initiate db connection, etc.
});
// Rest of your application handlers.
app.get('/', handler);
app.listen(8080);
Example of app.yaml addition:
inbound_services:
- warmup
A workaround it could be to use cron job that triggers every minute, so your instance it will be available to serve you. However, even with this approach 2 min_instance is a better solution.

Initializing Go AppEngine app with Cloud Datastore

in init() function for GAE's golang app, how can I set-up initial values for my application ?
How to read from Cloud Datastore in the init() function or immediately after applications start-up ? As I understand, server cannot write to the local filesystem and Cloud Datastore is the only option ?
I need some global variables and slices of data..
Using static files
On AppEngine you don't have access to the file system of the host operating system, but you can access files of your web application (you have read-only permission, you can't change them and you can't create new files in the app's folder).
So the question is: can your application's code change the data that you want to read and use for initialization? Or is it fine if it is deployed "statically" with your app's code?
If you don't need to change it (or only when you redeploy your app), easiest is to store it as a "static" file as part of your webapp. You may refer to files of your app using relative paths, where the current or working directory is your app's root. E.g. if your app contains a data folder in its root (where app.yaml resides), and there is an init_values.txt file inside the data folder, you can refer to it with the path: data/init_values.txt.
One important note: not every file is readable by code, this depends on the app configuration. Quoting from Configuring with app.yaml / Static file handlers:
If you have data files that need to be read by the application code, the data files must be application files, and must not be matched by a static file pattern.
Using the Datastore
You can't use AppEngine services that require a Context outside of handlers (because the creation of a Context requires an *http.Request value). This by nature means you can't use them in package init() functions either.
Note that you can use them from cron jobs and tasks added to task queues, because tasks and cron jobs are executed by issuing HTTP GET requests.
You have to restructure your code so that your initialization (e.g. reading from the Datastore) gets called from a handler.
Example of achieving this with Once.Do():
var once = sync.Once{}
func MainHandler(w http.ResponseWriter, r *http.Request) {
ctx := appengine.NewContext(r)
once.Do(func() { mysetup(ctx) })
// do your regular stuff here
}
func mysetup(ctx appengine.Context) {
// This function is executed only once.
// Read from Datastore and initialize your vars here.
}
"Utilizing" warmup requests
Yes, this may cause first requests to take considerably longer to serve. For this purpose (to avoid this) I recommend you to utilize Warmup requests. A warmup request is issued to a new instance before it goes "live", before it starts serving user requests. In your app.yaml config file you can enable warmup requests by adding -warmup to the inbound_services directive:
inbound_services:
- warmup
This will cause the App Engine infrastructure to first issue a GET request to /_ah/warmup. You can register a handler to this URL and perform initialization tasks. As with any other request, you will have an http.Request in the warmup handler.
But please note that:
..you may encounter loading requests, even if warmup requests are enabled in your app.
Which means that in rare cases it may happen a new instance will not receive a warmup request, so its best to check initialization state in user handlers too.
Related questions:
How do I store the private key of my server in google app engine?
Fetching a URL From the init() func in Go on AppEngine
Environment variables specified on app.yaml but it's not fetching on main.go

Long-running script on Google App Engine

I'm attempting to create a microservice on Google App Engine that is not intended to handle HTTP requests.
Instead, I was hoping to have a continuously running Python script that monitors a remote queue--RabbitMQ, to be precise--and sends out an api-call to another service as tasks are pushed to the queue.
I was wondering, firstly, is it possible to run a script upon deployment--one that did not originate with a user action/request?
Secondly, how would I accomplish this?
Thanks in advance for your time!
You can deploy your "script" as a manually scaled module -- see https://cloud.google.com/appengine/docs/python/modules/ -- with exactly one instance. As the docs say, "When you start a manual scaling instance, App Engine immediately sends a /_ah/start request to each instance"; so, just set that module's handler for /_ah/start to the handler you want to run (in the module's yaml file and the WSGI app in the Python code, using whatever lightweight framework you like -- webapp2, falcon, flask, bottle, or whatever else... the framework won't be doing much for you in this case save the one-off routing).
Note that the number of free machine hours for manual scaling modules is limited to 8 hours per day (for the smaller, B1 instance class; proportionally fewer for larger instance classes), so you may need to upgrade to paid-app status if you need to run for more than 8 hours.
Like #brant said, App Engine is designed to handle HTTP requests. It's not a perfect fit for background jobs, unless you try to wrap your logic into one http request.
Further, App Engine will emit an error when the response timeout, depending on your scaling settings. If you want to try it, consider basic or manual scaling.
For this type of workload, I would suggest you use a VM.
I think there are a few problems with this design.
First, App Engine is designed to be an HTTP request processor, not a RabbitMQ message processor. GAE is intended for many small requests, not one long-running process.
Second, "RabbitMQ should not be exposed to the public internet, it wasn't created for such use case."
I would recommend that you keep the RabbitMQ clients on the same internal network as the RabbitMQ broker, and have the clients send HTTP requests to App Engine.

detect AppEngine vs basic server

I am developing a web app to run on either Google's AppEngine or a basic server with file storage (it may not stay that way but that's the current status).
How do I detect whether the AppEngine services (most importantly blobstore) are available at runtime?
I have tried using code like the following:
try{
Class.forName( "com.google.appengine.api.blobstore.BlobstoreServiceFactory" );
logger.info( "Using GAE blobstore backend" );
return new GAEBlobService();
}catch( ClassNotFoundException e ){
logger.info( "Using filesystem-based backend" );
return new FileBlobService();
}
but it doesn't work because BlobstoreServiceFactory is available at compile time. What fails if trying to use GAE's blobstore without a GAE server is the following:
com.google.apphosting.api.ApiProxy$CallNotFoundException: The API package 'blobstore' or call 'CreateUploadURL()' was not found.
There's a few things you can use.
You can check the runtime environment to check the running version of App Engine. Check the section about "The Environment" in the runtime docs: https://developers.google.com/appengine/docs/java/runtime
You could also do what you were doing, and attempt to make an call that uses the SDK API functions (instead of just checking for the existence of a class) and catch the exception. This may negatively impact performance since you're making an extra RPC.
You could check request headers for GAE specific request headers too.

Apache Httpclient and the Cloud

I want to put a scraping service using Apache HttpClient to the Cloud. I read problems are possible with Google App Engine, as it's direct network access and threads creation are prohibited. What's about other cloud hosting providers? Have anyone experince with Apache HttpClient + cloud?
AppEngine has threads and direct network access (HTTP only). There is a workaround to make it work with HttpClient.
Also, if you plan to use many parse tasks in parallel, you might check out Task Queue or even mapreduce.
Btw, there is a "misfeature" in GAE that you can not fully set custom User-agent header on your requests - GAE always adds "AppEngine" to the end of it (this breaks requests to certain sites - most notably iTunes).
It's certainly possible to create threads and access other websites from CloudFoundry, you're just time limited for each process. For example, if you take a look at http://rack-scrape.cloudfoundry.com/, it's a simple rack application that inspects the 'a' tags from Google.com;
require 'rubygems'
require 'open-uri'
require 'hpricot'
run Proc.new { |env|
doc = Hpricot(open("http://www.google.com"))
anchors = (doc/"a")
[200, {"Content-Type" => "text/html"}, [anchors.inspect]]
}
As for Apache HttpClient, I have no experience of this but I understand it isn't maintained any more.

Resources