CityPulse: Các sự kiện trên Facebook có thể nhiều hơn thế
Một giải pháp phần mềm dựa trên Azure cho phép người dùng khám phá các sự kiện xung quanh họ trên quy mô lớn. Tính năng ASP.net MVC4, Entity Framework và một phụ trợ nhập lớn sử dụng các máy ảo Azure
Bài viết này là một mục trong Thử thách nhà phát triển Windows Azure của chúng tôi . Các bài viết trong phần này không bắt buộc phải là bài viết đầy đủ vì vậy cần thận trọng khi bỏ phiếu. Tạo Tài khoản dùng thử Azure miễn phí của bạn để tham gia Thử thách .
Xin lưu ý: Gần đây chúng tôi đã đổi thương hiệu thành 'CityPulse' (và đã thêm các tính năng mới vào trang web!). Văn bản và các liên kết trong bài viết này đã được cập nhật nhưng hình ảnh gốc với logo 'whatsonglobal' vẫn còn. Thích đọc sách!
Ý tưởng
Facebook là cơ sở dữ liệu sự kiện lớn nhất trên thế giới. Nó có tất cả mọi thứ, từ buổi sinh nhật của một người với một số ít người, đến các lễ hội lớn nhất và các cuộc tụ họp trên toàn thế giới với hàng ngàn người tham dự.
Vậy làm thế nào để bạn sử dụng các sự kiện Facebook? Hầu hết chúng ta gửi lời mời cá nhân đến tiệc sinh nhật, hoặc cho biết danh sách bạn bè của chúng ta rằng chúng ta sẽ tham dự sự kiện thể thao trong tuần này. Điều mà hầu hết mọi người không nhận ra là các sự kiện trên Facebook có thể còn nhiều hơn thế. Mỗi ngày, buổi tối, cuối tuần và tháng có hàng ngàn sự kiện trên Facebook ở mọi thành phố trên khắp thế giới chỉ muốn quảng bá một bộ phim mới, bắt đầu cơn sốt mơ hồ tiếp theo hoặc công khai một bài kiểm tra quán rượu hàng tuần mới chỉ quanh góc bạn sống.
Nhưng có một vấn đề - các sự kiện bị cô lập. Những thiếu sót trong nền tảng rất riêng của Facebook có nghĩa là có một cách dễ dàng để đăng nhập và tìm hiểu những điều tuyệt vời đang xảy ra trong khu vực của bạn vào cuối tuần tới. Đây là nơi CityPulse bước vào.
Với một chế độ xem duy nhất, ứng dụng web CityPulse sẽ hiển thị cho bạn trên bản đồ tương tác các sự kiện hàng đầu đang diễn ra, địa điểm diễn ra, ngày diễn ra và số lượng người sẽ tham gia.
Làm sao?
Điều này được thực hiện thông qua việc xử lý các sự kiện của mọi người trên Facebook. Miễn là một sự kiện trên Facebook được chủ sở hữu đánh dấu là công khai thì theo các điều khoản và điều kiện, các sự kiện đó có thể được xuất và tiêu thụ bởi các ứng dụng của bên thứ 3. Miễn là thông tin sự kiện được cập nhật và không có thông tin cá nhân nào được bán - thì các ứng dụng như CityPulse được phép sử dụng và hiển thị dữ liệu.
Có một số lượng đáng ngạc nhiên các sự kiện công cộng có sẵn cho những người tìm kiếm nó. Các nhà tiếp thị, nhà quảng bá sự kiện và chủ sở hữu địa điểm rất vui khi sử dụng nền tảng Facebook để tiếp cận đối tượng mục tiêu lớn nhất có thể.
Đó không phải là tất cả các sinh nhật và các bữa tiệc tại nhà!
Nội dung
- Phạm vi của điều này
- Kiến trúc
- Công nghệ
- Quá trình nhập với máy ảo Azure
- Bộ lưu trữ
- Trang web
- Di động
Bài viết này
Bây giờ trước khi chúng tôi đội chiếc mũ của nhà phát triển yêu thích của chúng tôi và nhận được một số mã nghiêm trọng, hãy đặt ra những kỳ vọng.
Đối tượng mục tiêu của bài viết này là những người đam mê công nghệ và đã khá thành thạo với các ngôn ngữ và công nghệ lập trình dựa trên Microsoft. Chúng tôi sẽ không phân tích từng dòng mã bởi vì:
- Nó sẽ mất một thời gian dài để đọc. Có rất nhiều mã, vì vậy mọi người đều quan tâm đến việc giữ cho bài viết ngắn gọn và thú vị nhất có thể.
- Đó không phải là những gì bài viết này là về. Chúng tôi sẽ đưa bạn đến với chúng tôi khi chúng tôi giải quyết một số thách thức công nghệ khá phức tạp bằng cách sử dụng nền tảng công nghệ và phần mềm mới nhất. Điều đó có nghĩa là hầu hết chúng ta sẽ giữ ở cấp độ kiến trúc và đi sâu vào kim loại khi nó trở nên thú vị hoặc có liên quan để giải quyết mấu chốt của các vấn đề.
Những gì bài viết này sẽ cho bạn thấy:
- Các công nghệ liên quan đến việc phát triển một trang web với phần phụ trợ dựa trên Azure phức tạp.
- Làm thế nào để lập trình kiểm soát việc triển khai đám mây, cả hiệu quả và thanh lịch.
- Cách thiết lập Azure VM / Dịch vụ đám mây để nhập lượng lớn dữ liệu từ Facebook, xử lý và phân phối dưới dạng nội dung trang web.
- Cách thiết lập một thành phần lập lịch dựa trên công việc, để quản lý các nhiệm vụ bảo trì trong đám mây.
- Các mẫu thiết kế liên quan đến xử lý đám mây dựa trên hàng đợi.
- Cách sử dụng API FQL của Facebook để trích xuất thông tin sự kiện.
- Làm thế nào để sử dụng trí thông minh nhân tạo để giải quyết một vấn đề trong thế giới thực.
- Cách phát triển nhanh cơ sở dữ liệu trên đám mây với Entity Framework Code-First.
- Cách thiết lập một ASP.net MVC4 với Web Api để phân phối và kết xuất nội dung.
- Làm thế nào thiết kế đáp ứng có thể làm cho một trang web thân thiện với càng nhiều thiết bị càng tốt.
Hy vọng bạn thích!
Vậy chúng ta cần gì?
Đầu tiên chúng ta cần truy cập vào Sự kiện của Facebook . Điều này sẽ được thực hiện thông qua việc thu thập dữ liệu lưu trữ sự kiện của Facebook.
Thứ hai, chúng ta cần một cơ chế nhập khẩu . Tất cả những sự kiện đó sẽ không tự tải xuống. Các sự kiện sẽ cần phải được nhập, phân loại, lọc và cập nhật thường xuyên. Đây không phải là công việc dễ dàng - chúng tôi cần một cái gì đó sẽ mở rộng để đáp ứng bất kỳ nhu cầu lớn nào, và môi trường cơ sở hạ tầng với độ tin cậy cao, yêu cầu ít hoặc không cần bảo trì và có thể quay vòng và thực hiện công việc nặng nhọc khi chúng tôi cần. Đây là nơi Azure xuất hiện và chúng ta sẽ tìm hiểu sâu hơn về vấn đề này sau trong bài viết.
Thứ ba, chúng tôi cần một số lưu trữ . Những sự kiện được nhập cần một ngôi nhà và chúng tôi sẽ cần một nơi để quản lý và giám sát quá trình nhập. Ngoài ra, các sự kiện sẽ cần được phân loại và người dùng đăng nhập vào ứng dụng của chúng tôi sẽ cần phải đăng nhập vì lý do bảo mật. Đối với điều này, chúng tôi sẽ sử dụng SQL Azure. Đây là khá nhiều SQL tiêu chuẩn của bạn, nhưng nó sống trên đám mây.
Cuối cùng, chúng ta cần một trang web . Đây là nơi tất cả sẽ đến với nhau. Trang web sẽ phục vụ những sự kiện tuyệt vời này cho khán giả toàn cầu. Chúng tôi cần quy mô, bảo mật và hiệu suất. Các trang web Azure sẽ phù hợp với hóa đơn tốt.
Kiến trúc
Ở cấp độ cao nhất, kiến trúc trông như sau:

Chúng ta hãy xem xét một số thực thể trong sơ đồ:
Windows AzureMôi trường lưu trữ. Chúng tôi sẽ đi sâu vào chi tiết này nhưng bây giờ, hãy nói rằng Azure lưu trữ tất cả các cơ sở hạ tầng mà CityPulse sẽ chạy.
FacebookNhà cung cấp sự kiện. Vì CityPulse sẽ thu thập dữ liệu và lưu trữ các sự kiện nên có một kết nối ở đây giữa Facebook và Azure.
Quản trị viên CityPulseChúng tôi cần một nơi nào đó để quản trị các quy trình nhập khẩu. Những gì chúng ta sẽ chạy nhập khẩu? Khi nào họ được lên lịch? Có bất kỳ lỗi nào không, và thông lượng như thế nào? Một ứng dụng máy tính để bàn kết nối với cơ sở dữ liệu bảo trì sẽ cho chúng ta câu trả lời và cho phép chúng ta khởi tạo các phiên nhập đang chạy trên các nút worker.
Người dùng trang webNgười dùng trung thành. Họ chỉ muốn tải lên trang web và tìm thấy một cái gì đó tuyệt vời để làm trong cuộc sống của họ. Họ không biết làm thế nào các sự kiện đã đến đó, họ chỉ muốn đến với họ!
Công nghệ
Microsoft Azure
- Trang web Azure : Lưu trữ trang web trên đám mây
- Dịch vụ đám mây Azure : Hỗ trợ cơ sở hạ tầng sẽ thúc đẩy quá trình nhập (Paas)
- Lưu trữ Azure : Lưu trữ Blob chứa dữ liệu Máy ảo được sử dụng trong quá trình nhập. Tin nhắn xếp hàng được lưu trữ ở đây.
- Azure SQL : Lưu trữ cơ sở dữ liệu SQL được sử dụng trong nhập và thông tin sự kiện
Microsoft ASP.net MVC 4 : Cách tiếp cận Trình điều khiển Chế độ xem Mô hình cho MVC
API Web ASP.net : Triển khai RESTful một dịch vụ web cung cấp nội dung Json. Quan trọng cho khả năng tương tác.
Entity Framework : Trừu tượng hóa cơ sở dữ liệu cho phép phát triển nhanh chóng trong Lớp truy cập dữ liệu
- Mã đầu tiên - tạo mô hình cơ sở dữ liệu từ Mã C #
- Cơ sở dữ liệu trước tiên - tạo mã C # từ các mô hình cơ sở dữ liệu
Thư viện web
- Javascript - cho phép AJAX trên trang web, rất quan trọng đối với giao diện lỏng
- Bản đánh máy - thư viện được gõ mạnh nằm trên đầu javascript. Tuyệt vời cho các nhà phát triển C #.
- JQuery - Thư viện cho javascript
- Google maps - API để kích hoạt google maps trên trang web
- Richmarker - cho phép nội dung HTML phong phú cho các điểm đánh dấu
- Google autocomplete - được sử dụng để tra cứu địa chỉ người dùng
- Ít hơn - bảng định kiểu động
- Knockout - Triển khai MVVM trong javascript. Tuyệt vời cho cơ sở dữ liệu
- Modernizr - phục vụ cho tất cả các trình duyệt và độ phân giải màn hình
- Khoảnh khắc - xử lý datetime tuyệt vời trong javascript
Azure Fluent Management - API 'thông thạo' để quản lý nền tảng phương vị
Quartz.net - Lập lịch công việc doanh nghiệp trong .net
Prowl - thông báo điện thoại tự động
Json.Net - Serilization nhanh trong .Net đến và từ Json
Telerik Radcontrols - bộ giao diện người dùng toàn diện cho WPF
Tại sao Azure?
Tôi nghĩ mỗi nhà phát triển sẽ trả lời khác nhau. Đối với tôi, sự lựa chọn của Azure rơi vào 4 loại sau.
Cơ sở hạ tầng miễn phí.
Đối với giải pháp này, chúng tôi cần phần cứng. Chúng tôi cần máy chủ để lưu trữ các trang web, cơ sở dữ liệu và chạy dịch vụ nhập sự kiện. Điều rất quan trọng là phần cứng phải đối mặt với bên ngoài phải được bảo mật, vì vậy chúng tôi sẽ cần phải luôn cập nhật các bản vá bảo mật bên cạnh các bản cập nhật Windows thông thường cần được áp dụng để giữ an toàn. Để chạy trang web, chúng tôi sẽ cần phải định cấu hình và duy trì IIS và nền tảng chạy quy trình nhập sẽ cần tình yêu liên tục chỉ để theo kịp và chạy.
Trên Azure, chúng tôi không cần phải lo lắng về bất kỳ nội dung nào trong số này. Cơ sở hạ tầng được Microsoft giữ, để chúng tôi có thể dành thời gian cho nhà phát triển quý giá của mình để tạo ra phần mềm tuyệt vời. Từ khía cạnh bảo mật của những thứ chúng ta có thể tập trung vào các nhiệm vụ như bảo mật API Web của ASP.net sẽ phân phối các sự kiện đến trang web của chúng tôi, thay vì bị cuốn vào việc đảm bảo khai thác hệ điều hành mới nhất. Về phía phát triển, chúng tôi có thể dành thời gian để đóng đinh dịch vụ nhập khẩu và tạo ra một dịch vụ vững chắc sẽ thúc đẩy giải pháp của chúng tôi.
Trên Azure, chúng tôi không cần phải lo lắng về bất kỳ nội dung nào trong số này. Cơ sở hạ tầng được Microsoft giữ, để chúng tôi có thể dành thời gian cho nhà phát triển quý giá của mình để tạo ra phần mềm tuyệt vời. Từ khía cạnh bảo mật của những thứ chúng ta có thể tập trung vào các nhiệm vụ như bảo mật API Web của ASP.net sẽ phân phối các sự kiện đến trang web của chúng tôi, thay vì bị cuốn vào việc đảm bảo khai thác hệ điều hành mới nhất. Về phía phát triển, chúng tôi có thể dành thời gian để đóng đinh dịch vụ nhập khẩu và tạo ra một dịch vụ vững chắc sẽ thúc đẩy giải pháp của chúng tôi.
Quy mô, quy mô, quy mô!
Mở rộng quy mô là những gì Azure làm xuất sắc. Chia tỷ lệ cho phép chúng tôi đối phó với tải nặng từ cơ sở người dùng đang mở rộng và cho phép chúng tôi xử lý các tác vụ lớn như nhập một công việc hàng loạt lớn qua đêm để đảm bảo các sự kiện tối đa được hiển thị vào ngày hôm sau.

