springcloud腳手架搭建

一.搭建空項目以及eureka服務

  1. New EmptyProject–>New Module–>Spring Cloud Discovery(選擇Eureka Server)
  2. pom.xml中添加阿里雲鏡像
<repositories>
    <repository>
        <id>central</id>
        <name>aliyun maven</name>
        <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
        <layout>default</layout>
        <!-- 是否開啓發布版構件下載 -->
        <releases>
            <enabled>true</enabled>
        </releases>
        <!-- 是否開啓快照版構件下載 -->
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>
</repositories>
  1. 修改配置文件
server:
  port: 8761

eureka:
  instance:
    hostname: localhost
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
  1. 訪問:http://localhost:8761/

二.搭建商品服務

  1. New Module–>Web(選擇Spring Web)、Spring Cloud Discovery(選擇Eureka Discovery Client)
  2. 新建一個controller,代碼如下
@RestController
@RequestMapping("/api/v1/product")
public class ProductController {

    @Autowired
    private ProductService productService;

    @RequestMapping("list")
    public Object list(){
        return productService.listProduct();
    }

    @RequestMapping("find")
    public Object list(@RequestParam("id") int id){
        return productService.findByid(id);
    }
}
  1. 新建service接口
public interface ProductService {

    List<Product> listProduct();

    Product findByid(int id);
}
  1. 新建service實現類
@Service
public class ProductServiceImpl implements ProductService {

    private static Map<Integer,Product> daoMap = new HashMap<>();

    static {
        Product p1 = new Product(1, "iphone1", 111, 11);
        Product p2 = new Product(2, "iphone2", 222, 12);
        Product p3 = new Product(3, "iphone3", 333, 13);
        Product p4 = new Product(4, "iphone4", 444, 14);
        Product p5 = new Product(5, "iphone5", 555, 15);
        Product p6 = new Product(6, "iphone6", 666, 16);
        Product p7 = new Product(7, "iphone7", 777, 17);

        daoMap.put(p1.getId(),p1);
        daoMap.put(p2.getId(),p2);
        daoMap.put(p3.getId(),p3);
        daoMap.put(p4.getId(),p4);
        daoMap.put(p5.getId(),p5);
        daoMap.put(p6.getId(),p6);
        daoMap.put(p7.getId(),p7);


    }

    @Override
    public List<Product> listProduct() {
        Collection<Product> collection = daoMap.values();
        List<Product> list = new ArrayList<>(collection);
        return list;
    }

    @Override
    public Product findByid(int id) {
        return daoMap.get(id);
    }
}
  1. 修改配置
server:
  port: 8771

spring:
  application:
    name: product-service

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
a. 註冊中心報錯:EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY'RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE

b. 原因:這是eureka自我保護的一個警告

c. 解決:可以在配置文件中配置
server:
  port: 8771

spring:
  application:
    name: product_service

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
  
#關閉自我保護    
  server:
    enable-self-preservation: false
  1. 訪問:
    http://localhost:8771/api/v1/product/list
    http://localhost:8771/api/v1/product/find?id=1

  2. 爲什麼只加配置文件,product服務就可以註冊到eureka服務上,只要類路徑下有spring-cloud-starter-netflix-client依賴,服務就會自動註冊到eureka服務上

三.搭建訂單服務

  1. 服務間調用方式
    a. RPC:遠程過程調用 ,像本地服務(方法)一樣調用服務器的服務,客戶端和服務器之間建立TCP連接,可以一次建立一個,也可以多個調用複用一次鏈接,數據包小
    b. Rest(Http):發起http請求,數據包大。使用HttpClient、URLConnection
  2. New Module–>Web(選擇Spring Web)、Spring Cloud Discovery(選擇Eureka Discovery Client)、Cloud Routing(選擇Ribbon)
  3. Ribbon類似httpClient、URLConnection
  4. 創建controller
@RestController
@RequestMapping("api/v1/order")
public class OrderController {

