|
| 1 | +### Tutorial |
| 2 | +1. Create network with `docker network create <your_network>` |
| 3 | +2. Use `docker network ls` and find your network. It should have prefix! |
| 4 | +E.g. when I used `docker network create some-net`, I have network with name `vuestorefrontapi_some-net` |
| 5 | +3. Open docker-compose.yml: |
| 6 | +At the end: |
| 7 | +```yaml |
| 8 | +networks: |
| 9 | + vuestorefrontapi_some-net: |
| 10 | + external: true |
| 11 | +``` |
| 12 | +Set vuestorefrontapi_some-net to your network name |
| 13 | +
|
| 14 | +4. Check each `docker-compose` file and set proper network name. |
| 15 | +5. In the docker-compose.nodejs.yml it should not have a prefix, e.g: |
| 16 | +```yaml |
| 17 | + networks: |
| 18 | + - some-net |
| 19 | +
|
| 20 | +networks: |
| 21 | + some-net: |
| 22 | + driver: bridge |
| 23 | +``` |
| 24 | +You can find Docker Compose files with applied network settings inside docker/varnish/docker-compose |
| 25 | + |
| 26 | +### How does it work? |
| 27 | +1. I add output tags to the VSF-API response: |
| 28 | +```js |
| 29 | +const tagsHeader = output.tags.join(' ') |
| 30 | +res.setHeader('X-VS-Cache-Tag', tagsHeader) |
| 31 | +``` |
| 32 | + |
| 33 | +2. After it invalidates cache in the Redis. I forward request to the: |
| 34 | +```js |
| 35 | +http://${config.varnish.host}:${config.varnish.port}/ |
| 36 | +``` |
| 37 | +With invalidate tag in headers: |
| 38 | +```js |
| 39 | +headers: { |
| 40 | + "X-VS-Cache-Tag": tag |
| 41 | +} |
| 42 | +``` |
| 43 | + |
| 44 | +I set Varnish invalidate method to `BAN` but you can change it in your config + varnish's config. |
| 45 | + |
| 46 | +3. Configuration of BANning we have inside `docker/varnish/config.vcl` in `vcl_recv`. |
| 47 | +It tries to BAN resource which has `X-VS-Cache-Tag` header: |
| 48 | +```vcl |
| 49 | +# Logic for the ban, using the X-Cache-Tag header. |
| 50 | +if (req.http.X-VS-Cache-Tag) { |
| 51 | + ban("obj.http.X-VS-Cache-Tag ~ " + req.http.X-VS-Cache-Tag); |
| 52 | +} |
| 53 | +``` |
| 54 | + |
| 55 | +Below under BANning logic. I have to tell Varnish what to cache. |
| 56 | +```vcl |
| 57 | +if (req.url ~ "^\/api\/catalog\/") { |
| 58 | + if (req.method == "POST") { |
| 59 | + # It will allow me to cache by req body in the vcl_hash |
| 60 | + std.cache_req_body(500KB); |
| 61 | + set req.http.X-Body-Len = bodyaccess.len_req_body(); |
| 62 | + } |
| 63 | + |
| 64 | + if ((req.method == "POST" || req.method == "GET")) { |
| 65 | + return (hash); |
| 66 | + } |
| 67 | +} |
| 68 | +``` |
| 69 | + |
| 70 | +I am caching request that starts with `/api/catalog/`. As you can see I cache both POST and GET. |
| 71 | +This is because in my project I use huge ES requests to compute Faceted Filters. I would exceed HTTP GET limit. |
| 72 | + |
| 73 | +Thanks to this line and `bodyaccess`, I can distinguish requests to the same URL by their body! |
| 74 | +```vcl |
| 75 | +std.cache_req_body(500KB); |
| 76 | +``` |
| 77 | + |
| 78 | +Then in `vcl_hash` I create hash for POST requests with `bodyaccess.hash_req_body()`: |
| 79 | +```vcl |
| 80 | +sub vcl_hash { |
| 81 | + # To cache POST and PUT requests |
| 82 | + if (req.http.X-Body-Len) { |
| 83 | + bodyaccess.hash_req_body(); |
| 84 | + } else { |
| 85 | + hash_data(""); |
| 86 | + } |
| 87 | +} |
| 88 | +``` |
| 89 | + |
| 90 | +By default, Varnish change each request to HTTP GET. We need to tell him to send POST requests to the VSF-API as POST - not GET. |
| 91 | +We will do it like that: |
| 92 | +```vcl |
| 93 | +sub vcl_backend_fetch { |
| 94 | + if (bereq.http.X-Body-Len) { |
| 95 | + set bereq.method = "POST"; |
| 96 | + } |
| 97 | +} |
| 98 | +``` |
| 99 | + |
| 100 | + |
| 101 | +### Caching Stock |
| 102 | +It might be a good idea to cache stock requests if you check it often (filterUnavailableVariants, configurableChildrenStockPrefetchDynamic) in VSF-PWA in visiblityChanged hook (product listing). |
| 103 | +In one project when I have slow Magento - it reduced Time-To-Response from ~2s to ~70ms. |
| 104 | + |
| 105 | +```vcl |
| 106 | +if (req.url ~ "^\/api\/stock\/") { |
| 107 | + if (req.method == "GET") { |
| 108 | + # M2 Stock |
| 109 | + return (hash); |
| 110 | + } |
| 111 | +} |
| 112 | +``` |
| 113 | + |
| 114 | +Then in `vcl_backend_response` you should set safe TTL (Time to live) for your stock cache. I've set 15 minutes (900 seconds) |
| 115 | +```vcl |
| 116 | +sub vcl_backend_response { |
| 117 | + # Set ban-lurker friendly custom headers. |
| 118 | + if (beresp.http.X-VS-Cache && beresp.http.X-VS-Cache ~ "Miss") { |
| 119 | + set beresp.ttl = 0s; |
| 120 | + } |
| 121 | + if (bereq.url ~ "^\/api\/stock\/") { |
| 122 | + set beresp.ttl = 900s; // 15 minutes |
| 123 | + } |
| 124 | + set beresp.http.X-Url = bereq.url; |
| 125 | + set beresp.http.X-Host = bereq.http.host; |
| 126 | +} |
| 127 | +``` |
| 128 | + |
| 129 | +For X-VS-Cache, I set TTL 0s so it is permanent. Because it will be automaticly invalidated when needed. |
| 130 | + |
| 131 | +### Caching Extensions |
| 132 | +You might want to cache response from various extensions. |
| 133 | +E.g. I am fetching Menus, Available Countries (for checkout) from M2 by VSF-API proxy. |
| 134 | +As in this project Magento is pretty slow. By caching responses I've changed response time from ~2s |
| 135 | +to around ~50ms. |
| 136 | + |
| 137 | +How to do that? |
| 138 | +Inside `vcl_recv` add: |
| 139 | +```vcl |
| 140 | +# As in my case I want to cache only GET requests |
| 141 | +if (req.method == "GET") { |
| 142 | + # Countries for storecode GET - M2 - /directory/countries |
| 143 | + if (req.url ~ "^\/api\/ext\/directory\/") { |
| 144 | + return (hash); |
| 145 | + } |
| 146 | + |
| 147 | + # Menus GET - M2 - /menus & /nodes |
| 148 | + if (req.url ~ "^\/api\/ext\/menus\/") { |
| 149 | + return (hash); |
| 150 | + } |
| 151 | +} |
| 152 | +``` |
| 153 | + |
| 154 | +How to invalidate extension's tag? |
| 155 | +You can do it by sending request with `X-VS-Cache-Ext` header. |
| 156 | +If value of this header is part of any cached URL - it will be invalidated. |
| 157 | +E.g. for menus extension: |
| 158 | +``` |
| 159 | +/api/ext/menus |
| 160 | +``` |
| 161 | +You could send: |
| 162 | +BAN `http://${config.varnish.host}:${config.varnish.port}/` |
| 163 | +headers: { |
| 164 | + "X-VS-Cache-Ext": "menus" |
| 165 | +} |
| 166 | + |
| 167 | +But sending HTTP requests is not so handy. So I've extended Invalidate endpoint. To the same you could just open: |
| 168 | +``` |
| 169 | +http://localhost:8080/invalidate?key=aeSu7aip&ext=menus |
| 170 | +``` |
| 171 | + |
| 172 | +As value of the `ext` will be searched inside `Cached URL`. |
| 173 | +If you would provide here `product` it would cache product's catalog. You should have it in mind. |
| 174 | + |
| 175 | +### Banning permissions |
| 176 | +It will be allowed only from certain IPs. In my case I put here only VSF-API IP. But here we have `app` as Docker will resolve it as VSF-API IP: |
| 177 | +```vcl |
| 178 | +acl purge { |
| 179 | + "app"; // IP which can BAN cache - it should be VSF-API's IP |
| 180 | +} |
| 181 | +``` |
| 182 | + |
| 183 | +### What to cache |
| 184 | +We should provide to Varnish - IP & Port to cache, there we have it: |
| 185 | +```vcl |
| 186 | +backend default { |
| 187 | + .host = "app"; |
| 188 | + .port = "8080"; |
| 189 | +} |
| 190 | +``` |
| 191 | + |
| 192 | +### URL |
| 193 | +Varnish by default using port `80` but by Docker's port mapping we are using `1234` |
| 194 | + |
| 195 | +### How to install on VPS |
| 196 | +1. Install Varnish |
| 197 | +2. Install Varnish Modules |
| 198 | +3. By using Reverse Proxy output `/api` from Varnish, to the world |
| 199 | + |
| 200 | +I'll try to prepare more detailed tutorial (with commands) as I will probably do it again in the following month. |
0 commit comments