Trong hầu hết các trường hợp, tỷ lệ có thể đạt được thông qua API quản lý Azure hoặc thông qua Lệnh PowerShell. Tôi sẽ trình bày chi tiết hơn về các mẫu tỷ lệ điển hình trong môi trường đám mây sau trong bài viết này.
độ tin cậy
Microsoft SLAs (nghĩa vụ sẵn có) nêu bất cứ nơi nào giữa thời gian hoạt động 99,5% và 99,9% tùy thuộc vào dịch vụ bạn sử dụng. Đó là một nghĩa vụ khá tốt, và chắc chắn tốt hơn rất nhiều so với những gì có thể mong đợi nếu chúng ta tự duy trì cơ sở hạ tầng. Điều đó nói rằng, Azure đã được biết là đã đi xuống trước đây - nhưng may mắn thay, chẳng hạn như 'Sao chép địa lý' cho phép chúng tôi phản ánh cơ sở hạ tầng trên các vị trí và thất bại nếu chúng tôi gặp bất kỳ khó khăn nào.
Tích hợp nặng cho Microsoft Tooling
Cho đến thời điểm hiện tại, chức năng mà Azure cung cấp có thể được kết hợp phần nào bởi các dịch vụ lưu trữ dựa trên đám mây khác, chẳng hạn như Amazon EC2. Tích hợp là nơi Azure trở thành của riêng mình. Nếu bạn sống và hít thở trong Visual Studio - thì lập trình dựa trên đám mây cho Azure sẽ khiến bạn cảm thấy như đang ở nhà.

Xuất bản quy trình công nhân lên cơ sở hạ tầng đám mây hoặc bản dựng mới nhất của trang web của bạn thường có thể đạt được chỉ bằng một cú nhấp chuột, trong khi Entity Framework đầu tiên mã cho phép chúng tôi hiệu quả tối đa khi tạo và duy trì cơ sở dữ liệu SQL dựa trên Azure của chúng tôi.
Quá trình nhập với máy ảo Azure
Quá trình nhập bao gồm ba công việc để khai thác một sự kiện từ Facebook, xử lý thông tin và lưu trữ nó trong cơ sở dữ liệu sự kiện trực tiếp của chúng tôi để nó có thể được phục vụ cho người dùng trang web của chúng tôi. Các công việc này được chạy bằng cách sử dụng mẫu thiết kế dựa trên hàng đợi để chúng có thể được thu nhỏ và được bắt đầu bằng tương tác người dùng hoặc bởi các công việc được lên lịch qua Quartz.net.
Mỗi công việc được gọi là "quy trình công nhân". Đây là một mô-đun mã sẽ chạy độc lập và có thể được đẩy lên đám mây để chạy theo miền của quy trình nhập của chúng tôi. Khi chạy trong Azure, mã worker được chạy dưới dạng 'Dịch vụ đám mây', về cơ bản là một Máy ảo mà chúng tôi không trực tiếp kiểm soát. Chúng tôi có thể sử dụng plugin Azure SDK cho Visual Studio để phát triển công nhân và sử dụng phương thức xuất bản bằng một cú nhấp để đẩy mã lên Azure. Trong đám mây, mã này sẽ được cung cấp và chạy cho đến khi chúng tôi bảo nó dừng thông qua cổng quản lý, nó không thành công hoặc chúng tôi gửi lệnh để gỡ nó xuống.

Bây giờ trước khi chúng tôi đi sâu vào chính mã, có 3 khái niệm chính về quy trình nhập của chúng tôi yêu cầu giải thích:
- Lập lịch công việc với Quartz.net: Do tính chất quy mô lớn của yêu cầu nhập khẩu, việc khởi tạo các công việc và nhiệm vụ cần phải được tự động hóa. Quartz.net cung cấp một công cụ lập kế hoạch công việc rất mạnh mẽ sẽ làm tốt.
- Tự động hóa việc triển khai VM trong đám mây: Cổng quản lý rất tuyệt, nhưng không đủ cho một giải pháp hoàn toàn tự động. Thư viện 'Fluent Management' của Elastaclouds sẽ giúp chúng tôi thực hiện điều này.
- Mẫu thiết kế dựa trên hàng đợi: Việc sử dụng quy mô trong đám mây để xử lý khối lượng công việc khác nhau là rất quan trọng. Hàng đợi là chìa khóa.
Lập lịch công việc với Quartz.net
Trích xuất, xử lý và lưu trữ các sự kiện của Facebook trên phạm vi toàn cầu có thể đòi hỏi rất nhiều quản trị, vì vậy một ứng dụng WPF nhỏ đã được phát triển để xử lý một số tự động hóa cần thiết để khởi tạo quy trình nhập. Ứng dụng sử dụng Quartz.net , một thư viện lập lịch công việc toàn diện để cho phép chúng tôi lên kế hoạch khi một số phần nhất định của quy trình nhập được chạy. Mục tiêu ở đây là để nó chạy như đồng hồ, sử dụng Prowl.net , prowldotnet và email tự động để gửi một số số liệu về sự thành công của việc nhập và khi cần chú ý (nếu có).

Ẩn Shrink
Sao chép Mã
Sao chép Mã/// <summary>
/// Schedule a job to run based on a cron string
/// </summary>
/// <param name="job" /> The job abstraction to be fired
/// <param name="cronString" /> An expression representing when and how often to fire job
public ScheduledJobViewModel ScheduleJob(ImporterJobBase job, string cronString)
{
//use quartz.net to create a job and assign a UI
IJobDetail jobDetail = JobBuilder.Create(job.GetType())
.WithIdentity("Job - " + Guid.NewGuid().ToString(), job.Name)
.Build();
//create a trigger for the event to fire, using a cron string
ICronTrigger trigger = (ICronTrigger)TriggerBuilder.Create()
.WithIdentity("Trigger - " + Guid.NewGuid().ToString(), job.Name)
.WithCronSchedule(cronString)
.Build();
//schedule the job to run
_scheduler.ScheduleJob(jobDetail, trigger);
//return a UI representation of the job so it can be shown on the form
return new ScheduledJobViewModel()
{
CronExpression = cronString,
JobType = job.GetType().ToString(),
Name = job.Name
};
}
Đoạn mã trên mô tả cách sử dụng Quartz.net để sắp xếp công việc. Biểu thức cron cho phép một cách ngắn gọn và mạnh mẽ để mô tả thời điểm và tần suất một công việc nên chạy.
- Cơ sở dữ liệu sự kiện trực tiếp Dọn dẹp có biểu thức '0 00 06 * *?'
- Công việc sẽ xóa các sự kiện dư thừa mỗi ngày vào lúc 6 giờ sáng.
- Khởi tạo Thu thập dữ liệu (thêm hàng vào quy trình công nhân A) có biểu thức '0 0 0? MON, THU, SAT '
- Công việc sẽ khởi tạo hàng đợi cho phiên thu thập dữ liệu mới trong Azure vào mỗi Thứ Hai, Thứ Năm và Thứ Bảy lúc 12 giờ đêm.
- Quy trình nhập đầy đủ (Quy trình công nhân A - C) có biểu thức '0 10 0? MON, THU, SAT '
- Công việc sẽ thực hiện phiên thu thập dữ liệu mới trong Azure vào mỗi Thứ Hai, Thứ Năm và Thứ Bảy lúc 10 phút trước nửa đêm.
- Trình cập nhật sự kiện trực tiếp có biểu thức '0 10 06 * *?'
- Các sự kiện hiện tại sẽ được làm mới với dữ liệu mới mỗi ngày vào lúc 6:10 sáng.
Bản thân các công việc bao gồm từ việc chạy các thủ tục được lưu trữ dựa trên cơ sở dữ liệu trực tiếp đến đóng gói và triển khai quy trình công nhân lên Azure. Miễn là họ thực hiện giao diện IJob, họ có thể được lên lịch bởi Quartz.
một lớp trừu tượng đã được phát triển ( ImporterJobBase ) để gói gọn một số chức năng phổ biến mà các công việc có thể cung cấp như thông báo email / điện thoại, ghi nhật ký lỗi & thông báo và cho phép cấu hình có thể được chuyển đến UI.
một lớp trừu tượng đã được phát triển ( ImporterJobBase ) để gói gọn một số chức năng phổ biến mà các công việc có thể cung cấp như thông báo email / điện thoại, ghi nhật ký lỗi & thông báo và cho phép cấu hình có thể được chuyển đến UI.

Quản lý thông thạo Azure của Elastaclouds
Thư viện Azure Fluent Management ( fluentman Quản lý.elastacloud.com ) là một thành phần đặc biệt hữu ích trong việc quản lý đám mây Azure, đặc biệt là khi cung cấp và triển khai lưu trữ cùng với các dịch vụ đám mây.
Thông thường, tự động kiểm soát các tác vụ như triển khai cơ sở dữ liệu Azure SQL, cung cấp máy ảo và xóa các bộ lưu trữ dự phòng sẽ đạt được thông qua các lệnh ghép ngắn Powershell, API RESTful hoặc Giao diện người dùng được cung cấp qua Cổng thông tin quản lý Azure. Nhưng cách tốt nhất để tự động hóa các tác vụ này trong mã là gì? Quản lý thông thạo cung cấp một cách thân thiện .Net để đạt được các nhiệm vụ này và hơn thế nữa. Điều này đặc biệt hấp dẫn với dự án này, vì chúng tôi đã phát triển một công cụ lập kế hoạch công việc mà Fluent Management sẽ phù hợp hoàn hảo. Chúng tôi chỉ đơn giản là tạo IJobs, tạo mã quy định công nhân xử lý lên đám mây và lên lịch cho chúng kích hoạt bất cứ khi nào chúng tôi muốn. Và đây chính xác là những gì sắp được chứng minh.
Đầu tiên chúng tôi tuyên bố một phương thức trợ giúp để đóng gói quá trình triển khai
Ẩn Shrink
Sao chép Mã
Sao chép Mãpublic static async Task DeployAzureWorkerAsync(
string name,
string roleName,
string cspkgEndPoint,
X509Certificate2 cert,
string subscriptionId,
DeploymentSlot deploymentSlot,
string location,
int instanceCount)
{
await Task.Run(() =>
{
//get an azure friendly name
var storageName = new RandomAccountName().GetPureRandomValue();
//deploy storage (used to host worker process)
var subscriptionManager = new SubscriptionManager(subscriptionId);
subscriptionManager.GetStorageManager()
.CreateNew(storageName)
.AddCertificateFromStore(cert.Thumbprint)
.WithDescription(name)
.WithLocation(location)
.Go()
.Commit();
//deploy worker process
var deploymentManager = subscriptionManager.GetDeploymentManager();
deploymentManager
.AddCertificateFromStore(cert.Thumbprint)
.ForNewDeployment("deployment" + storageName)
.SetCspkgEndpoint(cspkgEndPoint)
.WithNewHostedService(name)
.WithStorageAccount(storageName)
.AddDescription("automated deployment" + name)
.AddEnvironment(deploymentSlot)
.AddLocation(location)
.AddParams(DeploymentParams.StartImmediately)
.ForRole(roleName)
.WithInstanceCount(instanceCount)
.WaitUntilAllRoleInstancesAreRunning()
.Go()
.Commit();
});
}
Và cách sử dụng:
Ẩn Shrink
Sao chép Mã
Sao chép Mã/// <summary>
/// Running inside of a Quartz.net IJob
/// </summary>
public override async Task OnExecute(IJobExecutionContext context)
{
//define where to deploy to
var locationsToDeploy = new List<string>()
{
LocationConstants.EastAsia,
LocationConstants.NorthEurope,
LocationConstants.WesternEurope,
LocationConstants.WestUS,
LocationConstants.EastUS
};
//set the end point - this is the directory in which your packaged worker process was outputted to
string endPoint = @"[The root directory of your project packages ]";
//your azure subscription ID
var subscriptionId = "[You Azure Subscription ID - get this from .publishsettings]";
//Open the publish settings (you can download these from the Azure Management Portal)
using (var reader = new StreamReader(@"D:\Dev Apps\PublishSettings\settings.publishsettings"))
{
//extract an X509 certificate for security validation
string xml = reader.ReadToEnd();
var cert = PublishSettingsExtractor.AddPublishSettingsToPersonalMachineStore(xml);
//for every location specified, sequentially deploy a cloud service.
foreach (var location in locationsToDeploy)
{
//get an azure friendly name for the deployment
var nodeManage = new RandomAccountName();
//deploy
await DeploymentHelper.DeployAzureWorkerAsync(
name: "datagather" + locationsToDeploy.IndexOf(location),
roleName: "EventProcessing",
cspkgEndPoint: endPoint,
cert: cert,
subscriptionId: subscriptionId,
deploymentSlot: DeploymentSlot.Production,
location: location,
instanceCount: 1);
}
}
}
Khi công việc này thực thi, nó sẽ lặp các vị trí có sẵn và triển khai một quy trình worker. Mỗi quy trình worker chạy trong một Virtual Machine, do đó, một container lưu trữ phải được phân bổ trước.
Nếu bạn thắc mắc tại sao chúng tôi triển khai tới 5 trung tâm dữ liệu khác nhau thì đó là vì chúng tôi muốn sử dụng càng nhiều địa chỉ IP càng tốt khi truy vấn API Facebook để không bị chặn. Quá nhiều yêu cầu từ cùng một địa chỉ IP có thể dẫn đến điều chỉnh, điều này sẽ hạn chế thông lượng của quá trình nhập của chúng tôi. Đối với các giải pháp khác nhau, thông thường nên chạy nhiều quy trình từ cùng một vị trí / trung tâm dữ liệu.
Mẫu xử lý hàng đợi

Nếu trước đây bạn chưa phát triển xử lý dựa trên đám mây, sơ đồ bên trái sẽ quen thuộc nhất. Chúng tôi có một quy trình xử lý luồng duy nhất để lấy dữ liệu, thực hiện một số xử lý và lưu trữ kết quả - có lẽ vì vậy nó có thể được người dùng xem lại sau này. Thông thường, mẫu này chỉ hoạt động tốt đối với một lượng nhỏ dữ liệu có thể được xử lý nhanh chóng.
Nhưng nếu chúng ta có nhiều dữ liệu hơn thì sao? Điều gì sẽ xảy ra nếu chúng ta có hàng trăm ngàn bản ghi để xử lý và mỗi bản ghi cần một thời gian để xử lý? Một cách tiếp cận sẽ là để có được phần cứng tốt hơn - có thể bạn muốn một máy chủ tốt hơn để xử lý tải thêm.