    @Autowired
    private OrderService orderService;

    @RequestMapping("save")
    public Object save(@RequestParam("user_id")int userId, @RequestParam("product_id")int productId){
        return orderService.save(userId, productId);
    }
}
  1. 創建service接口
public interface OrderService {
    ProductOrder save(int userId, int productId);
}
  1. 創建service實現類
@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private RestTemplate restTemplate;

    @Override
    public ProductOrder save(int userId, int productId) {

        Object obj = restTemplate.getForObject("http://PRODUCT-SERVICE/api/v1/product/find?id=" + productId, Object.class);

        System.out.println(obj);

        ProductOrder productOrder = new ProductOrder();
        productOrder.setCreateTime(new Date());
        productOrder.setUserId(userId);
        productOrder.setTradeNo(UUID.randomUUID().toString());

        return productOrder;
    }
}
  1. 增加配置
server:
  port: 8781

spring:
  application:
    name: order-service

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
  1. 啓動類增加
@SpringBootApplication
public class OrderServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

}
  1. 請求訂單服務:http://127.0.0.1:8781/api/v1/order/save?product_id=3&user_id=4
  2. ribbon實現原理:調用方向註冊中心拉取可調用列表,然後ribbon根據策略選取調用服務
  3. 自定義負載均衡策略:
    a. 通過設置配置文件:
server:
  port: 8781

spring:
  application:
    name: order-service

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/

#自定義負載均衡策略
product-service:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
  1. 訪問:http://127.0.0.1:8781/api/v1/order/save?product_id=3&user_id=4

四.feigin改造訂單服務

  1. 新增feign依賴
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
  1. 在訂單服務service中新加一個接口,接口上新加註解以及添加需要調用服務的方法,請求url和入參需要和🔐調用服務方法一致
@FeignClient(name = "product-service")
public interface ProductClient {

    @RequestMapping("/api/v1/product/find")
    String findById(@RequestParam("id") int id);
}
  1. 然後對這個接口進行代碼調用
@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private ProductClient productClient;

    @Override
    public ProductOrder save(int userId, int productId) {

        //Object obj = restTemplate.getForObject("http://PRODUCT-SERVICE/api/v1/product/find?id=" + productId, Object.class);

        String response = productClient.findById(productId);
        JsonNode jsonNode = JsonUtils.str2JsonNode(response);


        System.out.println(jsonNode);

        ProductOrder productOrder = new ProductOrder();
        productOrder.setCreateTime(new Date());
        productOrder.setUserId(userId);
        productOrder.setTradeNo(UUID.randomUUID().toString());

        return productOrder;
    }
}
  1. 訪問:http://127.0.0.1:8781/api/v1/order/save?product_id=3&user_id=4

四.ribbon和feigin源碼解讀

  1. spring啓動時候會對註解進行掃描,掃到@EnableFeignClients,開啓一個feigin客戶端,會掃描@FeignClient類,並對類進行實例化,放到IOC容器中,然後攔截請求,然後通過動態代理生成RestTemplate

五.服務降級熔斷

概念相關

  1. 概念介紹

    a. 熔斷:防止整個系統故障,包含子系統和 下游服務
    b. 降級:拋棄非核心 的接口和數據。熔斷一般是下游 服務故障導致,服務降級一般是從整體系統負荷考慮,由調用方控制
    
  2. 參考文檔:
    a. https://github.com/Netflix/Hystrix
    b. https://github.com/Netflix/Hystrix/wiki

  3. hystrix超時策略配置類:HystrixCommandProperties

  4. 配置參考文檔:https://github.com/Netflix/Hystrix/wiki/Configuration

  5. 隔離策略:
    a. THREAD(線程池隔離,默認)
    b. SEMAPHORE(信號量)

  6. 隔離策略修改代碼,增加commandProperties參數

@RestController
@RequestMapping("api/v1/order")
public class OrderController {

