主頁 > 後端開發 > Spring Cloud 純干貨,從入門到實戰

Spring Cloud 純干貨,從入門到實戰

2020-11-10 02:50:24 後端開發

導讀

  之前寫過一篇SpringCloud從入門到精通的點我直達,微服務基礎知識點我直達,今天我們使用Spring Cloud模擬一個電商專案,分別有以下2個服務,商品、訂單,下面我們開始叭

技術堆疊

  1. SpringBoot整合SpringCloud
  2. 通信方式:http restful
  3. 注冊中心:eruka
  4. 斷路器:hystrix
  5. 網關:zuul

商品服務

功能點

  • 商品串列
  • 商品詳情

訂單服務

功能點

  • 我的訂單
  • 下單介面

搭建Eureka Server

創建專案

專案結構

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.18.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.ybchen</groupId>
    <artifactId>eureka_server</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>eureka_server</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.SR6</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

啟動類上加注解

application.properties

# 服務埠號
server.port=8761
# eureka主機名
eureka.instance.hostname=localhost
# 指定當前主機是否需要向注冊中心注冊(不用,因為當前主機是Server,不是Client)
eureka.client.register-with-eureka=false
# 指定當前主機是否需要獲取注冊資訊(不用,因為當前主機是Server,不是Client)
eureka.client.fetch-registry=false
# 注冊中心地址
eureka.client.service-url.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/

啟動服務并查看監控臺

搭建Eureka Client商品服務

創建專案

專案結構

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.18.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.ybchen</groupId>
    <artifactId>product_service</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>product_service</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.SR6</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
View Code

application.properties

# 服務埠號
server.port=8771
# 服務名稱
spring.application.name=product_service
# 將服務注冊到注冊中心,eureka_service的地址
eureka.client.service-url.defaultZone:http://localhost:8761/eureka/

ProductController.java

package com.ybchen.product_service.controller;

import com.ybchen.product_service.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * @ClassName:ProductController
 * @Description:商品
 * @Author:chenyb
 * @Date:2020/11/1 8:42 下午
 * @Versiion:1.0
 */
@RestController
@RequestMapping("/api/v1/product")
public class ProductController {
    @Autowired
    private ProductService productService;

    /**
     * 商品串列
     *
     * @return
     */
    @PostMapping("list")
    public Object list() {
        return productService.listProduct();
    }

    /**
     * 根據id查詢商品
     *
     * @param id
     * @return
     */
    @GetMapping("findById")
    public Object findById(@RequestParam("id") int id) {
        return productService.findById(id);
    }
}

Product.java

package com.ybchen.product_service.domain;

import java.io.Serializable;

/**
 * @ClassName:Product
 * @Description:商品物體類
 * @Author:chenyb
 * @Date:2020/11/1 8:43 下午
 * @Versiion:1.0
 */
public class Product implements Serializable {
    /**
     * 內碼
     */
    private String id;
    /**
     * 商品名稱
     */
    private String name;
    /**
     * 價格,分為單位
     */
    private int price;
    /**
     * 庫存
     */
    private int store;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    public int getStore() {
        return store;
    }

    public void setStore(int store) {
        this.store = store;
    }

    public Product() {

    }

    public Product(String id, String name, int price, int store) {
        this.id = id;
        this.name = name;
        this.price = price;
        this.store = store;
    }

    @Override
    public String toString() {
        return "product{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                ", price=" + price +
                ", store=" + store +
                '}';
    }
}
View Code

ProductService.java

package com.ybchen.product_service.service;

import com.ybchen.product_service.domain.Product;

import java.util.List;

/**
 * @ClassName:ProductService
 * @Description:商品service
 * @Author:chenyb
 * @Date:2020/11/1 8:45 下午
 * @Versiion:1.0
 */
public interface ProductService {
    /**
     * 商品串列
     * @return
     */
    List<Product> listProduct();

    /**
     * 根據id查詢商品
     * @param id
     * @return
     */
    Product findById(int id);
}

ProductServiceImpl.java

package com.ybchen.product_service.service.impl;

import com.ybchen.product_service.domain.Product;
import com.ybchen.product_service.service.ProductService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * @ClassName:ProductServiceImpl
 * @Description:ProductService實作類
 * @Author:chenyb
 * @Date:2020/11/1 8:47 下午
 * @Versiion:1.0
 */