Vấn đề với điều này là ở một số giai đoạn bạn sẽ cần mở rộng ra , thay vì lên . Bạn sẽ hết năng lượng xử lý và số lượng lõi trong phần cứng của bạn sẽ cần tăng lên, buộc bạn phải sử dụng đa luồng để tận dụng sức mạnh thêm - hoặc bạn có thể chọn có nhiều máy chủ và phân chia khối lượng công việc trên mỗi máy chủ. Đây là nơi hàng đợi phù hợp và nó hoạt động hoàn hảo cho Azure, nơi chúng ta có thể khởi tạo nhiều máy ảo để xử lý tải thêm.
Bạn có thể nghĩ về một hàng đợi như một danh sách việc cần làm được chia sẻ nhưng nó có chức năng đặc biệt ở chỗ nó cho phép một mục nhập danh sách việc cần làm bị khóa đối với một người. Hãy nghĩ về cuộc sống không có hàng đợi trong một giây, bạn có thể chọn để máy chủ của mình truy cập vào bảng SQL chứa danh sách dữ liệu cần xử lý, nhưng làm thế nào bạn đảm bảo rằng máy chủ không bắt đầu truy cập và xử lý cùng một danh sách chính xác ? Nếu các máy chủ khởi động cùng một lúc, sẽ có mọi cơ hội mà không có một hệ thống khóa đồng thời phức tạp nào đó, mỗi máy chủ sẽ kết thúc việc lưu trữ cùng một kết quả, vô hiệu hóa điểm mở rộng ở vị trí đầu tiên.
Hàng đợi giải quyết vấn đề này một cách thanh lịch. Khi một nhân viên xử lý truy cập một tin nhắn trong hàng đợi, tin nhắn sẽ bị ẩn trong một khoảng thời gian nhỏ. Điều này cho phép nhân viên có thời gian xử lý tin nhắn và ngăn bất kỳ nhân viên nào khác truy cập và xử lý dữ liệu trùng lặp.
Một điều nữa cần lưu ý - hàng đợi rất nhẹ và được thiết kế theo tốc độ thực hiện. Điều này có nghĩa là họ chỉ xử lý các tin nhắn nhỏ và thường có các hạn chế về kích thước đối với kích thước của tin nhắn có thể được thêm vào. Đối với CityPulse, chúng tôi chỉ cần thêm eids hoặc URL, nhưng đối với các trường hợp khác, chỉ cần thêm ID hoặc khóa vào tin nhắn, sau đó lưu tải trọng lớn hơn thực tế trong bộ lưu trữ blob hoặc trong một hàng SQL để có thể truy xuất nó nút worker.

Trong hàng đợi ở trên, nếu worker xử lý các thông báo yêu cầu 5 & 6 đồng thời, thiết kế dựa trên hàng đợi sẽ đảm bảo rằng một process worker sẽ nhận được tin nhắn 7 và cái còn lại sẽ nhận được tin nhắn 8 . Những thông điệp này sau đó sẽ bị khóa khỏi các quy trình công nhân khác.
QueueReaderSessionBase
Hầu hết các quy trình công nhân của chúng tôi trên đám mây sử dụng hàng đợi để quản lý và điều chỉnh khối lượng công việc. Để gói gọn một số chức năng phổ biến như gửi tin nhắn đến các hàng đợi khác, ghi nhật ký, xử lý các vấn đề chính về API (xem quy trình công nhân B), một lớp trừu tượng đã được tạo cho các công nhân kế thừa.


Mã quy trình công nhân
Quy trình công nhân a - Tập hợp sự kiện thô
Nhân viên này quan tâm đến việc thu thập dữ liệu Facebook cho Id sự kiện để có thể thêm chúng vào hàng xử lý và cuối cùng được thêm vào cơ sở dữ liệu sự kiện.

EventIds (sau đây gọi là eid ) là các định danh duy nhất cho các sự kiện của Facebook và đặc biệt quan trọng đối với Worker Process B khi chúng tôi sử dụng API Facebook (FQL) để chuyển đổi eid thành thông tin có ý nghĩa.
Dòng dữ liệu

Thêm vào hàng đợi vị trí tìm kiếm
Ở đầu sơ đồ, quá trình bắt đầu với quản trị viên CityPulse hoặc một sự kiện được lên lịch để thêm danh sách URL ban đầu vào hàng đợi vị trí tìm kiếm.
Thêm vào hàng đợi có thể đạt được bằng cách sử dụng .net với Azure SDK hoặc thông qua dịch vụ web RESTFUL. phương pháp Azure SDK sẽ được trình bày:
Ẩn mã sao chép
string[] urls = new[]
{
"http://facebook.com/myurl",
"http://facebook.com/myur2",
"http://facebook.com/myurl3"
};
string storageAccountUrl = "Your provided Azure Storage URL";
string queueName = "dataimporter1";
CloudStorageAccount account = CloudStorageAccount.Parse(storageAccountUrl);
CloudQueueClient queueClient = account.CreateCloudQueueClient();
CloudQueue queue = queueClient.GetQueueReference(queueName);
queue.CreateIfNotExist();
Parallel.ForEach(QueueHelper.SplitInto64K(urls), url =>
{
_queue.AddMessage(new CloudQueueMessage(url));
});
Trước hết, chúng tôi tạo tài khoản lưu trữ và máy khách xếp hàng, sử dụng URL tài khoản lưu trữ được cung cấp khi tài khoản lưu trữ mới được tạo trong Azure.
Sau đó, chúng tôi nhận được hàng đợi của mình (tạo một cái mới nếu nó không tồn tại).
Cuối cùng, chúng tôi lấy URL của mình và đẩy chúng lên hàng đợi dưới dạng tin nhắn. Chúng tôi có xu hướng tránh xa việc tải lên nhiều tin nhắn nhỏ vì việc truy cập, đọc và xóa các hoạt động có chi phí hiệu suất nhỏ. Azure hiện giới hạn các tin nhắn xếp hàng ở mức 64K, do đó,
QueueHelper.SplitInto64K(urls)có để ghép các tin nhắn thành định dạng kiểu csv. Điều này có nghĩa là ba URL của chúng tôi được nén thành một tin nhắn. Chúng tôi sử dụng Thư viện song song nhiệm vụ Parallel.ForEachđể tăng tốc hoạt động và đẩy các tin nhắn song song - điều này đặc biệt hữu ích cho số lượng lớn tin nhắn.
Khi mã này đã chạy, chúng ta có thể kiểm tra xem nó đã đẩy thành công các tin nhắn vào hàng đợi chưa. Bạn có thể làm điều này bằng cách viết một phương thức đơn giản hoặc một bài kiểm tra - nhưng tôi thích sử dụng một trình thám hiểm lưu trữ . Có một vài nhà thám hiểm để lựa chọn, nhưng cái tôi sử dụng là Azure Storage Explorer (azurest Storageexplorer.codeplex.com) . Bằng cách tải xuống ứng dụng và nhập chi tiết tài khoản lưu trữ của tôi, chúng tôi có thể thấy nội dung của hàng đợi và chúng tôi có thể thấy rằng các URL đã được nối và thêm.

Đọc từ hàng đợi vị trí tìm kiếm
Sử dụng ứng dụng nhập khẩu của chúng tôi và Quartz.net, chúng tôi có thể sắp xếp công việc để nhân viên này trong các trung tâm dữ liệu Azure trên toàn cầu và hưởng lợi từ tính toán song song ở bất kỳ quy mô nào được coi là cần thiết cho số lượng URL được thêm vào hàng đợi.
Ẩn Shrink
Sao chép Mã
Sao chép Mãpublic class RawEventImporterQueueSession : QueueReaderSessionBase
{
private readonly RawEventContext _dbContext;
private bool _continue = true;
private List<string> _addedSearchTermsToBuffer = new List<string>();
public int ImportedCount { get; set; }
public RawEventImporterQueueSession(RawEventContext dbContext, string queueUrl, string queueName)
: base(queueUrl, queueName)
{
//the event import database (code first EF will be covered later in the article)
_dbContext = dbContext;
}
public override async Task StartAsync()
{
try
{
while (_continue)
{
//this will pull 40 events
List<CloudQueueMessage> urlsToSearch = this.DeQueueEvents(1).ToList();
if (urlsToSearch.Any())
{
//start by deleting the messages
this.DeleteEvents(urlsToSearch);
//get urls
IEnumerable<string> splitUrls = this.GetCommaSeperated(urlsToSearch);
//inform import database that we are gathering
RecordResults();
//Save the URLs in the import database
//(this will us to block certain URLs that do not provide results)
var urls = splitUrls.Select(x => _dbContext.GetOrCreateURL(x));
//start the gathering process (an encapsulation of the actual website crawling)
var session = new EventGatheringSession(_dbContext, urls.ToList());
session.ExtractedEventIds += (facebookEvents) =>
{
//we have found some events!
ImportedCount += facebookEvents.Count();
//add to buffer which pushes the eids to the event processor queue
AddToImportedBuffer(facebookEvents.Select(x => x.Id));
//inform if a URL resulted in eids or not, block non relevent URLs
UpdateURLs(facebookEvents);
};
await session.StartAsync();
//we have completed this batch, sleep as to throttle the server
Thread.Sleep(2500);
}
else
{
//nothing in the queue, stop processing and shut down
_continue = false;
}
}
}
catch (Exception queueException)
{
//we have encountered an error, log so it can be analysed after the processing is complete
ImportException exception = new ImportException();
exception.ExceptionString = "Raw Event Import Queue Exception : "
+ queueException.Message;
exception.ExceptionStackTrace = queueException.StackTrace;
var rawExceptionContext = new RawEventContext();
rawExceptionContext.ImportExceptions.Add(exception);
rawExceptionContext.SaveChanges();
}
}
private void AddToImportedBuffer(IEnumerable<string> eids)
{
_toImportBuffer.AddRange(eids);
//if the buffer is full, add to the event processor queue asyncronously
if (_toImportBuffer.Count > 30)
{
Task.Run(() =>
{
this.AddMultipleToOtherQueue(Settings.Default.EventProcessingQueueName,
_toImportBuffer);
_toImportBuffer.Clear();
});
}
}
}
Khi quá trình xử lý hoàn tất và các eids đã được thêm vào hàng đợi của Công nhân B - các VM được tắt và xóa khỏi môi trường sản xuất để không phải chịu thêm bất kỳ chi phí nào từ mô hình thanh toán Azure.
Quy trình công nhân B - Xử lý EID
Khi Worker Process A đã trích xuất và thêm các eids vào hàng đợi Xử lý sự kiện thô, các thông báo sẽ như sau:

Mục tiêu của quy trình công nhân B : Để chuyển đổi các eids thành các sự kiện có ý nghĩa với tất cả thông tin mà chúng ta cần.
FQL Facebook
Facebook FQL ( Ngôn ngữ truy vấn của Facebook ) là một API cho phép các ứng dụng có khả năng tương tác với các đối tượng trong Facebook bằng cú pháp kiểu SQL. Đây là những gì chúng tôi sẽ sử dụng để trích xuất thông tin sự kiện của chúng tôi.
Khi bạn đã đăng ký trở thành nhà phát triển Facebook và đã đăng ký một ứng dụng với họ, bạn sẽ được cấp mã thông báo xác thực OAuth mà bạn có thể sử dụng để truy vấn API của họ ( hướng dẫn tại đây ). Bằng cách truy cập vào trình khám phá công cụ Facebook, chúng tôi có thể sử dụng một công cụ tuyệt vời mà Facebook cung cấp để truy vấn API của họ một cách nhanh chóng - điều này rất tốt để gỡ lỗi hoặc điều tra các hoạt động API.
Cho phép nói rằng chúng ta có một sự kiện công cộng eid mà chúng ta muốn giải quyết qua FQL. Eid đó là:
162478390590630
Bằng cách nghiên cứu tài liệu tham khảo FQL của Facebook, chúng tôi có thể suy ra rằng FQL để lấy thông tin chúng tôi cần về sự kiện này sẽ là:
Ẩn mã sao chép
SELECT eid, name, start_time, description, end_time, attending_count, declined_count, venue, timezone, version, all_members_count, update_time FROM event WHERE eid = 162478390590630

