目录
前言
本篇向大家介绍一下springcloud的一个入门程序,主要的思想就是利用提供者和消费者思想,然后将我们的springboot项目进行微服务化,让我们的消费者调用我们的提供者接口,这里的话我们会利用tomcat然后开启多个端口,然后集群我们的eureka注册中心
这张图的话就很清晰的展示了这个程序所用到的所有模块
一、springcloud解决了什么
上图是我们发起一个请求需要经历的过程,SpringCloud是一个整体的生态环境
版本兼容关系,需要大家自己去上网搜,现在版本更新的太快了
二、开始实操
基础的Maven父类的创建
导入依赖(示例):
<?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.yan</groupId>
<artifactId>springcloud1</artifactId>
<version>1.0-SNAPSHOT</version>
<modules>
<module>springcloud-api</module>
<module>springcloud-provider-dept--8001</module>
<module>springcloud-consumer-dept-80</module>
</modules>
<!--打包方式pom-->
<packaging>pom</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<junit.version>4.12</junit.version>
<lombok.version>1.16.10</lombok.version>
<log4j.version>1.2.17</log4j.version>
</properties>
<!-- 这里进行管理,模块需要用到什么需要自己去导-->
<dependencyManagement>
<dependencies>
<!-- 导入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>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<!-- springBoot mybatis启动器-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<!-- 日志测试-->
<!-- junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<!-- lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<!-- log4j-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
</build>
</project>
这里需要编辑打包方式为pom,然后导入我们的相关依赖
编写我们的modul-api
导入依赖(示例):
<?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>springcloud1</artifactId>
<groupId>com.yan</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud-api</artifactId>
<!-- 当前的module需要自己导入依赖,父类管理版本-->
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
</project>
这里我们只用写基础的pojo就可以了
public class Dept implements Serializable {
private Long deptno;
private String dname;
//可以查看数据存在哪个数据库的字段,一个数据库对应一个服务
private String db_sourse;
public Dept(String dname) {
this.dname = dname;
}
public Long getDeptno() {
return deptno;
}
public void setDeptno(Long deptno) {
this.deptno = deptno;
}
public String getDname() {
return dname;
}
public void setDname(String dname) {
this.dname = dname;
}
public String getDb_sourse() {
return db_sourse;
}
public void setDb_sourse(String db_sourse) {
this.db_sourse = db_sourse;
}
编写我们的springcloud-provider-dept–8001
导入依赖(示例):
<?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>springcloud1</artifactId>
<groupId>com.yan</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud-api</artifactId>
<!-- 当前的module需要自己导入依赖,父类管理版本-->
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
</project>
这里我们需要写mapper层和service层以及controller层
mapper层代码
package com.yan.springcloud.mapper;
import com.yan.springcloud.pojo.Dept;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface DeptMapper {
@Insert("insert into deptno(dname) values(#{dname})")
public boolean addDept(Dept dept);
@Select("select * from dept where deptno=#{deptno}")
public Dept queryById(Long deptno);
@Select("select * from dept")
public List<Dept> queryAll();
}
service层代码
package com.yan.springcloud.service;
import com.yan.springcloud.pojo.Dept;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Transactional
public interface DeptService {
public boolean addDept(Dept dept);
public Dept queryById(Long deptno);
public List<Dept> queryAll();
}
package com.yan.springcloud.service.impl;
import com.yan.springcloud.mapper.DeptMapper;
import com.yan.springcloud.pojo.Dept;
import com.yan.springcloud.service.DeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @author :Yan Guang
* @date :Created in 2020/11/28 17:07
* @description:
*/
@Service
public class DeptServiceImpl implements DeptService {
@Autowired
private DeptMapper deptDao;
@Override
public boolean addDept(Dept dept) {
return deptDao.addDept(dept);
}
@Override
public Dept queryById(Long deptno) {
return deptDao.queryById(deptno);
}
@Override
public List<Dept> queryAll() {
return deptDao.queryAll();
}
}
controller层代码:
package com.yan.springcloud.controller;
import com.yan.springcloud.pojo.Dept;
import com.yan.springcloud.service.DeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.websocket.server.PathParam;
import java.util.List;
/**
* @author :Yan Guang
* @date :Created in 2020/11/28 17:18
* @description:
*/
@RestController
public class DeptController {
@Autowired
private DeptService deptService;
@PostMapping("/dept/add")
public boolean addDept(Dept dept){
return deptService.addDept(dept);
}
@GetMapping("/dept/get/queryid")
public Dept queryById(@PathParam("deptno") Long deptno){
return deptService.queryById(deptno);
}
@GetMapping("/dept/queryall")
public List<Dept> queryAll(){
return deptService.queryAll();
}
}
最后别忘记配置我们的application.yml
server:
port: 8001
#配置mybatis
mybatis:
#设置别名
type-aliases-package: com.yan.springcloud.pojo
configuration:
map-underscore-to-camel-case: true #开启这个的作用是可以让数据库中的p_Addr与pojo中的pAddr对应
#spring的配置
spring:
application:
name: springcloud-provider-dept
datasource:
url: jdbc:mysql://localhost:3306/db01?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
driver-class-name: com.mysql.jdbc.Driver
username: root
password: 12345678
type: com.alibaba.druid.pool.DruidDataSource
然后这个模块写完了,记得先自己测试一下看会不会出现什么问题,记得创建我们的数据库,需要跟我们的类映射关系正确
编写springcloud-consumer-dept-80
导入依赖(示例):
<?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>springcloud1</artifactId>
<groupId>com.yan</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud-consumer-dept-80</artifactId>
<!-- 实体类+web 消费者是跟前端进行打交道的-->
<dependencies>
<dependency>
<groupId>com.yan</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>
这里需要写config层和controller层即可
config层代码:
package com.yan.springcloud.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
/**
* @author :Yan Guang
* @date :Created in 2020/11/28 17:57
* @description:
*/
@Configuration
//这个注解相当于applicationContext.xml
public class ConfigBean {
//这里就就是把bean注入到ioc容器中
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
这里主要是需要将我们的ioc容器中加入RestTemplate这个类,这个类可以自己去源码看一下,我在这里就不进行讲解了
下面是controller层代码:
package com.yan.springcloud.controller;
import com.yan.springcloud.pojo.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import java.util.List;
/**
* @author :Yan Guang
* @date :Created in 2020/11/28 17:54
* @description:
*/
@RestController
public class DeptConsumerController {
//理解:消费者不应该有service层
//RestFul风格请求RestTemplate是一个模版,里面很多方法供我们直接调用
//需要注册到spring中去
@Autowired
private RestTemplate restTemplate;//提供多种便捷访问远程http服务的方法,简单的Restful服务模版
private static final String REST_URL_PREFIX="http://localhost:8001";
//http://localhost:8001/dept/list
@GetMapping("/consumer/dept/get/{id}")
public Dept get(@PathVariable("id") long id){
return restTemplate.getForObject(REST_URL_PREFIX+"/dept/get/"+id,Dept.class);
}
@PostMapping("/consumer/dept/add")
public boolean add(Dept dept){
return restTemplate.postForObject(REST_URL_PREFIX+"/dept/add",dept,Boolean.class);
}
@RequestMapping("/consumer/dept/all")
public List<Dept> query(){
return restTemplate.getForObject(REST_URL_PREFIX+"/dept/queryall",List.class);
}
}
这个地方就是使用到了我们的restTemplate这个API,他可以帮我们直接现URL的访问,需要注意的是,这里的每一个RequestMapping的类型(post或者get)一定要和我们在springcloud-provider-dept–8001里面写的对应上,最后把不要忘记配置我们的端口号为80 server: port: 80
到这里来我们就可以开始编写我们的eureka服务注册中心了
eureka注册中心的编写
springcloud-eureka-7001
导入依赖:
<?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>springcloud1</artifactId>
<groupId>com.yan</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud-eureka-7001</artifactId>
<dependencies>
<!-- 提供注册中心服务-->
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
</project>
这里注意一下,我们的注册中心导入的依赖是eureka服务端,在maven中央仓库里面搜索的话会有三个,分别是服务端客户端和普通的服务
编写我们的application.yml
server:
port: 7001
#Eureka配置
eureka:
instance:
hostname: localhost #Eureka服务端的实例名称
client:
register-with-eureka: false #表示是否向服务中心注册自己
fetch-registry: false #false表示自己为注册中心
service-url: #监控页面
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
最后只需要在驱动类里面加入一行注解就可以了
package com.yan.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
/**
* @author :Yan Guang
* @date :Created in 2020/11/28 20:23
* @description:
*/
//启动之后访问http://localhost:7001/
@SpringBootApplication
@EnableEurekaServer//开启eureka服务,可以接受别人注册进来
//会有一个自我保护机制,心跳检测
public class EurekaServer_7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaServer_7001.class,args);
}
}
然后就可以开始测试启动一下我们的eureka服务器端,效果如下
启动完毕之后就可以编写我们的提供者模块的代码了
首先还是老样子 编写我们的pom加入新的依赖:
<!-- EUREKA 只是一个普通的eureka服务-->
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- 这里要看到我们的eureka上的信息,完善监控信息-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 我们需要拿到实体类,需要配置api module-->
这里只需要在原来的基础上添加一下就可以了,添加一个基础的eureka服务就行
同样的套路,编写我们的yml文件
#eureka的配置,服务注册到哪里
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/
instance:
instance-id: springcloud-provider-dept8001 #修改eureka上的默认描述信息
#info配置
info:
app.name: yan-springcloud
company.name: Cyan
在原来的基础上加上我们访问的注册中心的地址,然后我们一个服务的基本信息
然后在驱动main函数里面加一个注解
@SpringBootApplication
@MapperScan("com.yan.springcloud.mapper")
@EnableEurekaClient//在服务启动后,自动注册到eureka中
@EnableDiscoveryClient//服务发现,主要是用来看服务的信息
public class DeptProvider_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProvider_8001.class, args);
}
}
开启我们的eureka客户端,并且开启服务发现,可以获取服务的信息
最后在我们的controller层,新写一个获取信息的方法就可以了
//注册进来的微服务,获取他们的消息
@GetMapping("/dept/discovery")
public Object discovery(){
//获取微服务列表的清单
List<String> services = client.getServices();
//得到一个具体的微服务信息
//通过实例ID来获取对象
List<ServiceInstance> instances = client.getInstances("SPRINGCLOUD-PROVIDER-DEPT");
return this.client;
}
到这里位置就可以进行测试了,看看能不能获取到我们的服务信息
点击右边的绿色连接,然后访问我们自己加上去的新的controller层的代码,都是有信息展示的
这里给大家展示一下eureka的集群操作,因为在我们的开发中,注册中心不可能只有一个,万一要是服务器蹦了的话,那么注册中心也就蹦了,所以一般我们会配置eureka集群,当然这里先提示一下8G的电脑内存最多带三个eureka集群差不多了,因为idea占内存比较大,所以记得开的时候一直关注自己的内存使用情况
首先还是一样的创建三个模块
直接复制粘贴一下就好了
然后把7002和7003里面的yml文件修改一下
server:
port: 7002
#Eureka配置
eureka:
instance:
hostname: localhost7002 #Eureka服务端的实例名称
client:
register-with-eureka: false #表示是否向服务中心注册自己
fetch-registry: false #false表示自己为注册中心
service-url: #监控页面
#集群搭建
defaultZone: http://localhost:7001/eureka/,http://localhost:7003/eureka/
这里我就讲解一个,其他两个原理是一样的,首先我们的hostname需要设置的不一样,这个是我们的服务主机名,然后后面的defaultZone比方说我是配置的7001端口的,那么我的连接就需要填写7002端口和7003端口的,我这里显而易见是配置7002端口的,剩下的两个你们就自己配一下,核心思想就是访问两外两个注册中心
然后把我们提供者里面的代码也改一下
#eureka的配置,服务注册到哪里
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/,http://localhost:7003/eureka/
instance:
instance-id: springcloud-provider-dept8001 #修改eureka上的默认描述信息
这个地方访问的注册中心的url地址就是三个了,而不是一个,中间用逗号隔开就行
到这里为止就可以进行测试了,然后运行测试一下,这里注意一定要关注自己电脑内存
然后这里就可以看到我们集群之后的其他的eureka注册中心了,这里我因为电脑原因,就开了两个注册中心,集群就可以看到另外的一个注册中心的依赖,你们可以尝试开三个,然后开提供者
ribbon负载均衡的编写
首先复制粘贴写三个提供者哈,这里我就不演示了,效果大概就是这样
到这里的话你们就可以看一下一开始在最上面我给你们画的那个图了,意思就是我写的三个提供者都是支持一个服务的,然后我们的消费者可以通过ribbon负载均衡去我们的注册中心里面找,我们三个服务全部都注册在同一个服务下,如图:
然后ribbon和eureka可以的联合使用可以让我们作为消费者,不用去管不同服务器的IP地址和端口号问题,他们在注册中心统称为服务(这里三个服务器都执行一条服务),所以只需要在下面修改一下就可以了
首先把ribbon负载均衡的依赖导入一下:
<!-- ribbon-->
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-ribbon -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
然后改两个地方就可以,如下:
server:
port: 80
#eureka配置
eureka:
client:
register-with-eureka: false #不向注册中心注册自己,自己只用拿就可以了,只是获取服务列表
service-url:
#我们的客户端在进行访问服务器的时候,会进行一个选择,实现服务器的一个负载均衡
defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/,http://localhost:7003/eureka/
public class ConfigBean {
//这里就就是把bean注入到ioc容器中,restTemplate可以实现我们对接口的一个获取,可以发起请求
@Bean
@LoadBalanced//配置负载均衡ribbon
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
最后记得把这里写成服务的名称,记得是大写,在controller层里面
private static final String REST_URL_PREFIX="http://SPRINGCLOUD-PROVIDER-DEPT";//通过服务器的主机名去作为根地址
feign负载均衡的编写
首先还是老流程编写一个modul 然后导入我们的依赖
<dependencies>
<!-- feign另外一种负载均衡的方式,区别就是可以让我们写接口,而不是写服务,面向接口编程-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- ribbon-->
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-ribbon -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- eureka客户端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<dependency>
<groupId>com.yan</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>
然后这里yml不用改,主启动类要去加一个注解
@SpringBootApplication
@EnableEurekaClient //客户端
//ribbon和eureka整合以后,我们不用再去管IP地址和端口号了
//在微服务启动的时候就可以去加载我们自定义的Ribbon类
@EnableFeignClients(basePackages = {"com.yan.springcloud"}) //feign默认已经集成了我们的ribbon,所以我们直接写接口就可以了
public class DeptConsumer_feign {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer_feign.class,args);
}
}
这个地方就是开启我们的feign,其实他内部已经封装了我们的ribbon,就是让我们程序员实现面向接口的一个编程实现,继续往下面看就知道了
然后我们的controller层里面的代码就可以这样写了,而不用像之前的需要写resTemplate
@RestController
public class DeptConsumerController {
@Autowired
private DeptClientService deptClientService=null;
@GetMapping("/consumer/dept/get/{id}")
public Dept get(@PathVariable("id") long id){
return this.deptClientService.queryById(id);
}
@PostMapping("/consumer/dept/add")
public boolean add(Dept dept){
return this.deptClientService.addDept(dept);
}
@RequestMapping("/consumer/dept/all")
public List<Dept> query(){
return this.deptClientService.queryAll();
}
}
最后最重要的一步就是在我们的客户端添加上feign的所要去实现的那个类
@Component
@FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT",fallbackFactory = DeptClientServiceFallBackFactory.class)
/*feign的作用其实就是帮我们封装了一下restTemplate,可以让我们实现纯面向接口编程,可读性变高
意思就是本来我们在dept-80 controller层里面写的代码是去访问url连接,然后ribbon让我们使用写服务名称就可以了
在这里我们就是把需要实现的Service层上加上一个FeignClient注解,就可以直接调用了,默认就会去寻找改服务里面的方法
我们也可以写在controller层里面
这样一看的话,其实降级操作就是在产生熔断的时候同时基于降级操作,将原来的所有的方法替换成新的方法
*/
public interface DeptClientService {
@GetMapping("/dept/get/{id}")
public Dept queryById(@PathVariable("id") Long id);
@GetMapping("/dept/queryall")
public List<Dept> queryAll();
@PostMapping("/dept/add")
public Boolean addDept(Dept dept);
}
这里就基本上编写完了哈,然后就可以进行测试了,这里跟大家稍微说一下就是我们的负载均衡是写在我们客户端里面的哈,是我们客户端在进行注册中心调用服务的时候的一个算法,所以是在客户端的,主要就是解决一个高可用问题,所有的运行一个服务的服务器都可以得到均衡的请求分配,而不是集中在一台服务器上
下面带大家入门一下雪崩的解决方案熔断,和高并发的解决方案降级:
熔断机制的开启
熔断我们这里采用的是hystirx,意思就是当我们遇到不可避免的灾害的时候,比如断电打雷等等,我们的服务器不得不停下来的时候,我的程序但是不能报错停止,不然后续的服务就都运行不了了,所以我们需要采用一种熔断机制,给客户端返回一个提醒,告诉他们,服务器出现了问题,正在修复,但是其他的服务器仍然可以正常运转,下面开始实战:
老方法,首先创建一个模块,然后导入依赖,熔断机制是发生在服务端的,与后面的服务降级是不一样的
<dependencies>
<!-- hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- EUREKA 只是一个普通的eureka服务-->
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- 这里要看到我们的eureka上的信息,完善监控信息-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 我们需要拿到实体类,需要配置api module-->
<dependency>
<groupId>com.yan</groupId>
<!-- 这个地方需要导入自己写的module配置-->
<artifactId>springcloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<!-- test-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- jetty跟tomcat没什么区别-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
<!-- 热部署工具,不用每次重启我们的项目-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
然后再main启动类里面加上开启熔断机制的注解
@SpringBootApplication
@MapperScan("com.yan.springcloud.mapper")
@EnableEurekaClient//在服务启动后,自动注册到eureka中
@EnableDiscoveryClient//服务发现,主要是用来看服务的信息
@EnableCircuitBreaker//添加对熔断的支撑
public class DeptProvider_hystrix8002 {
public static void main(String[] args) {
SpringApplication.run(DeptProvider_hystrix8002.class, args);
}
}
最后只需要在我们的controller层里面修改我需要执行熔点机制的代码就可以了
@RestController
public class DeptController {
@Autowired
private DeptService deptService;
@GetMapping("/dept/get/queryid")
@HystrixCommand(fallbackMethod = "hystrixGet")//熔断调用方法
public Dept get(@PathParam("id") Long id){
Dept dept = deptService.queryById(id);
if(dept==null){
throw new RuntimeException("id=>"+id+"不存在该用户,信息无法找到");
}
return dept;
}
//熔断版本,请求失败很多次就会使用该方法
public Dept hystrixGet(@PathParam("id") Long id){
Dept dept = new Dept();
dept.setDeptno(id);
dept.setDname("id=>"+id+"不存在该用户,信息无法找到");
dept.setDb_sourse("数据库不存在!");
return dept;
}
}
写到这里就可以进行测试了,首先开启注册中心,然后开启服务器,然后测试发送一个错误过来,看熔断机制是否能够正常的处理
首先输入正确的,然后进行错误测试:
我们发现熔断机制成功的返回了我们想要看到的参数
服务降级
服务降级是发生在我们的客户端的,所以这里实例的话,我就去修改api里面的代码了,服务降级不用导入新的依赖包,是在feign负载均衡里面的,
首先这里加上一个注解,里面写入我们的回调函数
@Component
@FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT",fallbackFactory = DeptClientServiceFallBackFactory.class)
/*feign的作用其实就是帮我们封装了一下restTemplate,可以让我们实现纯面向接口编程,可读性变高
意思就是本来我们在dept-80 controller层里面写的代码是去访问url连接,然后ribbon让我们使用写服务名称就可以了
在这里我们就是把需要实现的Service层上加上一个FeignClient注解,就可以直接调用了,默认就会去寻找改服务里面的方法
我们也可以写在controller层里面
这样一看的话,其实降级操作就是在产生熔断的时候同时基于降级操作,将原来的所有的方法替换成新的方法
*/
public interface DeptClientService {
@GetMapping("/dept/get/{id}")
public Dept queryById(@PathVariable("id") Long id);
@GetMapping("/dept/queryall")
public List<Dept> queryAll();
@PostMapping("/dept/add")
public Boolean addDept(Dept dept);
}
然后开始写回调函数:
//降级,实现这个里面的方法
@Component
public class DeptClientServiceFallBackFactory implements FallbackFactory {
//这个方法的任务就是当产生回调的时候(当我们关闭服务器的时候),替换掉原来类里面所有的方法(采取客户端自己的方法)
@Override
public DeptClientService create(Throwable cause) {
return new DeptClientService() {
@Override
public Dept queryById(Long id) {
Dept dept = new Dept();
dept.setDeptno(id);
dept.setDname("没有对应的信息客户端提供降级信息,该服务已经关闭");
dept.setDb_sourse("没有数据~");
return dept;
}
@Override
public List<Dept> queryAll() {
return null;
}
@Override
public Boolean addDept(Dept dept) {
return null;
}
};
}
}
这里就是我们回调函数,就是当我们关闭服务器的时候,我们的客户端就会把服务器降级,然后执行我们的客户端这里的方法,我们现在可以在有feign的服务端开启然后进行测试
当我们关闭服务器发现,成功显示了如上效果,所以降级成功了,这里我专门为大家讲解一下服务降级跟熔断机制的区别,以免大家混淆:
1.负载均衡是针对于同一个服务的,不同服务器之间的选择
2.服务降级是针对于不同服务的,是放在客户端的
3.降级其实跟熔断是差不多的,就是在熔断的时候不光光替换原来所有的方法,而且降级服务。服务熔断是在服务端,用于处理一些自然灾害,服务降级是在客户端,用于处理高并发问题,和负载均衡一起解决
4.降级是主动的,熔断是被动的
如果还不理解可以回头去看我最上面的那张图
hystrix-dashboard面板展示
首先我们的dashboard是写在我们客户端的,然后下面展示需要导入的依赖,新写一个模块,然后端口号可以自行设定,但是不要用80就行
<dependencies>
<!-- hystrix这个只需要在服务端用-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- 下面这个需要在客户端使用-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- ribbon-->
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-ribbon -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- eureka客户端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<dependency>
<groupId>com.yan</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>
这里是需要导入的依赖,然后主要就是添加了一个hystrix(熔断机制)的dashboard,然后这个里面的内容很简单,首先还是去yml里面简单写一下端口号就可以了:
server:
port: 9001
然后写main里面的代码:
@SpringBootApplication
@EnableHystrixDashboard//开启监控页面,服务端要有actuator依赖
public class DeptConsumerDashboard_9001 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumerDashboard_9001.class,args);
}
}
这里需要用到@EnableHystrixDashboard注解哈,然后我们的客户端界面就写完了,下面去我们的服务端,服务端的话一定要注意,是需要已经使用hystrix熔断机制的服务端,下面给大家看一下需要修改的地方:
首先,pom依赖已经不用导入了,然后yml也不需要进行任何的修改,然后主要就是我们在这个地方需要加一个Servlet
@SpringBootApplication
@MapperScan("com.yan.springcloud.mapper")
@EnableEurekaClient//在服务启动后,自动注册到eureka中
@EnableDiscoveryClient//服务发现,主要是用来看服务的信息
@EnableCircuitBreaker//添加对熔断的支撑
public class DeptProvider_hystrix8002 {
public static void main(String[] args) {
SpringApplication.run(DeptProvider_hystrix8002.class, args);
}
//增加一个Servlet
@Bean
public ServletRegistrationBean hystrixMetricsStreamServlet(){
ServletRegistrationBean registrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet());
registrationBean.addUrlMappings("/actuator/hystrix.stream");
return registrationBean;
}
}
这一串代码是固定的,不用去改的,只用加上就行:
然后进行测试就可以了哈,先开启我们的监控界面,然后开启注册中心,然后开启服务端就可以了,等服务注册进去之后,后面就可以看到这样的画面了:
这个图给大家讲解一下:
圈圈的大小代表的是发起请求的个数,然后测试完毕,我们这一块的内容也就结束了哈
写到这里离我们的课程的结束也就不多了哈,下面进入到我们的路由zuul的编写
zuul的编写
我们的路由主要就是让通过域名来隐藏我们的真实路径,然后可以设置一个新的域名,然后进行请求过滤,实现真实项目路径的隐藏,首先还是老样子,写一个module然后导入pom依赖:
<?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>springcloud1</artifactId>
<groupId>com.yan</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud-zuul-9527</artifactId>
<dependencies>
<!-- zuul-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- hystrix这个只需要在服务端用-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- 下面这个需要在客户端使用-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- ribbon-->
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-ribbon -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- eureka客户端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<dependency>
<groupId>com.yan</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>
搞完写yml:
server:
port: 9527
spring:
application:
name: springcloud-zuul-getway
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/,http://localhost:7003/eureka/
instance:
instance-id: zuul9527.com
prefer-ip-address: true
info:
app.name: yan-springcloud
company.name: blog.yan.com
#配置路由
zuul:
routes:
mydept.serviceId: springcloud-provider-dept #原来要访问的微服务名称
mydept.path: /mydept/** #现在自定义的路径就可以访问到
# ignored-services: springcloud-provider-dept #不能再使用这个路径访问了,忽视掉
ignored-services: "*" #所有都不允许访问,除了自己配置的path
prefix: /yan #设置可以访问的前缀,加上就才可以访问(/yan/mydept/)
这里主要就是配置路径,上面我都批注好了,下面直接写一个启动类就可以了:
@SpringBootApplication
@EnableZuulProxy//开启路由代理,这里需要结合eureka一起使用,他会默认的是作为一个服务客户端
public class ZuulApplication_9527 {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication_9527.class, args);
}
}
这里一个代码就完事儿了,然后默认会是作为一个服务的客户端,然后就可以测试了:
这里就可以发现,通过我们自己配置的路由url可以正常进行访问,成功解决
下面最后在导入两个module,就是从github或者码云上可以获取到我们的yml里面的配置的一个模块,叫springConfig
SpringConfig
首先这里直接先创建两个模块吧,我把代码直接都先搞出来:
首先给出服务器端的:
<?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>springcloud1</artifactId>
<groupId>com.yan</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud-config-server-3344</artifactId>
<dependencies>
<!-- 这里注意一下,主要是设计SpringCloud的包,一般都是有springBoot的依赖的-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
</project>
server:
port: 3344
#设置服务名称
spring:
application:
name: springcloud-config-server
#连接远程仓库
cloud:
config:
server:
git:
#连接我们远程的git
uri: https://gitee.com/wodeyanguang/spring-cloud-config.git #https的,就是读配置
@SpringBootApplication
@EnableConfigServer //配置服务器
public class Config_Server_3344 {
public static void main(String[] args) {
SpringApplication.run(Config_Server_3344.class,args);
}
}
然后给出客户端的:
<?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>springcloud1</artifactId>
<groupId>com.yan</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud-config-client-3355</artifactId>
<dependencies>
<!-- 客户端配置-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
</project>
这里是bootstrap.yml
#用户级别的配置
spring:
application:
name: springcloud-config-client-3355
server:
port: 3355
这里是application.yml
#系统界别的配置
spring:
cloud:
config:
#客户端链接服务器,服务器连接远程项目,中转站
uri: http://localhost:3344
name: config-client #从git上读取的资源名称,不需要后缀
profile: dev
label: master
controller层:
package com.yan.springcloud.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author :Yan Guang
* @date :Created in 2020/12/2 14:04
* @description:
*/
@RestController
public class ConfigClientController {
@Value("${spring.application.name}")
private String applicationName;
@Value("${eureka.client.service-url.defaultZone}")
private String eurekaServer;
@Value("server.port")
private String serverPort;
@RequestMapping("/config")
public String getConfig(){
return "ConfigClientController{" +
"applicationName='" + applicationName + '\'' +
", eurekaServer='" + eurekaServer + '\'' +
", serverPort='" + serverPort + '\'' +
'}';
}
}
main:
@SpringBootApplication
public class ConfigClient_3355 {
public static void main(String[] args) {
SpringApplication.run(ConfigClient_3355.class,args);
}
}
搞定了,先启动服务端,然后启动客户端,在这之前一定要先配置好码云,这里我给你们看一看
内容:
spring:
profiles:
active: dev
---
#spring的配置
server:
prot: 8201
spring:
profiles: dev
application:
name: springcloud-provider-dept
#eureka的配置,服务注册到哪里
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/,http://localhost:7003/eureka/
---
#spring的配置
server:
prot: 8202
spring:
profiles: test
application:
name: springcloud-provider-dept
#eureka的配置,服务注册到哪里
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/,http://localhost:7003/eureka/
这里你们需要自己下载git哈,然后搞完进行测试就可以了:
这里给大家讲解一下哈,就是从我们的config客户端连接到config服务器,然后从config服务器再远程连接到了我们的码云上,把码云上面的yml数据给读取出来了
总结
到这里位置,整个项目就结束了,但是学习还没有止步,大家学到这里来的小伙伴相信都是已经学的东西很多了的,但是依然还没有结束,后续我们需要去了解框架源码,设计模式,Java新特性,Netty,Mycat,Http,Jvm和Mysql调优等等,这里大家也可以多多支持我一下,下面是我的码云连接,我所有的内容都放在上面了,大家有什么不懂的问题欢迎在评论区讨论
码云连接