@Service
public class ProductServiceImpl implements ProductService {
    //初始化記憶體商品資料,模擬資料庫中存盤的商品
    private static final Map<Integer, Product> daoMap = new HashMap<>();

    @Value("${server.port}")
    private String port;
    static {
        for (int i = 0; i < 5; i++) {
            daoMap.put(i, new Product(i + "", "iphone_" + i, 1000 * i, 10));
        }
    }

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

    @Override
    public Product findById(int id) {
        Product product = daoMap.get(id);
        product.setName(product.getName()+"_"+port);
        return product;
    }
}

ProductServiceApplication.java

package com.ybchen.product_service;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ProductServiceApplication {

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

}

啟動并查看監控臺

  啟動2個服務,并查看監控臺

搭建Eureka Client訂單服務

創建專案

專案結構

 

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.18.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.ybchen</groupId>
    <artifactId>order_service</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>order_service</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.SR6</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

application.properties

# 服務埠號
server.port=8781
# 服務名稱
spring.application.name=order-service
# 將服務注冊到注冊中心,eureka_service的地址
eureka.client.service-url.defaultZone:http://localhost:8761/eureka/

OrderServiceApplication.java

  啟動類添加Ribbon注解

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
package com.ybchen.order_service;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
public class OrderServiceApplication {
    /**
     * 負載均衡Ribbon
     * @return
     */
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

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

}

OrderController.java

package com.ybchen.order_service.controller;

import com.ybchen.order_service.service.ProductOrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("api/v1/order")
public class OrderController {
    @Autowired
    private ProductOrderService productOrderService;
    @RequestMapping("save")
    public Object save(@RequestParam("user_id")int userId,@RequestParam("product_id")int productId){
        return productOrderService.save(userId,productId);
    }
}

ProductOrder.java

package com.ybchen.order_service.domain;

import java.util.Date;

/**
 * 商品訂單物體類
 */
public class ProductOrder {
    /**
     * 主鍵
     */
    private int id;
    /**
     * 商品名稱
     */
    private String productName;
    /**
     * 訂單流水號
     */
    private String tradeNo;
    /**
     * 價格,以分位單位
     */
    private int price;
    /**
     * 創建時間
     */
    private Date createTime;
    /**
     * 用戶id
     */
    private String userId;
    /**
     * 用戶名稱
     */
    private String userName;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getProductName() {
        return productName;
    }

    public void setProductName(String productName) {
        this.productName = productName;
    }

    public String getTradeNo() {
        return tradeNo;
    }

    public void setTradeNo(String tradeNo) {
        this.tradeNo = tradeNo;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    @Override
    public String toString() {
        return "ProductOrder{" +
                "id=" + id +
                ", productName='" + productName + '\'' +
                ", tradeNo='" + tradeNo + '\'' +
                ", price=" + price +
                ", createTime=" + createTime +
                ", userId='" + userId + '\'' +
                ", userName='" + userName + '\'' +
                '}';
    }
}
View Code

ProductOrderService.java

package com.ybchen.order_service.service;

import com.ybchen.order_service.domain.ProductOrder;

public interface ProductOrderService {
    ProductOrder save(int userId, int productId);
}

ProductOrderServiceImpl.java

package com.ybchen.order_service.service.impl;

import com.ybchen.order_service.domain.ProductOrder;
import com.ybchen.order_service.service.ProductOrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * @ClassName:ProductOrderServiceImpl
 * @Description:產品訂單實作類
 * @Author:chenyb
 * @Date:2020/11/2 11:34 下午
 * @Versiion:1.0
 */
@Service
public class ProductOrderServiceImpl implements ProductOrderService {
    @Autowired
    private RestTemplate restTemplate;
    /**
     * 下單介面
     * @param userId 用戶id
     * @param productId 產品id
     * @return
     */
    @Override
    public ProductOrder save(int userId, int productId) {
        Object obj=productId;
        //get方式
        Object forObject = restTemplate.getForObject("http://product-service/api/v1/product/findById?id=" + productId, Object.class);
        //post方式
//        Map<String,String> map=new HashMap<>();
//        map.put("id","1");
//        String s = restTemplate.postForObject("http://product-service/api/v1/product/test", map, String.class);
//        System.out.println(s);
        System.out.println(forObject);
        //獲取商品詳情
        ProductOrder productOrder=new ProductOrder();
        productOrder.setTradeNo(UUID.randomUUID().toString());
        productOrder.setCreateTime(new Date());
        productOrder.setUserId(userId+"");
        return productOrder;
    }
}

測驗負載均衡

feign實戰

簡介