Điều này trả về một đối tượng JSON đại diện cho sự kiện và một số thông tin của nó. Chúng ta có thể thấy ở đây chúng ta có một sự kiện bắt đầu vào ngày 28 tháng 6 và 39 người hiện đang lên kế hoạch tham dự.
Nhưng chúng ta đang thiếu một điều. Ghi nhớ mục tiêu tổng thể của chúng tôi? Đó là trích xuất các sự kiện trên facebook và hiển thị chúng trên bản đồ. Chúng ta cần kinh độ và vĩ độ.
Id địa điểm của sự kiện này là 10647924275034 mà chúng ta có thể sử dụng điều này để giải quyết tọa độ địa lý.
Lần này FQL là:
Ẩn mã sao chép
SELECT page_id, type, were_here_count,location.latitude, location.longitude FROM page WHERE page_id IN (SELECT venue.id FROM #event_info)
Trả về đối tượng JSON:
Ẩn Shrink
Sao chép Mã
Sao chép Mã{
"data": [
{
"eid": 162478390590630,
"name": ">* Dub Theories - Part 2 ! *<",
"start_time": "2013-06-28T09:00:00+0100",
"description": "DUB THEORIES RETURNS FOR PART 2...\n''SHOWCASING DUB INFLUENCED
PRODUCTIONS''\n\n
***FRIDAY 28TH OF JUNE @ Cosies! ONLY £3***\n
*REGGAE DUB & JUNGLE*\n\n-> DUB INVASION (Live mix!)\n(Silverback recordings!)
\n\n-> MESSENJAH YOUTH\n(Swindon massive!)\n\n-> JONNY FISHA\n(Dubwise
theorist!)\n\n
-> J - MAN\n(Badman Junglist!)\n",
"end_time": null,
"attending_count": 39,
"declined_count": 22,
"venue": {
"id": 106479242750341
},
"timezone": "Europe/London",
"version": 2,
"all_members_count": 757,
"update_time": 1370464550
}
]
}
Hiện tại có một tối ưu hóa cuối cùng mà chúng ta có thể thực hiện để có được thông tin chúng ta cần thực hiện 2 cuộc gọi API, nhưng các cuộc gọi API điều tiết của Facebook sẽ giới hạn chúng ta số lần gọi API mà chúng ta có thể thực hiện liên tục. Bằng cách sử dụng FQL Batch Queries, chúng ta có thể cuộn thao tác này thành một lệnh gọi FQL duy nhất và sau đó tuần tự hóa kết quả thành một đối tượng Json duy nhất.
FQL cho điều này sẽ là:
Ẩn mã sao chép
{"event_info":"SELECT eid, name, start_time, end_time, attending_count, declined_count, venue, timezone, version, all_members_count, update_time FROM event WHERE eid = 162478390590630","event_venue":"SELECT page_id, type, were_here_count,location.latitude, location.longitude FROM page WHERE page_id IN (SELECT venue.id FROM #event_info)"}
Điều này trả về cho chúng ta một đối tượng json duy nhất đại diện cho một mảng kết quả (một cho sự kiện, một cho địa điểm của nó):
Ẩn Shrink
Sao chép Mã
Sao chép Mã{
"data": [
{
"name": "event_info",
"fql_result_set": [
{
"eid": 162478390590630,
"name": ">* Dub Theories - Part 2 ! *<",
"start_time": "2013-06-28T09:00:00+0100",
"end_time": null,
"attending_count": 39,
"declined_count": 22,
"venue": {
"id": 106479242750341
},
"timezone": "Europe/London",
"version": 2,
"all_members_count": 757,
"update_time": 1370464550
}
]
},
{
"name": "event_venue",
"fql_result_set": [
{
"page_id": 106479242750341,
"type": "ARTS/ENTERTAINMENT/NIGHTLIFE",
"were_here_count": 1493,
"location": {
"latitude": 51.460999084183,
"longitude": -2.5862773472242
}
}
]
}
]
}
Tuyệt vời - chúng tôi có tất cả thông tin chúng tôi cần để lưu trữ các đối tượng này vào SQL để chúng có thể được gửi đến người dùng trang web của chúng tôi.
Trước tiên, chúng tôi phải tự động hóa quy trình này, vì sử dụng công cụ thám hiểm dành cho nhà phát triển chỉ để gỡ lỗi và sẽ không bền vững để chúng tôi tích hợp vào quy trình nhân viên đám mây của mình.
HttpClient
Api FQL do Facebook cung cấp đáp ứng các yêu cầu http mà chúng tôi sẽ cần để xây dựng thủ công. Chúng tôi cũng sẽ cần một ứng dụng khách để gửi các yêu cầu này, đó là nơi mà HTTPClient phù hợp. HttpClient được giới thiệu trong .net Framework 4.5 và hoàn toàn phù hợp để thực hiện các yêu cầu HTTP và lưu trữ kết quả.
Ẩn mã sao chép
public event Action<RawEventProcessingResult> ProcessedEvent;
public async Task MakeFQLCall(string fql, string authToken)
{
//build URL
string url = "https://graph.facebook.com/fql?q=" + fql + "&format=json&access_token="
+ authToken;
//create client and call FQL
HttpClient client = new HttpClient();
var response = await client.GetAsync(url);
//get the response
var text = await response.Content.ReadAsStringAsync();
//deserialize the JSON into POCO objects
var results = JsonConvert.DeserializeObject<RawEventProcessingResult>(text);
//Signal to queue reader that we have extracted the event
ProcessedEvent(result);
}
RawEventProcessingResult là lớp giữ cho kết quả json. Nó được cấu trúc theo cách chính xác giống như cách trả về đối tượng Json để chúng ta có thể sử dụng lớp JsonConvert để giải tuần tự json vào đối tượng.
Sau đây là hệ thống phân cấp đối tượng cho kết quả Json:

Đây không phải là định dạng mà chúng tôi muốn lưu trữ các sự kiện của chúng tôi để một số lớp trực quan hơn đã được tạo để giữ thông tin sự kiện. Được xây dựng với tương lai, lớp cơ sở LiveEvent đã được tạo ra (bất khả tri thông tin của Facebook) với một FacebookLiveEvent chứa dữ liệu cụ thể của Facebook.

Sau đây là mã quy trình worker để xử lý eids và lưu trữ các sự kiện được trả về:
Ẩn Shrink
Sao chép Mã
Sao chép Mãpublic class EventProcessingQueueSession : QueueReaderSessionBase
{
private readonly ApiKeySession _apiSession;
private readonly ImportManagementContext _dbContext;
private LiveEventsContext _liveContext;
private List<EventCategory> _categories;
private bool _continue = true;
private EventProcessingSession _importSession;
private LiveEventsContext _liveEventsContext = new LiveEventsContext();
public EventProcessingQueueSession(ApiKeySession apiSession, ImportManagementContext dbContext,
string queueUrl,
string queueName)
: base(queueUrl, queueName)
{
_apiSession = apiSession;
_dbContext = dbContext;
_liveContext = new LiveEventsContext();
_categories = _liveContext.Categories.ToList();
}
public override async Task StartAsync()
{
try
{
while (_continue)
{
//retrieve block of eids in a comma seperated format
var messages = this.DeQueueMessages(1).ToList();
var idsToParse = this.GetCommaSeperated(messages);
if (idsToParse.Any())
{
//start an importing process to return event information from
//the Facebook FQL
_importSession = new EventProcessingSession(_liveContext,
_categories,
_apiSession.ApiKey.Key,
idsToParse.ToList());
_importSession.LiveEventProcessed += newEvent =>
{
//we have process a facebook event, store into the live database
FacebookLiveEvent fbEvent = newEvent;
_liveEventsContext.Events.Add(fbEvent);
//add the newly added live event ID so it can be categorised
this.AddToOtherQueue("categorisationQueue", newEvent.Id);
};
_importSession.ApiKeyBlocked += () =>
{
//the api key we are using for facebook has been temporary blocked due to
//throttling.this won't happen very often as we buffer the calls as to
// not strain the server, but handle anyway.
_continue = false;
this.NotifyApiBlocked();
};
await _importSession.StartAsync();
//delete the messages as we have processed them
this.DeleteMessages(messages);
_liveContext.SaveChanges();
Thread.Sleep(2000);
}
else
{
_continue = false;
}
}
}
catch (Exception queueException)
{
//an exception has been thrown, save details to the database so it can be investigated
ImportException exception = new ImportException();
exception.ExceptionString = "Event Processing Queue Exception : "
+ queueException.Message;
exception.ExceptionStackTrace = queueException.StackTrace;
var rawExceptionContext = new ImportManagementContext();
rawExceptionContext.ImportExceptions.Add(exception);
rawExceptionContext.SaveChanges();
}
}
}
Tại thời điểm này, các sự kiện đã được thêm vào cơ sở dữ liệu, nhưng chúng tôi vẫn còn thiếu một yêu cầu cuối cùng. Phân loại sự kiện.
Quy trình công nhân C - Phân loại sự kiện
Có rất nhiều sự kiện âm nhạc trên Facebook. Tin tôi đi Các nhà quảng bá câu lạc bộ và cuộc sống về đêm là một sự hiện diện thống trị khi họ cố gắng tiếp cận mọi sinh viên hoặc thanh niên tham dự đêm giải trí của họ. Tuyệt vời cho người dùng sinh viên trẻ tuổi của CityPulse - nhưng những người dùng khác thì sao? Họ có thể không quan tâm đến bất kỳ cuộc sống về đêm nào, nhưng sẽ sử dụng rất nhiều công cụ khám phá các lễ hội hoặc sự kiện cộng đồng vào cuối tuần này trong khu vực của họ.
Đây là nơi phân loại sự kiện phù hợp và là một yêu cầu nhập khẩu của ứng dụng nhưng có các biến chứng. Facebook không phân loại các sự kiện theo cách này có nghĩa là chúng ta phải tiếp cận quá trình phân loại theo cách thủ công hơn.
Một thành phần trí tuệ nhân tạo tùy chỉnh đã được tạo ra để phân tích một sự kiện để đưa ra chỉ dẫn về loại nào nó có thể phù hợp.
Các hạng mục sự kiện
- Âm nhạc & Cuộc sống về đêm
- Phim hài
- Nhà hát & Nghệ thuật
- Lễ hội
- Gia đình & Cộng đồng
- Thể thao
- Chương trình và hội chợ
- Xã hội & Gặp gỡ
- Cá nhân (ẩn)
Đây là những danh mục mà người dùng trang web sẽ có thể lọc các sự kiện. Một số sự kiện được phân loại thành cá nhân (bash sinh nhật lần thứ 21 của Mike, v.v.) được ẩn khỏi trang web.
Trí tuệ nhân tạo phù hợp ở đâu
Một cách tiếp cận đơn giản để phân loại có thể là lập danh sách các từ khóa sẽ được áp dụng cho từng danh mục. Âm nhạc & Cuộc sống về đêm có thể có 'âm nhạc', 'đồ uống', 'dub-step' và 'reggae', trong khi Thể thao có thể có 'trận đấu', 'cao độ' và 'khởi động'.
Vấn đề với cách tiếp cận là nó không toàn diện. Một lễ hội có thể có nhiều từ khóa liên quan đến âm nhạc, nhưng điều đó không làm cho nó trở thành một sự kiện Âm nhạc & Cuộc sống về đêm. Một hộp đêm có thể có "sinh nhật" liên quan đến năm địa điểm được tạo ra, nhưng điều đó cũng không có nghĩa là nó nên được lọc vào Cá nhân và ẩn khỏi kết quả trang web.
Cách tiếp cận chúng tôi đã chọn (như với nhiều thuật toán trí tuệ nhân tạo) là giữ trong số các dòng tương tự để duy trì danh sách từ khóa nhưng để giới thiệu một tập dữ liệu huấn luyện và một tập dữ liệu thực. Chúng tôi xây dựng tập dữ liệu huấn luyện theo cách thủ công, sau đó để thuật toán tiếp quản và sử dụng logic mờ để đoán các sự kiện cho tập dữ liệu 'thực', cập nhật từ điển từ khóa khi chúng tiến triển.
Bộ dữ liệu đào tạo
Xây dựng tập dữ liệu đào tạo là quy trình thủ công. Quản trị viên CityPulse sẽ sử dụng công cụ quản lý nhập để phân loại thủ công các sự kiện rõ ràng thành các danh mục tương ứng để xây dựng kiến thức máy tính của từng danh mục.

Sau khoảng 50 sự kiện đã được thêm vào mỗi danh mục, chúng tôi có đủ thông tin để tiến hành tập dữ liệu thực. Mỗi danh mục sau đó được gán một từ điển các từ khóa thường xảy ra nhất trong các sự kiện được gán cho nó.
Chúng tôi có thể truy vấn các sự kiện xảy ra hàng đầu cho mỗi danh mục bằng cách sử dụng mã sau:
Ẩn Shrink
Sao chép Mã
Sao chép MãDictionary<string, int> result = new Dictionary<string, int>();
//get category from live database
EventCategory liveCategory = new LiveEventsContext().Categories.Single(x => x.Name.Contains("Music"));
//use the import management database
using (var _dbContext = new ImportManagementContext())
{
//get a list of common words so they can be excluded from extraction
List<CommonWord> excluded = _dbContext.CommonWords.ToList();
//for every event in category, build up a list of occuring words
foreach (var anEvent in liveCategory.Events)
{
var extracted = KeywordExtractor.Extract(new[]
{
anEvent.Name,
anEvent.Description
}, excluded);
//add single extracted word list to 'result' list
foreach (var extract in extracted)
{
if (!result.ContainsKey(extract.Key))
{
result[extract.Key] = 0;
}
result[extract.Key]++;
}
}
}
foreach (var topKeyWord in result.OrderByDescending(x => x.Value))
{
Debug.WriteLine("Keyword {0} Occurs {1}", topKeyWord.Key, topKeyWord.Value);
}
Điều này dẫn đến kết quả đầu ra:
Ẩn mã sao chép
Keyword band Occurs 8
Keyword music Occurs 7
Keyword sound Occurs 4
Keyword incredible Occurs 4
Keyword scene Occurs 4
Keyword debut Occurs 4
Keyword pub Occurs 3
Keyword feel Occurs 3
Keyword real Occurs 3
Keyword steve Occurs 3
Keyword folk Occurs 3
Keyword songs Occurs 3
Không tệ, đây chắc chắn là những từ khóa liên quan đến Âm nhạc & Cuộc sống về đêm. Chúng ta có thể làm điều này tốt hơn bằng cách gán thủ công nhiều sự kiện hơn cho danh mục.
Bạn cũng có thể nhận thấy dòng mã
Ẩn mã sao chép
var excluded = _dbContext.CommonWords.ToList();
Commonemme là một bảng trong cơ sở dữ liệu chứa các từ phổ biến như 'the' và 'as'. Chúng được loại trừ khỏi quá trình trích xuất từ khóa vì chúng không cho chúng tôi biết bất cứ điều gì về sự kiện này. Chúng được tạo bởi một mô hình lai của việc thêm thủ công các thuật ngữ và trích xuất các từ xảy ra trong TẤT CẢ các sự kiện.
Chúng ta hãy xem danh mục 'Gia đình & Cộng đồng'. Tất cả chúng ta cần thay đổi là:
Ẩn mã sao chép
var liveCategory = new LiveEventsContext().Categories.Single(x => x.Name.Contains("Family"));
Điều này dẫn đến kết quả đầu ra:
Ẩn mã sao chép
Keyword charity Occurs 64
Keyword raise Occurs 26
Keyword good Occurs 13
Keyword aid Occurs 12
Keyword raffle Occurs 12
Keyword family Occurs 11
Keyword cancer Occurs 11
Keyword show Occurs 10
Keyword music Occurs 10
Keyword band Occurs 8
Keyword entertainment Occurs 7
Keyword inn Occurs 7
Keyword holding Occurs 7
Keyword funds Occurs 7
Keyword team Occurs 7
Có lẽ hơi quá nặng nề đối với các sự kiện từ thiện, nhưng chúng ta có thể khắc phục điều đó sau đó bằng cách thêm vào các mục thủ công từ các loại sự kiện cộng đồng khác.
Tập dữ liệu 'Real'
Đây là nơi thuật toán khởi động và chúng tôi để tự động hóa tiếp quản. Chúng tôi sử dụng từ điển tập hợp các từ khóa để cho phép máy tính đưa ra phán đoán tốt nhất về nơi một sự kiện có thể phù hợp. Nếu nó không thể xác định đúng danh mục mà nó di chuyển, nếu có thể, nó sẽ gán sự kiện cho đúng danh mục và cập nhật từ điển với các từ khóa sự kiện đó.
Do đó, khi chúng tôi tiếp tục quá trình phân loại, sự phù hợp sẽ trở nên tốt hơn theo thời gian.
PseudoCode cho thuật toán là như sau:
Ẩn mã sao chép
For 1.. n events extract event keywords compare against each categories keyword dictionary if 1 or more categories keywords match over a certain threshold value add event and update category keywords else Add event to ‘social & meetups’ continue
Lưu ý rằng nếu một sự kiện không thể được phân loại, nó sẽ được thêm vào danh mục 'Xã hội'. Điều này sẽ đảm bảo rằng các sự kiện vẫn được hiển thị trên bản đồ trong trang web.
Mã quy trình công nhân phân loại
Ẩn Shrink
Sao chép Mã
Sao chép Mãpublic class CategorisationQueueSession : QueueReaderSessionBase
{
private bool _continue = true;
private readonly LiveEventsContext _liveEventContext = new LiveEventsContext();
public CategorisationQueueSession(string queueUrl, string queueName)
: base(queueUrl, queueName)
{
}
public override async Task StartAsync()
{
while (_continue)
{
//get the live database event block
IEnumerable<CloudQueueMessage> messages = this.DeQueueMessages(5);
var liveEventIds = this.GetCommaSeperated(messages);
//grab the actual events in the message from the live database
List<FacebookLiveEvent> liveEvents = new List<FacebookLiveEvent>();
foreach (var liveEventId in liveEventIds.Select(x=>int.Parse(x)))
{
liveEvents.Add(_liveEventContext.Events.OfType<FacebookLiveEvent>()
.SingleOrDefault(x=>x.Id == liveEventId));
}
//run the categorisation session, start by injecting the non hidden categories
var nonHiddenCategories = _liveEventContext.Categories.Where(x=>x.Name != "Personal");
var runCategorisationSession = new CategorisationSession(nonHiddenCategories);
runCategorisationSession.Categorised += (categories, liveEvent) =>
{
//we have categorised an event, update the database row
foreach (var matchedCategory in categories)
{
liveEvent.Categories.Add(matchedCategory);
}
};
//start the process
await runCategorisationSession.StartCategorisationsAsync(new ImportManagementContext(),
liveEvents);
//categorisation ended for these events, update the database
_liveEventContext.SaveChanges();
}
}
}
Các kết quả

Thuật toán dường như đã thực hiện một công việc tốt trong phân loại. Điều đặc biệt thú vị là nó đang xử lý sự kiện này - 'Dagenham Dog Soldiers Bike and Band Night'. Nó đã được phân loại thành 'Gia đình và Cộng đồng' và 'Âm nhạc & Cuộc sống về đêm' khi xem xét nó có các ban nhạc nhưng chủ yếu là một sự kiện từ thiện.
Tóm tắt
Vì vậy, đó là cho quá trình nhập Azure. Tóm lại, chúng tôi có:
- Thu thập dữ liệu facebook cho id sự kiện
- Đã thêm id sự kiện vào hàng xử lý EID
- Đã xử lý các eids sử dụng api FQL của Facebook thành các sự kiện có thật
- Lưu trữ các sự kiện vào cơ sở dữ liệu Sự kiện trực tiếp
- Đã thêm id cơ sở dữ liệu vào hàng đợi phân loại
- Phân loại các sự kiện bằng thuật toán trí tuệ nhân tạo tùy chỉnh
Bộ lưu trữ
Mã khung thực thể đầu tiên
Tóm tắt sự phức tạp của việc duy trì Lớp truy cập dữ liệu liên tục có sức hấp dẫn khủng khiếp đối với bất kỳ nhà phát triển nào vì chúng ta có thể dành nhiều thời gian hơn để viết các ứng dụng của mình hơn là viết mã soạn sẵn bao quanh bất kỳ hoạt động CRUD tiêu chuẩn nào. Các cơ sở dữ liệu trong giải pháp CityPulse đã được tạo từ các mô hình mã đầu tiên của Entity Framework (EF). Các mã đầu tiên biến thể của EF cho phép chúng tôi để mô hình hóa các thực thể và các mối quan hệ của họ sử dụng Plain Old C # Objects (POCO đối tượng). Tại thời điểm chạy, thư viện EF sẽ xây dựng mô hình và nếu cần thiết, sẽ tạo cơ sở dữ liệu cho chúng tôi. Sức mạnh mà điều này mang lại là với tư cách là một nhà phát triển, chúng tôi không bao giờ thực sự cần phải rời khỏi thế giới .net.
Linq-to-Entities cho phép tương tác với cơ sở dữ liệu mà không cần viết SQL, một lần nữa làm giảm kích thước và độ phức tạp của Lớp truy cập dữ liệu.
Đi sâu hơn vào Mã khung thực thể Đầu tiên nằm ngoài phạm vi của bài viết này, nhưng nếu bạn chưa quen với khái niệm này, bài viết chắc chắn đáng để đọc
Theo sơ đồ kiến trúc chính, chúng tôi có hai cơ sở dữ liệu chính sẽ được sử dụng để lưu trữ thông tin trên đám mây:
Cơ sở dữ liệu quản lý nhập khẩu
Cơ sở dữ liệu này là dành cho tất cả mọi thứ nhập khẩu liên quan. Người dùng cuối sẽ không bao giờ thấy cơ sở dữ liệu này vì nó được sử dụng bởi Công cụ quản lý nhập khẩu WPF cùng với các quy trình công nhân trong Azure. Các URL được thu thập tồn tại ở đây, cũng như mọi thống kê, khóa api, báo cáo, ngoại lệ và ghi nhật ký để có thể sử dụng nó để điều tra bất kỳ biến chứng nào có thể xảy ra trong quá trình nhập. Các từ cụ thể hoặc phổ biến tồn tại ở đây để hỗ trợ thuật toán phân loại trong Quy trình công nhân C.

Cơ sở dữ liệu sự kiện trực tiếp

Cơ sở dữ liệu Live Events được sử dụng chủ yếu bởi trang web. Nó tổ chức các sự kiện trực tiếp, cùng với bất kỳ danh mục nào các sự kiện tồn tại bên trong.
Thế hệ đầu tiên và cập nhật mã trong Azure
Mã đầu tiên trong Azure rất dễ dàng và không yêu cầu bất kỳ biến thể nào từ việc sử dụng thông thường bên ngoài Azure. Khi các chuỗi kết nối được thêm vào ứng dụng / web.config, ứng dụng sẽ bắt đầu tương tác với cơ sở dữ liệu. Mã di chuyển đầu tiên cũng được xử lý tương tự, chỉ cần sử dụng Trình quản lý gói Visual Studio tích hợp :
Ẩn mã sao chép
PM > Enable-Migrations
Lệnh này tạo một tệp cấu hình mà chúng ta có thể sử dụng để kiểm soát các trường hợp sử dụng cụ thể khi triển khai di chuyển. Chúng ta có thể sử dụng nó cho dữ liệu hạt giống hoặc sử dụng Fluent Api để mô tả các mối quan hệ tồn tại trong mô hình của chúng ta.
Sau đây là một yêu cầu sử dụng di chuyển Code-First để thêm khả năng lưu trữ các mục 'Phản hồi' trong cơ sở dữ liệu của chúng tôi, khi người dùng muốn gửi ý kiến của mình cho người tạo trang web
Đây là những gì cơ sở dữ liệu trông giống như trước khi di chuyển:

Và đây là trạng thái của mô hình mã:
Ẩn mã sao chép
public class LiveEventsContext : DbContext
{
public DbSet<LiveEvent> Events { get; set; }
public DbSet<EventCategory> Categories { get; set; }
}
Chúng tôi sẽ bắt đầu bằng cách tạo một thực thể phản hồi dưới dạng POCO:
Ẩn mã sao chép
public class SiteFeedback
{
public int Id { get; set; }
public DateTime Created { get; set; }
public string Description { get; set; }
public string Email { get; set; }
}
Tạo một thuộc tính có tên Id cho phép Entity Framework phát hiện ra rằng đó là khóa chính khi mô hình được dịch sang bảng SQL. Chúng ta có thể đã sử dụng thuộc tính [Khóa] trên thuộc tính để nói rõ điều này, nhưng EF đủ thông minh để biết điều đó và trường Id là khóa.
Hãy thêm nó vào mô hình:
Ẩn mã sao chép
public class LiveEventsContext : DbContext
{
public DbSet<LiveEvent> Events { get; set; }
public DbSet<EventCategory> Categories { get; set; }
public DbSet<SiteFeedback> Feedback { get; set; }
}
Bằng cách tạo bộ sưu tập <T> Dbset và nhập nó vào thực thể mới của chúng tôi, chúng tôi đang biểu thị ý định của chúng tôi để ánh xạ loại thực thể này vào bảng cơ sở dữ liệu.
Bây giờ chúng ta có thể tạo tệp di chuyển để chạy với cơ sở dữ liệu, chỉ cần nhập lệnh sau vào Trình quản lý bảng điều khiển gói:
Ẩn mã sao chép
Add-Migration addFeedbackMigration
Điều này sẽ tự động tạo một tệp di chuyển và mở nó trong Visual Studio.
Ẩn mã sao chép
public partial class addFeedback : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.SiteFeedbacks",
c => new
{
Id = c.Int(nullable: false, identity: true),
Created = c.DateTime(nullable: false),
Description = c.String(),
Email = c.String(),
})
.PrimaryKey(t => t.Id);
}
public override void Down()
{
DropTable("dbo.SiteFeedbacks");
}
}
Bây giờ đang chạy lệnh trong Trình quản lý bảng điều khiển gói:
Ẩn mã sao chép
PM > Update-Database
kết quả trong bản cập nhật cơ sở dữ liệu dự định của chúng tôi:

