9

Tôi đang thử nghiệm với một thiết lập mà là rất giống với một chi tiết trong hình ảnh ở đây: https://raw.githubusercontent.com/Oreste-Luci/netflix-oss-example/master/netflix-oss-example.pngCách kinh điển để triển khai xanh/xanh với ngăn xếp Spring Cloud/Netflix trên PWS là gì?

enter image description here

Trong thiết lập của tôi, tôi đang sử dụng một ứng dụng client (https://www.joedog.org/siege-home/), một proxy (Zuul), một dịch vụ khám phá (Eureka) và một dịch vụ đơn giản. Mọi thứ được triển khai trên PWS.

Tôi muốn di chuyển từ một phiên bản của dịch vụ microservice đơn giản sang tiếp theo mà không có bất kỳ thời gian ngừng hoạt động. Ban đầu tôi bắt đầu với kỹ thuật được mô tả ở đây: https://docs.cloudfoundry.org/devguide/deploy-apps/blue-green.html

Theo tôi, cách tiếp cận này không tương thích với dịch vụ khám phá như Eureka. Trong thực tế, phiên bản mới của dịch vụ của tôi được đăng ký tại Eureka và nhận được lưu lượng truy cập ngay cả trước khi tôi có thể remap tất cả các tuyến đường (Bộ định tuyến CF).

này đưa tôi đến với cách tiếp cận khác, trong đó tôi dựa trên các cơ chế chuyển đổi dự phòng trong mùa xuân đám mây/Netflix:

  1. Tôi quay lên một mới (tương thích ngược) phiên bản của dịch vụ của tôi.
  2. Khi phiên bản này được chọn bởi Zuul/Eureka, nó bắt đầu nhận được 50% lưu lượng truy cập.
  3. Khi tôi đã xác minh rằng phiên bản mới hoạt động chính xác, tôi gỡ bỏ phiên bản "cũ". (Tôi chỉ cần nhấp vào nút "dừng" trong PWS)

Như tôi đã biết, Zuul sử dụng Ribbon (cân bằng tải) dưới mui xe sao cho phần thứ hai trong trường hợp phiên bản cũ vẫn còn ở Eureka nhưng thực sự tắt , Tôi mong đợi một thử lại trên trường hợp mới mà không có bất kỳ tác động nào đối với khách hàng.

Tuy nhiên, giả định của tôi là sai. Tôi nhận được một vài sai sót trong 502 khách hàng của tôi:

Lifting the server siege...  done. 

Transactions:    5305 hits 
Availability:    99.96 % 
Elapsed time:    59.61 secs 
Data transferred:   26.06 MB 
Response time:    0.17 secs 
Transaction rate:   89.00 trans/sec 
Throughput:    0.44 MB/sec 
Concurrency:    14.96 
Successful transactions:  5305 
Failed transactions:    2 
Longest transaction:   3.17 
Shortest transaction:   0.14 

Một phần của application.yml tôi

server: 
    port: ${PORT:8765} 

info: 
    component: proxy 

ribbon: 
    MaxAutoRetries: 2 # Max number of retries on the same server (excluding the first try) 
    MaxAutoRetriesNextServer: 2 # Max number of next servers to retry (excluding the first server) 
    OkToRetryOnAllOperations: true # Whether all operations can be retried for this client 
    ServerListRefreshInterval: 2000 # Interval to refresh the server list from the source 
    ConnectTimeout: 3000 # Connect timeout used by Apache HttpClient 
    ReadTimeout: 3000 # Read timeout used by Apache HttpClient 

hystrix: 
    threadpool: 
     default: 
     coreSize: 50 
     maxQueueSize: 100 
     queueSizeRejectionThreshold: 50 
    command: 
    default: 
     execution: 
     isolation: 
      thread: 
      timeoutInMilliseconds: 10000 

Tôi không chắc chắn những gì khó khăn.

Đây có phải là sự cố kỹ thuật không?

Hoặc tôi đang đưa ra giả định sai (tôi đã đọc ở đâu đó rằng POST chưa được thử lại, điều mà tôi thực sự không hiểu)?

Tôi rất muốn nghe cách bạn thực hiện.

Cảm ơn, Andy

Trả lời

2

Tôi cũng đã tự hỏi về điều này. Tôi sẽ không tuyên bố đã sử dụng Spring Cloud "In Anger". Tôi vừa thử nghiệm nó một lúc.

Giả định: chúng tôi giả định rằng nguồn gốc của sự thật cho tất cả các trạng thái thể hiện được lưu trữ trong Eureka, sau đó Eureka nên là cơ chế kiểm soát hoạt động của chúng tôi. Chúng ta có thể sử dụng Eureka để lấy một thể hiện ra khỏi dịch vụ bằng cách đặt trạng thái cá thể thành OUT_OF_SERVICE. Khi Ribbon làm mới danh sách máy chủ của nó, nó sẽ không sử dụng các trường hợp dịch vụ này. Eureka cung cấp một API REST cho các cá thể truy vấn và thiết lập trạng thái cá thể. Tuyệt quá.

Vấn đề là: Làm cách nào để xác định những trường hợp nào thuộc nhóm Màu xanh và các trường hợp nào thuộc nhóm Màu xanh lục?

Tôi đã suy nghĩ ... Eureka cung cấp bản đồ siêu dữ liệu cho từng trường hợp. Nói trong bước xây dựng/nướng của chúng tôi, chúng tôi đặt id phiên bản trong bản đồ siêu dữ liệu? Chúng tôi có thể sử dụng một Git cam kết Id hoặc một số chương trình phiên bản ngữ nghĩa hoặc bất cứ điều gì. Ok, bây giờ tôi có thể xem siêu dữ liệu Eureka và xác định các phiên bản Blue so với Green cho giá trị phiên bản đó. Chúng ta có thể thiết lập các giá trị siêu dữ liệu trong mỗi dịch vụ bằng cách sử dụng các thuộc tính.

ví dụ: eureka.instance.metadataMap.version=8675309

Bây giờ điều tốt đẹp là nếu chúng ta có thể nói với Eureka. "Mất tất cả các trường hợp cho dịch vụ FUBAR và phiên bản 8675309 không hoạt động." Vâng, tôi không nghĩ rằng nó được cung cấp ra khỏi hộp. Điều tuyệt vời về Spring Cloud là tất cả các dịch vụ này, bao gồm cả Eureka Server, chỉ là các ứng dụng Spring mà chúng ta có thể tự hack cho nhu cầu của mình. Mã dưới đây cho thấy điểm kết thúc đặt các trường hợp thành "ngoài dịch vụ" được cung cấp cho Tên ứng dụng và Phiên bản. Chỉ cần thêm bộ điều khiển này vào máy chủ Eureka của bạn. Nó không phải là sản xuất đã sẵn sàng, chỉ là một ý tưởng thực sự.

Bây giờ khi Eureka mất các trường hợp này ra khỏi dịch vụ và Ribbon làm mới danh sách máy chủ của nó là an toàn để giết hoặc định tuyến khỏi các trường hợp này.

POST để:

http://[eurekahost:port]/takeInstancesOutOfService?applicationName=FOOBAR&version=8675309 

Hy vọng rằng sẽ giúp?

import java.util.Collection; 
import java.util.function.Predicate; 
import java.util.stream.Collectors; 

import org.springframework.web.bind.annotation.RequestMapping; 
import org.springframework.web.bind.annotation.RequestMethod; 
import org.springframework.web.bind.annotation.RequestParam; 
import org.springframework.web.bind.annotation.RestController; 

import com.netflix.appinfo.InstanceInfo; 
import com.netflix.appinfo.InstanceInfo.InstanceStatus; 
import com.netflix.discovery.shared.Application; 
import com.netflix.eureka.EurekaServerContextHolder; 
import com.netflix.eureka.registry.PeerAwareInstanceRegistry; 

@RestController 
public class EurekaInstanceStateController { 

    @RequestMapping(value="/instancesQuery", method=RequestMethod.POST) 
    public Collection<String> queryInstancesByMetaData(
      @RequestParam("applicationName") String applicationNameCriteria, 
      @RequestParam("version") String versionCriteria) 
    { 
     return getRegistry().getSortedApplications() 
       .stream() 
       .filter(hasApplication(applicationNameCriteria)) 
       .flatMap(app -> app.getInstances().stream()) 
       .filter(hasVersion(versionCriteria)) 
       .map(info -> info.getAppName() + " - " + info.getId() + " - " + info.getStatus() + " - " + info.getMetadata().get("version")) 
       .collect(Collectors.toList()); 
    } 

    @RequestMapping(value="/takeInstancesOutOfService", method=RequestMethod.POST) 
    public Collection<String> takeInstancesOutOfService(
      @RequestParam("applicationName") String applicationNameCriteria, 
      @RequestParam("version") String versionCriteria) 
    { 
     return getRegistry().getSortedApplications() 
       .stream() 
       .filter(hasApplication(applicationNameCriteria)) 
       .flatMap(app -> app.getInstances().stream()) 
       .filter(hasVersion(versionCriteria)) 
       .map(instance -> updateInstanceStatus(instance, InstanceStatus.OUT_OF_SERVICE)) 
       .collect(Collectors.toList()); 
    } 

    /** 
    * @param instance 
    * @return 
    */ 
    private String updateInstanceStatus(InstanceInfo instance, InstanceStatus status) 
    { 
     boolean isSuccess = getRegistry().statusUpdate(instance.getAppName(), instance.getId(), 
     status, String.valueOf(System.currentTimeMillis()), 
     true); 

     return (instance.getAppName() + " - " + instance.getId() + " result: " + isSuccess); 
    } 

    /** 
    * Application Name Predicate 
    * @param applicationNameCriteria 
    * @return 
    */ 
    private Predicate<Application> hasApplication(final String applicationNameCriteria) 
    { 
     return application -> applicationNameCriteria.toUpperCase().equals(application.getName()); 
    } 

    /** 
    * Instance Version Predicate. Uses Eureka Instance Metadata value name "version".</br> 
    * 
    * Set/Bake the instance metadata map to contain a version value.</br> 
    * e.g. eureka.instance.metadataMap.version=85839c2 
    * 
    * @param versionCriteria 
    * @return 
    */ 
    private Predicate<InstanceInfo> hasVersion(final String versionCriteria) 
    { 
     return info -> versionCriteria.equals(info.getMetadata().get("version")); 
    } 

    private PeerAwareInstanceRegistry getRegistry() { 
     return EurekaServerContextHolder.getInstance().getServerContext().getRegistry(); 
    } 
} 
+0

Ý tưởng hay. Tôi cũng đang xem xét điều này. Nhưng tôi không chắc chắn về việc làm điều này ở phía Eureka - nếu dịch vụ gửi một nhịp tim mới, nó sẽ không thay đổi trạng thái của nó trở lại UP một lần nữa? Đám mây mùa xuân đi kèm với/tạm dừng và/tiếp tục điểm cuối, mà tôi nghĩ rằng thay đổi trạng thái máy khách thành OUT_OF_SERVICE hoặc XUỐNG. Tôi đã suy nghĩ về một kịch bản triển khai gửi đến/tạm dừng trước khi triển khai. Danh sách các trường hợp để gửi này để vẫn có thể được kéo từ Eureka và lọc theo phiên bản hoặc một cái gì đó. – nedenom

+0

Tôi cũng đang xem xét trạng thái OUT_OF_SERVICE. Từ những gì tôi hiểu, có vẻ như Asgard có cách tiếp cận tương tự: https://github.com/Netflix/asgard/wiki/Eureka-Integration Kết luận của tôi là, để thực hiện cập nhật về PWS, chúng tôi cần tùy chỉnh, homebrewn bảng điều khiển (chẳng hạn như Asgard) sẽ tạo điều kiện này. Chế độ xem PWS quá hạn chế để thực hiện việc này. AFAIK không có thư viện Spring nào thực hiện điều này. Tôi đã không nhận ra rằng tôi có thể phát triển các thiết bị đầu cuối REST của riêng tôi cho điều này như bạn đã làm, vì vậy tôi bắt đầu với REST api của chính Eureka. Tôi sẽ có một cái nhìn tại đó - cảm ơn! –

+0

@nedenom nếu bạn đặt trạng thái XUỐNG, nó sẽ tự động được đặt thành LÊN lại sau 30 giây. Nếu bạn thiết lập trạng thái thành OUT_OF_SERVICE, nó sẽ giữ nguyên cách đó cho đến khi bạn theo cách thủ công (thông qua REST api) thiết lập nó trở lại UP/DOWN. –

Các vấn đề liên quan