  改造訂單服務,呼叫商品服務獲取商品資訊

官網例子

點我直達

改造訂單服務

添加feign依賴

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.18.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.ybchen</groupId>
    <artifactId>order_service</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>order_service</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.SR6</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--openfeign依賴-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

啟動類上添加注解

  啟動類上添加:@EnableFeignClients

 

添加一個介面

 

ProductClient.java

package com.ybchen.order_service.service;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

/**
 * 商品服務客戶端
 */
// name=商品服務的服務名==========》spring.application.name=product-service
@FeignClient(name = "product-service")
@RequestMapping("/api/v1/product")
public interface ProductClient {

    @GetMapping("findById")
    String findById(@RequestParam("id") int id);
}

修改ProductOrderServiceImpl.java

原先

package com.ybchen.order_service.service.impl;

import com.ybchen.order_service.domain.ProductOrder;
import com.ybchen.order_service.service.ProductOrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * @ClassName:ProductOrderServiceImpl
 * @Description:產品訂單實作類
 * @Author:chenyb
 * @Date:2020/11/2 11:34 下午
 * @Versiion:1.0
 */
@Service
public class ProductOrderServiceImpl implements ProductOrderService {
    @Autowired
    private RestTemplate restTemplate;
    /**
     * 下單介面
     * @param userId 用戶id
     * @param productId 產品id
     * @return
     */
    @Override
    public ProductOrder save(int userId, int productId) {
        Object obj=productId;
        //get方式
        Object forObject = restTemplate.getForObject("http://product-service/api/v1/product/findById?id=" + productId, Object.class);
        //post方式
//        Map<String,String> map=new HashMap<>();
//        map.put("id","1");
//        String s = restTemplate.postForObject("http://product-service/api/v1/product/test", map, String.class);
//        System.out.println(s);
        System.out.println(forObject);
        //獲取商品詳情
        ProductOrder productOrder=new ProductOrder();
        productOrder.setTradeNo(UUID.randomUUID().toString());
        productOrder.setCreateTime(new Date());
        productOrder.setUserId(userId+"");
        return productOrder;
    }
}

修改為

 

package com.ybchen.order_service.service.impl;

import com.ybchen.order_service.domain.ProductOrder;
import com.ybchen.order_service.service.ProductClient;
import com.ybchen.order_service.service.ProductOrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.UUID;

/**
 * @ClassName:ProductOrderServiceImpl
 * @Description:產品訂單實作類
 * @Author:chenyb
 * @Date:2020/11/2 11:34 下午
 * @Versiion:1.0
 */
@Service
public class ProductOrderServiceImpl implements ProductOrderService {
    @Autowired
    private ProductClient productClient;

    /**
     * 下單介面
     *
     * @param userId    用戶id
     * @param productId 產品id
     * @return
     */
    @Override
    public ProductOrder save(int userId, int productId) {
        //-----------呼叫商品服務開始------------
        String byId = productClient.findById(productId);
        System.out.println(byId);
        //-----------呼叫商品服務結束------------
        //獲取商品詳情
        ProductOrder productOrder = new ProductOrder();
        productOrder.setTradeNo(UUID.randomUUID().toString());
        productOrder.setCreateTime(new Date());
        productOrder.setUserId(userId + "");
        return productOrder;
    }
}

測驗商品服務

補充(設定服務呼叫超時時間)

  默認連接10秒,讀取60秒,但是由于hystrix默認是1秒超時

官網案例,點我直達

application.properties

# 設定連接和讀取超時時間
feign.client.config.default.connect-timeout=5000
feign.client.config.default.read-timeout=1100

服務降級熔斷(Hystrix)

為什么要用?

  在一個分布式系統里,一個服務依賴多個服務,可能存在某個服務呼叫失敗,比如超時、例外等,如何能保證在一個依賴出問題的情況下,不會導致整體服務故障,可以通過Hystrix來解決,

官網例子

