參考博文:(5條消息) [狂神說Java]SpringCloud筆記_笶生的博客-CSDN博客
(5條消息) 狂神說SpringCloud學習筆記(附帶源碼和筆記)_學不死就往死里學-CSDN博客
1、SpringCloud入門概述
1.1 SpringCloud是什么
springcloud官網: https://spring.io/projects/spring-cloud#learn
1.3 Dubbo 和 SpringCloud技術選型
1、分布式+服務治理Dubbo
目前成熟的互聯網架構:應用服務化拆分+消息中間件
2、Dubbo 和 SpringCloud對比
可以看一下社區活躍度
https://github.com/dubbo
https://github.com/springcloud
1.4 SpringCloud能干什么
1.5 SpringCloud在哪下
官網 : https://spring.io/projects/spring-cloud/
2、總體介紹
2.1SpringCloud版本選擇
3、創建父工程
直接創建一個名為SpringCloud的Maven空項目即可
然后后面全部的項目都是父工程的一個子模塊,並且都是maven的空項目
建立一個數據庫:db01
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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.bupt</groupId> <artifactId>springcloudLast</artifactId> <packaging>pom</packaging> <version>1.0-SNAPSHOT</version> <modules> <module>springcloud-api</module> <module>springcloud-consumer-dept-80</module> <module>springcloud-eureka-7001</module> <module>springcloud-provider-dept-8001</module> <module>springcloud-provider-dept-8002</module> <module>springcloud-provider-dept-8003</module> </modules> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <junit.version>4.12</junit.version> <log4j.version>1.2.17</log4j.version> <lombok.version>1.16.18</lombok.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>0.2.0.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> <!--springCloud的依賴--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Greenwich.SR1</version> <type>pom</type> <scope>import</scope> </dependency> <!--SpringBoot--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.1.4.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> <!--數據庫--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.10</version> </dependency> <!--SpringBoot 啟動器--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency> <!--日志測試~--> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>${log4j.version}</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> </dependency> </dependencies> </dependencyManagement> </project>
同理springbootdependency和springclouddependy管理了所有spring-cloud-xxx-xxx和spring-boot-xxx-xxx的版本
注意是注意springboot的版本和springcloud版本的對應關系。
pom.xml
<!--當前的Module自己需要的依賴,如果父依賴中已經配置了,這里就不用寫了--> <dependencies> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies>
Dept.java
@Data @NoArgsConstructor @Accessors(chain = true) //鏈式寫法 //所有的實體類務必實現序列化,通訊需求 public class Dept implements Serializable {//Dept,實體類 orm 類表關系映射 private static final long serialVersionUID = 708560364349809174L; private Long deptno; //主鍵 private String dname; //看下這個數據存在哪個數據庫的字段~ 微服務 ,一個服務對應一個數據庫 //同一個信息可能存在不同的數據庫 private String db_source; public Dept(String dname) { this.dname = dname; } }
5、服務提供者:springcloud-provider-dept-8001
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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springcloudLast</artifactId>
<groupId>com.bupt</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud-provider-dept-8001</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.bupt</groupId>
<artifactId>springcloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
</dependencies>
</project>
DeptMapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.bupt.mapper.DeptMapper"> <insert id="addDept" parameterType="Dept"> insert into dept(dname,db_source) values (#{dname},DATABASE()); </insert> <select id="queryById" resultType="Dept" parameterType="Long"> select * from dept where deptno = #{deptno}; </select> <select id="queryall" resultType="Dept"> select * from dept; </select> </mapper>
DeptMapper
@Mapper @Repository public interface DeptMapper { //添加部門 boolean addDept(Dept dept); //根據ID查詢部門 Dept queryById(@Param("deptno") long id); //查詢全部部門 List<Dept> queryall(); }
DeptService
public interface DeptService { boolean addDept(Dept dept); Dept queryById(long id); List<Dept> queryall(); }
DeptServiceImpl
@Service public class DeptServiceImpl implements DeptService { @Autowired private DeptMapper deptMapper; @Override public boolean addDept(Dept dept) { return deptMapper.addDept(dept); } @Override public Dept queryById(long id) { return deptMapper.queryById(id); } @Override public List<Dept> queryall() { return deptMapper.queryall(); } }
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <!-- configuration核心配置文件 --> <configuration> <settings> <!--開啟二級緩存--> <setting name="cacheEnabled" value="true"/> </settings> </configuration>
application.yml
server: port: 8001 mybatis: mapper-locations: classpath:mybatis/mapper/*.xml type-aliases-package: com.bupt.pojo configuration-properties: classpath:mybatis/mybatis-config.xml spring: application: name: springcloud-provider-dept datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/db01?useUnicode=true&characterEncoding=UTF-8&useSSL=true&serverTimezone=UTC username: root password: root eureka: client: service-url: defaultZone: http://localhost:7001/eureka/
DeptController
package com.bupt.Controller; import com.bupt.DeptService.DeptServiceImpl; import com.bupt.pojo.Dept; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.List; //提供Restfull服務!! @RestController public class DeptController { @Autowired private DeptServiceImpl deptService; @RequestMapping("/dept/add") public boolean addDept(@RequestBody Dept dept) { System.out.println(dept); return deptService.addDept(dept); } @GetMapping("/dept/get/{id}") public Dept getDept(@PathVariable("id") Long id) { Dept dept = deptService.queryById(id); if (dept == null) { throw new RuntimeException("Fail"); } return dept; } @GetMapping("/dept/list") public List<Dept> queryAll() { return deptService.queryall(); } }
DeptProvider_8001
package com.bupt; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; //@EnableEurekaClient @SpringBootApplication public class DeptProvider_8001 { public static void main(String[] args) { SpringApplication.run(DeptProvider_8001.class,args); } }
最后啟動項目訪問Controller里面的接口測試即可,這個pojo類在別的項目里面,我們照樣可以拿到,這就是微服務的簡單拆分的一個小例子
6、服務消費者:springcloud-consumer-dept-80
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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>springcloudLast</artifactId> <groupId>com.bupt</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>springcloud-consumer-dept-80</artifactId> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>com.bupt</groupId> <artifactId>springcloud-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> </dependencies> </project>
這里要用到 RestTemplate
,但是它的類中沒有Bean,所以我們要把它注冊到Bean中
ConfigBean
package com.bupt.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; @Configuration public class ConfigBean { @Bean public RestTemplate getRestTemplate(){ return new RestTemplate(); } }
DeptConsumerController
package com.bupt.controller; import com.bupt.pojo.Dept; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.client.RestTemplate; import java.util.HashMap; import java.util.List; @Controller public class DeptConsumerController { @Autowired private RestTemplate restTemplate; private static final String REST_URL_PREFIX = "http://localhost:8001"; @RequestMapping("/consumer/dept/get/{id}") @ResponseBody public Dept getDept(@PathVariable("id") long id) { return restTemplate.getForObject(REST_URL_PREFIX + "/dept/get/" + id,Dept.class); } @RequestMapping("/consumer/dept/add") @ResponseBody public boolean add(Dept dept){ HashMap<String, Object> map = new HashMap<>(); map.put("dname",dept.getDname()); map.put("deptNo",dept.getDeptno()); map.put("db_source",dept.getDb_source()); System.out.println(map); return restTemplate.postForObject(REST_URL_PREFIX + "/dept/add", map, Boolean.class); } @RequestMapping("/consumer/dept/list") @ResponseBody public List<Dept> queryAll(){ return restTemplate.getForObject(REST_URL_PREFIX + "/dept/list",List.class); } }
然后你會發現,原來遠程的post請求直接在url是拒絕訪問的,但是在這個里面可以訪問,只是結果為null
application.yml
server: port: 80
主啟動類DeptConsumer_80
@SpringBootApplication public class DeptConsumer_80 { public static void main(String[] args) { SpringApplication.run(DeptConsumer_80.class, args); } }
最后啟動服務提供者 springcloud-provider-dept-8001
然后啟動服務消費者 springcloud-consumer-dept-80
通過服務消費者的url請求去獲取服務提供者對應的請求,照樣可以拿到
7、Eureka服務注冊與發現
7.1、什么是Eureka
7.2、原理講解
7.3、springcloud-eureka-7001
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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>springcloudLast</artifactId> <groupId>com.bupt</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>springcloud-eureka-7001</artifactId> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> </dependencies> </project>
application.yml
server: port: 7001 # Eureka配置 eureka: instance: # Eureka服務端的實例名字 hostname: 127.0.0.1 client: # 表示是否向 Eureka 注冊中心注冊自己(這個模塊本身是服務器,所以不需要) register-with-eureka: false # fetch-registry如果為false,則表示自己為注冊中心,客戶端的化為 ture fetch-registry: false # Eureka監控頁面~ service-url: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
主啟動類:EurekaServer_7001
@SpringBootApplication @EnableEurekaServer //EnableEurekaServer表示服務端的啟動類,可以接收別人注冊進來 public class EurekaServer_7001 { public static void main(String[] args) { SpringApplication.run(ConfigEurekaServer_7001.class, args); } }
啟動之后訪問 http://localhost:7001/
Eureka的自我保護機制
7.4、8001服務注冊與發現
springcloud-provider-dept-8001
首先肯定是要導入對應的依賴
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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>springcloudLast</artifactId> <groupId>com.bupt</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>springcloud-provider-dept-8001</artifactId> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> <version>1.4.6.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jetty</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.bupt</groupId> <artifactId>springcloud-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-netflix-ribbon</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.21</version> </dependency> </dependencies> </project>
主要是spring-cloud-starter-eureka依賴,注意版本
然后在配置文件中添加對應的Eureka注冊配置
application.yml
server: port: 8001 mybatis: mapper-locations: classpath:mybatis/mapper/*.xml type-aliases-package: com.bupt.pojo configuration-properties: classpath:mybatis/mybatis-config.xml spring: application: name: springcloud-provider-dept datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/db01?useUnicode=true&characterEncoding=UTF-8&useSSL=true&serverTimezone=UTC username: root password: root eureka: client: service-url: defaultZone: http://localhost:7001/eureka/
最后在主啟動類上添加注解
@EnableEurekaClient //在服務啟動后自動注冊到Eureka中
1
啟動springcloud-config-eureka-7001,啟動完畢后再啟動下面的服務
啟動springcloud-provider-dept-8001,等一會再次訪問 http://localhost:7001/
actuator完善監控信息
所以這個時候我們應該是少了什么東西,然后我們繼續在 8001 里面添加依賴
<!--actuator完善監控信息--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
重啟8001項目再次點擊服務狀態信息,跳到了一個頁面,但是里面什么都沒有,這個時候我們就要配置一些信息了,這個信息只是在團隊開發的時候別人會通過這個信息來了解這個服務是誰寫的
現在我們在8001項目的配置文件中添加一些配置
#eureka 的配置,服務注冊到哪里 eureka: client: service-url: defaultZone: http://localhost:7001/eureka/ instance: instance-id: springcloud-provider-dept8001 #修改Eureka上的默認的狀態名字 #info配置(點擊狀態信息會返回的東西,可以百度) info: app.name: wulei-springcloud company.name: blog.wulei2921625957.com
然后你重啟8001項目,再次點擊項目狀態信息會返回你在上面添加的信息
那如何通過代碼來讓別人發現自己呢?
服務發現
在8001項目的controller里面添加
import org.springframework.cloud.client.discovery.DiscoveryClient; //獲取一些配置的信息,得到一些具體微服務 @Autowired private DiscoveryClient client; //注冊進來的微服務~ ,獲取一些信息 @GetMapping("/dept/discovery") public Object discovery() { //獲取微服務列表的清單 List<String> services = client.getServices(); System.out.println("discovery=>services:" + services); //得到一個具體的微服務信息,通過具體的微服務ID applicationName List<ServiceInstance> instances = client.getInstances("SPRINGCLOUD-PROVIDER-DEPT"); for (ServiceInstance instance : instances) { System.out.println( instance.getHost() + "\t" + instance.getPort() + "\t" + instance.getUri() + "\t" + instance.getServiceId() ); } return instances; }
然后在8001項目主啟動類上添加服務發現注解即可
這個注解我試了一下,不加也可以訪問上面的接口返回信息
@EnableDiscoveryClient //服務發現
- 1
重啟8001項目並訪問 http://localhost:8001/dept/discovery
@EnableDiscoveryClient和@EnableEurekaClient的區別
為啥在客戶端EnableEurekaClient可以省略不寫呢?
https://www.pianshen.com/article/22731105426/
server.servlet.context-path配置的作用和springboot2.0變革后的配置區別
8、Eureka集群的搭建
8.1、修改域名映射
為了體驗集群搭載在不同的電腦上,我們進入C:\Windows\System32\drivers\etc里面修改hosts文件,在文件的末尾添加下面幾行
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
127.0.0.1 eureka7003.com
8.2、修改7001配置文件
application.yml
server: port: 7001 #Eureka配置 eureka: instance: hostname: eureka7001.com #Eureka服務端的實例名字 client: register-with-eureka: false #表示是否向 Eureka 注冊中心注冊自己(這個模塊本身是服務器,所以不需要) fetch-registry: false #fetch-registry如果為false,則表示自己為注冊中心 service-url: #監控頁面~ #重寫Eureka的默認端口以及訪問路徑 --->http://localhost:7001/eureka/ # 單機: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ # 集群(關聯):7001關聯7002、7003 defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
8.3 、springcloud-eureka-7002
創建Eureka注冊中心7002項目(和7001一模一樣)
pom.xml
依賴和7001一樣
EurekaServer_7002
主啟動類
package com.bupt; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; @SpringBootApplication @EnableEurekaServer //EnableEurekaServer表示服務端的啟動類,可以接收別人注冊進來 public class EurekaServer_7002 { public static void main(String[] args) { SpringApplication.run(EurekaServer_7002.class, args); } }
application.yml
server: port: 7002 #Eureka配置 eureka: instance: hostname: eureka7002.com #Eureka服務端的實例名字 client: register-with-eureka: false #表示是否向 Eureka 注冊中心注冊自己(這個模塊本身是服務器,所以不需要) fetch-registry: false #fetch-registry如果為false,則表示自己為注冊中心 service-url: #監控頁面~ #重寫Eureka的默認端口以及訪問路徑 --->http://localhost:7001/eureka/ # 單機: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ # 集群(關聯):7002關聯7001、7003 defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7003.com:7003/eureka/
8.4 、springcloud-eureka-7003
創建Eureka注冊中心7003項目(和7001一模一樣)
pom.xml
依賴和7001一樣
EurekaServer_7003
主啟動類
package com.bupt; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; @SpringBootApplication @EnableEurekaServer //EnableEurekaServer表示服務端的啟動類,可以接收別人注冊進來 public class EurekaServer_7003 { public static void main(String[] args) { SpringApplication.run(EurekaServer_7003.class, args); } }
application.yml
server: port: 7003 #Eureka配置 eureka: instance: hostname: eureka7003.com #Eureka服務端的實例名字 client: register-with-eureka: false #表示是否向 Eureka 注冊中心注冊自己(這個模塊本身是服務器,所以不需要) fetch-registry: false #fetch-registry如果為false,則表示自己為注冊中心 service-url: #監控頁面~ #重寫Eureka的默認端口以及訪問路徑 --->http://localhost:7001/eureka/ # 單機: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ # 集群(關聯):7002關聯7001、7003 defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
然后啟動7001、7002、7003項目
然后訪問:http://localhost:7001/ 、http://localhost:7002/ 、http://localhost:7003/
8.5、8001項目注冊多個注冊中心
要把8001項目注冊到多個注冊中心上去,其實很簡單,只需要改動配置文件即可
application.yml(8001)
server: port: 8001 mybatis: mapper-locations: classpath:mybatis/mapper/*.xml type-aliases-package: com.bupt.pojo configuration-properties: classpath:mybatis/mybatis-config.xml spring: application: name: springcloud-provider-dept datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/db01?useUnicode=true&characterEncoding=UTF-8&useSSL=true&serverTimezone=UTC username: root password: root # Eureka配置:配置服務注冊中心地址 eureka: client: service-url: # 注冊中心地址7001-7003 defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/ instance: instance-id: springcloud-provider-dept-8001 #修改Eureka上的默認描述信息 info: app.name: wulei-springcloud company.name: blog.wulei2921625957.com
然后啟動8001項目,刷新http://localhost:7001/ 、http://localhost:7002/ 、http://localhost:7003/ 即可發現
9、CAP原則及對比Zookeeper
作為服務注冊中心,Eureka比Zookeeper好在那里?
張大胖和CAP定理(分布式系統、可用性、一致性、分區容錯性)_ITPUB博客
10、Ribbon負載均衡
ribbon是什么?
ribbon能干什么?
10.1、springcloud-consumer-dept-80使用Ribbon
首先80項目要添加兩個依賴
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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>springcloudLast</artifactId> <groupId>com.bupt</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>springcloud-consumer-fdept-feign</artifactId> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>com.bupt</groupId> <artifactId>springcloud-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> <!--Ribbon--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-ribbon</artifactId> <version>1.4.6.RELEASE</version> </dependency> <!--Eureka: Ribbon需要從Eureka服務中心獲取要拿什么--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> <version>1.4.6.RELEASE</version> </dependency> <!--Feign的依賴--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> <version>1.4.6.RELEASE</version> </dependency> </dependencies> </project>
由於我們消費者客戶端是利用RestTemplate來進行服務的讀取,所以我們讓RestTemplate實現負載均衡,只需要加一個注解即可@LoadBalanced
ConfigBean
@LoadBalanced @Bean public RestTemplate getRestTemplate(){ return new RestTemplate(); }
由於我們導入了Eureka,所以我們要配置Eureka
application.yml
server: port: 80 # Eureka配置 eureka: client: register-with-eureka: false # 不向 Eureka注冊自己 service-url: # 從三個注冊中心中隨機取一個去訪問 defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/ #spring: # application: # name: SpringBoot_easyPOI # main: # allow-bean-definition-overriding: true
DeptConsumer_80
package com.bupt; //import com.bupt.MyRule.MyRule; import com.MyRule.MyRules; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.cloud.netflix.ribbon.RibbonClient; @EnableEurekaClient @SpringBootApplication @RibbonClient(name = "SPRINGCLOUD-PROVIDER-DEPT", configuration = MyRules.class) public class DeptConsumer_80 { public static void main(String[] args) { SpringApplication.run(DeptConsumer_80.class,args); } }
最后還有一個問題,就是我們的RestTemplate實現了負載均衡,那么怎么體現它呢?我們現在就只是在它身上加了一個注解,那肯定是不行的,我們還要改變RestTemplate的請求路徑,讓其自動選擇,而不是寫死
package com.MyRule; import com.netflix.loadbalancer.IRule; import com.netflix.loadbalancer.RandomRule; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MyRules { @Bean public IRule testRule(){ return new MyRandomRule(); } }
DeptConsumerController
//private static final String REST_URL_PREFIX = "http://localhost:8001";
//用Ribbon做負載均衡的時候不應該寫它,不應該寫死,地址應該是一個變量,通過服務名來訪問
private static final String REST_URL_PREFIX = "http://SPRINGCLOUD-PROVIDER-DEPT";
package com.bupt.controller; import com.bupt.pojo.Dept; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.client.RestTemplate; import java.util.HashMap; import java.util.List; @Controller public class DeptConsumerController { @Autowired private RestTemplate restTemplate; // private static final String REST_URL_PREFIX = "http://localhost:8001"; //Ribbon:我們這里的地址,應該是一個變量,通過服務名來訪問 //private static final String REST_URL_PREFIX = "http://localhost:8001"; private static final String REST_URL_PREFIX = "http://SPRINGCLOUD-PROVIDER-DEPT"; @RequestMapping("/consumer/dept/get/{id}") @ResponseBody public Dept getDept(@PathVariable("id") long id) { return restTemplate.getForObject(REST_URL_PREFIX + "/dept/get/" + id,Dept.class); } @RequestMapping("/consumer/dept/add") @ResponseBody public boolean add(Dept dept){ HashMap<String, Object> map = new HashMap<>(); map.put("dname",dept.getDname()); map.put("deptNo",dept.getDeptno()); map.put("db_source",dept.getDb_source()); System.out.println(map); return restTemplate.postForObject(REST_URL_PREFIX + "/dept/add", map, Boolean.class); } @RequestMapping("/consumer/dept/list") @ResponseBody public List<Dept> queryAll(){ return restTemplate.getForObject(REST_URL_PREFIX + "/dept/list",List.class); } }
最后啟動7001、7002、7003項目后再啟動8001項目,等8001項目注冊到它們3個中后啟動80項目
然后訪問 http://localhost/consumer/dept/list 可以看到正常返回結果,當然了,在這里也看不出負載均衡,所以下面會配置多個服務提供者和多個數據庫,來測試負載均衡的效果。
10.2、使用Ribbon實現負載均衡
創建另外兩個數據庫:db02、db03
創建另外兩個服務提供者:8002、8003
直接新建兩個子model,然后把8001的所有文件全部拷貝(提供相同的服務),一摸一樣的,然后更改一下配置文件即可
pom.xml依賴
application.yml的端口號,對應的數據庫,還有instance-id,例如:instance-id: springcloud-provider-dept8002
注意:下面的這個服務ID不要改
spring:
application:
name: springcloud-provider-dept # 3個服務名稱一致是前提
現在的項目預覽
然后,啟動
springcloud-config-eureka-7001
springcloud-config-eureka-7002
springcloud-config-eureka-7003
springcloud-provider-dept-8001
springcloud-provider-dept-8002
springcloud-provider-dept-8003
springcloud-consumer-dept-80
然后訪問http://localhost/consumer/dept/list ,多訪問幾次,查詢的數據沒變,但是數據庫變了
你會發現是輪詢,就是每個服務輪流來,這也Ribbon的默認算法
Ribbon自定義均衡算法
里面有個接口非常重要:IRule,基本上全部的均衡算法都實現了這個接口
里面有這么多均衡算法,因為默認是輪詢算法,也就是RoundRobinRule,那我們要怎么使用別的算法呢?
只需要在80項目的config類里面注冊Bean即可
//IRule //RoundRobinRule:輪詢 //RandomRule:隨機 //AvailabilityFilteringRule:會先過濾掉跳閘、訪問故障的服務~,對剩下的進行輪詢 //RetryRule:會先按照輪詢獲取服務,如果服務獲取失敗,則會在指定的時間內進行重試 @Bean public IRule myRule() { return new RandomRule(); //默認為輪詢,現在我們使用隨機的 }
然后啟動80項目,訪問http://localhost/consumer/dept/list,多訪問幾次,發現每次出現的數據庫都沒規律可循
我們要學會自定義負載均衡算法,為了體現我們使用了自定義的負載均衡算法,我們建包不建在主啟動類的同級目錄(官方建議)
當兩個Irule在同一個文件夾下,此時會沖突報錯
把剛剛寫在ConfigBean里面的Bean注釋掉(不能在掃描包下出現兩個Irule),我們來模仿它的算法寫一個自己的算法
自定義類MyRandomRule
package com.MyRule; import com.netflix.client.config.IClientConfig; import com.netflix.loadbalancer.AbstractLoadBalancer; import com.netflix.loadbalancer.AbstractLoadBalancerRule; import com.netflix.loadbalancer.ILoadBalancer; import com.netflix.loadbalancer.Server; import java.util.List; import java.util.concurrent.ThreadLocalRandom; public class MyRandomRule extends AbstractLoadBalancerRule { //每個機器,訪問5次,換下一個服務(總共3個) //total = 0 默認=0,如果=5,我們指向下一個服務結點 //index = 0 默認=0,如果total=5,那么index+1, private int total = 0; //被調用的次數 private int currentIndex = 0; //當前是誰在提供服務 public Server choose(ILoadBalancer lb, Object key) { if (lb == null) { return null; } Server server = null; while (server == null) { if (Thread.interrupted()) { return null; } List<Server> upList = lb.getReachableServers(); //獲得還活着的服務 List<Server> allList = lb.getAllServers(); //獲得全部的服務 int serverCount = allList.size(); if (serverCount == 0) { return null; } //============================================================= if (total < 5) { total++; } else { total = 0; currentIndex++; if (currentIndex >= serverCount) { currentIndex = 0; } } server = upList.get(currentIndex); //============================================================= if (server == null) { Thread.yield(); continue; } if (server.isAlive()) { return (server); } server = null; Thread.yield(); } return server; } protected int chooseRandomInt(int serverCount) { return ThreadLocalRandom.current().nextInt(serverCount); } @Override public Server choose(Object key) { return choose(getLoadBalancer(), key); } @Override public void initWithNiwsConfig(IClientConfig clientConfig) { } }
MyRules
package com.MyRule; import com.netflix.loadbalancer.IRule; import com.netflix.loadbalancer.RandomRule; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MyRules { @Bean public IRule testRule(){ return new MyRandomRule(); } }
最后還要在主啟動類添加掃描注解,在微服務啟動的時候就能去加載我們自定義的Ribbon類
DeptConsumer_80
package com.bupt; //import com.bupt.MyRule.MyRule; import com.MyRule.MyRules; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.cloud.netflix.ribbon.RibbonClient; @EnableEurekaClient @SpringBootApplication @RibbonClient(name = "SPRINGCLOUD-PROVIDER-DEPT", configuration = MyRules.class) public class DeptConsumer_80 { public static void main(String[] args) { SpringApplication.run(DeptConsumer_80.class,args); } }
然后重啟80項目,訪問http://localhost/consumer/dept/list,多訪問幾次,可以發現訪問的服務每5次切換一下
11、Feign負載均衡
11.1、簡介
11.2、Feign能干什么?
11.3、Feign集成了Ribbon
11.4 springcloud-consumer-dept-feign
創建一個springcloud-consumer-dept-feign空maven的空項目,這也是一個消費者,端口也是80,只是這個消費者使用Feign實現的負載均衡
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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>springcloudLast</artifactId> <groupId>com.bupt</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>springcloud-consumer-fdept-feign</artifactId> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>com.bupt</groupId> <artifactId>springcloud-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> <!--Ribbon--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-ribbon</artifactId> <version>1.4.6.RELEASE</version> </dependency> <!--Eureka: Ribbon需要從Eureka服務中心獲取要拿什么--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> <version>1.4.6.RELEASE</version> </dependency> <!--Feign的依賴--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> <version>1.4.6.RELEASE</version> </dependency> </dependencies> </project>
application.yml
和springcloud-consumer-dept-80項目的一摸一樣
server: port: 80 # Eureka配置 eureka: client: register-with-eureka: false # 不向 Eureka注冊自己 service-url: # 從三個注冊中心中隨機取一個去訪問 defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/ #spring: # application: # name: SpringBoot_easyPOI # main: # allow-bean-definition-overriding: true
修改springcloud-api
-
添加依賴
-
並寫上幾個注解
- feign注解實現的服務請求接口是和provider的controller請求路由保持一致,但是沒有方法體
package com.bupt.service; import com.bupt.pojo.Dept; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import java.util.List; @Component @FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT") //在接口上添加注解就可以實現相關服務的負載均衡 public interface DeptClientService { @RequestMapping("/dept/add") public boolean addDept(@RequestBody Dept dept) ; @GetMapping("/dept/get/{id}") public Dept getDept(@PathVariable("id") Long id) ; @GetMapping("/dept/list") public List<Dept> queryAll() ; }
然后在springcloud-consumer-dept-feign項目的controller也要做相應的修改
DeptConsumerController
package com.bupt.controller; import com.bupt.pojo.Dept; import com.bupt.service.DeptClientService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.List; @RestController public class DeptConsumerController { @Autowired private DeptClientService deptClientService = null; @RequestMapping("/consumer/dept/get/{id}") public Dept queryById(@PathVariable("id") long id){ return this.deptClientService.getDept(id); } @RequestMapping("/consumer/dept/add") public boolean add(@RequestBody Dept dept){ return this.deptClientService.addDept(dept); } @RequestMapping("/consumer/dept/list") public List<Dept> queryAll(){ return this.deptClientService.queryAll(); } }
最后還要在啟動類上添加FeignClient注解
FeignDeptConsumer_80
package com.bupt; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.cloud.openfeign.FeignClient; @SpringBootApplication @EnableFeignClients(basePackages = {"com.bupt"}) public class FeignDeptConsumer_80 { public static void main(String[] args) { SpringApplication.run(FeignDeptConsumer_80.class,args); } }
最后啟動7001、… 、8001、… 、feign的80項目,測試