做招聘網(wǎng)站的客戶想退錢網(wǎng)站seo快速排名優(yōu)化
didi單體應(yīng)用架構(gòu)
將項(xiàng)目所有模塊(功能)打成jar或者war,然后部署一個進(jìn)程--醫(yī)院掛號系統(tǒng);
> 優(yōu)點(diǎn):
> 1:部署簡單:由于是完整的結(jié)構(gòu)體,可以直接部署在一個服務(wù)器上即可。
> 2:技術(shù)單一:項(xiàng)目不需要復(fù)雜的技術(shù)棧,往往一套熟悉的技術(shù)棧ssm就可以完成開發(fā)。
>
> 缺點(diǎn):
> 1:系統(tǒng)啟動慢,一個進(jìn)程包含了所有的業(yè)務(wù)邏輯,涉及到的啟動模塊過多,導(dǎo)致系統(tǒng)的啟動、重啟時間周期過長;
>
> 2:系統(tǒng)錯誤隔離性差、可用性差,任何一個模塊的錯誤均可能造成整個系統(tǒng)的宕機(jī);
>
> 3:可伸縮性差:系統(tǒng)的擴(kuò)容只能只對這個應(yīng)用進(jìn)行擴(kuò)容,無法結(jié)合業(yè)務(wù)模塊的特點(diǎn)進(jìn)行伸縮。
>
> 4: 線上問題修復(fù)周期長:任何一個線上問題修復(fù)需要對整個應(yīng)用系統(tǒng)進(jìn)行全面升級。
>
> 5: 跨語言程度差 [必須統(tǒng)一用一門編程一些]
>
> 6: 不利于安全管理,所有開發(fā)人員都擁有全量代碼。> 單體架構(gòu)===管理類項(xiàng)目 比如: 倉庫管理系統(tǒng) OA辦公系統(tǒng) 銀行系統(tǒng){內(nèi)部使用的系統(tǒng)}
>
> 不適合電商系統(tǒng)---->
微服務(wù)應(yīng)用70%
微服務(wù)架構(gòu)論文: Microservices
譯文: 微服務(wù)譯文理解_微服務(wù)架構(gòu)翻譯-CSDN博客
In short, the microservice architectural style [1] is an approach to developing a single application as a suite of small services, each running in its own process and communicating with lightweight mechanisms, often an HTTP resource API. These services are built around business capabilities and independently deployable by fully automated deployment machinery. There is a bare minimum of centralized management of these services, which may be written in different programming languages and use different data storage technologies
簡單來說,微服務(wù)架構(gòu)風(fēng)格[1]是一種將一個單一應(yīng)用程序開發(fā)為一組小型服務(wù)的方法,每個服務(wù)運(yùn)行在自己的進(jìn)程中,服務(wù)間通信采用輕量級通信機(jī)制(通常用HTTP資源API)。這些服務(wù)圍繞業(yè)務(wù)能力構(gòu)建并且可通過全自動部署機(jī)制獨(dú)立部署。這些服務(wù)共用一個最小型的集中式的管理,服務(wù)可用不同的語言開發(fā),使用不同的數(shù)據(jù)存儲技術(shù)。
--把一個完整的項(xiàng)目--拆分為若干個小型的項(xiàng)目
解讀微服務(wù)
1:微服務(wù)是一種==項(xiàng)目架構(gòu)思想==(風(fēng)格)
2:微服務(wù)架構(gòu)是一系列小服務(wù)的組合(組件化與多服務(wù))
3:任何一個微服務(wù),都是一個獨(dú)立的進(jìn)程(獨(dú)立開發(fā)、獨(dú)立維護(hù)、獨(dú)立部署)
4:輕量級通信http協(xié)議(跨語言,跨平臺) 服務(wù)與服務(wù)之間可以通過http協(xié)議進(jìn)行相互調(diào)用
5:服務(wù)粒度(圍繞業(yè)務(wù)功能拆分---模塊拆分【系統(tǒng)管理服務(wù)】【日志服務(wù)】【焦慮測試】【抑郁測試系統(tǒng)】)
6:去中心化管理(去中心化"地治理技術(shù)、去中心化地管理數(shù)據(jù));
微服務(wù)架構(gòu)的優(yōu)勢
1.易于開發(fā)和維護(hù)
一個微服務(wù)只關(guān)注一個特定的業(yè)務(wù)功能,所以它的業(yè)務(wù)清晰、代碼量較少。開發(fā)和維護(hù)單個微服務(wù)相對比較簡單,整個應(yīng)用是由若干個微服務(wù)構(gòu)建而成,所以整個應(yīng)用也會維持在可控狀態(tài);
⒉.單個微服務(wù)啟動較快
單個微服務(wù)代碼量較少,所以啟動會比較快;
3.局部修改容易部署
單體應(yīng)用只要有修改,就要重新部署整個應(yīng)用,微服務(wù)解決了這樣的問題。一般來說,對某個微服務(wù)進(jìn)行修改,只需要重新部署這個服務(wù)即可;
4.技術(shù)棧不受限
在微服務(wù)中,我們可以結(jié)合項(xiàng)目業(yè)務(wù)及團(tuán)隊(duì)的特點(diǎn),合理地選擇技術(shù)棧;
5.按需伸縮
患者系統(tǒng)訪問量大,只需要對患者系統(tǒng)進(jìn)行擴(kuò)展;
微服務(wù)架構(gòu)的缺點(diǎn)--(挑戰(zhàn))
l 這么多小服務(wù),如何管理他們?
l 這么多小服務(wù),他們之間如何通訊?
l 這么多小服務(wù),客戶端怎么訪問他們?
l 這么多小服務(wù),一旦出現(xiàn)問題了,應(yīng)該如何自處理?
l 這么多小服務(wù),一旦出現(xiàn)問題了,應(yīng)該如何排錯?
SpringCloud與微服務(wù)的關(guān)系
-
Springcloud為微服務(wù)思想提供了完美的解決方案;
-
Springcloud是一些列框架的集合體(服務(wù)的注冊與發(fā)現(xiàn)【注冊中心】、服務(wù)間遠(yuǎn)程調(diào)用、服務(wù)降級、服務(wù)熔斷、服務(wù)限流、分布式事務(wù)等);
-
微服務(wù):它是一種架構(gòu)思想;
springcloud:就是解決微服務(wù)架構(gòu)思想面臨的挑戰(zhàn);
SpringBoot與SpringCloud的關(guān)系
-
SpringBoot專注于快速方便的開發(fā)單個個體微服務(wù)。
-
SpringCloud是關(guān)注全局的微服務(wù)協(xié)調(diào)、整理、治理的框架,它將SpringBoot開發(fā)的單體整合并管理起來。
-
SpringBoot可以離開SpringCloud獨(dú)立使用開發(fā)項(xiàng)目,但是SpringCloud離不開SpringBoot,屬于依賴關(guān)系。
電商案例:(搭建的是微服務(wù)架構(gòu)項(xiàng)目服務(wù))
第一步:準(zhǔn)備/創(chuàng)建數(shù)據(jù)庫 ;(商品數(shù)據(jù)庫? ,訂單數(shù)據(jù)庫)
商品數(shù)據(jù)庫;? 數(shù)據(jù)庫名:springcloud
創(chuàng)建表結(jié)構(gòu)
DROP TABLE IF EXISTS `shop_product`;
CREATE TABLE `shop_product` (`pid` bigint(0) NOT NULL AUTO_INCREMENT COMMENT '商品id',`pname` varchar(255) CHARACTER SET utf8mb4 NULL DEFAULT NULL COMMENT '商品名',`pprice` decimal(10, 2) NULL DEFAULT NULL COMMENT '商品價格',`stock` varchar(255) CHARACTER SET utf8mb4 NULL DEFAULT NULL COMMENT '商品庫存',PRIMARY KEY (`pid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 ROW_FORMAT = Dynamic;
插入數(shù)據(jù)
INSERT INTO `shop_product` VALUES (1, '華為手機(jī)', 1999.00, '100');
INSERT INTO `shop_product` VALUES (2, 'vivo手機(jī)', 2999.00, '100');
INSERT INTO `shop_product` VALUES (3, '小米', 2222.00, '1000');SET FOREIGN_KEY_CHECKS = 1;
訂單數(shù)據(jù)庫;數(shù)據(jù)庫名:springcloud-order
#訂單數(shù)據(jù)庫#
DROP TABLE IF EXISTS `shop_order`;
CREATE TABLE `shop_order` (`oid` bigint(0) NOT NULL AUTO_INCREMENT COMMENT '訂單id',`uid` int(0) NULL DEFAULT NULL COMMENT '用戶id',`username` varchar(255) CHARACTER SET utf8mb4 NULL DEFAULT NULL COMMENT '用戶名',`pid` bigint(0) NULL DEFAULT NULL COMMENT '商品id',`pname` varchar(255) CHARACTER SET utf8mb4 NULL DEFAULT NULL COMMENT '商品名稱',`pprice` decimal(10, 2) NULL DEFAULT NULL COMMENT '商品價格',`number` int(0) NULL DEFAULT NULL COMMENT '購買數(shù)量',PRIMARY KEY (`oid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 61741 CHARACTER SET = utf8mb4 ROW_FORMAT = Dynamic;
第二步:創(chuàng)建一個父工程;
創(chuàng)建一個工程
注意: 把src目錄刪除,并在pom.xml中添加打包方式為pom打包。
引入相關(guān)依賴
<!--引入父依賴--><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.2.RELEASE</version></parent><!--定義版本號--><properties><java.version>1.8</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><!--springcloud的版本--><spring-cloud.version>Hoxton.SR8</spring-cloud.version><!--springcloudalibaba的版本號--><spring-cloud-alibaba.version>2.2.3.RELEASE</spring-cloud-alibaba.version></properties><!--dependencyManagement:只用于jar的管理 不負(fù)責(zé)jar的下載。 后期子工程使用jar時無需指定版本號--><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><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>${spring-cloud-alibaba.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement>
注意: springboot和springcloud以及springcloud alibaba他們版本必須匹配https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E
第三步:創(chuàng)建一個公共模塊?
模塊名字:spring-common
所有微服務(wù)都需要用到的內(nèi)容,可以放入到公共模塊中。未來:放置實(shí)體類 工具類等;
引入依賴
<dependencies><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.1</version></dependency></dependencies>
創(chuàng)建實(shí)體類entity?
?product類
package com.aaa.entity;import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;import java.math.BigDecimal;/**/
@Data
@TableName(value = "shop_product")
public class Product {@TableId(type = IdType.AUTO)private Integer pid;private String pname;//double 存在精度丟失 金額不要使用double 使用類型bigdecimalprivate BigDecimal pprice;private Integer stock;}
Order類
package com.aaa.entity;import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;import java.math.BigDecimal;@Data
@TableName(value = "shop_order")
public class Order {@TableId(type = IdType.AUTO)private Integer oid;private Integer uid;private String username;//并沒有只寫商品idprivate Integer pid;private String pname;private BigDecimal pprice;private Integer number;}
第四步:創(chuàng)建商品微服務(wù);
模塊名字:spring-product
??
? ?引入依賴
<!--引入公共模塊--><dependency><groupId>com.aaa</groupId><artifactId>spring-common</artifactId><version>1.0-SNAPSHOT</version></dependency><!--mysql驅(qū)動--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!--springbootweb依賴--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--引入nacos的依賴--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency>
創(chuàng)建配置文件
application.properties
# 商品微服務(wù)的端口號[8001~8009]
server.port=8001
# 數(shù)據(jù)源
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/springcloud?serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=root# mybatis sql日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl#指定nacos服務(wù)器地址
spring.cloud.nacos.discovery.server-addr=localhost:8848
#指定服務(wù)名
spring.application.name=qy172-product
創(chuàng)建dao層接口
ProductDao
package com.aaa.dao;import com.aaa.entity.Product;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;public interface ProductDao extends BaseMapper<Product> {
}
創(chuàng)建service層
ProductService接口
package com.aaa.service;import com.aaa.entity.Product;public interface ProductService {/*** 根據(jù)商品id查詢商品信息* @param id* @return*/public Product getBiId(Integer id);
}
創(chuàng)建service實(shí)現(xiàn)層
? ProductServiceImpl?
package com.aaa.service.impl;import com.aaa.dao.ProductDao;
import com.aaa.entity.Product;
import com.aaa.service.ProductService;
import org.springframework.stereotype.Service;import javax.annotation.Resource;@Service
public class ProductServiceImpl implements ProductService {@Resourceprivate ProductDao productDao;@Overridepublic Product getBiId(Integer id) {return productDao.selectById(id);}
}
創(chuàng)建controller
ProductController
package com.aaa.controller;import com.aaa.entity.Product;
import com.aaa.service.ProductService;
import com.baomidou.mybatisplus.annotation.TableId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/product")
public class ProductController {@Autowiredprivate ProductService productService;@GetMapping("/selectById/{id}")public Product selecId(@PathVariable Integer id){Product product = productService.getBiId(id);return product;}}
創(chuàng)建主啟動類
package com.aaa;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
@MapperScan(basePackages = "com.aaa.dao") //dao接口掃描
public class ProductApp {public static void main(String[] args) {SpringApplication.run(ProductApp.class,args);}
}
訪問
第五步:創(chuàng)建訂單微服務(wù)?
模塊名字? ?spring-order
引入依賴
<!--引入公共模塊--><dependency><groupId>com.aaa</groupId><artifactId>spring-common</artifactId><version>1.0-SNAPSHOT</version></dependency><!--mysql驅(qū)動--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!--springbootweb依賴--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--引入nacos的依賴--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency>
創(chuàng)建配置文件
application.properties? ? ?
注:訂單微服務(wù)和商品微服務(wù)配置文件唯一不同的就是數(shù)據(jù)庫名字不同
# 訂單微服務(wù)的端口號[8001~8009]
server.port=8001
# 數(shù)據(jù)源
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/springcloud-order?serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=root# mybatis sql日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl#指定nacos服務(wù)器地址
spring.cloud.nacos.discovery.server-addr=localhost:8848
#指定服務(wù)名
spring.application.name=qy172-order
創(chuàng)建dao層接口
OrderDao
package com.aaa.dao;import com.aaa.entity.Order;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;public interface Orderdao extends BaseMapper<Order> {
}
創(chuàng)建service層
OrderService
package com.aaa.service;import com.aaa.entity.Order;public interface OrderService {public int inserorder(Order order);
}
創(chuàng)建service實(shí)現(xiàn)層
OrderServiceimpl
package com.aaa.service.impl;import com.aaa.dao.Orderdao;
import com.aaa.entity.Order;
import com.aaa.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import javax.annotation.Resource;@Service
public class OrderServiceimpl implements OrderService {@Resourceprivate Orderdao orderdao;@Overridepublic int inserorder(Order order) {int insert = orderdao.insert(order);return insert;}
}
創(chuàng)建controller
OrderController
package com.aaa.controller;import com.aaa.entity.Order;
import com.aaa.entity.Product;
import com.aaa.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;import javax.annotation.Resource;
import java.net.URI;
import java.util.List;
import java.util.Random;@RestController
@RequestMapping("order")
public class OrderController {@Resourceprivate OrderService orderService;@Autowiredprivate RestTemplate restTemplate;@Autowiredprivate DiscoveryClient discoveryClient;@GetMapping("save")//傳參 商品編號+數(shù)量public String save(Integer pid,Integer num){Order order = new Order();//訂單購買數(shù)量order.setNumber(num);//獲取商品ID+用戶信息order.setUid(1);order.setUsername("小明");//獲取注冊中心實(shí)例表List<ServiceInstance> instances = discoveryClient.getInstances("qy172-product");//獲取列表的第一個實(shí)列
// ServiceInstance serviceInstance = instances.get(0);
// String scheme = serviceInstance.getScheme();
// System.out.println("協(xié)議類型" + scheme);
// String host = serviceInstance.getHost();
// System.out.println("主機(jī)地址" + host);
// int port = serviceInstance.getPort();
// System.out.println("實(shí)列的端口號" + port);
// URI uri = serviceInstance.getUri();
// System.out.println("uri:"+uri);//隨機(jī)生成一個實(shí)例下標(biāo)int index = new Random().nextInt(instances.size());ServiceInstance serviceInstance = instances.get(index);URI uri = serviceInstance.getUri();Product product = restTemplate.getForObject(uri+"/product/selectById/"+pid,Product.class);// Product product = restTemplate.getForObject("http://qy172-product/product/selectById/"+pid,Product.class);// Product product = restTemplate.getForObject("http://localhost:8001/product/selectById/"+pid,Product.class);if(product == null){return "下單失敗,商品不存在";}order.setPid(product.getPid());order.setPname(product.getPname());order.setPprice(product.getPprice());int i = orderService.inserorder(order);return "下單成功";}
}
注:上面RestTemplate在spring容器中默認(rèn)不存在的,需要創(chuàng)建該對象并交于spring容器管理。
創(chuàng)建啟動類
OrderApp
package com.aaa;import org.mybatis.spring.annotation.MapperScan;
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
@MapperScan(basePackages = "com.aaa.dao") //dao接口掃描
public class OrderApp {public static void main(String[] args) {SpringApplication.run(OrderApp.class,args);}//告訴RestTemplate遠(yuǎn)程調(diào)用時需要使用負(fù)載均衡ribbon@LoadBalanced@Beanpublic RestTemplate restTemplate(){return new RestTemplate();}
}
訪問??
服務(wù)治理
什么是服務(wù)治理?
服務(wù)治理是微服務(wù)架構(gòu)中最核心最基本的模塊。用于實(shí)現(xiàn)各個微服務(wù)的自動化注冊與發(fā)現(xiàn)。
服務(wù)注冊:在服務(wù)治理框架中,都會構(gòu)建一個注冊中心,每個服務(wù)單元向注冊中心登記自己提供服
務(wù)的詳細(xì)信息。并在注冊中心形成一張服務(wù)的清單,服務(wù)注冊中心需要以心跳30s 90s的方式去監(jiān)測清單中 的服務(wù)是否可用,如果不可用,需要在服務(wù)清單中剔除不可用的服務(wù)。
服務(wù)發(fā)現(xiàn):服務(wù)調(diào)用方向服務(wù)注冊中心咨詢服務(wù),并獲取所有服務(wù)的實(shí)例清單,實(shí)現(xiàn)對具體服務(wù)實(shí);
通過上面的調(diào)用圖會發(fā)現(xiàn),除了微服務(wù),還有一個組件是服務(wù)注冊中心,它是微服務(wù)架構(gòu)非常重要
的一個組件,在微服務(wù)架構(gòu)里主要起到了協(xié)調(diào)者的一個作用。注冊中心一般包含如下幾個功能:
????????1. 服務(wù)發(fā)現(xiàn):
服務(wù)注冊:保存服務(wù)提供者和服務(wù)調(diào)用者的信息;
服務(wù)訂閱:服務(wù)調(diào)用者訂閱服務(wù)提供者的信息,注冊中心向訂閱者推送提供者的信息;
????????2. 服務(wù)配置:
配置訂閱:服務(wù)提供者和服務(wù)調(diào)用者訂閱微服務(wù)相關(guān)的配置;
配置下發(fā):主動將配置推送給服務(wù)提供者和服務(wù)調(diào)用者;
????????3. 服務(wù)健康檢測
檢測服務(wù)提供者的健康情況,如果發(fā)現(xiàn)異常,執(zhí)行服務(wù)剔除;
常見的注冊中心
Zookeeper
zookeeper是一個分布式服務(wù)框架,是Apache Hadoop 的一個子項(xiàng)目,它主要是用來解決分布式
應(yīng)用中經(jīng)常遇到的一些數(shù)據(jù)管理問題,如:統(tǒng)一命名服務(wù)、狀態(tài)同步服務(wù)、集群管理、分布式應(yīng)用
配置項(xiàng)的管理等。
Eureka
springcloud很多組件都是使用的是netflix公司。--停更了---springcloud alibaba
Eureka是Springcloud Netflix中的重要組件,主要作用就是做服務(wù)注冊和發(fā)現(xiàn)。但是現(xiàn)在已經(jīng)閉
源 ,停更不停用。
Consul
Consul是基于GO語言開發(fā)的開源工具,主要面向分布式,服務(wù)化的系統(tǒng)提供服務(wù)注冊、服務(wù)發(fā)現(xiàn)
和配置管理的功能。Consul的功能都很實(shí)用,其中包括:服務(wù)注冊/發(fā)現(xiàn)、健康檢查、Key/Value
存儲、多數(shù)據(jù)中心和分布式一致性保證等特性。Consul本身只是一個二進(jìn)制的可執(zhí)行文件,所以
安裝和部署都非常簡單,只需要從官網(wǎng)下載后,在執(zhí)行對應(yīng)的啟動腳本即可。
Nacos (服務(wù)治理 配置中心)
Nacos是一個更易于構(gòu)建云原生應(yīng)用的動態(tài)服務(wù)發(fā)現(xiàn)、配置管理和服務(wù)管理平臺。它是 Spring
Cloud Alibaba 組件之一,負(fù)責(zé)服務(wù)注冊發(fā)現(xiàn)和服務(wù)配置,可以這樣認(rèn)為nacos=eureka+config。
nacos簡介
Nacos 致力于幫助您發(fā)現(xiàn)、配置和管理微服務(wù)。Nacos 提供了一組簡單易用的特性集,幫助您快速實(shí)現(xiàn)動態(tài)服務(wù)發(fā)現(xiàn)、服務(wù)配置、服務(wù)元數(shù)據(jù)及流量管理。
從上面的介紹就可以看出,nacos的作用就是一個注冊中心,用來管理注冊上來的各個微服務(wù)。
nacos實(shí)戰(zhàn)入門
接下來,我們就在現(xiàn)有的環(huán)境中加入nacos,并將我們的兩個微服務(wù)注冊上去。
Releases · alibaba/nacos · GitHub?nacos安裝網(wǎng)站
安裝nacos服務(wù)治理組件
解壓即可
?默認(rèn)nacos啟動模式為集群模式。 修改模式為單機(jī)模式。
修改文件目錄:nacos/bin/startup.cmd文件
修改完成以后雙擊啟動nacos組件(startup.cmd)
訪問nacos服務(wù)
注:無法啟動nacosl常見的兩種情況:
1.nacos放在中文目錄;
2.jdk環(huán)境設(shè)置有問題;
把微服務(wù)注冊到注冊中心nacos
引入依賴
分別把該依賴分別添加在商品微服務(wù)架構(gòu)和訂單微服務(wù)架構(gòu)中
<!--引入nacos的依賴--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency>
修改配置文件
分別修改商品微服務(wù)和訂單微服務(wù)的application.properties配置文件
商品微服務(wù)
訂單微服務(wù)
啟動微服務(wù)
修改訂單服務(wù)和商品服務(wù)的耦合問題
通過以上修改訂單服務(wù)和商品服務(wù)的耦合問題解決了 一旦服務(wù)提供者地址變化,就需要手工修改代碼;我們修改完地址以后,只需要重新運(yùn)行一下修改地址的微服務(wù)即可;
實(shí)現(xiàn)服務(wù)調(diào)用的負(fù)載均衡
什么是負(fù)載均衡?
通俗的講, 負(fù)載均衡就是將負(fù)載(工作任務(wù),訪問請求)進(jìn)行分?jǐn)偟蕉鄠€操作單元(服務(wù)器,組件)上進(jìn)行執(zhí)行。
根據(jù)負(fù)載均衡發(fā)生位置的不同,一般分為服務(wù)端負(fù)載均衡和客戶端負(fù)載均衡。
服務(wù)端負(fù)載均衡指的是發(fā)生在服務(wù)提供者一方,比如常見的nginx負(fù)載均衡
而客戶端負(fù)載均衡指的是發(fā)生在服務(wù)請求的一方,也就是在發(fā)送請求之前已經(jīng)選好了由哪個實(shí)例處理請求;
我們在微服務(wù)調(diào)用關(guān)系中一般會選擇*客戶端負(fù)載均衡*,也就是在服務(wù)調(diào)用的一方來決定服務(wù)由哪個提供者執(zhí)行.
搭建商品微服務(wù)的集群
自定義負(fù)載均衡
上面是自己手寫了一個隨機(jī)負(fù)載均衡策略代碼,如果后期我想使用其他的負(fù)載均衡策略,手動改上面的代碼。--使用第三方提供的負(fù)載均衡組件來實(shí)現(xiàn)。---ribbon
什么是ribbon?
專門實(shí)現(xiàn)微服務(wù)客戶端負(fù)載均衡的。目前阿里巴巴沒有提供負(fù)載均衡器。
是 Netflix 發(fā)布的一個負(fù)載均衡器,有助于控制 HTTP 和 TCP客戶端行為。在 SpringCloud 中, nacos一般配合Ribbon進(jìn)行使用,Ribbon提供了(客戶端負(fù)載均衡的功能),Ribbon利用從nacos中讀 取到的服務(wù)信息,在調(diào)用服務(wù)節(jié)點(diǎn)提供的服務(wù)時,會合理(策略)的進(jìn)行負(fù)載。 在SpringCloud中可以將注冊中心和Ribbon配合使用,Ribbon自動的從注冊中心中獲取服務(wù)提供者的 列表信息,并基于內(nèi)置的負(fù)載均衡算法,請求服務(wù);
注:使用ribbon無需引入任何依賴。因?yàn)閚acos依賴中自帶了ribbon;
修改controller類
測試結(jié)果為:ribbon的負(fù)載均衡策略為輪詢。 ?
ribbon內(nèi)部提供了哪些負(fù)載均衡策略
你可以自己定義策略【很少】---你可以仿造上面的策略寫自己的策略類。
如何改變相應(yīng)的策略
#指定ribbon的負(fù)載均衡策略
qy172-product.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
#服務(wù)提供者的名稱.ribbon.NFLoadBalancerRuleClassName=負(fù)載均衡策略類全路徑
openfeign
openfeign組件,完成我們的遠(yuǎn)程調(diào)用的。 我們的編程習(xí)慣controller---->service--->dao
需要在controller注入一個service對象并調(diào)用service鐘相應(yīng)的方法傳遞相應(yīng)的參數(shù),根據(jù)方法返回的結(jié)果 使用相應(yīng)的類型接受;
什么是openfeign
OpenFeign是Spring Cloud提供的一個聲明式的偽Http客戶端, 它使得(調(diào)用遠(yuǎn)程服務(wù)就像調(diào)用本地服務(wù)一樣簡單), 只需要創(chuàng)建一個接口并添加一個注解即可。?
Nacos很好的兼容了OpenFeign, OpenFeign負(fù)載均衡默認(rèn)集成了 Ribbon, 所以在Nacos下使用Fegin默認(rèn)就實(shí)現(xiàn)了負(fù)載均衡的效果。
如何使用openfeign
第一步:引入openfeign依賴;
<!--引入openfeign依賴--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency>
第二步:創(chuàng)建一個接口;
?ProductFeign接口
package com.aaa.feign;import com.aaa.entity.Product;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;@FeignClient(value ="qy172-product" )
public interface ProductFeign {@GetMapping("/product/selectById/{id}")public Product selectById(@PathVariable Integer id);
}
第三步:開啟openfeign注解;
第四步:修改controller代碼;
Gateway服務(wù)網(wǎng)關(guān)
思考問題:
大家都都知道在微服務(wù)架構(gòu)中,一個系統(tǒng)會被拆分為很多個微服務(wù)。那么作為客戶端(pc android ios 平板)要如何去調(diào)用這么多的微服務(wù)呢?如果沒有網(wǎng)關(guān)的存在,我們只能在客戶端記錄每個微服務(wù)的地址,然后分別去調(diào)用。
這樣的架構(gòu),會存在著諸多的問題:
l 客戶端多次請求不同的微服務(wù),增加客戶端代碼或配置編寫的復(fù)雜性
l 認(rèn)證復(fù)雜,每個服務(wù)都需要獨(dú)立認(rèn)證。
l 存在跨域請求,在一定場景下處理相對復(fù)雜。
上面的這些問題可以借助API網(wǎng)關(guān)來解決。
所謂的API網(wǎng)關(guān),就是指系統(tǒng)的統(tǒng)一入口,它封裝了應(yīng)用程序的內(nèi)部結(jié)構(gòu),為客戶端提供統(tǒng)一服 務(wù),一些與業(yè)務(wù)本身功能無關(guān)的公共邏輯可以在這里實(shí)現(xiàn),諸如認(rèn)證、鑒權(quán)、監(jiān)控(黑白名單)、路由轉(zhuǎn)發(fā)等等。 添加上API網(wǎng)關(guān)之后,系統(tǒng)的架構(gòu)圖變成了如下所示:
在業(yè)界比較流行的網(wǎng)關(guān),有下面這些:
Ngnix+lua
使用nginx的反向代理和負(fù)載均衡可實(shí)現(xiàn)對api服務(wù)器的負(fù)載均衡及高可用
lua是一種腳本語言,可以來編寫一些簡單的邏輯, nginx支持lua腳本
Kong
基于Nginx+Lua開發(fā),性能高,穩(wěn)定,有多個可用的插件(限流、鑒權(quán)等等)可以開箱即用。 問題:
只支持Http協(xié)議;二次開發(fā),自由擴(kuò)展困難;提供管理API,缺乏更易用的管控、配置方式。
?Zuul 1.0(慢 servlet 2.0 ) zuul2.0 沒出來。
Netflix開源的網(wǎng)關(guān),功能豐富,使用JAVA開發(fā),易于二次開發(fā) 問題:缺乏管控,無法動態(tài)配
置;依賴組件較多;處理Http請求依賴的是Web容器,性能不如Nginx
?Spring Cloud Gateway
Spring公司為了替換Zuul而開發(fā)的網(wǎng)關(guān)服務(wù),將在下面具體介紹。
注意:**SpringCloud alibaba技術(shù)棧中并沒有提供自己的網(wǎng)關(guān),我們可以采用Spring Cloud Gateway來做網(wǎng)關(guān)**
Gateway簡介
Spring Cloud Gateway是Spring公司基于Spring 5.0,Spring Boot 2.0 和 Project Reactor 等術(shù)開發(fā)的網(wǎng)關(guān),它旨在為微服務(wù)架構(gòu)提供一種簡單有效的統(tǒng)一的 API 路由管理方式。它的目標(biāo)是替代 Netflix Zuul,其不僅提供統(tǒng)一的路由方式,并且基于 Filter 鏈的方式提供了網(wǎng)關(guān)基本的功能,例如:安全,監(jiān)控和限流。
優(yōu)點(diǎn):
l 性能強(qiáng)勁:是第一代網(wǎng)關(guān)Zuul的1.6倍;
l 功能強(qiáng)大:內(nèi)置了很多實(shí)用的功能,例如轉(zhuǎn)發(fā)、監(jiān)控、限流等;
l 設(shè)計(jì)優(yōu)雅,容易擴(kuò)展.;
缺點(diǎn):
l 其實(shí)現(xiàn)依賴Netty與WebFlux,不是傳統(tǒng)的Servlet編程模型,學(xué)習(xí)成本高;
l 不能將其部署在Tomcat、Jetty等Servlet容器里,只能打成jar包執(zhí)行 web.Jar;
l 需要Spring Boot 2.0及以上的版本,才支持;
gateway內(nèi)置了服務(wù)器 netty服務(wù)器。千萬不要在使用tomcat作為服務(wù)器。---千萬不要引入;springboot-web依賴;
Gateway快速入門
第一步:創(chuàng)建網(wǎng)關(guān)微服務(wù);
第二步:引入依賴;
<!--加入網(wǎng)關(guān)依賴--><!--引入網(wǎng)關(guān)以后,不要在引入web依賴--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency>
第三步:修改配置文件;
server:port: 7777
spring:application:name: shopping-gatewaycloud:gateway:routes:- id: qy172-producturi: http://localhost:8001predicates:- Path=/product/**- id: qy172-orderuri: http://localhost:9001predicates:- Path=/order/**
第四步:創(chuàng)建主啟動類;
第五步:訪問;
增強(qiáng)版
現(xiàn)在在配置文件中寫死了轉(zhuǎn)發(fā)路徑的地址, 前面我們已經(jīng)分析過地址寫死帶來的問題, 接下來我們從注冊中心獲取此地址。
gateway本身也是一個微服務(wù),那么它也可以從nacos注冊中心拉取其他服務(wù)信息,調(diào)用其他微服務(wù)。
第一步:從Gateway網(wǎng)關(guān)微服務(wù)引入nacos依賴;
<!--加入網(wǎng)關(guān)依賴--><!--引入網(wǎng)關(guān)以后,不要在引入web依賴--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency><!--加入nacos依賴--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency>
第二步:拉去微服務(wù)信息從而完成轉(zhuǎn)發(fā)功能;
server:port: 7777
spring:application:name: shopping-gatewaycloud:gateway:routes:- id: qy172-producturi: lb://qy172-productpredicates:- Path=/product/**- id: qy172-orderuri: lb://qy172-orderpredicates:- Path=/order/**nacos:discovery:server-addr: localhost:8848register-enabled: falseenabled: true
第三步:測試訪問;
簡寫版
如果未來增加了一個微服務(wù),那么網(wǎng)關(guān)這里還需要修改配置文件。
可以使用網(wǎng)關(guān)簡寫版--->可能不太使用。--開啟網(wǎng)關(guān)的路由自動定位功能;
第一步:修改配置文件
server:port: 7777
spring:application:name: qy172-gatewaynacos:discovery:server-addr: localhost:8848register-enabled: falseenabled: truecloud:gateway:discovery:locator:enabled: true
第二步:訪問
Gateway核心架構(gòu)
基本概念
路由(Route) 是 gateway 中最基本的組件之一,表示一個具體的路由信息載體。主要定義了下面的幾個信息:
l id,路由標(biāo)識符,區(qū)別于其他 Route。默認(rèn)生成一個 UUID
l uri,路由指向的目的地 uri,即客戶端請求最終被轉(zhuǎn)發(fā)到的微服務(wù)。
l order,用于多個 Route 之間的排序,數(shù)值越小排序越靠前,匹配優(yōu)先級越高。
l predicate,斷言的作用是進(jìn)行條件判斷,只有斷言都返回真,才會真正的執(zhí)行路由。
l filter,過濾器用于修改請求和響應(yīng)信息。
執(zhí)行流程
執(zhí)行流程大體如下:
1. Gateway Client向Gateway Server發(fā)送請求;
2. 請求首先會被HttpWebHandlerAdapter進(jìn)行提取組裝成網(wǎng)關(guān)上下文;
3. 然后網(wǎng)關(guān)的上下文會傳遞到DispatcherHandler,它負(fù)責(zé)將請求分發(fā)給 RoutePredicateHandlerMapping;
4. RoutePredicateHandlerMapping負(fù)責(zé)路由查找,并根據(jù)路由斷言判斷路由是否可用;
5. 如果過斷言成功,由FilteringWebHandler創(chuàng)建過濾器鏈并調(diào)用;
6. 請求會一次經(jīng)過PreFilter--微服務(wù)--PostFilter的方法,最終返回響應(yīng);
斷言
Predicate(斷言, 謂詞) 用于進(jìn)行條件判斷,只有斷言都返回真,才會真正的執(zhí)行路由。
斷言就是說: 在 什么條件下 才能進(jìn)行路由轉(zhuǎn)發(fā);
內(nèi)置路由斷言工廠
SpringCloud Gateway包括許多內(nèi)置的斷言工廠,所有這些斷言都與HTTP請求的不同屬性匹配體如下:
l *基于**Datetime**類型的斷言工廠*
此類型的斷言根據(jù)時間做判斷,主要有三個:
AfterRoutePredicateFactory: 接收一個日期參數(shù),判斷請求日期是否晚于指定日期
BeforeRoutePredicateFactory: 接收一個日期參數(shù),判斷請求日期是否早于指定日期
BetweenRoutePredicateFactory: 接收兩個日期參數(shù),判斷請求日期是否在指定時間段內(nèi)
-After=2019-12-31T23:59:59.789+08:00[Asia/Shanghai]
l *基于遠(yuǎn)程地址的斷言工廠* *RemoteAddrRoutePredicateFactory**:*
接收一個IP地址段,判斷請求主機(jī)地址是否在地址段中
-RemoteAddr=192.168.1.1/24
l *基于**Cookie**的斷言工廠*
CookieRoutePredicateFactory:接收兩個參數(shù),cookie 名字和一個正則表達(dá)式。 判斷請求
cookie是否具有給定名稱且值與正則表達(dá)式匹配。
-Cookie=chocolate, ch.
l *基于**Header**的斷言工廠*
HeaderRoutePredicateFactory:接收兩個參數(shù),標(biāo)題名稱和正則表達(dá)式。 判斷請求Header是否
具有給定名稱且值與正則表達(dá)式匹配。 key value
-Header=X-Request-Id, \d+
l *基于**Host**的斷言工廠*
HostRoutePredicateFactory:接收一個參數(shù),主機(jī)名模式。判斷請求的Host是否滿足匹配規(guī)則。
-Host=**.testhost.org
l *基于**Method**請求方法的斷言工廠*
MethodRoutePredicateFactory:接收一個參數(shù),判斷請求類型是否跟指定的類型匹配。
-Method=GET
l *基于**Path**請求路徑的斷言工廠*
PathRoutePredicateFactory:接收一個參數(shù),判斷請求的URI部分是否滿足路徑規(guī)則。
-Path=/foo/{segment}基于Query請求參數(shù)的斷言工廠
QueryRoutePredicateFactory :接收兩個參數(shù),請求param和正則表達(dá)式, 判斷請求參數(shù)是否具
有給定名稱且值與正則表達(dá)式匹配。
-Query=baz, ba.
l *基于路由權(quán)重的斷言工廠*
WeightRoutePredicateFactory:接收一個[組名,權(quán)重], 然后對于同一個組內(nèi)的路由按照權(quán)重轉(zhuǎn)發(fā)
routes:
-id: weight_route1 uri: host1 predicates:
-Path=/product/**
-Weight=group3, 1
-id: weight_route2 uri: host2 predicates:
-Path=/product/**
-Weight= group3, 9
如果上面的斷言無法滿足要求 可以自定義斷言。 仿造人家提供的斷言。
第一步:創(chuàng)建?AgeRoutePredicateFactory類
package com.aaa.predicate;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.server.ServerWebExchange;import javax.validation.constraints.NotNull;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;@Component
public class AgeRoutePredicateFactory extends AbstractRoutePredicateFactory<AgeRoutePredicateFactory.Config> {public AgeRoutePredicateFactory() {super(AgeRoutePredicateFactory.Config.class);}public List<String> shortcutFieldOrder() {return Arrays.asList("min", "max");}@Overridepublic Predicate<ServerWebExchange> apply(Config config) {return new Predicate<ServerWebExchange>() {@Overridepublic boolean test(ServerWebExchange serverWebExchange) {//獲取請求攜帶年齡--請求地址中或請求頭ServerHttpRequest request = serverWebExchange.getRequest();String age = request.getHeaders().getFirst("age");if(age==null){return false;}int i = Integer.parseInt(age);if(i>=config.getMin()&&i<=config.getMax()){return true;}return false;}};}@Validatedpublic static class Config {//最大年齡和最小年齡@NotNullprivate Integer min;@NotNullprivate Integer max;public Config() {}public Integer getMin() {return min;}public void setMin(Integer min) {this.min = min;}public Integer getMax() {return max;}public void setMax(Integer max) {this.max = max;}}
}
第二步:修改配置文件
server:port: 7777
spring:application:name: qy172-gatewaycloud:gateway:routes:- id: qy172-producturi: lb://qy172-productpredicates:- Path=/product/**- Age=18,50- id: qy172-orderuri: lb://qy172-orderpredicates:- Path=/order/**nacos:discovery:server-addr: localhost:8848register-enabled: falseenabled: true
第三步:使用第三方測試軟件-------Postman測試年齡是否在min和max區(qū)間,在區(qū)間可以跳轉(zhuǎn),不在區(qū)間顯示404;
gateway網(wǎng)關(guān)完成統(tǒng)一認(rèn)證
認(rèn)證判斷
當(dāng)客戶端第一次請求服務(wù)時,服務(wù)端對用戶進(jìn)行信息認(rèn)證(登錄);
認(rèn)證通過,將用戶信息進(jìn)行加密形成token,返回給客戶端,作為登錄憑證;
以后每次請求,客戶端都攜帶認(rèn)證的token;
服務(wù)端對token進(jìn)行解密,判斷是否有效。
1. 在網(wǎng)關(guān)中需要寫的業(yè)務(wù)代碼:[1]獲取訪問的資源路徑[2]判斷該資源路徑是否為白名單--放行的資源的路徑 比如登錄 注冊 發(fā)生驗(yàn)證碼[3]判斷是否攜帶token令牌以及令牌是否有效。[4]如果攜帶了token令牌--放行[5]如果沒有攜帶token令牌--返回一個json數(shù)據(jù)
網(wǎng)關(guān)中如何實(shí)現(xiàn)認(rèn)證的校驗(yàn) ?
第一步:創(chuàng)建filter層? ??LoginFilter類
package com.aaa.filter;import com.aaa.vo.Result;
import com.aaa.white.WhiteList;
import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;import java.nio.charset.StandardCharsets;
import java.util.List;
//表示該類的對象交于spring容器管理
@Component
public class LoginFilter implements GlobalFilter, Ordered {@Autowiredprivate WhiteList whiteList;@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {//1.獲取request對象ServerHttpRequest request = exchange.getRequest();ServerHttpResponse response = exchange.getResponse();//2.獲取訪問資源路徑String path = request.getPath().toString();//3.獲取屬于白名單 無需認(rèn)證即可訪問的資源List<String> list = whiteList.getList();if(list.contains(path)){return chain.filter(exchange); //放行}//4.獲取攜帶token--請求頭String token = request.getHeaders().getFirst("token");if(token != null && "admin".equals(token)){return chain.filter(exchange); //放行}//5.返回一個json數(shù)據(jù)Result result = new Result(402,"登錄無效",null);//json轉(zhuǎn)換---沒有引入fastjson依賴byte[] bytes = JSON.toJSONString(result).getBytes(StandardCharsets.UTF_8);//調(diào)用bufferFactory方法,生成DataBuffer對象DataBuffer buffer = response.bufferFactory().wrap(bytes);//調(diào)用Mono中的just方法,返回要寫給前端的JSON數(shù)據(jù)return response.writeWith(Mono.just(buffer));}//值越小優(yōu)先級越高@Overridepublic int getOrder() {return 0;}
}
第二步:創(chuàng)建 vo 和 white層???????
???
Result類
package com.aaa.vo;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result {private Integer code;private String msg;private Object data;
}
WhiteList類
package com.aaa.white;import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;import java.util.List;@Data
@Component
@ConfigurationProperties(prefix = "white")
public class WhiteList {private List<String> list;
}
第三步:配置application.yml文件?
server:port: 7777
spring:application:name: qy172-gatewaycloud:gateway:routes:- id: qy172-producturi: lb://qy172-productpredicates:- Path=/product/**- id: qy172-orderuri: lb://qy172-orderpredicates:- Path=/order/**nacos:discovery:server-addr: localhost:8848register-enabled: falseenabled: truewhite:list:- /sso/login- /user/register- /msg/sendMsg- /order/save
第四步:使用第三方測試軟件Postman測試數(shù)據(jù);
不帶token
帶token? ?密碼必須是admin
可以正常獲取數(shù)據(jù)
帶token? ?密碼是admin
Gateway解決跨域
Gateway解決跨域的兩種方式
第一種方式:基于跨域配置類;
1.創(chuàng)建config層? CorConfig類
package com.aaa.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;@Configuration
public class CorConfig {// 當(dāng)前跨域請求最大有效時長。這里默認(rèn)1天private static final long MAX_AGE = 24 * 60 * 60;@Beanpublic CorsWebFilter corsFilter() {CorsConfiguration config = new CorsConfiguration();config.addAllowedOrigin("*"); //允許任意域跨域訪問該服務(wù)config.addAllowedHeader("*");// springboot升級成2.4.0以上時對AllowedOrigin設(shè)置發(fā)生了改變,不能有”*“,可以替換成AllowedOriginPatternconfig.addAllowedMethod("*"); //允許任意的請求方式 GET POSTconfig.setAllowCredentials(true);// 必須是reactive包下的UrlBasedCorsConfigurationSourceUrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();source.registerCorsConfiguration("/**", config);return new CorsWebFilter(source);}
}
?2.使用WebStorm運(yùn)行前端項(xiàng)目
3.在這里輸入npm run serve運(yùn)行前端項(xiàng)目
?下面這種情況就是出現(xiàn)的跨域問題
第二種方式:使用配置文件的方式解決;(企業(yè)常用解決跨域的方案)
配置application.yml文件
注:不添加允許訪問的路徑的話,允許控制臺會出現(xiàn)”跨域問題“?;
server:port: 7777
spring:application:name: qy172-gatewaycloud:gateway:routes:- id: qy172-producturi: lb://qy172-productpredicates:- Path=/product/**- id: qy172-orderuri: lb://qy172-orderpredicates:- Path=/order/**globalcors:cors-configurations:'[/**]':allowedOrigins:- "http://localhost:8001"- "http://localhost:9001"- "http://localhost:8080"allowedMethods:- "GET"- "POST"- "DELETE"- "PUT"- "OPTIONS"allowedHeaders: "*"allowedCredentials: truemaxAge: 36000add-to-simple-url-handler-mapping: truenacos:discovery:server-addr: localhost:8848register-enabled: falseenabled: truewhite:list:- /sso/login- /user/register- /msg/sendMsg- /order/save
鏈路追蹤
在大型系統(tǒng)的微服務(wù)化構(gòu)建中,一個系統(tǒng)被拆分成了許多微服務(wù)。這些微服務(wù)負(fù)責(zé)不同的功能,組合成系統(tǒng),最終可以提供豐富的功能。在這種架構(gòu)中,一次請求往往需要涉及到多個服務(wù)。互聯(lián)網(wǎng)應(yīng)用構(gòu)建在不同的軟件模塊集上,這些軟件模塊,有可能是由不同的團(tuán)隊(duì)開發(fā)、可能使用不同的編程語言來實(shí)現(xiàn)、有可能布在了幾千臺服務(wù)器,橫跨多個不同的數(shù)據(jù)中心【區(qū)域】,也就意味著這種架構(gòu)形式也會存在一些問題:
l 如何快速發(fā)現(xiàn)問題?
l 如何判斷故障影響范圍?
l 如何梳理服務(wù)依賴?
l 如何分析鏈路性能問題以及實(shí)時容量規(guī)劃?
分布式鏈路追蹤(Distributed Tracing),就是將一次分布式請求還原成調(diào)用鏈路,進(jìn)行日志記錄,性能監(jiān)控并將一次分布式請求的調(diào)用情況集中展示。比如各個服務(wù)節(jié)點(diǎn)上的耗時、請求具體到達(dá)哪臺機(jī)器上IP、每個服務(wù)節(jié)點(diǎn)的請求狀態(tài)200 500等等。
常見鏈路追蹤技術(shù)棧有哪些
cat 由大眾點(diǎn)評開源,基于Java開發(fā)的實(shí)時應(yīng)用監(jiān)控平臺,包括實(shí)時應(yīng)用監(jiān)控,業(yè)務(wù)監(jiān)控 。 集成
方案是通過代碼埋點(diǎn)【代碼】的方式來實(shí)現(xiàn)監(jiān)控,比如: 攔截器,過濾器等。 對代碼的侵入性很大,集成成本較高。風(fēng)險(xiǎn)較大。
zipkin 由Twitter公司開源,開放源代碼分布式的跟蹤系統(tǒng),用于==收集服務(wù)的定時數(shù)據(jù)==,以解決微
服務(wù)架構(gòu)中的延遲問題,包括:數(shù)據(jù)的收集、存儲、查找和展現(xiàn)《圖形化》。該產(chǎn)品結(jié)合spring-cloud-sleuth 使用較為簡單, 集成很方便, 但是功能較簡單。
pinpoint Pinpoint是韓國人開源的基于字節(jié)碼注入的調(diào)用鏈分析,以及應(yīng)用監(jiān)控分析工具。特點(diǎn)
是支持多種插件,UI功能強(qiáng)大,接入端無代碼侵入。
skywalking 【未來企業(yè)會使用的多】
SkyWalking是本土開源的基于字節(jié)碼注入的調(diào)用鏈分析,以及應(yīng)用監(jiān)控分析工具。特點(diǎn)是支持多
種插件,UI功能較強(qiáng),接入端無代碼侵入。目前已加入Apache孵化器。 ---火。
Sleuth (日志記錄每一條鏈路上的所有節(jié)點(diǎn),以及這些節(jié)點(diǎn)所在的機(jī)器,和耗時。)log4j
SpringCloud 提供的分布式系統(tǒng)中鏈路追蹤解決方案。
注:在微服務(wù)架構(gòu)使用的是sleuth+zipkin結(jié)合使用;
鏈路追蹤就是為了快速定位微服務(wù)故障所在地。
sleuth就是為了生成微服務(wù)日志。---查看起來比較麻煩。
zipkin搜集sleuth生成的日志,并以圖像化展示。
sleuth介紹
SpringCloud Sleuth主要功能就是在分布式系統(tǒng)中提供追蹤解決方案。它大量借用了Google Dapper的設(shè)計(jì), 先來了解一下Sleuth中的術(shù)語和相關(guān)概念。
*Trace* *(一條完整鏈路--包含很多span(微服務(wù)接口))*
由一組Trace Id(貫穿整個鏈路)相同的Span串聯(lián)形成一個樹狀結(jié)構(gòu)。為了實(shí)現(xiàn)請求跟蹤,當(dāng)請求到達(dá)分布式系統(tǒng)的入口端點(diǎn)時,只需要服務(wù)跟蹤框架為該請求創(chuàng)建一個唯一的標(biāo)識(即TraceId),同時在分布式系統(tǒng)內(nèi)部流轉(zhuǎn)的時候,框架始終保持傳遞該唯一值,直到整個請求的返回。那么我們就可以使用該唯一標(biāo)識將所有的請求串聯(lián)起來,形成一條完整的請求鏈路。
*Span*
代表了一組基本的工作單元。為了統(tǒng)計(jì)各處理單元的延遲,當(dāng)請求到達(dá)各個服務(wù)組件的時候,也通過一個唯一標(biāo)識(SpanId)來標(biāo)記它的開始、具體過程和結(jié)束。通過SpanId的開始和結(jié)束時間戳,就能統(tǒng)計(jì)該span的調(diào)用時間,除此之外,我們還可以獲取如事件的名稱。請求信息等元數(shù)據(jù)。
*Annotation*
用它記錄一段時間內(nèi)的事件,內(nèi)部使用的重要注釋:
l cs(Client Send)客戶端發(fā)出請求,開始一個請求的命令
l sr(Server Received)服務(wù)端接受到請求開始進(jìn)行處理, sr-cs = 網(wǎng)絡(luò)延遲(服務(wù)調(diào)用的時間)
l ss(Server Send)服務(wù)端處理完畢準(zhǔn)備發(fā)送到客戶端,ss - sr = 服務(wù)器上的請求處理時間
l cr(Client Reveived)客戶端接受到服務(wù)端的響應(yīng),請求結(jié)束。 cr - cs = 請求的總時間
sleuth使用
注:在各個微服務(wù)引入sleuth的依賴即可,為了方便使用我這里把依賴就加在父工程pom文件里;
在父工程中加入sleuth依賴
<dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-sleuth</artifactId></dependency></dependencies>
從上面sleuth產(chǎn)生的日志可以看出,他們的traceId是相同,每個微服務(wù)都有自己的spanid,把這些traceId相同的span串聯(lián)在一起形成了一個分布式鏈路。可以每個span的時間。我也可以看出該span消化的時間。如果這樣來看的話,非常麻煩。在實(shí)際開發(fā)中是絕對不允許。 我們應(yīng)該使用zipkin來搜集上面這樣的日志,然后形成一個圖像化界面,通過查看界面則可看出那個微服務(wù)出現(xiàn)故障;
zipkin原理圖
根據(jù)上面的原理; 我們需要安裝一個zipkin的服務(wù)端。
Central Repository: io/zipkin/zipkin-server
訪問網(wǎng)址??
在父工程中添加zipkin依賴
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-zipkin</artifactId></dependency>
把搜集的日志存在數(shù)據(jù)庫中
上面我們?nèi)绻匦聠觶ipkin 我們可以看到鏈路丟失 默認(rèn)存在內(nèi)存。 我們可以指定存放在數(shù)據(jù)庫
CREATE TABLE IF NOT EXISTS zipkin_spans (`trace_id_high` BIGINT NOT NULL DEFAULT 0 COMMENT 'If non zero, this means the trace uses 128 bit traceIds instead of 64 bit',
`trace_id` BIGINT NOT NULL,
`id` BIGINT NOT NULL,
`name` VARCHAR(255) NOT NULL,
`parent_id` BIGINT,
`debug` BIT(1),
`start_ts` BIGINT COMMENT 'Span.timestamp(): epoch micros used for endTs query and to implement TTL',
`duration` BIGINT COMMENT 'Span.duration(): micros used for minDuration and maxDuration query' )ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;ALTER TABLE zipkin_spans ADD UNIQUE KEY(`trace_id_high`, `trace_id`, `id`) COMMENT 'ignore insert on duplicate';
ALTER TABLE zipkin_spans ADD INDEX(`trace_id_high`, `trace_id`, `id`) COMMENT 'for joining with zipkin_annotations';
ALTER TABLE zipkin_spans ADD INDEX(`trace_id_high`, `trace_id`) COMMENT 'for getTracesByIds';ALTER TABLE zipkin_spans ADD INDEX(`name`) COMMENT 'for getTraces and getSpanNames';ALTER TABLE zipkin_spans ADD INDEX(`start_ts`) COMMENT 'for getTraces ordering and range';
CREATE TABLE IF NOT EXISTS zipkin_annotations (`trace_id_high` BIGINT NOT NULL DEFAULT 0 COMMENT 'If non zero, this means the trace uses 128 bit traceIds instead of 64 bit',
`trace_id` BIGINT NOT NULL COMMENT 'coincides with zipkin_spans.trace_id',
`span_id` BIGINT NOT NULL COMMENT 'coincides with zipkin_spans.id',`a_key` VARCHAR(255) NOT NULL COMMENT 'BinaryAnnotation.key or Annotation.value if type == -1',
`a_value` BLOB COMMENT 'BinaryAnnotation.value(), which must be smaller than 64KB',
`a_type` INT NOT NULL COMMENT 'BinaryAnnotation.type() or -1 if Annotation',
`a_timestamp` BIGINT COMMENT 'Used to implement TTL; Annotation.timestamp or zipkin_spans.timestamp',`endpoint_ipv4` INT COMMENT 'Null when Binary/Annotation.endpoint is null',
`endpoint_ipv6` BINARY(16) COMMENT 'Null when Binary/Annotation.endpoint is null, or no IPv6 address',
`endpoint_port` SMALLINT COMMENT 'Null when Binary/Annotation.endpoint is null',
`endpoint_service_name` VARCHAR(255) COMMENT 'Null when Binary/Annotation.endpoint is null' )ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;
ALTER TABLE zipkin_annotations ADD UNIQUE KEY(`trace_id_high`, `trace_id`, `span_id`, `a_key`, `a_timestamp`) COMMENT 'Ignore insert on duplicate';
ALTER TABLE zipkin_annotations ADD INDEX(`trace_id_high`, `trace_id`, `span_id`) COMMENT 'for joining with zipkin_spans';
ALTER TABLE zipkin_annotations ADD INDEX(`trace_id_high`, `trace_id`) COMMENT 'for getTraces/ByIds';
ALTER TABLE zipkin_annotations ADD INDEX(`endpoint_service_name`) COMMENT 'for getTraces and getServiceNames';ALTER TABLE zipkin_annotations ADD INDEX(`a_type`) COMMENT 'for getTraces';ALTER TABLE zipkin_annotations ADD INDEX(`a_key`) COMMENT 'for getTraces';
ALTER TABLE zipkin_annotations ADD INDEX(`trace_id`, `span_id`, `a_key`) COMMENT 'for dependencies job';
CREATE TABLE IF NOT EXISTS zipkin_dependencies (
`day` DATE NOT NULL,
`parent` VARCHAR(255) NOT NULL,
`child` VARCHAR(255) NOT NULL,`call_count` BIGINT )
ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;
ALTER TABLE zipkin_dependencies ADD UNIQUE KEY(`day`, `parent`, `child`);
運(yùn)行zipkin
java -jar zipkin-server-2.25.2-exec.jar --STORAGE_TYPE=mysql --
MYSQL_HOST=127.0.0.1 --MYSQL_TCP_PORT=3306 --MYSQL_DB=zipkin --MYSQL_USER=root --MYSQL_PASS=root
?
微服務(wù)保護(hù)
初始Sentinel
雪崩問題
微服務(wù)調(diào)用鏈路中的某個服務(wù)故障,引起整個鏈路中的所有微服務(wù)都不可用,這就是雪崩;
解決雪崩問題的常見四種方式:
超時處理:設(shè)定超時時間,請求超過一定時間沒有響應(yīng)就返回錯誤信息,不會無休止等待;
艙壁模式:限定每個業(yè)務(wù)就使用的線程數(shù),避免耗盡整個tomcat的資源,因此也叫線程隔離;
熔斷降級:由斷路器統(tǒng)計(jì)業(yè)務(wù)執(zhí)行的異常比例,如果超出閾值則會熔斷該業(yè)務(wù),攔截訪問業(yè)務(wù)的一切請求;
流量控制:限制業(yè)務(wù)訪問的QPS,避免服務(wù)因流量的突增而故障;
如何避免因瞬間高并發(fā)流量而導(dǎo)致服務(wù)故障?? 流量控制;
如何避免因服務(wù)故障引起的雪崩問題?超時處理,艙壁模式(線程隔離),熔斷降級;
服務(wù)保護(hù)技術(shù)對比
?
認(rèn)識Sentinel
官方網(wǎng)址:http://sentinelguard.io/zh-cn/index.html
要想Sentinel結(jié)合微服務(wù)一起使用,項(xiàng)目結(jié)構(gòu)Springcloud;
第一步:我們也可以在父工程或者指定的模塊pom文件添加Sentinel依賴;
我這里使用的在指定的模塊添加Sentienl依賴? ?spring-product商品服務(wù)模塊
<!--添加Sentienl依賴--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId></dependency>
第二步:在配置文件中添加訪問路徑
spring.cloud.sentinel.transport.dashboard=localhost:8090
注:要是在父工程添加依賴的話,所有模塊的配置文件都需要添加訪問路徑;
配置文件yml結(jié)構(gòu)
第三步:訪問spring-product商品服務(wù)模塊
第四步:在Sentienl組件中查看spring-product商品服務(wù)模塊的信息;
限流規(guī)則
簇點(diǎn)鏈路? ? ?
簇點(diǎn)鏈路:就是項(xiàng)目內(nèi)的調(diào)用鏈路,鏈路中被監(jiān)控的每個接口就是一個資源,默認(rèn)情況下? sentienl會監(jiān)控SpringMVC的每一個端點(diǎn),因此SpringMVC的每一個端點(diǎn)就是調(diào)用鏈路中的一個資源;
流控、熔斷等都是針對簇點(diǎn)鏈路中的資源來設(shè)置的,因此我們可以點(diǎn)擊對應(yīng)資源后面的按鈕來設(shè)置規(guī)則;
快速入門
流控模式
在添加限流規(guī)則時,點(diǎn)擊高級選項(xiàng),可以選擇三種流控模式;
直接:統(tǒng)計(jì)當(dāng)前資源的請求,觸發(fā)閾值時對資源直接限流,也就是默認(rèn)的模式;
誰觸發(fā)閾值,誰限流;
關(guān)聯(lián):統(tǒng)計(jì)與當(dāng)前資源相關(guān)的另一個資源,觸發(fā)閾值時,對當(dāng)前資源限流;
鏈路:統(tǒng)計(jì)從指定鏈路訪問到本資源請求,觸發(fā)閾值時,對指定的鏈路限流;
誰使用鏈路,誰限流;
?
線程數(shù)限流
???????
?
?
?
?