  點我直達

修改訂單服務

添加依賴

        <!--hystrix依賴-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.18.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.ybchen</groupId>
    <artifactId>order_service</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>order_service</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.SR6</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--openfeign依賴-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!--hystrix依賴-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
View Code

啟動類加注解

  @EnableCircuitBreaker

修改控制層

  添加注解,@HystrixCommand,并定義回呼方法,回傳值、入參必須一致!!!!

入參、回傳值,不一致會報錯

feign結合Hystrix

修改訂單服務

開啟hystrix

 

# 開啟hystrix
feign.hystrix.enabled=true

ProductClient.java

 

package com.ybchen.order_service.service;

import com.ybchen.order_service.fallback.ProductClientFallBack;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;

/**
 * 商品服務客戶端
 */
// name=商品服務的服務名==========》spring.application.name=product-service
@FeignClient(name = "product-service",fallback = ProductClientFallBack.class)
//@RequestMapping("/api/v1/product")
public interface ProductClient {

    @GetMapping("/api/v1/product/findById")
    String findById(@RequestParam("id") int id);
}

ProductClientFallBack.java

 

package com.ybchen.order_service.fallback;

import com.ybchen.order_service.service.ProductClient;
import org.springframework.stereotype.Component;

/**
 * 針對商品服務,做降級處理
 */
@Component
public class ProductClientFallBack implements ProductClient {
    @Override
    public String findById(int id) {
        System.out.println("商品服務被降級了~~~~~~~");
        return null;
    }
}

驗證商品服務熔斷

  為什么對商品服務做了熔斷,還回傳這個結果呢,那是因為service實作類,內部發生了錯誤

 

熔斷降級服務報警通知(重要)

  下面寫一些偽代碼,比如:xxx微服務掛了,然后通過短信、郵件的方式,通知相應的開發人員,緊急處理事故等,

修改訂單服務

修改hystrix超時時間

禁用超時時間(不推薦)

hystrix.command.default.execution.timeout.enabled=false

設定超時時間(推薦)

hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=4000

原始碼位置講解

  通過這種方法,還可以設定更多的hystrix默認值

斷路器Dashboard監控儀表盤

修改訂單服務

添加依賴

        <!--hystrix儀表盤-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.18.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.ybchen</groupId>
    <artifactId>order_service</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>order_service</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.SR6</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--openfeign依賴-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!--hystrix依賴-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <!--hystrix儀表盤-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
pom.xml

啟動類上加注解

  添加:@EnableHystrixDashboard

 

修改組態檔

# 暴露全部的監控資訊
management.endpoints.web.exposure.include=*

訪問儀表盤

http://127.0.0.1:8781/hystrix

http://127.0.0.1:8781/actuator/hystrix.stream

  儀表盤實際作業中用處不大(仁者見仁智者見智),純屬學習用,具體引數,請自行百度,只要把微服務熔斷/降級報警通知處理好,比啥都好??

微服務網關Zuul

創建專案

專案結構

 

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.11.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.ybchen</groupId>
    <artifactId>api-gateway</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>api-gateway</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR8</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
pom.xml

application.properties

server.port=8800
# 服務名稱
spring.application.name=api-gateway
# 將服務注冊到注冊中心,eureka_service的地址
eureka.client.service-url.defaultZone:http://localhost:8761/eureka/

默認訪問規則

  http://gateway:port/service-id/**

  比如,原先下單地址:127.0.0.1:8781/api/v1/order/save?user_id=1&product_id=1

  現在下單地址:127.0.0.1:8800/order-service/api/v1/order/save?user_id=1&product_id=1

自定義路由規則