Trang web
Trang web này là cách người dùng CityPulse sẽ tương tác với ứng dụng. Trước khi họ kết nối quy trình nhập Azure sẽ thực hiện công việc của mình và chúng tôi sẽ có vô số các sự kiện được phân loại có sẵn để hiển thị trên bản đồ. Bản đồ là yếu tố 'công khai' của giải pháp và là thứ mang tất cả lại với nhau. Hãy xem nó hoạt động như thế nào.
Công nghệ
Đối với trang web, chúng tôi đã chọn một ASP.net MVC4 để cung cấp HTML cho các trình duyệt và Api Web để phục vụ các sự kiện ở định dạng json. Để có trải nghiệm người dùng tốt nhất có thể, trang web chủ yếu hoạt động như một Ứng dụng Trang đơn và như vậy phụ thuộc khá nhiều vào javascript để hiển thị giao diện người dùng.
Phục vụ các sự kiện - Api Web
API Web ASP.net cung cấp một cách nhanh chóng và dễ dàng để cung cấp các dịch vụ web RESTful. Trong khi thuật ngữ REST (Chuyển giao trạng thái đại diện) mở rộng một số nguyên tắc - điều này có nghĩa với chúng tôi là khả năng tiếp cận tốt. Không có sự phụ thuộc cụ thể của nền tảng trong việc triển khai Web Api, vì vậy việc sử dụng công nghệ của chúng tôi có thể được truy cập từ điện thoại di động và máy tính để bàn, xa và rộng.
Nhược điểm của điều này là không có việc tạo mã phía máy khách cho các Đối tượng truyền dữ liệu (như trong các công nghệ như WCF) nhưng chúng tôi nghĩ rằng đây là một sự đánh đổi chấp nhận được. Web Api cũng không trạng thái, cho phép chúng tôi mở rộng quy mô dễ dàng đến mức tải cao hơn.
Nhược điểm của điều này là không có việc tạo mã phía máy khách cho các Đối tượng truyền dữ liệu (như trong các công nghệ như WCF) nhưng chúng tôi nghĩ rằng đây là một sự đánh đổi chấp nhận được. Web Api cũng không trạng thái, cho phép chúng tôi mở rộng quy mô dễ dàng đến mức tải cao hơn.
Chúng ta hãy xem làm thế nào để viết Trình điều khiển Api sẽ phục vụ các sự kiện của chúng ta.
Trình điều khiển API sự kiện
Chúng ta có thể bắt đầu bằng cách thêm bộ điều khiển, tạo kết nối đến cơ sở dữ liệu trực tiếp thông qua bối cảnh Khung thực thể cơ sở dữ liệu đầu tiên, chúng ta sẽ gọi đây là LiveEventsEntities . Điều đáng chú ý là điều này đã được tạo trong Bộ điều khiển / api / EventsContoder. / Api / part không cần thiết, nhưng nó giúp giữ nó tách biệt khỏi các bộ điều khiển MVC thông thường nếu bạn muốn chạy trong một trang web.
Như một yêu cầu cơ bản, chúng tôi sẽ muốn cung cấp thông tin cho một sự kiện cụ thể, bằng cách cung cấp Id của sự kiện được yêu cầu
Ẩn Shrink
Sao chép Mã
Sao chép Mãpublic class EventsController : ApiController
{
/// <summary>
/// Queryable storage of the live event table.
/// - IQueryable as to not load the entire table into memory in the constructor
/// </summary>
private IQueryable<LiveEvent> _liveEvents;
public EventsController()
{
//create an instance of the database context
var liveEventContext = new LiveEventsEntities();
//The WebApi doesn't like to serialize dynamic proxy objects, turn off lazy loading
liveEventContext.Configuration.LazyLoadingEnabled = false;
liveEventContext.Configuration.ProxyCreationEnabled = false;
//lazy loading is disabled so manually load in the categories
//so they get delivered in the results too
_liveEvents = liveEventContext.LiveEvents.Include("EventCategories");
}
/// <summary>
/// Retrieve an event by its Id
/// </summary>
public HttpResponseMessage Get(int id)
{
var foundEvent = _liveEvents.SingleOrDefault(x => x.Id == id);
if (foundEvent == null)
{
return Request.CreateErrorResponse(
HttpStatusCode.NotFound,
String.Format("Event {0} wasn't found", id);
}
return Request.CreateResponse(HttpStatusCode.OK, foundEvent); ;
}
}
Các HttpResponseMessage Nhận (int id) là chìa khóa ở đây, và là những gì các trang web sẽ sử dụng để lấy thông tin sự kiện. công cụ định tuyến đảm nhiệm việc truy cập vào phương thức này sẽ được thực hiện thông qua yêu cầu http đến / api / Event / {event id}
Cũng có một vài điều cần lưu ý ở đây, trước tiên là việc xử lý các đối tượng proxy động. Để bật tính năng tuần tự hóa json, phải tắt proxy động (cho phép tải nhanh trong EF) nếu các thực thể EF đang được sử dụng làm đối tượng trả về hoặc được nhúng trong một HTTPResponseMessage.
Thứ hai, chính httpResponseMessage - điều này không thực sự cần thiết vì chúng tôi có thể trả lại LiveEvent , nhưng nó được coi là cách thực hành tốt nhất vì sau đó chúng tôi có thể tận dụng httpStatusCode và nắm lấy http theo cách nó được thiết kế để sử dụng.
Thứ hai, chính httpResponseMessage - điều này không thực sự cần thiết vì chúng tôi có thể trả lại LiveEvent , nhưng nó được coi là cách thực hành tốt nhất vì sau đó chúng tôi có thể tận dụng httpStatusCode và nắm lấy http theo cách nó được thiết kế để sử dụng.
Cuối cùng, đoạn mã trên sẽ trả về xml theo mặc định. Để đảm bảo rằng chỉ có json được trả về, hãy thêm đoạn mã sau vào lớp WebApiConfig.cs .
Ẩn mã sao chép
//ensure only json is returned from the Api
var appXmlType = config.Formatters.XmlFormatter.SupportedMediaTypes.FirstOrDefault(t =>
t.MediaType == "application/xml");
config.Formatters.XmlFormatter.SupportedMediaTypes.Remove(appXmlType);
Bằng cách chạy trang web, giờ đây chúng tôi có thể truy vấn API để đảm bảo nó đang trả về thông tin sự kiện. Fiddler là một công cụ gỡ lỗi web tuyệt vời (fiddler2.com) mà chúng ta có thể sử dụng để xem kết quả
Một yêu cầu của
Ẩn mã sao chép
http://localhost:8671/api/events/3
kết quả trong một câu trả lời json của
Ẩn Shrink
Sao chép Mã
Sao chép Mã{
"Id": 3,
"Name": "Hip Route",
"Description": ""Hip Route have a universal sound that should appeal to all lovers of real music",
"ImageNormal": "https://profile-a.xx.fbcdn.net/hprofile-ash4/261164_136561216508726_1794165337_s.jpg",
"ImageSmall": "https://profile-a.xx.fbcdn.net/hprofile-ash4/261164_136561216508726_1794165337_t.jpg",
"ImageCoverUrl": null,
"Start": "2013-12-21T21:00:00",
"End": null,
"Updated": null,
"Longitude": -1.74417424,
"Latitude": 51.54513,
"Street": null,
"City": null,
"State": null,
"Country": null,
"Zip": null,
"Created": "2013-06-09T15:16:27.57",
"IsHidden": false,
"FacebookId": "136561216508726",
"AttendingCount": 7,
"InvitedCount": 399,
"FacebookPrivicyType": 1,
"Discriminator": "FacebookLiveEvent",
"EventCategories": [{
"Id": 1,
"Name": "Music & Nightlife"
}]
}
Tuyệt vời, nó hoạt động tốt. Mặc dù Yêu cầu tiếp theo phức tạp hơn một chút:
khi chúng tôi cung cấp một số tọa độ địa lý, ngày bắt đầu và ngày kết thúc, chúng tôi muốn trả về tất cả các sự kiện phù hợp với tiêu chí đó
Các vị trí địa lý có thể được mô hình hóa bằng cách lấy giới hạn của bản đồ người dùng, được đo bằng Vĩ độ và Kinh độ.
Ngày bắt đầu và ngày kết thúc cho phép chúng tôi cung cấp bộ lọc về các sự kiện, vì vậy người dùng có thể xem những sự kiện nào đang diễn ra hôm nay hoặc có lẽ vào cuối tuần này.
Ngày bắt đầu và ngày kết thúc cho phép chúng tôi cung cấp bộ lọc về các sự kiện, vì vậy người dùng có thể xem những sự kiện nào đang diễn ra hôm nay hoặc có lẽ vào cuối tuần này.
Mã này trông như sau:
Ẩn mã sao chép
/// <summary>
/// Retrieve an event by specifying geocoordinates
/// </summary>
[HttpGet]
public HttpResponseMessage Get(DateTime startTime, DateTime startEndTime, float neLat, float neLong, float swLat, float swLong)
{
try
{
var foundEvents = _liveEvents.Where(liveEvent =>
liveEvent.Start >= startTime &&
liveEvent.End <= startEndTime &&
liveEvent.Longitude >= swLong && liveEvent.Longitude <= neLong &&
liveEvent.Latitude >= swLat && liveEvent.Latitude <= neLat);
return Request.CreateResponse(HttpStatusCode.OK, foundEvents);
}
catch (Exception e)
{
return Request.CreateErrorResponse(HttpStatusCode.NotFound, e);
}
}
Lần này yêu cầu cần thêm một vài tham số, nhưng nó vẫn có thể được xử lý theo cùng một tuyến đường được xác định. Bây giờ bằng cách yêu cầu:
Ẩn mã sao chép
//Area in London on the week starting 16th June Events? startTime=2013-06-16T23:00:00.000Z& startEndTime=%202013-06-23T22:59:59.000Z& neLat=51.5195& neLong=-0.143411& swLat=51.506520& swLong=-0.184609
chúng tôi nhận được phản hồi từ:

Api đã trả về một danh sách một số sự kiện phù hợp với vị trí đó, Tuyệt vời! Nhưng để sử dụng API mới này, trước tiên chúng ta sẽ phải trình bày một số UI cho người dùng trên máy khách:
Đọc thêm
Google Maps phía khách hàng với Bản in
Để hiển thị các sự kiện mới có thể truy xuất trên trình duyệt người dùng, chúng tôi cần hoàn thành các tác vụ sau:
- 1. Tải vào bản đồ google bằng API của họ
- 2. Đáp ứng tương tác của người dùng với bản đồ (pan người dùng, thu phóng người dùng)
- 3. Nhận giới hạn bản đồ hiện tại bằng Lat / Long và truy vấn API bằng các cuộc gọi được trình bày ở trên
- 4. Hủy nối tiếp phản hồi json thành các sự kiện và thêm chúng làm điểm đánh dấu vào bản đồ
Để đạt được điều này, chúng tôi sẽ sử dụng một hỗn hợp lành mạnh của Typecript, Html & Javascript.
Bản đánh máy
Bản mô tả là sản phẩm trí tuệ của Microsoft và mở rộng javascript bằng cách cung cấp tùy chọn gõ tĩnh và lập trình hướng đối tượng dựa trên lớp. Điều này có nghĩa là với tư cách là một nhà phát triển, chúng tôi nhận được một số hỗ trợ intellisense đáng yêu trong Visual Studio do các định nghĩa loại được cung cấp cho hầu hết các thư viện javascript được sử dụng phổ biến. Điều đó có nghĩa là thời gian tìm kiếm API trên các trang web của bên thứ ba ít hơn và mã hóa thời gian nhiều hơn. Ngoài ra, từ góc độ bảo trì, việc sử dụng các không gian tên sẽ dễ dàng hơn nhiều, nó bổ sung cho một giải pháp dựa nhiều vào javascript như CityPulse.

Đối với những người vẫn yêu thích javascript của họ và muốn tiếp tục khám phá bản chất năng động của ngôn ngữ có thể vui mừng: Javascript có thể được phát triển hài hòa với bản thảo khi cả hai đều biên dịch thành javascript dưới mui xe. Điều này mang lại cho chúng ta những lợi ích của việc gõ tĩnh, nhưng chỉ khi chúng ta muốn nó.
Hiển thị bản đồ google
Đầu tiên chúng ta tạo một ứng dụng Asp.net MVC4 tiêu chuẩn trong Visual Studio. Đảm bảo rằng chúng tôi đã cài đặt plugin bản thảo, hãy bắt đầu bằng cách tạo mapManager . Tóm tắt mã thành các mô-đun, thay vì có mọi thứ ở trang dưới cùng thường là một ý tưởng hay - đặc biệt là trong một ứng dụng web có khả năng sử dụng nhiều loại / javascript. Bản mô tả làm cho điều này đặc biệt dễ dàng, vì các lớp được chuyển đổi thành một mẫu thiết kế javascript thường được sử dụng - một mô-đun tiết lộ.
Bắt đầu bằng cách tạo một tệp bản thảo, trong Tập lệnh / lib / mapManager.ts.
Ẩn Shrink
Sao chép Mã
Sao chép Mã//Make sure we have the google typings referenced or we wont get intellisense /// <reference path="../../typings/google.maps.d.ts" /> module My { export class mapManager { _map: google.maps.Map; constructor () { } public init() { //standard options, center the map over the world by default var mapOptions = { center: new google.maps.LatLng(30.23804, -70), zoom: 3, panControl: false, streetViewControl: false, maxZoom : 18, minZoom : 2, mapTypeControl: false, zoomControlOptions: { position: google.maps.ControlPosition.RIGHT_CENTER }, mapTypeId: google.maps.MapTypeId.ROADMAP }; //create map on the canvas this._map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions); } } }
Lưu ý tham chiếu đến các định nghĩa loại google maps. Vì google maps là một triển khai javascript, nên các định nghĩa kiểu tĩnh phải được tô điểm cho thư viện. Bạn có thể tải chúng xuống bằng cách sử dụng trình quản lý gói Nuget trong Visual Studio
Ẩn mã sao chép
PM> Install-Package google.maps.d.ts
Tiếp theo chúng ta có thể tạo phần tử đồ thị html và khởi tạo trình quản lý.
Khi ứng dụng MVC4 được thiết lập, Visual Studio thường tạo chế độ xem Home / Index.html cho chúng tôi. Xóa nội dung tiêu chuẩn trong đó và thêm:
Ẩn mã sao chép
<div id="map_canvas" style="width: 100%; height:100%;"></div> <script type="text/javascript"> $(document).ready(function () { //initialise the map manager var mapManager = new My.mapManager(); mapManager.init(); }); </script>
Hàm jQuery này đã sẵn sàng chờ cho đến khi trang được tải, định vị mapManager được tạo bằng javascript xuất phát từ tệp bản thảo của chúng tôi và khởi chạy nó.
Cuối cùng, chúng tôi đảm bảo tất cả các tập lệnh đúng được tham chiếu trong tệp Layout.cshtml:
Ẩn mã sao chép
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=true"></script> <script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?libraries=places&sensor=true"></script> <script src="~/Scripts/lib/mapManager.js"></script>
Một trong những lợi ích của việc sử dụng MVC4 là khả năng đóng gói các tập lệnh và thu nhỏ chúng. Ví dụ trên đã không thực hiện điều này cho ngắn gọn, nhưng hành vi bó và thu nhỏ để làm mờ mã và cải thiện thời gian tải trình duyệt. xem ASP.NET MVC4: Gói và thu nhỏ
Lưu ý rằng mapManager đang tham chiếu tệp mapManager.js , đây là javascript được biên dịch mà bản thảo được chuyển đổi thành lưu.

Được! Chúng tôi có một bản đồ làm việc!
Hãy di chuyển và móc một số sự kiện trên bản đồ để chúng tôi có thể chuyển thông tin đến Api của chúng tôi khi người dùng nhập liệu:
Ẩn Shrink
Sao chép Mã
Sao chép Mãpublic init() {
var self = this;
//(code suppressed) Create map
google.maps.event.addListener(this._map, 'zoom_changed', () => {
self.loadEvents();
});
google.maps.event.addListener(this._map, 'mouseup', () => {
self.loadEvents();
});
}
public loadEvents() {
//get bounds of map viewport
var bounds = this._map.getBounds();
//get events for the next week
var today = new Date();
var weekFromNow = new Date();
weekFromNow.setDate(today.getDate() + 7);
//create url request to Api
var jsonRequest =
"/api/Event?" +
"startTime=" + today.toISOString() +
"&startEndTime= " + weekFromNow.toISOString() +
"&neLat=" + bounds.getNorthEast().lat() +
"&neLong=" + bounds.getNorthEast().lng() +
"&swLat=" + bounds.getSouthWest().lat() +
"&swLong=" + bounds.getSouthWest().lng();
//just log to console for now
console.log(jsonRequest);
}
Chúng tôi tạo một chức năng truy xuất chế độ xem hiện tại của bản đồ và xây dựng yêu cầu API, được xây dựng trong Json và gửi đến máy chủ. Điều này giữ cho UI phản hồi nhanh vì không có postback toàn trang.
hàm toISOString () được gọi vào các đối tượng ngày trước khi chúng được gửi đến webApi. Điều này giữ cho chúng ở một định dạng có thể được đưa trực tiếp vào danh sách tham số của bộ điều khiển api
Nhấn F5 để chạy mã và di chuyển bản đồ xung quanh một chút kết quả sau đây được gửi đến cửa sổ bảng điều khiển trình duyệt:
Ẩn mã sao chép
/api/Event?startTime=2013-06-21T07:19:22.775Z&startEndTime= 2013-06-28T07:19:22.775Z&neLat=78.54&neLong=180&swLat=-49.8820&swLong=-180 /api/Event?startTime=2013-06-21T07:19:33.819Z&startEndTime= 2013-06-28T07:19:33.819Z&neLat=71.72&neLong=180&swLat=-6.38316&swLong=-180 /api/Event?startTime=2013-06-21T07:19:34.421Z&startEndTime= 2013-06-28T07:19:34.421Z&neLat=59.38&neLong=91.19&swLat=18.3684&swLong=-94.60 /api/Event?startTime=2013-06-21T07:19:34.645Z&startEndTime= 2013-06-28T07:19:34.645Z&neLat=50.64&neLong=48.037&swLat=29.7178&swLong=-44.86 /api/Event?startTime=2013-06-21T07:19:36.777Z&startEndTime= 2013-06-28T07:19:36.777Z&neLat=60.34&neLong=42.80&swLat=43.47777&swLong=-50.09 /api/Event?startTime=2013-06-21T07:19:38.185Z&startEndTime= 2013-06-28T07:19:38.185Z&neLat=57.65&neLong=19.45&swLat=49.43960&swLong=-26.99 /api/Event?startTime=2013-06-21T07:19:38.414Z&startEndTime= 2013-06-28T07:19:38.414Z&neLat=55.95&neLong=7.761&swLat=51.87276&swLong=-15.46 /api/Event?startTime=2013-06-21T07:19:38.953Z&startEndTime= 2013-06-28T07:19:38.953Z&neLat=55.07&neLong=1.916&swLat=53.032449&swLong=-9.69
Bây giờ chúng tôi chỉ cần gửi chúng đến bộ điều khiển api và đọc phản hồi - thêm vào các điểm đánh dấu trên bản đồ cho mỗi sự kiện được gửi lại cho chúng tôi:
Đang tải các sự kiện vào bản đồ dưới dạng các điểm đánh dấu
Để lấy kết quả từ WebApi và thêm bộ sưu tập các sự kiện (được biểu thị bằng json), chúng ta cần thực hiện một số thay đổi cho mã:
Ẩn Shrink
Sao chép Mã
Sao chép Mãpublic loadEvents() {
var thisObject = this;
//clear any existing markers
this.clearEvents();
//get bounds of map viewport
var bounds = this._map.getBounds();
var today = new Date();
var weekFromNow = new Date();
weekFromNow.setDate(today.getDate() + 7);
//create url request to Api
var jsonRequest =
"/api/Events?" +
"startTime=" + today.toISOString() +
"&startEndTime= " + weekFromNow.toISOString() +
"&neLat=" + bounds.getNorthEast().lat() +
"&neLong=" + bounds.getNorthEast().lng() +
"&swLat=" + bounds.getSouthWest().lat() +
"&swLong=" + bounds.getSouthWest().lng();
//make ajax request to the server
$.getJSON(jsonRequest, (eventCollection) => {
//response received, iterate events
$.each(eventCollection, (index, event) => {
//add each event as a map marker
thisObject.addEventToMap(event);
});
});
}
Thay vì xuất các yêu cầu Api ra cửa sổ giao diện điều khiển, chúng tôi đã thực hiện một yêu cầu AJAX đến máy chủ. $ .getJson () là một hàm không đồng bộ yêu cầu hàm gọi lại, trong trường hợp này lặp lại kết quả và gọi hàm addEventToMap rất riêng của chúng tôi . Chúng ta có thể thấy thành phần của kết quả máy chủ từ ảnh chụp màn hình bên dưới, được chụp từ các công cụ dành cho nhà phát triển trong trình duyệt Google Chrome.
Lưu ý trình duyệt sẽ chỉ hiển thị cho chúng tôi javascript được biên dịch từ tệp bản thảo, điều này được biểu thị từ phần mở rộng .js trên tệp được hiển thị

Cũng lưu ý rằng kích thước của mảng trả về là 100. Điều này là do chúng tôi đã giới hạn số lượng sự kiện có thể được trả về từ máy chủ. Tại thời điểm viết có hơn 100.000 sự kiện trong cơ sở dữ liệu LiveEvents của chúng tôi. Cho phép tất cả chúng được tải vào (ngay cả khi lọc theo vị trí địa lý và thời gian một tuần) có thể sẽ chặn máy chủ quá lâu để cung cấp trải nghiệm người dùng linh hoạt.
Hai chức năng cuối cùng thêm một số chức năng rất quan trọng vào mapManager của chúng tôi.
Ẩn Shrink
Sao chép Mã
Sao chép Mã//takes an event and adds it as a marker to the map
public addEventToMap(eventJson) {
var eventId = eventJson.Id;
var longitude = eventJson.Longitude;
var latitude = eventJson.Latitude;
var eventName = eventJson.Name;
//create a google LatLng representation
var eventLatLong = new google.maps.LatLng(latitude,longitude);
//create the marker containing the LatLng & the name of the event
var marker = new google.maps.Marker({
position: eventLatLong,
title: eventName
});
//add to local instance, so it can be cleared later
this._markerArray.push(marker);
//add to the map
marker.setMap(this._map);
}
//clears all events from the map
public clearEvents() {
//iterate current markers and remove from map
$.each(this._markerArray, (index, marker) => {
marker.setMap(null);
});
//clear the array
this._markerArray = [];
}
Sau đó, hàm addEventToMap chấp nhận một trong các sự kiện json làm tham số, tạo một đối tượng google.map.Marker từ các geocoord tọa độ có trong và thêm nó vào bản đồ của chúng tôi. Hàm ClearEventsđược gọi trước mỗi yêu cầu ajax để không thêm vào các sự kiện hai lần.
Và cuối cùng điều này dẫn đến một kết xuất đáng yêu của bản đồ google của chúng tôi, với tất cả các sự kiện thú vị mà chúng tôi đã nhập.

Tất nhiên cũng rất hữu ích khi biết một số nhà thiết kế trang web xuất sắc, vì vậy sau một số kiểu css / html ấn tượng và một số điều chỉnh, chúng tôi kết thúc với giải pháp hoàn thành của mình!

Điều đó hoạt động rất tốt trên máy tính để bàn, nhưng chúng ta vẫn chưa hoàn thành!
Đi di động
Với sự gia tăng nhanh chóng của các loại thiết bị và kích cỡ khác nhau có khả năng truy cập internet mang lại cho CityPulse một thách thức khác. Chúng tôi cần cung cấp cho mọi người trải nghiệm hấp dẫn, liền mạch và thú vị cho dù họ đang sử dụng thiết bị nào, từ những chiếc điện thoại cũ nhỏ bé có kết nối chuyển vùng đáng ngờ đến màn hình TV độ phân giải cao 42 "băng thông rộng siêu tốc. cách tuyệt vời để đạt được điều này mà không phải phát triển và duy trì nhiều cơ sở mã cho một nền tảng khác nhau, chúng ta có thể thực hiện điều này bằng cách sử dụng Responsive Design.
Vậy thiết kế Responsive là gì?
Tóm lại, Responsive Design cho phép chúng tôi sử dụng một trang web có bố cục linh hoạt, điều chỉnh tùy thuộc vào kích thước màn hình, chúng tôi có thể làm điều này bằng cách tận dụng các truy vấn phương tiện HTML5 & CSS3 để nhắm mục tiêu kích thước màn hình cụ thể với các kiểu CSS khác nhau. Nếu bạn chưa quen với Responsive Design, có một hướng dẫn tuyệt vời bao gồm mọi thứ chính xác bởi Shay Howe .
Nó có đơn giản không?
Vâng có và không, Responsive Design sẽ giúp chúng tôi tối ưu hóa trải nghiệm xem cho từng kích thước của thiết bị, nhưng liệu đó có phải là thiết bị màn hình cảm ứng, tốc độ kết nối chậm hay những kỳ vọng khác nhau mà người dùng có thể có trên các thiết bị khác nhau? Chúng tôi sẽ cần đưa vào một vài kỹ thuật và công nghệ khác nhau để bao quát mọi thứ. Hãy để tôi giải thích cách chúng tôi đã tạo CityPulse để thích ứng với từng kịch bản theo từng điểm:
1. Thiết bị có màn hình kích thước khác nhau
2. Thiết bị màn hình cảm ứng
3. Băng thông thay đổi / gián đoạn
4. Hành trình / mong đợi của người dùng khác nhau
2. Thiết bị màn hình cảm ứng
3. Băng thông thay đổi / gián đoạn
4. Hành trình / mong đợi của người dùng khác nhau
1. Thiết bị có màn hình kích thước khác nhau
Thiết kế đáp ứng giải quyết vô số kích thước màn hình và tỷ lệ pixel mà một ứng dụng có thể được tiếp xúc trong môi trường thế giới thực. Bố cục được tạo với chiều rộng và chiều cao dựa trên tỷ lệ phần trăm chất lỏng mở rộng và hợp đồng với các màn hình có kích thước khác nhau (bạn có thể thực hiện bằng cách truy cập CityPulselvà thay đổi kích thước trình duyệt của bạn, hãy thử xem!) Văn bản cũng thay đổi kích thước để có thể đọc được, và một số hình ảnh có kích thước thay thế để điện thoại di động sẽ không gặp phải sự khó chịu không cần thiết khi tải bất kỳ hình ảnh lớn nào. Ngay cả khi bố cục của chúng tôi hoạt động trôi chảy khi màn hình thay đổi kích thước, vẫn có các kích thước mà thiết kế của chúng tôi bị phá vỡ (được gọi là 'điểm dừng') và đây là nơi chúng tôi can thiệp vào các truy vấn phương tiện CSS3.
Một điều đầu tiên trước khi chúng tôi gặp khó khăn với các điểm dừng, các truy vấn phương tiện css không được hỗ trợ trong Internet Explorer 8 trở xuống, vì vậy chúng tôi cũng sẽ sử dụng Trình cắm JavaScript 'css3-mediaqueries' để duy trì hỗ trợ trình duyệt chéo. Điều này chỉ được tải vào các phiên bản IE dưới 9 bằng cách sử dụng đoạn mã sau trong các thẻ <head> html.
Ẩn mã sao chép
!--[if lt IE 9]> <script src="/css3-mediaqueries.js"></script> <![endif]-->
Trình cắm được tạo bởi Manish Salunke và có thể được truy cập trên trang web cssmatters của anh ấy . Nó khéo léo thực hiện tất cả công việc cho bạn bằng cách chuyển đổi các truy vấn phương tiện CSS sang JavaScript có thể nhận ra bởi IE.
Điểm dừng
Chúng tôi có ba điểm dừng chiều rộng chính tại: dưới 764px, nhỏ hơn 1024px và hơn 1024px. Trong thực tế tốt nhất, các điểm dừng của một thiết kế được xác định bởi thiết kế và không phải kích thước thiết bị phổ biến, nhưng bởi một sự ngẫu nhiên vui vẻ, các điểm dừng của thiết kế đặc biệt này nằm rất gần các nhóm thiết bị chính của điện thoại, máy tính bảng và màn hình rộng một chút tinh chỉnh!). Những thay đổi chúng tôi đã thực hiện là cố tình tinh tế để không làm đảo lộn trải nghiệm người dùng tổng thể, nhưng đủ để điều chỉnh sự thay đổi trong không gian và bố cục có sẵn. Chúng tôi cũng có một số điểm ngắt nhỏ: chiều rộng lớn hơn 1200px, lớn hơn 1650px về ẩm thực, chiều cao nhỏ hơn 360px và 500px.
Vì vậy, để đi vào một số chi tiết về cách thiết kế của chúng tôi thích ứng với từng điểm dừng chính này, hãy để tôi chỉ cho bạn một sơ đồ bố trí thiết kế đáp ứng chính theo kích thước thiết bị và sau đó đi sâu vào chi tiết với các ví dụ mã bên dưới:
Vì vậy, để đi vào một số chi tiết về cách thiết kế của chúng tôi thích ứng với từng điểm dừng chính này, hãy để tôi chỉ cho bạn một sơ đồ bố trí thiết kế đáp ứng chính theo kích thước thiết bị và sau đó đi sâu vào chi tiết với các ví dụ mã bên dưới:

A. Điểm dừng khó khăn nhất là độ rộng màn hình dưới 764px (kích thước xấp xỉ điện thoại di động hoặc máy tính bảng nhỏ). Ở kích thước này, không có đủ không gian có thể sử dụng để có bảng thông tin và bản đồ cạnh nhau, vì vậy thay vào đó, cả hai đều trở thành chế độ xem toàn chiều rộng được truy cập thay thế thông qua các nút ở mỗi bên của màn hình. Điều hướng bên và các bộ lọc hàng đầu nén vào các menu bật ra để chúng giải phóng không gian có giá trị khi không cần thiết. Ngoài ra còn có một số quyết định thiết kế nhất định được đưa ra dựa trên cách mọi người tương tác với điện thoại di động sẽ được đề cập trong phần 'hành trình / mong đợi của người dùng khác nhau'.
Tất cả các kiểu cho CityPulse đã được tạo bằng LESS, một phần mở rộng của CSS có thêm các tính năng mới và hành vi động cho CSS, bạn có thể đọc thêm trên trang web LESS. Đây là dòng LESS / CSS đầu tiên chúng tôi sử dụng để bắt đầu truy vấn phương tiện:
Ẩn mã sao chép
@media all and (max-width: 764px){
Dòng đầu tiên này tuyên bố rằng các kiểu này áp dụng cho tất cả các loại phương tiện (màn hình, in, v.v.) và nó cho biết chiều rộng tối đa. Tất cả các kiểu truy vấn không phải phương tiện mà chúng tôi đã khai báo bên ngoài các kiểu này sẽ vẫn được áp dụng, nhưng mọi kiểu nhắm mục tiêu cùng một yếu tố sẽ bị ghi đè trừ khi chúng bao gồm! Quan trọng hoặc cụ thể hơn ( xem bài viết trên Tạp chí Smashing về tính đặc hiệu CSS ).
Dưới đây là một số kiểu chính mà sau đó chúng tôi áp dụng trong truy vấn phương tiện này cho thiết kế:
Ẩn mã sao chép
.map-button {
display:block;
position:absolute;
bottom:0;
right:0;
z-index:250;
}
Nút bản đồ này được đặt thành 'display: none;' trong các kiểu chính và chỉ hiển thị cho các màn hình rộng dưới 764px bằng cách đặt thuộc tính thành 'chặn'. Vì ở độ rộng này, bản đồ và danh sách sự kiện không thể hiển thị cùng nhau, vì vậy nút cho phép người dùng lật giữa chúng bằng cách sử dụng chức năng JavaScript để tạo hiệu ứng cho bản đồ trượt vào.
Ẩn mã sao chép
#smalldevice-intro {
display: block;
text-align: center;
h5 {
max-width:70%;
margin: 0 auto;
color:@accentcolor3;
font-weight:normal;
text-shadow:1px 1px 1px #000;
}
}
Với kích thước nhỏ này, chúng tôi không hiển thị màn hình giật gân tiêu chuẩn tải các sự kiện toàn cầu hàng đầu, vì vậy thay vì những người dùng này bỏ lỡ khi xem khẩu hiệu của chúng tôi, chúng tôi đưa nó vào màn hình đầu tiên họ nhìn thấy. Nó được ẩn như nút bản đồ bằng cách sử dụng cài đặt hiển thị và sau đó được đặt thành hiển thị cho kích thước cụ thể này.
Ẩn Shrink
Sao chép Mã
Sao chép Mã#sidemenu-tablet-refine-search {
display: block;
}
#header-right.tabletmenuopen {
//when menu is active change refine button colour
#tablet-refine-search.button-type2 {
background: url(/Images/dark-texture.png) repeat #212121;
}
}
#arrow-left-close {
display: none;
}
#header-left {
height: 40px;
background: none;
width: 100%;
.site-title {
display: none;
}
}
#tablet-refine-search {
display: none;
}
#sidenavbar-wrapper {
width: 100%;
}
.sidemenu-scrollinner {
margin-top: 110px;
}
// hide rhs header - but raise z-index for menu button
#header-right {
width: 100%;
z-index: 200;
height: 0px;
.whatsonselect {
z-index: 200;
margin-top: 0px;
background: url(/Images/dark-texture.png) repeat #212121;
.whatsonlocation {
top: 60%;
}
#clickmenu-date {
top: 30%;
padding:3px 0;
}
#clickmenu-category {
top: 45%;
padding:3px 0;
}
}
}
.whatsonselect-choose {
.BOX-SHADOW(0, 0, 5px, 0, 0.7) !important;
}
Trích xuất mã này thực hiện một số điều: nó đặt chiều rộng bảng điều khiển bên của chúng tôi để chiếm toàn màn hình, nó hiển thị một nút mới trong dải điều hướng bên cạnh để chuyển đổi giữa chế độ xem bộ lọc và chế độ xem danh sách và điều chỉnh việc thả xuống bộ lọc bằng cách cài đặt chúng một khoảng cách phần trăm hợp lý từ đỉnh container của chúng.
Các ảnh chụp màn hình sau đây cho thấy ảnh hưởng của sự thay đổi kích thước màn hình:

