App Engine xác định rằng một yêu cầu đến là để dành cho ứng dụng của bạn bằng cách sử dụng tên miền củ a yêu c ầu. Mợt u cầu có tên miền là id.appspot.com đƣợc chuyển đến các ứng dụng có ID là “id”. Mỗi ứng dụng có một tên miền appspot.com miễn phí.
Sinh viên thực hiê ̣n : Đào Phan Quang K50 Lớp CNPM 27
Tên miền appspot .com cũng hỗ trợ các tên miền phụ của id .appspot.com theo mấu subdomain .id.appspot.com, nơi tên miền phụ có thể đƣợc bất kỳ chuỗi đƣợc cho phép trong một phần của một tên miền (không phải “.”). Yêu cầu gửi đến bất kỳ tên miền phụ theo cách này đƣợc chuyển đến ứng dụng của bạn.
Bạn có thể thiết lập mợt tên miền cấp cao tùy chỉnh bằng cách sử dụng Google Apps. Với Google Apps, bạn chỉ định tên miền phụ của lĩnh vực kinh doanh của bạn để ứng dụng khác nhau, chẳng hạn nhƣ Google Mail hay trang web. Bạn cũng có thể kết hợp một ứng dụng App Engine với một tên miền phụ. Để thuận tiện, bạn có thể thiết lập mợt Google Apps miền khi bạn đăng ký ứng dụng của bạn ID, hoặc sau đó từ Administrator Console.
Tất cả các yêu cầu đến các URL này đều đƣợc chuyển tới phiên bản ứng du ̣ng mà ta đã cho ̣n làm phiên bản mă ̣c đi ̣nh trong Administrator Console. Mỗi phiên bản của ứng dụng cũng có URL riêng của mình, vì vậy ta có thể triển khai và thử nghiệm mợt phiên bản mới trƣớc khi đặt nó là phiên bản mặc định. URL cuả các phiê n bản cụ thể sử dụng các định danh phiên bản từ tập tin cấu hình ứng dụng đi kèm với tên miền appspot.com, theo mẫu sau : version-id.latest.application-id.appspot.com. Ta cũng có thể sử dụng tên miền phụ với các URL của phiên bản cụ thể: subdomain.version-id.latest.application-id.appspot.com
Các tên miền đƣợc sử dụng để yêu cầu đƣợc bao gồm trong các dữ liệu của yêu cầu chuyển vào ứng dụng. Nếu bạn muốn ứng dụng của bạn có thể trả lời khác nhau tùy thuộc vào tên miền đƣợc sử dụng để truy cập vào nó (để hạn chế truy cập vào các lĩnh vực nhất định, hoặc chuyển hƣớng đến một tên miền chính thức), bạn có thể kiểm tra các dữ liệu yêu cầu (chẳng hạn nhƣ phần tiêu đề Host củ a yêu cầu) bằng mã ứng dụng và đáp ứng phù hợp.
Khi App Engine nhận đƣợc một yêu cầu cho ứng dụng web của bạn, nó gọi các servlet tƣơng ứng với URL, nhƣ mơ tả trong mô tả triển khai của ứng dụng (các file web.xml trong thƣ mục /WEB-INF). Nó sử dụng Java Servlet API để cung cấp dữ liệu yêu cầu cho servlet, và nhận về các dữ liệu phản hồi.
App Engine sử dụng nhiều máy chủ web để chạy ứng dụng của bạn, và tự động điều chỉnh số lƣợng các máy chủ để xử lý yêu cầu mô ̣t cách tin cậy. Một yêu cầu nhất định có thể đƣợc chuyển đến máy chủ nào đó, và nó có thể khơng cùng mợt máy chủ đã xử lý một yêu cầu trƣớc đó từ cùng một ngƣời dùng.
Ví dụ lớp servlet sau đây sẽ hiển thị mợt thơng điệp đơn giản trên trình duyệt của ngƣời dùng.
import java.io.IOException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class MyServlet extends HttpServlet {
Sinh viên thực hiê ̣n : Đào Phan Quang K50 Lớp CNPM 28 throws IOException { resp.setContentType("text/plain"); resp.getWriter().println("Hello, world"); } }
App Engine gọi servlet với một đối tƣợng yêu cầu và một đối tƣợng phản hồi, sau đó đợi cho servlet để thiết lâ ̣p đối tƣợng phản hồi và trả về. Khi servlet trả về đối tƣơ ̣ng phản hồi, dữ liệu về đối tƣợng phản hồi đƣợc gửi đến ngƣời dùng.
Một hàm xử lý yêu cầu có mợt lƣợng hạn chế thời gian để tạo ra và trả về một đáp ứng yêu cầu, thông thƣờng khoảng 30 giây. Một khi thời hạn đã đƣợc đạt tới, các hàm xử lý yêu cầu bị gián đoạn.
Môi trƣờng Java runtime ngắt các servlet bằng cách đƣa ra một ngoa ̣i lê ̣ com.google.apphosting.api.DeadlineExceededException. Nếu xử lý yêu cầu không bắt ngoại lệ này, nhƣ với tất cả các trƣờng hợp ngoại lệ còn chƣa đƣợc xử lý , môi trƣờng thƣ̣c thi sẽ trả lại một lỗi HTTP 500 máy chủ cho khách hàng.
Việc xử lý u cầu có thể bắt lỡi này để tuỳ chỉnh các phản ứng. Môi trƣờng thƣ̣c thi cho các xử lý yêu cầu thêm mợt ít thời gian (ít hơn một giây) sau khi đƣa ra ngoại lệ để chuẩn bị một phản ứng đã đƣơ ̣c tùy biến.
Trong khi mợt u cầu có thể dùng đến 30 giây để đƣợc trả lời, App Engine đƣợc tối ƣu hóa cho các ứng dụng với các yêu cầu có thời gian phản hồi ngắn, thƣờng là những yêu cầu cần một vài trăm mili giây. Một ứng dụng hiệu quả phải có phản hồi nhanh chóng cho phần lớn các yêu cầu. Một ứng dụng không nhƣ vậy sẽ không có khả năng mở rộng với cơ sở hạ tầng quy mô App Engine của.
II.4. Hộp cát (sandbox)
Để cho phép App Engine để phân phối các yêu cầu cho các ứng dụng giƣ̃a các máy chủ web, và để ngăn chặn một ứng dụng can thiệp đến các ứng dụng khác, ứng dụng đƣợc cho chạy trong môi trƣờng sandbox. Trong môi trƣờng này, các ứng dụng có thể thực thi mã, lƣu trữ và truy vấn dữ liệu trong kho dữ liệu App Engine, sử dụng dịch vụ App Engine mail, URL fetch, dịch vụ ngƣờ i dùng (xem thêm ở phần các di ̣ch vu ̣ của GEA ), và kiểm tra yêu cầu web của ngƣời dùng và chuẩn bị các kết quả trả về.
Một ứng dụng App Engine không thể:
* Thay đổi hệ thống tâ ̣p tin. Các ứng dụng phải sử dụng App Engine datastore để lƣu trữ dữ liệu. Cho phép đ ọc dƣ̃ liê ̣u từ hệ thống tập tin, và tƣ̀ các tâ ̣p tin tải lên cùng với ứng dụng.
* Mở một socket hoặc truy cập trực tiếp mợt máy chủ khác. Mợt ứng dụng có thể sử dụng dịch vụ App Engine URL fetch để tạo yêu cầu HTTP và HTTPS đến máy chủ khác trên cổng 80 và 443, tƣơng ứng.
Sinh viên thực hiê ̣n : Đào Phan Quang K50 Lớp CNPM 29
* Sinh ra mợt tiến trình phụ hoặc l̀ng. Một yêu cầu phải đƣợc xử lý trong mợt tiến trình duy nhất trong vịng vài giây. Tiến trình mất mợt thời gian rất dài để đáp ứng đƣợc chấm dứt để tránh quá tải máy chủ web.
* Tạo các loại cuộc gọi hệ thống khác.
Luồng
Một ứng dụng Java không thể tạo một đối tƣợng kiểu java.lang.ThreadGroup hay java.lang.Thread. Những hạn chế này cũng áp dụng cho các lớp JRE sƣ̉ du ̣ng Thread. Ví dụ, mợt ứng dụng không thể tạo ra một đối tƣợng kiểu java.util.concurrent.ThreadPoolExecutor, hoặc java.util.Timer. Mợt ứng dụng có thể thực hiện các hoạt động đối vớ i các luồng hiện hành, chẳng hạn nhƣ Thread.currentThread (). DumpStack ().
Hệ thống tập tin
Một ứng dụng Java không thể sử dụng bất kỳ lớp nào để thay đổi hệ thống tập tin, chẳng hạn nhƣ java.io.FileWriter. Mợt ứng dụng có thể đọc các tập tin của mình từ hệ thống tập tin bằng cách sử dụng các lớp nhƣ java.io.FileReader. Các ứng dụng cũng có thể truy cập các tập tin của nhƣ "tài nguyên", chẳng hạn nhƣ Class.getResource () hoặc ServletContext.getResource() .
Chỉ có tập tin đƣợc coi là "các tập tin tài nguyên" là có thể đƣợc truy cập vào ứng dụng thông qua hệ thống tập tin. Theo mặc định, tất cả các file trong WAR là "các tập tin tài nguyên". Bạn có thể loại bỏ các tập tin từ thiết lập này bằng cách sử dụng tập tin appengine-web.xml.
java.lang.System
Các tính năng của lớp java.lang.System mà không áp dụng cho App Engine bị vô hiê ̣u hoa.
Các phƣơng pháp System sau đây khơng phải làm gì trong App Engine: exit(), go(), runFinalization(), runFinalizersOnExit().
Các phƣơng pháp System sau trả về null: inheritedChannel(), console().
Một ứng dụng không thể cung cấp hoặc trực tiếp gọi bất kỳ mã JNI. Các phƣơng pháp System sau tạo ra một ngoại lệ java.lang.SecurityException: load(), loadLibrary(), setSecurityManager().
Reflection
Một ứng dụng đƣợc cho phép đầy đủ, không hạn chế, phản xạ truy cập đến các lớp riêng của mình. Nó có thể truy vấn bất kỳ thành phần riêng , sử dụng java.lang.reflect.AccessibleObject.setAccessible(), và đọc/ghi các thành phần riêng. Một ứng dụng cũng có thể lấy thông tin về các lớp của JRE và API , chẳng hạn nhƣ java.lang.String và javax.servlet.http.HttpServletRequest. Tuy nhiên, nó chỉ có thể truy cập các thành phần công khai của lớp , không thể vào các phần đƣơ ̣c bảo vệ hay riêng tƣ.
Sinh viên thực hiê ̣n : Đào Phan Quang K50 Lớp CNPM 30
Một ứng dụng không thể lấy phản ánh cảu bất kỳ lớp nào khác khơng tḥc về chính nó, và nó khơng thể sử dụng phƣơng pháp setAccessible() để phá vỡ những hạn chế này.
II.5. Logging
Ứng dụng của bạn có thể ghi thơng tin vào log bằng cách sử dụng java.util.logging.Logger. Dƣ̃ liệu trong log có thể đƣợc xem và phân tích bằng cách sử dụng điều khiển chính, hoặc tải về bằng cách sử dụng request_logs appcfg.sh. Ở bản điều khiển chính ta có thể nhận ra mức độ log đang sƣ̉ du ̣ng , và hiển thị các thông điê ̣p trong log ở các cấp độ khác nhau.
Tất cả mọi thứ servlet ghi ra dòng ra tiêu chuẩn (System.out) và dịng lỡi tiêu chuẩn (System.err) đều đƣợc App Engine bắt và đƣợc ghi lại trong log của ứng dụng. Các thông điệp ghi ra dòng ra tiêu chuẩn đƣợc log la ̣i ở cấp độ "INFO", và các thông điệp ghi vào dòng lỗi tiêu chuẩn đƣợc log lại ở cấp "WARNING". Bất kỳ các log framework nào (nhƣ log4j) log la ̣i dòng ra và dòng lỗi đều có thể hoa ̣t đô ̣ng bình thƣờng . Tuy nhiên, để hoạt động của màn hình hiển thị log trong bảng điều khiển chính hoa ̣t đô ̣ng mi ̣n, các log framework phải sử dụng java.util.logging.
import java.util.logging.Logger; // ...
public class MyServlet extends HttpServlet { private static final Logger log =
Logger.getLogger(MyServlet.class.getName());
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
log.info("An informational message."); log.warning("A warning message."); log.severe("An error message."); }
}
App Engine Java SDK có file logging.properties mẫu, nằm trong thƣ mục appengine-java-sdk/config/user/ . Để sử dụng nó, chép nó vào thƣ mục WEB- INF/classes của bạn (hoặc ở nơi khác trong WAR), sau đó chỉnh tḥc tính hệ thớng java.util.logging.config.file tới "WEB-INF/classes/logging.properties" (hoặc đƣờ ng đẫn khác nếu chép đến thƣ̣ mu ̣c khác ). Bạn có thể thiết lập các tḥc tính hệ thống trong tập tin appengine-web.xml nhƣ sau:
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0"> ...
<system-properties>
Sinh viên thực hiê ̣n : Đào Phan Quang K50 Lớp CNPM 31
INF/classes/logging.properties" /> </system-properties>
</appengine-web-app>
II.6. Các biến môi trƣờng
Tất cả các thuộc tính của hệ thống và biến môi trƣờng là riêng cho mỗi ứng dụng của bạn. Thiết lập một thuộc tính hê ̣ thống chỉ ảnh hƣởng đến giá trị của thuộc tính đó trong ƣ́ng du ̣ng đó mà không ảnh hƣởng đến toàn bô ̣ JVM.
Bạn có thể thiết lập tḥc tính hê ̣ thớng và biến môi trƣờng cho ứng dụng trong mô tả triển khai.
App Engine họ tḥc tính hệ thống mà để xác định môi trƣờng thƣ̣c thi:
* Com.google.appengine.runtime.environment là "Production" khi chạy trên App Engine, và "Development" khi đang chạy trong máy chủ phát triển.
* Com.google.appengine.runtime.version là ID phiên bản của mơi trƣờng thƣ̣c thi, nhƣ "1.3.0".
Ngồi việc sử dụng System.getProperty (), bạn có thể truy cập hệ thống bằng cách sử dụng các type-safe API. Ví dụ:
if (SystemProperty.environment.value() ==
SystemProperty.Environment.Value.Production) { // The app is running on App Engine...
}
App Engine cũng đặt các tḥc tính hệ thống sau khi nó khởi tạo các JVM trên một máy chủ ứng dụng: file.separator path.separator line.separator java.version java.vendor java.vendor.url java.class.version java.specification.version java.specification.vendor java.specification.name java.vm.vendor java.vm.name java.vm.specification.version java.vm.specification.vendor java.vm.specification.name user.dir
Sinh viên thực hiê ̣n : Đào Phan Quang K50 Lớp CNPM 32
II.7. Các dịch vụ đi kèm
II.7.1. DataStore
GAE dùng 2 API để thƣ̣c hiê ̣n viê ̣c lƣu trƣ̃ dƣ̃ liêu : JDO (Java data object ) và JPA (Java persistence API ). JPA rất phổ biến trong các framework EJB (Spring,Struts…)
Một dữ liệu đối tƣợng trong Datastore App Engine đƣợc biết đến nhƣ mợt thƣ̣c thể. Mợt thƣ̣c thể có một hoặc nhiều thuô ̣c tính, giá trị có tên c ủa một trong số các loại dữ liệu, bao gồm các số nguyên, phƣ́ c, xâu, ngày tháng v.v ..
Mỗi thƣ̣c thể cũng có một khóa để phâ ̣n biê ̣t các th ực thể. Các khóa có kiểu và mô ̣t số ID do Datastore cung cấp . ID cũng có thể là một chuỗi cung cấp bởi ứng dụng.
Mợt ứng dụng có thể lấy mợt thực thể từ Datastore bằng cách sử dụng khóa c ủa nó, hoặc bằng cách thực hiện mợt truy vấn phù hợp với thuộc tính của thực thể. Mợt truy vấn có thể trở lại khơng hay nhiều thực thể, và có thể trả lại kết quả đƣợc sắp xếp theo thuộc tính. Một truy vấn cũng có thể hạn chế số lƣợng kết quả trả về bởi Datastore để bảo tồn bộ nhớ và thời gian chạy.
Không giống nhƣ cơ sở dữ liệu quan hệ, các App Engine Datastore không đòi hỏi rằng tất cả các thực thể của một loại nhất định có properties tƣơng tự nhau. Các ứng dụng có thể chỉ định và thi hành mơ hình dữ liệu của nó bằng cách sử dụng thƣ viện bao gồm với SDK, hoặc mã riêng của mình.
Mợt tḥc tính có thể có mợt hoặc nhiều giá trị. Một thuô ̣c tính với nhiều giá trị có thể có giá trị của các loại hỗn hợp. Một truy vấn trên một tḥc tính có giá trị nhiều kiểm tra xem liệu các giá trị đáp ứng các tiêu chuẩn truy vấn. Điều này làm cho thuộc tính đó hữu ích cho việc thử nghiệm tính thành viên.
Mỗi câu truy vấn sẽ đƣợc thƣ̣c hiê ̣n trên mo ̣i thƣ̣c thể của kiểu đang truy vấ n. Nó sẽ xác định một bộ lọc trên các thuô ̣c tính và khóa của các thực thể loại đó . Mô ̣t thƣ̣c thể sẽ đƣợc trả về nếu nó thỏa mãn bô ̣ lo ̣c.
Mỗi truy vấn Datastore sử dụng một chỉ mục, một bảng chứa các kết quả cho truy vấn theo thứ tự mong muốn. App Engine một ứng dụng xác định các chỉ số của nó trong tập tin cấu hình.Các máy chủ web sẽ tự động phát triển thêm các đề xuất đến tập tin nhƣ nó gặp các truy vấn mà chƣa đƣợc chỉnh . Bạn có thể điều chỉnh chỉ số bằng tay bằng cách chỉnh sửa tập tin trƣớc khi tải các ứng dụng. Khi ứng dụng làm thay đổi các thực thể Datastore, các Datastore cập nhật các chỉ số với kết quả chính xác. Khi ứng dụng thực hiện một truy vấn, Datastore các lấy k ết quả trực tiếp từ các chỉ số tƣơng ứng.
Sinh viên thực hiê ̣n : Đào Phan Quang K50 Lớp CNPM 33
II.7.2. Memcahe
Nhƣ̃ng ƣ́ng du ̣ng web yêu cầu hiê ̣u năng cao luôn sƣ̉ du ̣ng mô ̣t bô ̣ nhớ ta ̣m thời có tốc độ nhanh hơn nhiều lần bộ nhớ tĩnh. Memcahe là di ̣ch vu ̣ nhắm đến mu ̣c đích này.
GEA sƣ̉ du ̣ng package net.sf.jsr107 để thao tác với memcahe. Ví dụ : import java.util.Collections; import net.sf.jsr107.Cache; import net.sf.jsr107.CacheException; import net.sf.jsr107.CacheFactory; import net.sf.jsr107.CacheManager; // ... Cache cache;
// Xác định thời gian cache hết hạn Map props = new HashMap();
props.put(GCacheFactory.EXPIRATION_DELTA, 3600); try {
CacheFactory cacheFactory =
CacheManager.getInstance().getCacheFactory(); // Factory design pattern cache = cacheFactory.createCache(props);
String key; byte[] value;
// Lưu dữ liệu vào cache cache.put(key, value); // Lấy dữ liệu từ cache
value = (byte[]) cache.get(key); } catch (CacheException e) {
// ... }
Các hằng số dùng xác định thời gian hết hạn của cache:
* GCacheFactory.EXPIRATION_DELTA: xác định thời gian hết hạn tƣơng đối tính theo thời điểm khởi ta ̣o, tính theo giây.
* GCacheFactory.EXPIRATION_DELTA_MILLIS: xác định thời gian hết hạn tƣơng đối tính theo thời điểm khởi ta ̣o, tính theo mili giây.
* GCacheFactory.EXPIRATION: xác định thời điểm hết hạn là một thời điểm cố đi ̣nh kiểu java.util.Date.
Ngoài ra ta còn có thể xác định xem nên xử lý thế nào khi thêm cache
Map props = new HashMap();
Sinh viên thực hiê ̣n : Đào Phan Quang K50 Lớp CNPM 34
* MemcacheService.SetPolicy.SET_ALWAYS: thêm giá trị nếu khơng có giá trị với khóa đó tồn tại, thay thế một giá trị hiện tại nếu một giá trị với khóa đó đã tồn tại, đây là mặc định.
* MemcacheService.SetPolicy.ADD_ONLY_IF_NOT_PRESENT: thêm giá trị nếu không có giá trị với khóa đó tồn tại, khơng phải làm gì nếu khóa đã tồn tại.
* MemcacheService.SetPolicy.REPLACE_ONLY_IF_PRESENT: khơng phải làm gì nếu khơng có giá trị với khóa đó tồn tại, thay thế một giá trị hiện tại nếu một giá trị với khóa đã tồn tại.
Ta có thể lấy thông tin về cache nhƣ ví du ̣ sau :