  添加application.properties資訊

 

# 自定義路由規則,語法:zuul.routes.服務名=自定義路由
zuul.routes.order-service=/apigate/**
# 不讓默認的服務對外暴露介面,語法:zuul.ignored-patterns=服務名
zuul.ignored-patterns=/order-service/**
# 忽略所有服務
# zuul.ignored-patterns=*

處理http請求頭為空的問題

   默認zuul過濾3個值("Cookie", "Set-Cookie", "Authorization"),解決版本,設定為不過濾

原始碼解讀

添加屬性

# 處理http請求頭為空的問題
zuul.sensitive-headers=

自定義Zuul過濾器之登錄鑒權

改造api-gateway專案

新建LoginFilter類

  新建該類,并繼承ZuulFilter,重寫里面的方法

 

package com.ybchen.apigateway.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import java.io.PrintWriter;

import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;

/**
 * @ClassName:LoginFilter
 * @Description:登錄過濾器
 * @Author:chenyb
 * @Date:2020/11/8 11:16 下午
 * @Versiion:1.0
 */
@Component //讓Spring掃描到
public class LoginFilter extends ZuulFilter {
    /**
     * 過濾型別,有以下型別
     * 1、pre
     * 2、route
     * 3、post
     * 4、error
     *
     * @return
     */
    @Override
    public String filterType() {
        return PRE_TYPE;
    }

    /**
     * 過濾器順序,越小越先執行
     *
     * @return
     */
    @Override
    public int filterOrder() {
        return 4;
    }

    /**
     * 過濾器是否生效
     *
     * @return
     */
    @Override
    public boolean shouldFilter() {
        //1、獲取背景關系
        RequestContext currentContext = RequestContext.getCurrentContext();
        //2、獲取HttpServletRequest
        HttpServletRequest request = currentContext.getRequest();
        //3、拿到請求路徑,判斷是否進行攔截
        //System.out.println(request.getRequestURL()); //http://127.0.0.1:8800/apigate/order/api/v1/order/save
        //System.out.println(request.getRequestURI()); ///apigate/order/api/v1/order/save
        String url = request.getRequestURI();
        System.out.println("請求路徑url=========>" + url);
        if ((url == null ? "" : url.toLowerCase()).startsWith("/apigate/order")) {
            return true;
        }
        return false;
    }

    /**
     * 過濾器邏輯,業務邏輯
     *
     * @return
     * @throws ZuulException
     */
    @Override
    public Object run() throws ZuulException {
        System.out.println("請求被攔截啦==============");
        //JWT方式
        //1、獲取背景關系
        RequestContext currentContext = RequestContext.getCurrentContext();
        //2、獲取HttpServletRequest
        HttpServletRequest request = currentContext.getRequest();
        //3、拿到token
        String token = request.getHeader("token"); //請求頭拿token
        if (token == null || "".equals(token)) {
            token = request.getParameter("token"); //get方式拿token
        }
        //登錄校驗邏輯,這里推薦JWT方式,做登錄鑒權
        if (token == null || "".equals(token)) {
            //4、不讓繼續往下走
            currentContext.setSendZuulResponse(false);
            //5、設定狀態碼,401,Unauthorized
            currentContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
            sendJsonMessage(currentContext.getResponse(),"用戶未登錄");
        }
        return null;
    }
    /**
     * 回應json資料給前端
     *
     * @param response
     * @param obj
     */
    private void sendJsonMessage(HttpServletResponse response, Object obj) {
        try {
            ObjectMapper objectMapper = new ObjectMapper();
            response.setContentType("application/json;charset=utf-8");
            PrintWriter writer = response.getWriter();
            writer.print(objectMapper.writeValueAsString(obj));
            writer.close();
            writer.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

用戶登錄鑒權測驗

補充

  登錄鑒權,推薦使用JWT方式,下面我提供我之前的一個專案,JWT的工具類,和攔截器的部分關鍵代碼

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.7.0</version>
        </dependency>
pom.xml依賴
package net.ybclass.online_ybclass.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import net.ybclass.online_ybclass.model.entity.User;

import java.util.Date;

/**
 * JWT工具類
 * 注意點:
 * 1、生成的token,是可以通過base64進行解密出銘文資訊
 * 2、base64進行解密出明文資訊,修改再進行編碼,則會解密失敗
 * 3、無法作廢已頒布的token,除非改密鑰
 */
public class JWTUtils {
    /**
     * 過期時間,一周
     */
    static final long EXPIRE = 60000 * 60 * 24 * 7;
    /**
     * 加密密鑰
     */
    private static final String SECRET = "ybclass.net168";
    /**
     * 令牌前綴
     */
    private static final String TOKEN_PREFIX = "ybclass";
    /**
     * 主題
     */
    private static final String SUBJECT = "ybclass";

    /**
     * 根據用戶資訊,生成令牌
     *
     * @param user
     * @return
     */
    public static String geneJsonWebToken(User user) {
        String token = Jwts.builder().setSubject(SUBJECT)
                .claim("head_img", user.getHeadImg())
                .claim("id", user.getId())
                .claim("name", user.getName())
                .setIssuedAt(new Date()) //令牌頒布時間
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRE)) //過期時間
                .signWith(SignatureAlgorithm.HS256, SECRET) //加密方式
                .compact();
        token = TOKEN_PREFIX + token;
        return token;
    }

    /**
     * 校驗token方法
     *
     * @param token
     * @return
     */
    public static Claims checkJWT(String token) {
        try {
            final Claims claims = Jwts.parser()
                    .setSigningKey(SECRET)
                    .parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
                    .getBody();
            return claims;
        } catch (Exception e) {
            return null;
        }
    }
}
JWTUtils.java
try {
            String accesToken = request.getHeader("token");
            if (accesToken == null) {
                accesToken = request.getParameter("token");
            }
            if (StringUtils.isNoneBlank(accesToken)) {
                Claims claims = JWTUtils.checkJWT(accesToken);
                if (claims == null) {
                    sendJsonMessage(response, JsonData.buildError("登陸過期,請重新登陸"));
                    //告訴登陸過期,重新登陸
                    return false;
                }
                Integer id = (Integer) claims.get("id");
                String name = (String) claims.get("name");
                request.setAttribute("user_id", id);
                request.setAttribute("name", name);
                return true;
            }
        } catch (Exception e) {
        }
        //登陸失敗
        sendJsonMessage(response, JsonData.buildError("登陸過期,請重新登陸"));
        return false;



=================
        User user = userMapper.findByPhoneAndPwd(phone, CommonUtils.MD5(pwd));
        return user == null ? null : JWTUtils.geneJsonWebToken(user);
攔截器部分代碼

網關Zuul介面限流