B. Đối với kích thước thiết bị trong khoảng 764 đến 1024px (kích thước máy tính bảng tiêu chuẩn hoặc kích thước màn hình nhỏ), bảng điều khiển bên và bản đồ hiện được tạo kiểu là 50% chiều rộng màn hình. Điều này là để giữ cho văn bản bảng điều khiển bên với số lượng từ tối ưu trên mỗi dòng để dễ đọc. Chúng tôi cũng nén menu bộ lọc vào một nút 'Tinh chỉnh' thay vì trải rộng các bộ lọc như trên các thiết bị / màn hình rộng hơn. Khi chiều rộng trở nên quá nhỏ, các nút bộ lọc bắt đầu rơi ra khỏi các thùng chứa của chúng và khi chiều cao quá nhỏ, các bộ lọc thả xuống sẽ biến mất khỏi đáy màn hình, vì vậy thay vào đó chúng được sắp xếp vào bảng điều khiển của riêng chúng với các thanh cuộn xuất hiện bằng cách đặt tràn thành 'tự động' nếu chiều cao màn hình quá ngắn so với nội dung.
Ẩn mã sao chép
@media (min-width: 765px) and (max-width:1024px), (max-height:500px) {
Khai báo truy vấn phương tiện áp dụng cho kích thước màn hình lớn hơn 765px nhưng nhỏ hơn 1024px và cả kích thước màn hình có chiều cao nhỏ hơn 500px.
Ẩn mã sao chép
#tablet-refine-search {
display: block;
position: absolute;
top: 10px;
right: 20px;
padding: 10px;
cursor: pointer;
}
#header-left {
width: 50%;
}
#sidenavbar-wrapper {
width: 50%;
}
#whatsonsearch {
display: none;
}
#header-right {
width: 50%;
}
}
Các dòng của LESS / CSS đặt chiều rộng của bảng điều khiển bên là 50%, nằm trong vùng chứa '# tiêu đề bên trái' & '# sidenavbar-Wrapper'. Nó cũng hiển thị một nút mới được ẩn từ kích thước màn hình lớn hơn và nhỏ hơn, bật tắt menu bộ lọc bằng cách trượt từ bên trái.
Ẩn Shrink
Sao chép Mã
Sao chép Mã.whatsonlocation {
width: 80%;
position: absolute;
top: 50%;
right: 20%;
display: block;
}
#clickmenu-date {
width: 80%;
position: absolute;
top: 10%;
right: 20%;
}
#clickmenu-category {
width: 80%;
position: absolute;
top: 30%;
right: 20%;
}
#whatsonsearch {
width: 100%;
display: block;
position: absolute;
bottom: 0;
background: url(/Images/dark-texture.png) repeat #212121;
button {
width: 60%;
margin: 10px auto;
// float: right;
padding: 0 5px;
.button-type2;
color: @accentcolor3;
text-shadow: none;
&:hover {
.SELECTEDMENU;
}
}
}
Điều này sẽ cung cấp cho các nút bộ lọc một bố cục mới phù hợp với chiều rộng 50% mới của menu. Đối với nút có ID 'whatsonsearch' thường được hiển thị ở phía trên bên phải màn hình dưới dạng nút 'GO' màu đen, chúng tôi cung cấp cho nó một hộp chứa nền nổi bật hơn, màu xanh sáng hơn và chúng tôi tạo kiểu cho phần tử nút nhiều hơn lớn hơn nên sẽ dễ dàng / chính xác hơn khi nhấn bằng ngón tay trên thiết bị màn hình cảm ứng.