    @Autowired
    private OrderService orderService;
    @Autowired
    private StringRedisTemplate redisTemplate;

    @RequestMapping("save")
    @HystrixCommand(fallbackMethod = "saveOrderFail",commandProperties = {@HystrixProperty(name = "execution.isolation.strategy", value = "SEMAPHORE")})
    public Object save(@RequestParam("user_id")int userId, @RequestParam("product_id")int productId, HttpServletRequest request){
        Map<String, Object> msg = new HashMap<>();
        msg.put("code",0);
        msg.put("data",orderService.save(userId, productId));
        return msg;
    }

    public Object saveOrderFail(int userId, int productId, HttpServletRequest request){

        new Thread(()->{
            //監控報警
            String saveOrderKey = "save-order";
            String sendValue = redisTemplate.opsForValue().get(saveOrderKey);
            String ip = request.getRemoteAddr();

            if (StringUtils.isNullOrEmpty(sendValue)){
                System.out.println("緊急短信下發,ip地址是:"+ip);
                redisTemplate.opsForValue().set(saveOrderKey,"save-order-fail",20, TimeUnit.SECONDS);
            }else {
                System.out.println("已經發送過短信,20s內不重複發");
            }
        }).start();


        Map<String, Object> msg = new HashMap<>();
        msg.put("code",-1);
        msg.put("msg","清稍後再試");
        return msg;
    }

}
  1. 其他配置:
    c. execution.isolation.thread.timeoutInMilliseconds,默認1000毫秒
    b. execution.timeout.enabled,是否開啓超時限制
    e. execution.isolation.semaphore.maxConcurrentRequests隔離策略爲信號量的時候,如果達到最大併發數,後續請求會被拒絕,默認是10

Ribbon消費使用hystrix代碼相關

  1. 添加依賴
<dependency>
    <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
 </dependency>
  1. 啓動類增加註解:@EnableCircuitBreaker,如果註解太多可以用@SpringCloudApplication代替
  2. api方法上增加@HystrixCommand(fallbackMethod = “saveOrderFail”)
  3. 編寫fallback方法實現,方法簽名(saveOrderFail)和api方法簽名一致
@RestController
@RequestMapping("api/v1/order")
public class OrderController {

    @Autowired
    private OrderService orderService;

    @RequestMapping("save")
    @HystrixCommand(fallbackMethod = "saveOrderFail")
    public Object save(@RequestParam("user_id")int userId, @RequestParam("product_id")int productId){
        Map<String, Object> msg = new HashMap<>();
        msg.put("code",0);
        msg.put("data",orderService.save(userId, productId));
        return msg;
    }

    public Object saveOrderFail(int userId, int productId){
        Map<String, Object> msg = new HashMap<>();
        msg.put("code",-1);
        msg.put("msg","清稍後再試");
        return msg;
    }
    
}

Feign結合Hystrix斷路器

  1. 創建FeignClient接口類,實現feign接口,爲fallback參數指定fallback方法
@FeignClient(name = "product-service", fallback = ProductClientFallback.class)
public interface ProductClient {

    @RequestMapping("/api/v1/product/find")
    String findById(@RequestParam("id") int id);
}
  1. 創建接口類,實現ProductClient接口,
@Component
public class ProductClientFallback implements ProductClient {
    @Override
    public String findById(int id) {
        System.out.println();
        return "feign中斷路器已開啓";
    }
}
  1. 在配置文件中開啓斷路器
server:
  port: 8781

spring:
  application:
    name: order-service

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/

#自定義負載均衡策略
product-service:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

