视频来源:狂神说Java https://www.bilibili.com/video/BV1jJ411S7xr
代码已上传码云 :https://gitee.com/ning_fei_fei/springcloud-config.git
1、微服务
什么是微服务
是一种架构思想;从技术角度理解就是将传统的一站式应用,根据业务拆分成一个一个的服务,彻底地去耦合,每一个微服务提供单个业务功能的服务,一个服务做一件事情,从技术角度看就是一种小而独立的处理过程,类似进程的概念,能够自行单独启动或销毁,拥有自己独立的数据库。
微服务强调的是服务的大小,它关注的是某一个点,是具体解决某一个问题/提供落地对应服务的一个服务应用。
微服务架构
微服务架构是一种架构模式,它体长将单一应用程序划分成一组小的服务,服务之间相互协调,互相配合,为用户提供最终价值。每个服务运行在其独立的进程中,服务与服务之间采用轻量级的通信机制**(如HTTP)互相协作,每个服务都围绕着具体的业务进行构建,并且能够被独立的部署到生产环境中,另外,应尽量避免统一的,集中式的服务管理机制,对具体的一个服务而言,应根据业务上下文,选择合适的语言、工具(如Maven)**对其进行构建。
微服务优缺点
优点
- 单一职责原则;
- 每个服务足够内聚,足够小,代码容易理解,这样能聚焦一个指定的业务功能或业务需求;
- 开发简单,开发效率高,一个服务可能就是专一的只干一件事;
- 微服务能够被小团队单独开发,这个团队只需2-5个开发人员组成;
- 微服务是松耦合的,是有功能意义的服务,无论是在开发阶段或部署阶段都是独立的;
- 微服务能使用不同的语言开发;
- 易于和第三方集成,微服务允许容易且灵活的方式集成自动部署,通过持续集成工具,如jenkins,Hudson,bamboo;
- 微服务易于被一个开发人员理解,修改和维护,这样小团队能够更关注自己的工作成果,无需通过合作才能体现价值;
- 微服务允许利用和融合最新技术;
- 微服务只是业务逻辑的代码,不会和HTML,CSS,或其他的界面混合;
- 每个微服务都有自己的存储能力,可以有自己的数据库,也可以有统一的数据库;
缺点
- 开发人员要处理分布式系统的复杂性;
- 多服务运维难度,随着服务的增加,运维的压力也在增大;
- 系统部署依赖问题;
- 服务间通信成本问题;
- 数据一致性问题;
- 系统集成测试问题;
- 性能和监控问题;
2、架构使用springcloud
spring cloud 和dubbo对比
- 注册中心不同,dubbo是zookeeper,springcloud是eureka
- dubbo是基于RPC异步通信方式,springcloud是http的REST方式
- 服务的监控不同,一个是dubbo-monitor,一个是springboot-admin
- dubbo更多的服务需要借助其他的技术,springcloud则自己具备,例如网关,服务配置等
springcloud组件
- Netflix-eureka,注册中心,服务的注册与发现
- Netflix-hystrix,服务熔断和降级
- 负载均衡,Netflix-ribbon和feign
- 网关,Netflix-zuul
- spring cloud config,分布式配置
spring boot和spring cloud的关系
- Spring Boot专注于开发单个个体微服务;
- Spring Cloud是关注全局的微服务协调整理治理框架,它将Spring Boot开发的一个个单体微服务,整合并管理起来,为各个微服务之间提供:配置管理、服务发现、断路器、路由、为代理、事件总栈、全局锁、决策竞选、分布式会话等等集成服务;
- Spring Boot可以离开Spring Cloud独立使用,开发项目,但Spring Cloud离不开Spring Boot,属于依赖关系;
- Spring Boot专注于快速、方便的开发单个个体微服务,SpringCloud关注全局的服务治理框架;
版本选择
Spring Cloud没有采用数字编号的方式命名版本号,而是采用了伦敦地铁站的名称,同时根据字母表的顺序来对应版本时间顺序,比如最早的Realse版本:Angel,第二个Realse版本:Brixton,然后是Camden、Dalston、Edgware;spring cloud和spring boot的版本的选择也是有要求的 查看版本对的对应关系(https://spring.io/projects/spring-cloud#overview
或者
3、spring cloud服务配置和搭建
3.1 版本选择
版本选择
spring boot 选择2.2.4,对应可以选择Hoxton.SR12
3.2 创建父项目
创建一个maven项目(名称springcloud),用于管理版本依赖,src可以删除;
父项目管理版本依赖使用
<dependencyManagement></dependencyManagement>
所有的版本都放在这里,子项目只用导入依赖不需要使用版本
导入spring cloud依赖
<!-- springcloud依赖 版本要和springboot的版本对应,可在官网查看-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR12</version>
<type>pom</type>
<scope>import</scope>
</dependency>
热部署,没有特殊说明所有springboot子项目都导入
<!-- spring-boot-devtools 热部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<version>2.2.4.RELEASE</version>
</dependency>
之后所有带版本的依赖都放在在父项目中
所有的父依赖:
<properties>
<mybatis.version>2.2.0</mybatis.version>
<mybatis-plus-boot-starter.version>3.0-RELEASE</mybatis-plus-boot-starter.version>
<fastjson.version>1.2.75</fastjson.version>
</properties>
<!--springcloud 父依赖管理-->
<dependencyManagement>
<dependencies>
<!-- springcloud依赖 版本要和springboot的版本对应,可在官网查看-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR12</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- eureka-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- zuul -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- eureka 客户端client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- ribbon 负载均衡loadbalance-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</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>
<!-- hystrix -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- spring-boot springboot也可以以parent依赖的方式导入(<parent></parent>)-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
<version>2.2.4.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.version}</version>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus-boot-starter.version}</version>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<!-- logback-core -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
<!-- fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<!-- spring-boot-devtools 热部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<version>2.2.4.RELEASE</version>
</dependency>
</dependencies>
</dependencyManagement>
3.3 Eureka
注册中心eureka
服务的注册和发现,服务提供者直接将服务注册到注册中心,消费者到服务注册中心取,这个和dubbo+zookeeper一样,但是,spring cloud的服务注册是将自己整个全部注册到了注册中心,而dubbo+zookeeper则是注册哪里的服务,扫描哪里的,通过注解;
Eureka采用了C-S的架构设计,EurekaServer作为服务注册功能的服务器,他是服务注册中心。
而系统中的其他微服务,使用Eureka的客户端连接到EurekaServer并维持心跳连接。这样系统的维护人员就可以通过EurekaServer来监控系统中各个微服务是否正常运行,Springcloud 的一些其他模块 (比如Zuul) 就可以通过EurekaServer来发现系统中的其他微服务,并执行相关的逻辑。服务的存活up
创建maven项目springcloud-eureka(也可以直接创建boot项目),导入依赖web,Eureka
<!-- eureka-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
resources下面创建application.yml
,之后不再解释,没有特殊情况yml都放在resources下面,配置eureka
server:
port: 7001
#eureka 服务端(注册中心)配置
eureka:
instance:
hostname: eureka7001.com
client:
register-with-eureka: false #是否向注册中心注册自己
fetch-registry: false #自己不是注册中心,true表示是自己不是注册中心,false表示是注册中心
service-url: # 注册中心页面地址
# 单个注册中心
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
server:
enable-self-preservation: false #关闭自我保护机制,一般开启;测试的时候很烦,暂时关闭
创建启动类,开启注册中心服务
@SpringBootApplication
@EnableEurekaServer //开启eurekaserver,服务端
public class springcloudeurekaApplication {
public static void main(String[] args) {
SpringApplication.run(springcloudeurekaApplication.class,args);
}
}
ok ,启动服务,访问
eureka7001.com:7001
搭建model
创建maven项目springcloud-model来存放所有的model和util以及接口;
导入依赖:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
依照数据库表创建model,这里我用的springboot+mybatis-plus
由于配置了自动装配,所以需要依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<!--model不需要jdbc连接配置,将依赖中的自动配置排除掉,不让消费者会启动报错-->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</exclusion>
</exclusions>
</dependency>
ok ,model 整理好啦。
搭建服务提供者
导入依赖
<!--eureka 客户端client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<!--完善监控信息,配置中info的信息-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</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>
配置文件配置:
server:
port: 8001
spring:
application:
name: springcloud-provide
#eureka 客户端client配置,注册服务到注册中心
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
instance:
instance-id: springcloud-provide8001 #修改默认的描述信息
prefer-ip-address: true # 显示服务的ip地址
# 设置微服务的信息,在注册中心点击Status对应的服务信息查看(看到的是个json,没什么用,太可不必)
info:
app.auhor: ning
app.name: provide-server
company.name: hello-cloud
company.phone: 123456789
启动类加上注解
@EnableEurekaClient //开启eureka客户端,注册服务到注册中心
查看注册到注册中心服务的信息,启动类加上注解
@EnableDiscoveryClient //服务发现,可以查看到服务的信息,用来获取一些配置的信息,得到具体的微服务
获取信息
/**
* DiscoveryClient 可以用来获取一些配置的信息,得到具体的微服务!
*/
@Autowired
private DiscoveryClient client;
/**
* 获取一些注册进来的微服务的信息~,
*
* @return
*/
@GetMapping("/dept/discovery")
public Object discovery() {
// 获取微服务列表的清单
List<String> services = client.getServices();
System.out.println("discovery=>services:" + services);
// 得到一个具体的微服务信息,通过具体的微服务id,applicaioinName;
List<ServiceInstance> instances = client.getInstances("SPRINGCLOUD-PROVIDER");
for (ServiceInstance instance : instances) {
System.out.println(
instance.getHost() + "\t" + // 主机名称
instance.getPort() + "\t" + // 端口号
instance.getUri() + "\t" + // uri
instance.getServiceId() // 服务id
);
}
return this.client;
}
启动项目
访问7001注册中心,服务提供者注册进来了。。。
由于测试的时候不方便,所以关掉了自动保护机制,一般开启,就是上面的一串红字;如果服务挂掉,up会显示为down,但是它依旧在注册中心注册着。
EureKa自我保护机制
- 默认情况下,当eureka server在一定时间内没有收到实例的心跳,便会把该实例从注册表中删除(默认是90秒),但是,如果短时间内丢失大量的实例心跳,便会触发eureka server的自我保护机制,比如在开发测试时,需要频繁地重启微服务实例,但是我们很少会把eureka server一起重启(因为在开发过程中不会修改eureka注册中心),当一分钟内收到的心跳数大量减少时,会触发该保护机制。可以在eureka管理界面看到Renews threshold和Renews(last min),当后者(最后一分钟收到的心跳数)小于前者(心跳阈值)的时候,触发保护机制,会出现红色的警告:
EMERGENCY!EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY'RE NOT.RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEGING EXPIRED JUST TO BE SAFE.
从警告中可以看到,eureka认为虽然收不到实例的心跳,但它认为实例还是健康的,eureka会保护这些实例,不会把它们从注册表中删掉。 - 该保护机制的目的是避免网络连接故障,在发生网络故障时,微服务和注册中心之间无法正常通信,但服务本身是健康的,不应该注销该服务,如果eureka因网络故障而把微服务误删了,那即使网络恢复了,该微服务也不会重新注册到eureka server了,因为只有在微服务启动的时候才会发起注册请求,后面只会发送心跳和服务列表请求,这样的话,该实例虽然是运行着,但永远不会被其它服务所感知。所以,eureka server在短时间内丢失过多的客户端心跳时,会进入自我保护模式,该模式下,eureka会保护注册表中的信息,不在注销任何微服务,当网络故障恢复后,eureka会自动退出保护模式。自我保护模式可以让集群更加健壮。
- 但是我们在开发测试阶段,需要频繁地重启发布,如果触发了保护机制,则旧的服务实例没有被删除,这时请求有可能跑到旧的实例中,而该实例已经关闭了,这就导致请求错误,影响开发测试。所以,在开发测试阶段,我们可以把自我保护模式关闭,只需在eureka server配置文件中加上如下配置即可:
eureka.server.enable-self-preservation=false
【不推荐关闭自我保护机制】
Eureka集群
就是多个注册中心相互有关联
在创建两个eureka项目,端口号为7002和7003,配置依赖和之前的一致,可以直接复制;
这里修改了一下本机的ip名称,来模拟一下多个ip:
hosts文件的位置:C:\Windows\System32\drivers\etc
右键notepad打开,在最后添加:
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
127.0.0.1 eureka7003.com
保存一下,如果不能添加,右键,属性,把只读权限关掉,安全中勾选写入,保存;我的是这么操作的,一般这个文件不要乱改;
创建成功之后端口号7001修改配置文件:
#多个注册中心,集群;就是几个注册中心互相建立关联关系;本机关注其他注册中心即可
defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
一个注册中心defaultZone
配置本机的注册中心地址,集群的话就是注册中心相互有联系,本机关注其他注册中心即可,7001关注7002和7003,7002关注7001和7003,7003关注7001和7002
修改7002:
server:
port: 7002
#eureka 服务端(注册中心)配置
eureka:
instance:
hostname: eureka7002.com
client:
register-with-eureka: false #是否向注册中心注册自己
fetch-registry: false #自己不是注册中心,true表示是自己不是注册中心,false表示是注册中心
service-url: # 注册中心页面地址
# 单个注册中心
# defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
#集群
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7003.com:7003/eureka/
server:
enable-self-preservation: false #关闭自我保护机制,一般开启;测试的时候很烦,暂时关闭
修改7003:
server:
port: 7003
#eureka 服务端(注册中心)配置
eureka:
instance:
hostname: eureka7003.com
client:
register-with-eureka: false #是否向注册中心注册自己
fetch-registry: false #自己不是注册中心,true表示是自己不是注册中心,false表示是注册中心
service-url: # 注册中心页面地址;
# 单个注册中心
# defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
#集群
defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7001.com:7001/eureka/
server:
enable-self-preservation: false #关闭自我保护机制,一般开启;测试的时候很烦,暂时关闭
全部启动:
访问7002和7003也是基本一致的。
服务提供者注册到集群环境中
只需要修改配置文件,把它注册到所有的注册中心里面:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
有热部署,等一下,刷新页面,发现三个注册中心都注册进去了。。。
如果provide配置的注册中心没有全部启动,那么provide启动时会异常,但是不关键,只是连接不上到配置的没有启动的注册中心。
Ribbon 客户端的负载均衡
客户端的负载均衡,provide的项目再来两个,provide02和provide03,用于实现负载均衡,配置的服务端名称要一致,依赖一样;客户端访问服务端的访问地址和端口可以用注册中心application的名称来替代,例如:
http://localhost:8001/systemUserController/getSystemUserList
可以替换为:
http://SPRINGCLOUD-PROVIDE/systemUserController/getSystemUserList
所以,不论这个服务放在那里,访问的路径都是一样的,实现一个入口的效果。
配置客户端的负载均衡,springboot只需要一个注解
@LoadBalanced //Ribbon 实现负载均衡,通过Restful;默认策略轮询
springcloud是http的rest方式,所以配置一下rest的模板RestTemplate
,开启它的负载均衡
创建客户端consumer
依赖导入eureka客户端依赖
<!-- ribbon 负载均衡loadbalance-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
<!--eureka 客户端client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
配置文件:
server:
port: 80 # 80端口不需要使用端口访问
#eureka配置
eureka:
client:
register-with-eureka: false #不向注册中心注册自己
service-url: #到哪个注册中心取服务
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
开启eureka客户端:
@EnableEurekaClient
配置配置类:
@Configuration
public class MyResquestConfig {
@Bean
@LoadBalanced //Ribbon 实现负载均衡,通过Restful;默认策略轮询
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
为了更加显示效果,将数据库复制两份,每个provide连各自的数据库,数据库表一样;
写个controller使用RestTemplate
调用服务端接口:
@RestController
public class SystemUserConsumerController {
@Autowired
RestTemplate restTemplate;
//该类中请求的前缀都一致,定义一下
// http://localhost:8001/systemUserController/get
//SPRINGCLOUD-PROVIDE 服务的名称,注册中心Application对应的值
public static final String url_prefix= "http://SPRINGCLOUD-PROVIDE/systemUserController";
@RequestMapping("/systemUserConsumerController/getList")
public String getList(){
return restTemplate.getForObject(url_prefix+"/getSystemUserList",String.class);
}
@GetMapping("/addUser")
public Boolean addUser(){
SystemUser systemUser = new SystemUser();
systemUser.setUserAccount("ning").setPassword("123456").setDescribtion("hellocloud");
return restTemplate.postForObject(url_prefix+"/addSystemUser",systemUser,Boolean.class);
}
}
访问:
http://localhost/systemUserConsumerController/getList
数值代表是哪个数据库,客户端访问,发现是01,02,03,01,02,03…
可以看出实现了负载均衡,而且是轮询的模式,Ribbon默认的是轮询模式;
也可以自定义负载均衡的模式:
创建自己的类,继承AbstractLoadBalancerRule
,然后编写自定义的模式,使用配置类注入到bean中,最后启动类的注解@RibbonClient
加上属性configuration指定这个配置类;
//自定义模式类
public class MyRandomRule extends AbstractLoadBalancerRule{
/**
* 每个服务访问5次则换下一个服务(总共3个服务)
* <p>
* total=0,默认=0,如果=5,指向下一个服务节点
* index=0,默认=0,如果total=5,index+1
*/
private int total = 0;//被调用的次数
private int currentIndex = 0;//当前是谁在提供服务
//@edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE")
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) {
/*
* No servers. End regardless of pass, because subsequent passes
* only get more restrictive.
*/
return null;
}
//int index = chooseRandomInt(serverCount);//生成区间随机数
//server = upList.get(index);//从或活着的服务中,随机获取一个
//=====================自定义代码=========================
if (total < 5) {
server = upList.get(currentIndex);
total++;
} else {
total = 0;
currentIndex++;
if (currentIndex > upList.size()) {
currentIndex = 0;
}
server = upList.get(currentIndex);//从活着的服务中,获取指定的服务来进行操作
}
//======================================================
if (server == null) {
/*
* The only time this should happen is if the server list were
* somehow trimmed. This is a transient condition. Retry after
* yielding.
*/
Thread.yield();
continue;
}
if (server.isAlive()) {
return (server);
}
// Shouldn't actually happen.. but must be transient or a bug.
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) {
// TODO Auto-generated method stub
}
}
配置类:
@Configuration
public class MyRule {
@Bean
public IRule myRule(){
return new MyRandomRule();//默认是轮询RandomRule,现在自定义为自己的
}
}
开启负载均衡和指定配置类(在启动类上加注解):
@RibbonClient(name = "SPRINGCLOUD-PROVIDER",configuration = MyRule.class)//开启负载均衡,并指定自定义的规则
注意,配置类MyRule
不在主应用程序上下文的@ComponentScan
中,否则将由所有@RibbonClients
共享。如果您使用@ComponentScan
(或@SpringBootApplication
),则需要采取措施避免包含(例如将其放在一个单独的,不重叠的包中,或者指定要在@ComponentScan
),一般直接放在启动类的上一级。
Feign 服务端的负载均衡
服务端提供接口,客户端直接注入调用方法。
这里我把接口放在了model里面,导入依赖:
<!-- feign 实现负载均衡,定义接口-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
接口interface
@Component
//value值是注册中心Application的值
@FeignClient(value = "SPRINGCLOUD-PROVIDE")
public interface SystemUserService {
@GetMapping(value = "/systemUserController/getSystemUserList")
public String getSystemUserList();
@PostMapping(value = "/systemUserController/addSystemUser")
public Boolean addSystemUser(@RequestBody SystemUser systemUser);
@GetMapping(value = "/systemUserController/removeSystemUser/{id}")
public Boolean removeSystemUser(@PathVariable("id") Long id);
}
consumer客户端
直接导入model的依赖和feign的依赖(和上面一样),启动类上加上feign的注解和服务的包的扫描(扫描model):
@EnableFeignClients(basePackages = {"com.ning"})
@ComponentScan("com.ning")
创建配置类:
@Configuration
public class MyResquestConfig {
@Bean
@LoadBalanced //Ribbon 实现负载均衡,通过Restful;默认策略轮询
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
创建controller,注入接口,直接调用提供者提供的服务
@RestController
public class SystemUserConsumerController {
@Autowired
private SystemUserService systemUserService = null; //只是一个接口,没有实现类,为null就行
/**
* feign调用
* @return
*/
@GetMapping("/getList")
public String getList(){
return systemUserService.getSystemUserList();
}
}
Hystrix 服务熔断和服务降级
Hystrix是一个应用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时,异常等,Hystrix 能够保证在一个依赖出问题的情况下,不会导致整个体系服务失败,避免级联故障,以提高分布式系统的弹性。
熔断可以说是发生错误了整个应用还可以继续往下面走,返回一个发生错误的状态或者发生错误时执行其他程序后返回的结果,使用注解@HystrixCommand
,其中注解fallbackMethod
属性指向发生错误后执行的程序。
服务熔断
服务的提供者provide进行操作,服务熔断
导入依赖:
<!--导入Hystrix依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
启动类加上注解@EnableCircuitBreaker
开启熔断服务
controller类中写一个方法作为出错的时候执行的方法,这个方法根据自己的业务逻辑来定,在需要熔断的方法上加上注解@HystrixCommand
并指定fallbackMethod
属性为写的方法:
@RestController
@RequestMapping("/systemUserController")
public class SystemUserController {
@Autowired
SystemUserService systemUserService;
@HystrixCommand(fallbackMethod = "error")
@GetMapping(value = "/getSystemUserList")
public String getSystemUserList(){
return JSONObject.toJSONString(systemUserService.list(new QueryWrapper<SystemUser>()));
}
public String error(){
return "hello_hystrix";
}
}
可以写个bug在getSystemUserList()中,调用的时候就会返回error的返回值;
服务降级
服务降级是指 当服务器压力剧增的情况下,根据实际业务情况及流量,对一些服务和页面有策略的不处理,或换种简单的方式处理,从而释放服务器资源以保证核心业务正常运作或高效运作。说白了,就是尽可能的把系统资源让给优先级高的服务。
资源有限,而请求是无限的。如果在并发高峰期,不做服务降级处理,一方面肯定会影响整体服务的性能,严重的话可能会导致宕机某些重要的服务不可用。所以,一般在高峰期,为了保证核心功能服务的可用性,都要对某些服务降级处理。比如当双11活动时,把交易无关的服务统统降级,如查看蚂蚁深林,查看历史订单等等。
服务降级主要用于什么场景呢?当整个微服务架构整体的负载超出了预设的上限阈值或即将到来的流量预计将会超过预设的阈值时,为了保证重要或基本的服务能正常运行,可以将一些 不重要 或 不紧急 的服务或任务进行服务的 延迟使用 或 暂停使用。
服务接口我放在了model中的service下,直接导入依赖,依赖是一样的,hystrix的,在接口上加上注解:
//value值是注册中心Application的值 , fallbackFactory指定服务降级配置类
@FeignClient(value = "SPRINGCLOUD-PROVIDE",fallbackFactory = SystemUserServiceFallBack.class)
创建hystrix的fallback类:
@Component //扔给spring管理
public class SystemUserServiceFallBack implements FallbackFactory {
public Object create(Throwable throwable) {
return new SystemUserService() {
public String getSystemUserList() {
return "服务暂停了-------";
}
public Boolean addSystemUser(SystemUser systemUser) {
return null;
}
public Boolean removeSystemUser(Long id) {
return null;
}
};
}
}
消费者consumer侧操作
直接配置文件配置开启:
# 开启降级feign.hystrix
feign:
hystrix:
enabled: true
hystrixDashboard流监控
直接创建项目springcloud-consumer-hystrix-dashboard:
pom依赖:
<!-- hystrix -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- hystrix-dashboard 监控页面 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
配置文件:
server:
port: 9001
hystrix:
dashboard:
proxy-stream-allow-list: "*" #表明监控所有的访问流
启动类:
@SpringBootApplication
@EnableHystrixDashboard //开启dashboard监控
public class ConsumerHystrixDashboardApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerHystrixDashboardApplication.class,args);
}
}
启动访问:
http://localhost:9001/hystrix
豪猪(hystrix)来咯
上面写的蛮清楚的,默认的集群、自定义集群以及单一的应用怎么写地址,delay监控间隔时间默认的2000就可以,名称自己随便可以定义,例如:demo
monitor stream:监控流,所以要给服务的提供者provide配置一个servlet,让它可以被监控:
依赖:
<!--完善监控信息,配置中info的信息-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
servlet:
@Bean //添加一个servlet,监控服务,使用hystrix-dashboard
public ServletRegistrationBean hystrixMetricsStreamServlet(){
ServletRegistrationBean registrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet());
registrationBean.addUrlMappings("/actuator/hystrix.stream");
return registrationBean;
}
填写要监控的项目地址端口:http://localhost:8001//actuator/hystrix.stream
进来啦:
访问8001项目的接口就可以看到变化。
对应的意义
注入的提供者越多,这个页面的这样的图越多,对每个应用的监控也一目了然;
zuul 网关
Zull包含了对请求的路由(用来跳转的)和过滤两个最主要功能:
其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础,而过滤器功能则负责对请求的处理过程进行干预,是实现请求校验,服务聚合等功能的基础。Zuul和Eureka进行整合,将Zuul自身注册为Eureka服务治理下的应用,同时从Eureka中获得其他服务的消息,也即以后的访问微服务都是通过Zuul跳转后获得。
依赖:
<!-- zuul -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
使用的是父依赖中的依赖,使用的是父依赖的版本;这里注册中心的依赖也是需要的,和上面的一样,省略。
配置zuul:
zuul:
routes:
ning.serviceId: springcloud-provide #之前的访问路径
ning.path: /ning/** #访问路径
# prefix: /hello #设置访问前缀
ignored-services: '*' #忽略所有的访问
既然注册到注册中心,肯定需要配置一下注册中心和自己的名称、端口,和eureka一样,省略。
创建启动类,使用注解@EnableZuulProxy
开启网关的服务,启动项目,到注册中心看一下:
使用http://localhost:9527/springcloud-provide/systemUserController/getSystemUserList
访问一下,发现找不到了,配置生效了:
访问下配置的地址http://localhost:9527/ning/systemUserController/getSystemUserList
springcloud-config 分布式配置
微服务意味着要将单体应用中的业务拆分成一个个子服务,每个服务的粒度相对较小,因此系统中会出现大量的服务,由于每个服务都需要必要的配置信息才能运行,所以一套集中式的,动态的配置管理设施是必不可少的。
对每个yml配置文件集中进行管理,则是springcloud-config。
创建一个服务端用于从配置库中读取配置信息,客户端都连接这个服务端,来获取各自的配置信息:
spring cloud config 分布式配置中心与GitHub整合
配置gitee
由于GitHub网页登录和打开太慢,这里使用gitee;
安装git环境,创建gitee账号,登录进去直接到最下面找到:
都可以,输入命令来生成公钥,
直接Windows+r输入cmd ,执行ssh-keygen -t rsa -C "1132023770@qq.com"
,1132023770@qq.com
这是个邮箱地址,什么邮箱都可以,注意==自己的邮箱!!!!==然后三次回车。OK,到c盘用户下面的.ssh文件夹里邮件打开:
然后复制出公钥:
OK,可以创建仓库,并且拉取和推送东西了,这个省略了。
创建读取配置的服务端
创建项目springcloud-config-server
导入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- spring-cloud-config-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
顺便导入热部署依赖,每个项目都导入热部署的依赖,这里省略!
配置文件:
server:
port: 8888
spring:
application:
name: springcloud-config-server
cloud:
config:
server:
git:
uri: https://gitee.com/ning_fei_fei/springcloud-config.git # Https
search-paths: springcloud-config-yml/ #扫描git这个包下面的所有文件
一定要注意是uri,如果创建了文件夹来放文件,要扫描文件夹,search-paths
,多个值用,
隔开就OK,这个可以查看官方文档。
创建启动类,使用注解@EnableConfigServer
开启服务
@SpringBootApplication
@EnableConfigServer //开启服务配置
public class SpringCloudConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudConfigServerApplication.class,args);
}
}
创建一个客户端,当然也可以直接用其他的项目操作,注册中心,zuul,provide等等,除了读取配置的服务端,都是可以的,单独创建一个是为了简单的配置一下,就可以明了的看到;
依赖:
<!-- config-client 客户端依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
其他的依赖根据项目的类型添加自己的依赖。
创建两个配置文件,一个application.yml
,另一个bootstrap.yml
;bootstrap里面放连接读取服务端配置信息:
#系统级饿配置文件
spring:
cloud:
config:
name: application #从git上读取的资源文件名称,不需要后缀
label: master #从git哪个分支获取
profile: dev #读取哪个配置
uri: http://localhost:8888 #获取资源配置文件的服务端访问路径
application里面只用指定一下自己的客户端的名称,毕竟是微服务,各自的服务都要有一个自己的名字:
spring:
application:
name: config-client
启动类正常写,springboot项目嘛,不多说。
当然,gitee上还是需要这个配置文件的:
文件内容:
spring:
profiles:
active: dev
---
server:
port: 9999
spring:
profiles: dev
application:
name: springcloud-config-dev
---
spring:
profiles: test
application:
name: springcloud-config-test
启动客户端,启动之前要保证之前的项目处于启动状态,读取配置的服务端;
可以看到客户端连接服务端远程读取配置文件了,读取到了gitee上面给他配置的端口号9999,tomcat默认端口号8080,如果没有断区到,端口号是8080。自己可以把gitee的配置文件修改一下,尝试尝试。
其他的微服务和这个客户端一样,同样的操作流程和配置,这里就不一一替换了,自己可以尝试操作;