C. Đối với các thiết bị có chiều rộng lớn hơn 1024px, nội dung của chúng tôi hiện có chỗ để trải rộng. Các bộ lọc cho kích thước nhỏ hơn được đặt trong bảng điều khiển trượt ra hiện có thể nhìn thấy mọi lúc dọc theo phía trên bên phải của tiêu đề. Khi bất kỳ bộ lọc nào được nhấp vào, menu của nó sẽ hiện ra trên bản đồ. Chiều rộng của bảng điều khiển bên cạnh hiện được cách điệu là 35%, do đó, với không gian thêm, bản đồ và điểm đánh dấu giờ đây có thể có nhiều sự lão hóa hơn trong thiết kế.
Tất cả các kiểu cho kích thước màn hình này không được bao gồm trong truy vấn phương tiện vì chúng được đặt làm kiểu trang web chung. Thay vì bao gồm tất cả các kiểu trong bài viết này (trong đó có tải!) Tôi sẽ chỉ bao gồm một vài đoạn trích đặt bố cục chính mà chúng ta đã thảo luận cho kích thước này.
Ẩn Shrink
Sao chép Mã
Sao chép Mã#header-left {
position: absolute;
top: 0;
left: 0;
width: 35%;
height: 60px;
background: url('/images/header-lbg.png') repeat-x #313131;
z-index: 120;
}
#header-right {
width: 65%;
height: 60px;
background: url('/images/header-lbg.png') repeat-x #313131;
position: absolute;
top: 0;
right: 0;
height: 60px;
z-index: 110;
}
.whatsonselect {
float: right;
width: 92%;
padding: 0;
margin: 0;
.whatsonlocation {
float: left;
width: 31%;
max-width: 550px;
min-width: 175px;
color: @accentcolor3;
line-height: 16px;
margin-top: 10px;
height: 35px;
font-size: 14px;
cursor: pointer;
position: relative;
border-radius: 0 6px 6px 0;
text-align:right;
}
#whatson-icon-location {
position: absolute;
top: 0px;
right: 0px;
border-radius: 0 5px 5px 0;
}
Ở đây chúng tôi đang đặt chiều rộng của bảng điều khiển bên và tiêu đề bên trái là 35%. Kích thước tiêu đề bên phải được đặt thành 65% phù hợp với chiều rộng của bản đồ. Trong tiêu đề bên phải, các nút Bộ lọc được đặt với bộ chứa của chúng là 92% chiều rộng của trình bao bọc tiêu đề bên phải 65%. Chúng tôi cũng đang đặt chiều rộng của mỗi nút lọc là khoảng một phần ba chiều rộng của thùng chứa của chúng, chiều rộng tối đa và tối thiểu cho mỗi nút này cũng được đặt để ngăn bố cục của chúng bị phá vỡ.

