狂神说springcloud笔记1_p1-p13
参考博文:(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项目,测试