SpringCloud 基础
目录
springcloud 基础
一、概述
- 微服务:将传统的一站式应用,拆分成一个个的服务,彻底去耦合,一个微服务就是单功能业务,只做一件事。
- 微服务是一种架构模式或者一种架构风格,提倡将单一应用程序划分成一组小的服务独立部署服务之间相互配合、相互协调,每个服务运行于自己的进程中。服务与服务间采用轻量级通讯,如http的restful api等避免统一的、集中式的服务管理机制 。
- 微服务优点
- 每个服务足够内聚,足够小,比较容易聚焦。
- 开发简单且效率高,一个服务只做一件事情。
- 微服务能用不同的语言开发
- 易于和第三方集成,微服务允许容易且灵活的自动集成部署(持续集成工具有jenkins,hudson,bamboo等)
- 微服务易于被开发人员理解,修改和维护。
- 微服务只是业务逻辑的代码,不会和html,css或其他界面组件融合
- 每个微服务都可以有自己的存储能力,数据库可自有也可以统一,十分灵活
- 微服务缺点
- 开发人员要处理分布式系统的复杂性
- 多服务运维难度,随着服务的增加,运维的压力也会增大
- 依赖系统部署
- 服务间通讯的成本
- 性能监控的难度大
- 系统集成测试难度大
- 数据的一致性维护比较困难
- spring cloud是一个基于spring boot实现的云原生应用开发工具,它为基于jvm的云原生应用开发中涉及的配置管理、服务发现、熔断器、智能路由、微代理、控制总线、分布式会话和集群状态管理等操作提供了一种简单的开发方式。
- springcloud 核心子项目
- spring cloud netflix:核心组件,可以对多个netflix oss开源套件进行整合,包括以下几个组件:
- eureka:服务治理组件,包含服务注册与发现
- hystrix:容错管理组件,实现了熔断器
- ribbon:客户端负载均衡的服务调用组件
- 基于ribbon和hystrix的声明式服务调用组件
- zuul:网关组件,提供智能路由、访问过滤等功能
- archaius:外部化配置组件
- spring cloud config:配置管理工具,实现应用配置的外部化存储,支持客户端配置信息刷新、加密/解密配置内容等。
- …
- spring cloud netflix:核心组件,可以对多个netflix oss开源套件进行整合,包括以下几个组件:
二、服务发现组件 eureka
1. 介绍
- eureka是spring cloud netflix微服务套件中的一部分,是一套成熟的服务注册和发现组件,可以与springboot构建的微服务很容易的整合起来。eureka包含了服务器端和客户端组件。eureka客户端是一个java客户端,用来简化与服务器的交互、作为轮询负载均衡器,并提供服务的故障切换支持。
2. 搭建 maven 父工程
-
pom.xml
<parent> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-parent</artifactid> <version>2.1.3.release</version> <relativepath/> <!-- lookup parent from repository --> </parent> <groupid>com.offcn</groupid> <artifactid>apartenproject</artifactid> <version>1.0-snapshot</version> <packaging>pom</packaging> <properties> <java.version>1.8</java.version> </properties> <dependencymanagement> <dependencies> <dependency> <groupid>org.springframework.cloud</groupid> <artifactid>spring-cloud-dependencies</artifactid> <version>greenwich.release</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencymanagement> <build> <plugins> <plugin> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-maven-plugin</artifactid> </plugin> </plugins> </build>
3. 创建 eureka 集群
-
新建子模块 eureka-server01
-
pom.xml
<parent> <artifactid>apartenproject</artifactid> <groupid>com.offcn</groupid> <version>1.0-snapshot</version> </parent> <groupid>com.offcn</groupid> <artifactid>eureka-server01</artifactid> <version>0.0.1-snapshot</version> <name>eureka-server01</name> <description>demo project for spring boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>greenwich.sr2</spring-cloud.version> </properties> <dependencies> <dependency> <groupid>org.springframework.cloud</groupid> <artifactid>spring-cloud-starter-netflix-eureka-server</artifactid> </dependency> </dependencies> <dependencymanagement> <dependencies> <dependency> <groupid>org.springframework.cloud</groupid> <artifactid>spring-cloud-dependencies</artifactid> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencymanagement> <build> <plugins> <plugin> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-maven-plugin</artifactid> </plugin> </plugins> </build>
-
配置 application.yml
#内置的tomcat服务启动监听端口号 server: # port: 8888 port: 10086 #应用名称 spring: application: name: eureka-server #eurekaserver配置 eureka: client: # register-with-eureka: false #此eurekaserver不再注册到其他的注册中心 # fetch-registry: false #不再从其他中心中心拉取服务器信息 service-url: #defaultzone: http://localhost:${server.port}/eureka #注册中心访问地址 defaultzone: http://localhost:10087/eureka #指向另外一台eureka服务器 server: enable-self-preservation: false # eureka开启自动保护模式 eviction-interval-timer-in-ms: 4000
-
为启动类添加注解
@springbootapplication // 开启 eurekaserver @enableeurekaserver public class eurekaserver01application { public static void main(string[] args) { springapplication.run(eurekaserver01application.class, args); }}
-
-
和 eureka-server01 一样,新建一个子模块eureka-server02,只改一下配置文件 application.yml,其它保持一致。
server: port: 10087 spring: application: name: eureka-server eureka: server: enable-self-preservation: false eviction-interval-timer-in-ms: 4000 client: service-url: defaultzone: http://localhost:10086/eureka
运行两个服务器 和 都可以
4. 创建服务提供方集群
-
新建子模块 userprovider01
-
pom.xml
<dependencies> <dependency> <groupid>org.springframework.cloud</groupid> <artifactid>spring-cloud-starter-netflix-eureka-client</artifactid> </dependency> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-web</artifactid> </dependency> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-data-jpa</artifactid> </dependency> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-jdbc</artifactid> </dependency> <dependency> <groupid>mysql</groupid> <artifactid>mysql-connector-java</artifactid> </dependency> <dependency> <groupid>org.projectlombok</groupid> <artifactid>lombok</artifactid> </dependency> </dependencies>
-
application.yml
server: port: 8001 spring: application: name: userprovider datasource: url: jdbc:mysql://localhost:3306/test?servertimezone=gmt%2b8 username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.driver jpa: hibernate: ddl-auto: update show-sql: true #一定注意eureka与spring属于平级 注意格式 eureka: client: service-url: defaultzone: http://localhost:10086/eureka/,http://localhost:10087/eureka/ # 数据 providerversion: userprovider:0.02v
-
创建实体类user
@entity @data @noargsconstructor @allargsconstructor public class user { @id @generatedvalue private long id; @column(name="name",nullable = true,length = 200) private string name; @column(name = "age",nullable = true,length = 4) private integer age; }
-
userdao 实现 jparepository<user, long>,创建userservice、userserviceimpl、usercontroller(restful风格)
// usercontroller @restcontroller @requestmapping("/user") public class usercontroller { @autowired userservice userservice; @value("${providerversion}") private string providerversion; @getmapping("/getall") @apioperation(value = "获取全部用户信息", notes = "获取全部用户信息") public map<string,object> getusers() { map<string,object> map=new hashmap<>(); list<user> list = userservice.getuserlist(); map.put("list", list); map.put("providerversion", providerversion); return map; } ...... }
启动类添加注解
@enablediscoveryclient
-
-
新建子模块 userprovider02,除了 application.yml 和 usercontroller的getall方法有些差别外,其它全部一样
server: port: 8002 spring: application: name: userprovider datasource: url: jdbc:mysql://localhost:3306/test?servertimezone=gmt%2b8 username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.driver jpa: hibernate: ddl-auto: update show-sql: true #一定注意eureka与spring属于平级 注意格式 eureka: client: service-url: defaultzone: http://localhost:10086/eureka/,http://localhost:10087/eureka/
// usercontroller 中的getuser方法中 providerversion 的值与 userprovider01项目有所区别(providerversion: userprovider:0.02v),这是为了后面负载均衡的时候看出差别。 @getmapping("/getall") public map<string,object> getusers() { map<string,object> map=new hashmap<>(); list<user> list = userservice.getuserlist(); map.put("list", list); string providerversion="用户服务userprovdier002:0.01v"; map.put("providerversion", providerversion); return map; }
-
运行两个提供者
5. 创建服务消费方
新建子模块 userweb01
-
pom.xml
<dependencies> <dependency> <groupid>org.springframework.cloud</groupid> <artifactid>spring-cloud-starter-netflix-eureka-client</artifactid> </dependency> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-web</artifactid> </dependency> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-thymeleaf</artifactid> </dependency> <dependency> <groupid>org.webjars</groupid> <artifactid>bootstrap</artifactid> <version>4.2.1</version> </dependency> <dependency> <groupid>org.projectlombok</groupid> <artifactid>lombok</artifactid> </dependency> </dependencies>
-
配置 application.yml
server: port: 9001 spring: thymeleaf: cache: false application: name: userweb01 eureka: client: service-url: defaultzone: http://localhost:10086/eureka/,http://localhost:10087/eureka/
-
修改启动类
@springbootapplication @enablediscoveryclient public class userweb01application { public static void main(string[] args) { springapplication.run(userweb01application.class, args); } @bean public resttemplate getresttemplate() { return new resttemplate(); } }
-
新建 user,usercontroller,userservice,userserivceimpl
// usercontroller.java // 因为使用了 thymeleaf,所以需要转发到相应模版,使用 model 携带数据 @controller public class usercontroller { @autowired userservice userservice; @getmapping("/") public string getuserlist(model model){ map map = userservice.getusermap(); list<user> list=(list<user>) map.get("list"); model.addattribute("page", list); model.addattribute("providerversion", map.get("providerversion")); return "user/list"; } ...... }
// userserivceimpl.java @service public class userserviceimpl implements userservice { //远程服务调用客户端 @autowired resttemplate resttemplate; //eureka客户端 @autowired discoveryclient discoveryclient; /*** * 通过客户端负载均衡器获取生产者服务器基础地址 * @return */ public string getserverurl() { //通过客户端调用器查找指定服务 list<serviceinstance> instlist = discoveryclient.getinstances("userprovider"); //获取第一个服务器 serviceinstance inst = instlist.get(0); //获取服务提供者服务器ip、端口号 string ip = inst.gethost(); int port = inst.getport(); //拼接调用地址 string url="http://"+ip+":"+port+"/user"; return url; } @override public map getusermap() { map map = resttemplate.getforobject(getserverurl()+"/getall", map.class); return map; } @override public void createuser(user user) { resttemplate.postforobject(getserverurl()+"/save", user,string.class); } @override public user getuser(long id) { return resttemplate.getforobject(getserverurl()+"/get/"+id, user.class); } @override public void updateuser(long id, user user) { resttemplate.put(getserverurl()+"/update/"+id, user); } @override public void deleteuser(long id) { resttemplate.delete(getserverurl()+"/delete/"+id); } }
-
启动服务,跳转地址
三、服务调用组件
springcolud中已经帮我们集成了一系列负载均衡组件:loadbalancerclient、ribbon(缎带)、feign(装作),简单修改代码即可使用。
1. 调用服务基于 loadbalancerclient
新建子模块 userweb02
-
pom.xml
<dependencies> <dependency> <groupid>org.springframework.cloud</groupid> <artifactid>spring-cloud-starter-netflix-eureka-client</artifactid> </dependency> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-web</artifactid> </dependency> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-thymeleaf</artifactid> </dependency> <dependency> <groupid>org.webjars</groupid> <artifactid>bootstrap</artifactid> <version>4.2.1</version> </dependency> <dependency> <groupid>org.projectlombok</groupid> <artifactid>lombok</artifactid> </dependency> </dependencies>
-
配置文件 application.yml
server: port: 9002 spring: thymeleaf: cache: false application: name: userweb02 eureka: client: service-url: defaultzone: http://localhost:10086/eureka/,http://localhost:10087/eureka/
-
启动类、bean、usercontroller、userservice 还和 userweb01 项目保持一直,只有userserviceimpl 有一些变化
// userserviceimpl.java @service public class userserviceimpl implements userservice { //远程服务调用客户端 @autowired resttemplate resttemplate; //支持负载均衡的调用客户端 @autowired loadbalancerclient loadbalancerclient; /*** * 通过客户端负载均衡器获取生产者服务器基础地址 * @return */ public string getserverurl() { //通过客户端调用器查找指定服务,只有这里发生了变化。 serviceinstance inst = loadbalancerclient.choose("userprovider"); //获取服务提供者服务器ip、端口号 string ip = inst.gethost(); int port = inst.getport(); //拼接调用地址 string url="http://"+ip+":"+port+"/user"; return url; } ...... // 方法还和以前保持一致 }
运行服务,
2. 调度服务基于 ribbon
spring cloud ribbon是基于netflix ribbon实现的一套客户端负载均衡的工具。它是一个基于http和tcp的客户端负载均衡器。
新建子模块 userweb03
-
pom.xml
<dependencies> <dependency> <groupid>org.springframework.cloud</groupid> <artifactid>spring-cloud-starter-netflix-eureka-client</artifactid> </dependency> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-web</artifactid> </dependency> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-thymeleaf</artifactid> </dependency> <dependency> <groupid>org.webjars</groupid> <artifactid>bootstrap</artifactid> <version>4.2.1</version> </dependency> <dependency> <groupid>org.projectlombok</groupid> <artifactid>lombok</artifactid> </dependency> <dependency> <groupid>org.springframework.cloud</groupid> <artifactid>spring-cloud-starter-netflix-ribbon</artifactid> </dependency> <dependency> <groupid>org.springframework.retry</groupid> <artifactid>spring-retry</artifactid> </dependency> </dependencies>
-
配置文件 application.yml
- 负载均衡策略:
- com.netflix.loadbalancer.roundrobinrule:轮询
- com.netflix.loadbalancer.randomrule:随机
- com.netflix.loadbalancer.retryrule:重试
- com.netflix.loadbalancer.weightedresponsetimerule:根据响应时间权重分配一个weight,响应时间越长,weight越小,被选中的可能性越低。
- com.netflix.loadbalancer.bestavailablerule:选择一个最小的并发请求的server
- availabilityfilteringrule:过滤掉那些因为一直连接失败的被标记为circuit tripped的后端server,并过滤掉那些高并发的的后端server(active connections 超过配置的阈值)
- zoneavoidancerule:复合判断server所在区域的性能和server的可用性选择server
server: port: 9003 spring: thymeleaf: cache: false application: name: userweb03 #开启spring cloud的重试功能 cloud: loadbalancer: retry: enabled: true eureka: client: service-url: defaultzone: http://localhost:10086/eureka/,http://localhost:10087/eureka/ userprovider: ribbon: # 配置指定服务的负载均衡策略 nfloadbalancerruleclassname: com.netflix.loadbalancer.roundrobinrule # ribbon的连接超时时间 connecttimeout: 250 # ribbon的数据读取超时时间 readtimeout: 250 # 是否对所有操作都进行重试 oktoretryonalloperations: true # 切换实例的重试次数 maxautoretriesnextserver: 1 # 对当前实例的重试次数 maxautoretries: 1
-
启动类修改
@springbootapplication @enablediscoveryclient public class userweb03application { public static void main(string[] args) { springapplication.run(userweb03application.class, args); } @bean // 开启 ribbon @loadbalanced public resttemplate getresttemplate() { return new resttemplate(); } }
-
user、usercontroller、userservice都和上一个保持一致,只有userserviceimpl有所不同
@service public class userserviceimpl implements userservice { //远程服务调用客户端 @autowired resttemplate resttemplate; //开启ribbon后,resttemplate直接使用服务名就可以发起调用 string url="http://userprovider"; ...... 其它代码一致,取消了geturl方法 }
5.运行服务
3. 调度基于 feign
feign是一个声明性的web服务客户端,使用feign创建接口并对其进行注释,就可以通过该接口调用生产者提供的服务。spring cloud对feign进行了增强,使得feign支持了spring mvc注解。
- feign采用的是接口加注解;
- feign 整合了ribbon
创建子模块 userweb04
-
pom.xml
<dependencies> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-web</artifactid> </dependency> <dependency> <groupid>org.springframework.cloud</groupid> <artifactid>spring-cloud-starter-netflix-eureka-client</artifactid> </dependency> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-thymeleaf</artifactid> </dependency> <dependency> <groupid>org.webjars</groupid> <artifactid>bootstrap</artifactid> <version>4.2.1</version> </dependency> <dependency> <groupid>org.projectlombok</groupid> <artifactid>lombok</artifactid> </dependency> <dependency> <groupid>org.springframework.retry</groupid> <artifactid>spring-retry</artifactid> </dependency> <dependency> <groupid>org.springframework.cloud</groupid> <artifactid>spring-cloud-starter-openfeign</artifactid> </dependency> </dependencies>
-
配置文件 application.yml
server: port: 9004 spring: thymeleaf: cache: false application: name: userweb04 #开启spring cloud的重试功能 cloud: loadbalancer: retry: enabled: true eureka: client: service-url: defaultzone: http://localhost:10086/eureka/,http://localhost:10087/eureka/ userprovider: ribbon: # 配置指定服务的负载均衡策略 nfloadbalancerruleclassname: com.netflix.loadbalancer.roundrobinrule # ribbon的连接超时时间 connecttimeout: 250 # ribbon的数据读取超时时间 readtimeout: 250 # 是否对所有操作都进行重试 oktoretryonalloperations: true # 切换实例的重试次数 maxautoretriesnextserver: 1 # 对当前实例的重试次数 maxautoretries: 1 # 设置对应包的日志级别 logging.level.com.offcn.userweb04: debug
-
编写配置类,定义日志级别,feign支持4种级别:
- none:不记录任何日志信息,这是默认值。
- basic:仅记录请求的方法,url以及响应状态码和执行时间
- headers:在basic的基础上,额外记录了请求和响应的头信息
- full:记录所有请求和响应的明细,包括头信息、请求体、元数据。
@configuration public class feignconfig { @bean public logger.level getfeignlogger(){ return logger.level.full; } }
启动类添加注解
@enablefeignclients
-
user、usercontroller与其它 userweb 项目保持一致,但是要删除userserviceimpl,修改userservice
@feignclient(value = "userprovider", configuration = feignconfig.class) public interface userservice { @getmapping("/user/getall") public map<string, object> getusermap(); @postmapping("/user/save") public void createuser(user user); @getmapping("/user/get/{id}") public user getuser(@requestparam("id") long id); @putmapping("/user/update/{id}") public void updateuser(@requestparam("id") long id, @requestbody user user); @deletemapping("/user/delete/{id}") public void deleteuser(@requestparam("id") long id); }
-
运行服务 <>
四、熔断器组件 hystrix
1. 介绍
hystrix是netflix开源的一个延迟和容错库,用于隔离访问远程服务、第三方库,防止出现级联失败。在分布式系统中应用这一模式之后,服务调用方可以自己进行判断某些服务反应慢或者存在大量超时的情况时,能够主动熔断,防止整体系统被拖垮。不同于电路熔断只能断不能自动重连,hystrix可以实现弹性容错,当情况好转之后,可以自动重连。
2. ribbon使用hystrix
-
修改子模块 userweb03,引入依赖
<dependency> <groupid>org.springframework.cloud</groupid> <artifactid>spring-cloud-starter-netflix-hystrix</artifactid> </dependency>
-
修改配置文件
# 添加hystrix熔断超时时间,要求熔断超时 > ribbon 读取超时 hystrix: command: default: execution: isolation: thread: timeoutinmilliseconds: 700
在启动类添加注解
@enablecircuitbreaker
-
修改 userserviceimpl
@override @hystrixcommand(fallbackmethod="getusermapfallbackmethod") public map getusermap() { long begintime = system.currenttimemillis(); map map = resttemplate.getforobject(url+"/user/getall", map.class); long endtime=system.currenttimemillis(); system.out.println("程序执行时间:"+(endtime-begintime)); return map; } // 熔断超时,就会执行该方法 public map<string, object> getusermapfallbackmethod() { map map = new hashmap(); map.put("list", new arraylist<>()); map.put("providerversion", "获取远程调用失败"); return map; }
-
修改 userprovider01,模拟超时情况
@getmapping("/getall") @apioperation(value = "获取全部用户信息", notes = "获取全部用户信息") public map<string,object> getusers() { map<string,object> map=new hashmap<>(); list<user> list = userservice.getuserlist(); map.put("list", list); map.put("providerversion", providerversion); // 模拟超时 try { thread.sleep(900); } catch (interruptedexception e) { e.printstacktrace(); } return map; }
7.运行服务
3. feign使用hystrix
-
pom 引入依赖
<dependency> <groupid>org.springframework.cloud</groupid> <artifactid>spring-cloud-starter-netflix-hystrix</artifactid> </dependency>
-
feign默认也有对hystrix的集成,只不过,默认情况下是关闭的。我们需要通过下面的参数来开启,修改userweb04 模块的配置文件:
feign: hystrix: enabled: true #设定hystrix熔断超时时间 hystrix: command: default: execution: isolation: thread: timeoutinmilliseconds: 700
-
添加类 userserviceimpl
@service public class userserviceimpl implements userservice { @override public map<string, object> getusermap() { map map = new hashmap(); map.put("list", new arraylist<>()); map.put("providerversion", "获取远程调用失败"); return map; } @override public void createuser(user user) { system.out.println("创建用户失败:"+user); } @override public user getuser(long id) { system.out.println("获取id:"+id+" 的用户失败"); return null; } @override public void updateuser(long id, user user) { system.out.println("更新id:"+id+"的用户失败"); } @override public void deleteuser(long id) { system.out.println("删除id为:"+id+"的用户失败"); } }
在 userservice中,使用注解@feignclient声明熔断调用实现类
@feignclient(value = "userprovider", configuration = feignconfig.class, fallback = userserviceimpl.class)
。熔断超时,就会到该指定类执行对应的方法。-
运行服务
4. hystrix监控服务器
1. 搭建hystrix dashboard管理控制中心
-
新建子模块 hystrix-dashboard,编辑pom.xml
<dependencies> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-actuator</artifactid> </dependency> <dependency> <groupid>org.springframework.cloud</groupid> <artifactid>spring-cloud-starter-netflix-hystrix</artifactid> </dependency> <dependency> <groupid>org.springframework.cloud</groupid> <artifactid>spring-cloud-starter-netflix-hystrix-dashboard</artifactid> </dependency> </dependencies>
-
修改配置文件 application.yml
spring: application: name: hystrix-dashboard server: port: 1301
启动类添加注解
@enablehystrixdashboard
-
运行服务
2. 启用客户端hystrix监控
-
pom 引入依赖
<dependency> <groupid>org.springframework.cloud</groupid> <artifactid>spring-cloud-starter-netflix-hystrix</artifactid> </dependency> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-actuator</artifactid> </dependency>
-
修改 userweb04 启动类,在服务实例的主类中已经使用@enablecircuitbreaker或@enablehystrix注解,开启断路器功能。同时增加监控路径访问地址定义/hystrix.stream可以访问。
@springbootapplication @enablediscoveryclient // 开启伪装客户端 @enablefeignclients // 开启断路器功能 @enablehystrix public class userweb04application { public static void main(string[] args) { springapplication.run(userweb04application.class, args); } @bean @loadbalanced public resttemplate getresttemplate() { return new resttemplate(); } @bean public servletregistrationbean getservlet(){ hystrixmetricsstreamservlet streamservlet = new hystrixmetricsstreamservlet(); servletregistrationbean registrationbean = new servletregistrationbean(streamservlet); registrationbean.setloadonstartup(1); //系统启动时加载顺序 registrationbean.addurlmappings("/hystrix.stream");//路径 registrationbean.setname("hystrixmetricsstreamservlet"); return registrationbean; } }
-
启动服务,先执行请求后,然后查看
-
使用hystrix dashboard对hystrix监控数据进行图形化监控。在hystrix dashboard的首页输入http://localhost:9004/hystrix.stream,点击“monitor stream”按钮。
五、分布式配置中心组件spring cloud config
1. 介绍
- 在springboot应用中,配置内容写在application.yml,也可以使用 application-{profile}.yml 的形式设置,但是在微服务架构中,配置文件的分散并不利于系统的管理和维护。
- 微服务对配置管理有更高的要求:
- 集中管理:服务需要集中管理配置,否则维护困难、容易出错。
- 运行期动态调整:某些参数需要在应用运行时动态调整,并且调整时不停止服务。
- 自动更新配置:微服务能够在配置发生变化是自动更新配置。
- spring cloud config主要是为了分布式系统的外部配置提供了服务器端和客户端的支持,只要体现为config server和config client两部分。
- config server: 是一个看横向扩展的,集中式的配置服务器,它用于集中管理应用程序各个环境下配置,默认使用git存储配置内容。
- config client: 是一个config server的客户端,用于操作存储在config server上的配置属性,所有微服务都指向config server,启动的时候会请求它获取所需要的配置属性,然后缓存这些属性以提高性能。
2. 构建config server
-
新建子模块 configserver001,修改pom.xml
<dependencies> <dependency> <groupid>org.springframework.cloud</groupid> <artifactid>spring-cloud-config-server</artifactid> </dependency> <dependency> <groupid>org.springframework.cloud</groupid> <artifactid>spring-cloud-starter-netflix-eureka-client</artifactid> </dependency> </dependencies>
-
修改配置文件 application.yml
server: port: 7001 spring: application: name: config-server cloud: config: server: git: # 根据自己的情况进行配置 uri: https://github.com/username/repositoryname search-paths: src/main/resources username: username password: password eureka: client: service-url: defaultzone: http://localhost:10086/eureka,http://localhost:10087/eureka
-
修改userprovide01的配置文件 application.yml 为 userprovider01-test.yml 并上传,运行服务执行 http://localhost:7001/userprovider01-test.yml
3. 构建config client
将 userprovider01、userprovider02、userweb01、userweb02、userweb03、userweb04 的配置文件 application.yml 修改为 application-dev.yml 并上传到 git 远程仓库,项目本身的配置文件不修改,只是上传git的时候需要改名。
修改以上子模块
-
pom.xml 引入config
<dependency> <groupid>org.springframework.cloud</groupid> <artifactid>spring-cloud-starter-config</artifactid> </dependency>
-
新建配置文件 bootstrap.yml
spring: application: # 根据自己上传的 yml 文件来定 name: userprovider01 cloud: config: discovery: enabled: true # 根据 configserver001 的 application.name 来定。 service-id: config-server # 根据自己上传的 yml 文件来定 profile: dev label: master eureka: client: service-url: defaultzone: http://localhost:10086/eureka,http://localhost:10087/eureka
-
将原有的 application.yml 清空,加上新的内容,可以在配置文件中的内容有改动的时候,不用重启服务,动态刷新,需要在相应的调用类上加新的注解
@refreshscope
。management: endpoints: web: exposure: include: refresh,health,info
-
运行服务,然后启动任意一个客户端,查看运行情况
六、服务网关组件netflix zuul
1. 介绍
- zuul是netflix开源的微服务网关,和eureka,ribbon,hystrix等组件配合使用。zuul组件的核心是一系列的过滤器,这些过滤器可以完成以下功能:
- 认证和安全 :对每一个resource进行身份认证
- 追踪和监控 :实时观察后端微服务的tps、响应时间,失败数量等准确的信息
- 日志 :记录所有请求的访问日志数据,可以为日志分析和查询提供统一支持
- 动态路由 : 动态的将request路由到后端的服务上去
- 压力测试 :逐渐的增加访问集群的压力,来测试集群的性能
- 负载分配:为每一种负载类型分配对应容量,并弃用超出限定值的请求
- 静态响应 :直接在网关返回一些响应,而不是通过内部的服务返回响应
2.zuul服务网关搭建
-
新建子模块 zuulgateway,修改 pom.xml
<dependencies> <dependency> <groupid>org.springframework.cloud</groupid> <artifactid>spring-cloud-starter-netflix-eureka-client</artifactid> </dependency> <dependency> <groupid>org.springframework.cloud</groupid> <artifactid>spring-cloud-starter-netflix-zuul</artifactid> </dependency> </dependencies>
-
修改配置文件:application.yml
spring: application: name: zull-gateway server: port: 80 # 通过 url 直接映射 #zuul: # routes: # userprovider001: # # userprovider001 部分为路由的名字,可以任意定义,但是一组映射关系的path和url要相同 # path: /userprovider001/** # url: http://localhost:8001/ # userprovider002: # path: /userprovider002/** # url: http://localhost:8002/ # 通过 serviceid的映射方式支持了断路器,对于服务故障的情况下,可以有效的防止故障蔓延到服务网关上而影响整个系统的对外服务。 eureka: client: service-url: defaultzone: http://localhost:10086/eureka,http://localhost:10087/eureka zuul: routes: userprovider: path: /service/** service-id: userprovider # 就是将web中的配置 到zuul中 这样多个web都用熔断 仅需要写一次 而不必要每个web 都配置。 retryable: true #打开重试 ribbon: nfloadbalancerruleclassname: com.netflix.loadbalancer.roundrobinrule connectiontimeout: 250 readtimeout: 1000 oktoretryonalloperations: true maxautoretriesnextserver: 1 maxautoretries: 1 #设定hystrix熔断超时时间 hystrix: command: default: execution: isolation: thread: timeoutinmilliseconds: 2000
-
启动类
@enablezuulproxy // 包含了 @springbootapplication、@enablediscoveryclient、@enablecircuitbreaker @springcloudapplication public class zuulgatewayapplication { public static void main(string[] args) { springapplication.run(zuulgatewayapplication.class, args); } }
-
zuul服务网关过滤器
public class accessfilter extends zuulfilter { /** * 四种不同生命周期 * pre:可以在请求被路由之前调用 * routing:在路由请求时候被调用 * post:在routing和error过滤器之后被调用 * error:处理请求时发生错误时被调用 * @return */ @override public string filtertype() { return "pre"; } /** * 过滤器的执行顺序 * @return */ @override public int filterorder() { return 0; } /** * 过滤器是否要执行 * @return */ @override public boolean shouldfilter() { return true; } @override public object run() throws zuulexception { requestcontext ctx= requestcontext.getcurrentcontext(); httpservletrequest request = ctx.getrequest(); string token = request.getparameter("accesstoken"); if(token == null) { // 过滤该请求,不对其进行路由 ctx.setsendzuulresponse(false); // 返回的错误码。也可以通过ctx.setresponsebody(body)对返回 ctx.setresponsestatuscode(401); return null; } return null; } }
-
在启动类中实例化该过滤器
@bean public accessfilter accessfilter() { return new accessfilter(); }
-
启动服务,进行测试