feign:
  hystrix:
    enabled: true
  1. 報錯相關:
    a. 報錯:springboot使用Fegin,創建服務接口,在controller裏面注入Feign接口對象,結果啓動報錯
    b.原因:註解 @EnableFeignClients 與 @ComponentScan 有衝突,兩種註解都會搜索注入指定目錄中的 bean 。@EnableFeignClients 引入了 FeignClientsRegistrar 類,實現了 Spring 的bean 資源的加載。FeignClientsRegistrar中registerFeignClients方法獲取了@EnableFeignClients註解中的basepackage 屬性值,並進行注入。如果兩種註解都使用時,其中@EnableFeignClients會覆蓋 @ComponentScan 中指定的目錄,從而恢復到默認目錄。
    c. 解決:在註解@EnableFeignClients指定clients,例如:@EnableFeignClients(clients = ProductClient.class)

Hystrix斷路器整合redis預警

  1. 加入redis依賴
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  1. controller引入StringRedisTemplate
@RestController
@RequestMapping("api/v1/order")
public class OrderController {

    @Autowired
    private OrderService orderService;
    @Autowired
    private StringRedisTemplate redisTemplate;

    @RequestMapping("save")
    @HystrixCommand(fallbackMethod = "saveOrderFail")
    public Object save(@RequestParam("user_id")int userId, @RequestParam("product_id")int productId, HttpServletRequest request){
        Map<String, Object> msg = new HashMap<>();
        msg.put("code",0);
        msg.put("data",orderService.save(userId, productId));
        return msg;
    }

    public Object saveOrderFail(int userId, int productId, HttpServletRequest request){

        new Thread(()->{
            //監控報警
            String saveOrderKey = "save-order";
            String sendValue = redisTemplate.opsForValue().get(saveOrderKey);
            String ip = request.getRemoteAddr();

            if (StringUtils.isNullOrEmpty(sendValue)){
                System.out.println("緊急短信下發,ip地址是:"+ip);
                redisTemplate.opsForValue().set(saveOrderKey,"save-order-fail",20, TimeUnit.SECONDS);
            }else {
                System.out.println("已經發送過短信,20s內不重複發");
            }
        }).start();

        Map<String, Object> msg = new HashMap<>();
        msg.put("code",-1);
        msg.put("msg","清稍後再試");
        return msg;
    }
}

Hystrix儀表盤使用

參考博客:https://baijiahao.baidu.com/s?id=1623004854011062838&wfr=spider&for=pc

  1. 新建hystrix_dashboard模塊
  2. 添加依賴
<!-- hystrix監控web依賴 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
  1. 啓動類增加註解@EnableHystrixDashboard
  2. 配置文件新增endpoint(目的是springboot2.0之後不會開啓全部的監控元數據信息)
server.port=8791
spring.application.name=hystrix-dashboard
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/

# actuator監控
management.endpoints.web.exposure.include=*
  1. 訪問:http://localhost:8781/hystrix
  2. Hystrix Dashboard支持三種監控方式
    a. 通過URL:turbine-hostname:port/actuator/turbine.stream開啓,實現對默認集羣的監控
    b. 通過URL:turbine-hostname:port/actuator/turbine.stream?cluster=[clusterName]開啓,實現對clusterName集羣的監控
    c. 通過URL/hystrix-app:port/actuator/hystrix.stream開啓,實現對具體某個服務實例的監控
  3. Delay參數:控制服務器上輪詢監控信息的延遲時間,默認2000毫秒
6.監控具體服務
  1. 對於Ribbon工程,在Dashboard輸入: http://localhost:8781/actuator/hystrix.stream
    如果顯示:Unable to connect to Command Metric Stream
    是因爲:配置文件中沒配置actuator,因爲監控路徑默認不開放
  2. 對於Feign工程,Feign自己集成了斷路器,需要在啓動類加@EnableCircuitBreaker註解

2.Hystrix儀表盤參數

參數 含義
Host 請求速率
Circuit 閥值
  1. 儀表盤數據通過sse server-send-event推送到前端
7.Turbine項目聚合監控
  1. 原理:通過將將自己註冊到註冊中心,發現同一個註冊中心上的hystrix服務,然後聚合數據。再通過暴露自己的端點,在儀表盤上進行展示
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章