2. Thiết bị màn hình cảm ứng
Với một thiết bị màn hình cảm ứng không có khả năng thực hiện một số chức năng mà người dùng có thể làm với chuột như 'di chuột', nhưng nó mang lại một loạt các cử chỉ mới như chụm, vuốt, v.v. Để tối ưu hóa cho thiết bị màn hình cảm ứng trước tiên chúng tôi phát hiện xem thiết bị có màn hình cảm ứng có khả năng hay không bằng cách sử dụng trình cắm 'Modenizr'. Sau đó, chúng tôi sử dụng kết quả của việc này để quyết định các biểu định kiểu và hàm JavaScript nào sẽ chạy, liên quan đến việc có thân thiện với cảm ứng hay không. Nếu chúng tôi thấy thiết bị có màn hình cảm ứng có khả năng, chúng tôi cũng tải plugin ' TouchSwipe ', cho phép chúng tôi sử dụng nhiều cử chỉ trên màn hình cảm ứng, chẳng hạn như sử dụng cử chỉ 'vuốt' để vuốt giữa bản đồ và chế độ xem danh sách khi ở trên di động.
Vì vậy, để phát hiện xem thiết bị có khả năng chạm hay không, chúng tôi chạy đoạn script sau
Ẩn mã sao chép
if ( Modernizr.touch ) {
...
}
Trong câu lệnh if này, chúng tôi tải trong trình cắm của mình, cho phép cử chỉ vuốt để trượt trên bản đồ. Một trong những hạn chế của việc sử dụng phương pháp này là khả năng màn hình cảm ứng của một số phiên bản trình duyệt và một số thiết bị có thể không trả lời chính xác và vì lý do này, chúng tôi cũng có khả năng nhấp vào nút bản đồ.
3. Băng thông thay đổi / gián đoạn
Khi trang web được truy cập từ điện thoại di động, chúng tôi khái quát rằng tốc độ kết nối sẽ chậm hơn các thiết bị khác và thực hiện một số điều khác nhau để tối ưu hóa cho việc này. Menu bộ lọc là màn hình đầu tiên mà người dùng sẽ nhìn thấy trước bản đồ và tất cả dữ liệu của nó được tải vào. Điều này thật tuyệt vì nó cho phép người dùng chọn vị trí, ngày và loại sự kiện họ muốn vì vậy chỉ những sự kiện phù hợp với yêu cầu của họ mới được gọi từ máy chủ, giữ cho người dùng của chúng tôi kết nối 3G với các khoản cho phép dữ liệu hạn chế.

Một cách khác để chúng tôi giữ dữ liệu được yêu cầu từ máy chủ thấp hơn cho các thiết bị nhỏ hơn là giảm số lượng điểm đánh dấu sự kiện hàng đầu được hiển thị trên bản đồ. Điều này cũng có ý nghĩa trực quan, một bản đồ nhỏ hơn có diện tích bề mặt nhỏ hơn để hiển thị các điểm đánh dấu, do đó, màn hình màn hình rộng của chúng tôi hiển thị khoảng 100 điểm đánh dấu sự kiện hàng đầu trên mỗi lượt xem, điện thoại sẽ hiển thị 30.
Đối với những người dùng 'đang di chuyển', những người có thể gặp phải sự cố gián đoạn kết nối internet của họ, chúng tôi sử dụng bộ nhớ đệm để đảm bảo rằng các sự kiện được tải trước đó vẫn có thể được truy cập tạm thời.
4. Hành trình / mong đợi của người dùng khác nhau
Trong khi thiết kế và mã hóa trang web để thích ứng với các loại thiết bị khác nhau với kích thước màn hình khác nhau, suy nghĩ cũng được đưa ra cho các cách khác nhau mà người dùng của chúng tôi có thể muốn tương tác với trang web.
Người dùng thiết bị di động của chúng tôi có thể đang 'di chuyển' để tìm kiếm các sự kiện gần kề, xảy ra ngay bây giờ. Và do đó, menu bộ lọc của chúng tôi cũng tăng gấp đôi là 'bộ chọn nhanh' cũng như bộ bảo vệ băng thông. Người dùng của chúng tôi đang sử dụng máy tính xách tay và thiết bị máy tính để bàn có thể truy cập trang web từ nhà hoặc nơi làm việc, có lẽ đang tìm kiếm một sự kiện trước. Những người dùng này được cung cấp một trải nghiệm khác nhau, bản đồ nổi bật sẽ là điều đầu tiên họ nhìn thấy với các sự kiện hàng đầu ở bên cạnh, điều này khuyến khích họ khám phá các sự kiện xung quanh họ. Các bộ lọc cũng rõ ràng đối với những người dùng này, nằm trong tiêu đề nhưng chúng tôi có cài đặt trước ngày để những người dùng này hiển thị tất cả các sự kiện trong tuần tới.
Người dùng thiết bị di động của chúng tôi có thể đang 'di chuyển' để tìm kiếm các sự kiện gần kề, xảy ra ngay bây giờ. Và do đó, menu bộ lọc của chúng tôi cũng tăng gấp đôi là 'bộ chọn nhanh' cũng như bộ bảo vệ băng thông. Người dùng của chúng tôi đang sử dụng máy tính xách tay và thiết bị máy tính để bàn có thể truy cập trang web từ nhà hoặc nơi làm việc, có lẽ đang tìm kiếm một sự kiện trước. Những người dùng này được cung cấp một trải nghiệm khác nhau, bản đồ nổi bật sẽ là điều đầu tiên họ nhìn thấy với các sự kiện hàng đầu ở bên cạnh, điều này khuyến khích họ khám phá các sự kiện xung quanh họ. Các bộ lọc cũng rõ ràng đối với những người dùng này, nằm trong tiêu đề nhưng chúng tôi có cài đặt trước ngày để những người dùng này hiển thị tất cả các sự kiện trong tuần tới.
Phần kết luận
Vì vậy, có bạn có nó! Chúng tôi đã tạo một trang web đầy đủ chức năng cung cấp các sự kiện facebook từ khắp nơi trên thế giới chỉ bằng một cú nhấp chuột.
Quá trình nhập tự động trong Azure giúp cơ sở dữ liệu sự kiện được cập nhật, trang web cung cấp trải nghiệm người dùng thông thạo bằng cách chạy như một Ứng dụng Trang đơn & thiết kế đáp ứng cho phép mọi người dùng sử dụng bất kỳ thiết bị nào.
Quá trình nhập tự động trong Azure giúp cơ sở dữ liệu sự kiện được cập nhật, trang web cung cấp trải nghiệm người dùng thông thạo bằng cách chạy như một Ứng dụng Trang đơn & thiết kế đáp ứng cho phép mọi người dùng sử dụng bất kỳ thiết bị nào.
Nhưng đừng tin lời chúng tôi, hãy truy cập trang web và tìm hiểu kỹ - Bạn thậm chí có thể tìm hiểu những gì bạn đang làm vào cuối tuần này!