  采用谷歌guava框架,網關限流

改造api-gateway專案

創建OrderRatelimiterFilter

  然后繼承ZuulFilter,并使用springcloud繼承的guava技術,只針對訂單介面限流!!!

 

package com.ybchen.apigateway.filter;

import com.google.common.util.concurrent.RateLimiter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.http.HttpStatus;

import javax.servlet.http.HttpServletRequest;

import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;

/**
 * @ClassName:OrderRateLimiterFilter
 * @Description:訂單介面限流
 * @Author:chenyb
 * @Date:2020/11/9 11:32 下午
 * @Versiion:1.0
 */
public class OrderRateLimiterFilter extends ZuulFilter {
    //限流令牌,每秒創建多少令牌,注意:springcloud 默認集成guava
    private static final RateLimiter RATE_LIMITER = RateLimiter.create(1000);

    @Override
    public String filterType() {
        return PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        return -4;
    }

    @Override
    public boolean shouldFilter() {
        //1、獲取背景關系
        RequestContext currentContext = RequestContext.getCurrentContext();
        //2、獲取HttpServletRequest
        HttpServletRequest request = currentContext.getRequest();
        String url = request.getRequestURI();
        System.out.println("限流請求路徑url=========>" + url);
        //只對訂單介面限流
        if ((url == null ? "" : url.toLowerCase()).startsWith("/apigate/order")) {
            return true;
        }
        return false;
    }

    @Override
    public Object run() throws ZuulException {
        RequestContext currentContext = RequestContext.getCurrentContext();
        HttpServletRequest request = currentContext.getRequest();
        if (!RATE_LIMITER.tryAcquire()) {
            currentContext.setSendZuulResponse(false);
            currentContext.setResponseStatusCode(HttpStatus.TOO_MANY_REQUESTS.value());
        }
        return null;
    }
}

Zuul集群

  技術堆疊:nginx+lvs+keepalive

案例原始碼下載

鏈接: https://pan.baidu.com/s/1bNIh-8nSCMcU7FjVVzlBnA  密碼: 4wf9

 

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/208613.html

標籤:其他

上一篇:編碼風格:Mvc模式下SSM環境,代碼分層管理

下一篇:Java高并發6-ThreadLocal內部各種方法實作原理

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more