SpringCloud的搭建
源码
SpringCloud笔记,该项目必须配合提交版本查看,这里只是写了主要的操作,细节操作必须搭配代码一起查看
Eureka的创建步骤(最终版为2021-5-23版本,每次提交对应下面的一个步骤)
1、Eureka保证AP
eureka 在设计时就先保证可用性。Eureka各个节点都是平等的,几个节点的down机不会影响正常节点的工作,剩余的节点依然可以提供注册和查询服务。而eureka客户端再向某个eureka服务端注册时如果发现连接失败,则会自动切换至其他节点。只要有一台eureka server 是正常运行的,就能保证服务可用(保证服务可用)。只不过查询到的信息可能不是最新的(不保证强一致性)。
除此之外,eureka还有一种自我保护机制:如果在15分钟内超过85%的节点都没有正常的心跳,那么eureka就认为客户端与注册中心出现了网络故障,此时会出现如下几种情况:
- eureka 不再从注册列表中移除因为长时间没收到响应和请求而应该过期的服务。
- eureka 仍然能够接受新服务的注册和查询请求,但是不会被同步到其他节点上(即保证当前节点依然可用)
- 当网络稳定时,当前实例新的注册信息会被同步到其他节点中,因此,eureka可以很好的应对网络故障导致部分节点失去联系的情况而不会像ZooKeeper那样使整个注册服务瘫痪。
2、Eureka的使用
- 创建一个Maven父工程,将项目使用工具的版本号和一些子模块常用的依赖添加到pom中
- 创建一个api工程里面写入常用的对象和方法
- 创建一个provider_dept_8081工程,导入api工程,并且实现一个基于ssm的crud服务
- 创建一个consumer_dept_8000工程,导入api工程,并且实现基于provider_dept_8081工程的Restful格式接口
- Eureka的Server端建立,创建一个Eureka_7001工程,添加eureka-server服务端依赖,填写yml配置文件,在启动类添加启动标签@EnableEurekaServer
server:
port: 7001
eureka:
instance:
hostname: localhost #eureka服务端的实例名称
client:
register-with-eureka: false #false表示不向注册中心注册自己。
fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址(单机)。
-
将provider_dept_8081工程添加到Eureka的客户端:
(1)添加pom依赖<!-- 将微服务provider侧注册进eureka --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency>
(2)添加yml配置文件
#客户端注册进eureka服务列表内 eureka: client: service-url: defaultZone: http://localhost:7001/eureka
(3)启动类添加启动标签@EnableEurekaClient
-
Eureka的展示界面中Eureka客户端自定义模块名字且访问路径可以显示IP地址,只需要在在Eureka的客户端的yml配置文件中添加配置
eureka: client: service-url: defaultZone: http://localhost:7001/eureka instance: instance-id: 8081ProviderDept #自定义名字,否则Eureka自己起的名字 prefer-ip-address: true #访问路径可以显示IP地址
-
解决Eureka的展示界面中Eureka客户端的info超链接error问题:
(1)起始父工程pom添加<!--放开文件的读取,用于配置Eureka展示界面客户端超链接点击的info界面配置--> <build> <finalName>SpringCloud</finalName> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> </resource> </resources> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <configuration> <delimiters> <delimiter>$</delimiter> </delimiters> </configuration> </plugin> </plugins> </build>
(2) privoider_dept_8081工程添加监视器依赖
<!-- actuator监控信息完善 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
(3)Eureka客户端添加actuator监控依赖且对yml进行配置
#Eureka展示界面客户端超链接点击的info界面返回的json串信息 info: mess: 信息随意配置 app.name: Pning-SpringCloud #软件名字 build.artifactId: $project.artifactId$ #父工程的pom中设置的标识符 build.version: $project.version$ #父工程的pom中设置的标识符
-
Eureka的服务发现,类似于快递公司给客户的一个查询接口
(1)修改provider_dept_8081的controller//添加Eureka的服务发现,即给一个接口让客户使用查询服务信息 @Autowired private DiscoveryClient client; @GetMapping("/discovery") public Object discovery() { List<String> list = client.getServices(); System.out.println("获取到的所有客户端" + list); List<ServiceInstance> srvList = client.getInstances("DEPT8081"); System.out.println("客户端的信息"); for (ServiceInstance element : srvList) { System.out.println(element.getServiceId() + "\t" + element.getHost() + "\t" + element.getPort() + "\t" + element.getUri()); } return this.client; }
(2)启动类添加启动标签@EnableDiscoveryClient
-
Eureka的集群:
(1)先进行域名映射,windows系统在C:\Windows\System32\drivers\etc\hosts文件中添加127.0.0.1 eureka7001.com 127.0.0.1 eureka7002.com 127.0.0.1 eureka7003.com
(2)模仿Eureka_7001工程创建Eureka_7002和Eureka_7003工程
(3)provider_dept_8081的yml配置文件也添加集群的Eureka集群的地址路径
Ribbon的使用步骤(最终版为2021-5-24版本,每次提交对应下面的一个步骤)
-
在consumer_dept_8000项目的pom文件添加Ribbon需要的依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-ribbon</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
-
consumer_dept_8000的DeptConsumerController文件里面的固定ip改为微服务名
//private static final String REST_URL_PREFIX = "http://localhost:8081";
//把ip名改为微服务名
private static final String REST_URL_PREFIX = "http://DEPTPRIVOIDER";
- consumer_dept_8000的ConfigBean文件添加负载均衡注解(默认轮询算法)
@Configuration
public class ConfigBean{
/**
* 使用restful风格进行接口访问
* @return
*/
@Bean
@LoadBalanced
public RestTemplate getRestTemplate()
{
return new RestTemplate();
}
}
- 启动类添加Eureka启动标签
@EnableEurekaClient
- 模仿privoider_dept_8081创建privoider_dept_8082和privoider_dept_8083项目进行负载均衡模拟
注意点:
(1)yml配置文件的spring.application.name
必须三个都一致spring: application: name: deptPrivoider
(2)yml配置文件的eureka.instance.instance-id
必须三个都不一致
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
instance:
instance-id: ProviderDept-8083 #自定义名字,否则Eureka自己起的名字
prefer-ip-address: true #访问路径可以显示IP地址
自定义负载均衡方法
- 创建一个不在启动类同一个包下的配置文件MySelfRule.java(官网规定不能与拥有扫描注解类处于同一个包下,而启动类的SpringBoot注解就拥有包括了扫描注解)
package com.customize; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.netflix.loadbalancer.IRule; import com.netflix.loadbalancer.RoundRobinRule; @Configuration public class MySelfRule { @Bean public IRule myRule() { return new RandomRule_Customize();// 自定义负载均衡算法 } }
- 创建一个继承了AbstractLoadBalancerRule,在choose方法里面编写自己的负载均衡算法
import java.util.List; import com.netflix.client.config.IClientConfig; import com.netflix.loadbalancer.AbstractLoadBalancerRule; import com.netflix.loadbalancer.ILoadBalancer; import com.netflix.loadbalancer.Server; /** * 自定义的负载均衡算法:一个服务调用5次后再调用下一个服务 */ public class RandomRule_Customize extends AbstractLoadBalancerRule { private int total = 0; // 总共被调用的次数,目前要求每台被调用5次 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) { server = upList.get(currentIndex); total++; }else { total = 0; currentIndex++; if(currentIndex >= upList.size()) { currentIndex = 0; } } if (server == null) { Thread.yield(); continue; } if (server.isAlive()) { return (server); } server = null; Thread.yield(); } return server; } @Override public Server choose(Object key) { return choose(getLoadBalancer(), key); } @Override public void initWithNiwsConfig(IClientConfig clientConfig) { // TODO Auto-generated method stub } }
- 在启动类添加注解,启动时候启动我们自己编写的负载均衡算法
@SpringBootApplication @EnableEurekaClient //在启动该微服务的时候就能去加载我们的自定义Ribbon配置类,从而使配置生效 @RibbonClient(name="DEPTPRIVOIDER",configuration= MySelfRule.class) public class DeptConsumer8000_App { public static void main(String[] args) { SpringApplication. run(DeptConsumer8000_App.class,args); } }
Feign的使用步骤(最终版为2021-5-28版本)
- 模仿consumer_dept_8000模块创建feign_8001,只需删除与Ribbon的相关信息即可:
例如:自定义的负载均衡算法,Ribbon依赖(Feign集合了Ribbon无需在导入)和启动类的相关注解 - pom添加feign的依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency>
- 把接口放在api模块中,因为这个接口不仅仅使用一次
(1)给api模块的pom添加feign依赖
(2)在api模块中创建<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency>
DeptClientService.java
,然后重新对api模块进行clean
和package
操作,对maven仓库进行更新package com.pning.service; /** * @Author Pning * @Date 2021/5/25 19:34 **/ import com.pning.entitys.Dept; import org.springframework.cloud.netflix.feign.FeignClient; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import java.util.List; @FeignClient(value = "DEPTPRIVOIDER") public interface DeptClientService { @RequestMapping(value = "/dept/get/{id}", method = RequestMethod.GET) public Dept get(@PathVariable("id") long id); @RequestMapping(value = "/dept/list", method = RequestMethod.GET) public List<Dept> list(); @RequestMapping(value = "/dept/add", method = RequestMethod.POST) public boolean add(Dept dept); }
- 进入feign_8001模块,修改
DeptConsumerController.java
package com.pning.controller; import com.pning.entitys.Dept; import com.pning.service.DeptClientService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.List; /** * @Author Pning * @Date 2021/5/21 11:16 **/ @RestController @RequestMapping("/consumer") public class DeptConsumerController { /** * Feign通过接口的方法调用Rest服务(之前是Ribbon+RestTMplate), * 一个请求发送到Eureka服务器(例如:Http://DEPTPRIVOIDER:8001/consumer/list) * 该请求通过Feign直接找到服务接口,因为Feign在进行服务调用的时候已经整合了Ribbon, * 所以无需我们再次添加Ribbon进行负载均衡(默认调用轮询算法) */ @Autowired private DeptClientService deptClientService; @RequestMapping(value = "/get/{id}") public Dept get(@PathVariable("id") Long id) { return this.deptClientService.get(id); } @RequestMapping(value = "/list") public List<Dept> list() { return this.deptClientService.list(); } @RequestMapping(value = "/add") public Object add(Dept dept) { return this.deptClientService.add(dept); } }
- 启动类
Feign_8001_App.java
添加启动标签@EnableFeignClients
Hystrix的使用步骤(最终版为2021-5-28版本)
-
模仿之前的privoider_dept_8081模块创建一个privoider_dept_hystrix_8084和privoider_dept_hystrix_8085模块
-
添加Hystrix依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency>
-
修改
DeptController.java
,controller有三种请求方法,但是我只修改了其中一个get方法,作为案例已经足够了。//方法失败并抛出了错误信息后,会自动调用@HystrixCommand标注好的fallbackMethod调用类中的指定方法 @HystrixCommand(fallbackMethod = "processHystrix_Get") @GetMapping("/{id}") public Dept get(@PathVariable("id") Long id){ Dept dept = service.get(id); if(dept==null){ throw new RuntimeException(); } return dept; } public Dept processHystrix_Get(@PathVariable("id") Long id){ return new Dept().setDeptno(id).setDname("该ID:" + id + "没有没有对应的信息,null--@HystrixCommand") .setDb_source("no this database in MySQL"); }
-
启动类添加
@EnableCircuitBreaker
开启package com.pning; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; @EnableEurekaClient @SpringBootApplication @EnableDiscoveryClient @EnableCircuitBreaker//对hystrixR熔断机制的支持 public class DeptProvider8084_App{ public static void main(String[] args){ SpringApplication.run(DeptProvider8084_App.class, args); } }
-
服务降级:通过步骤3可以起到熔断的作用,但是这样代码的耦合性太高了,而且Controller层的每一个方法都需要写一个对应的熔断返回方法,非常不利于后期的维护和业务拓展,所以我将其提炼出来,将原本注解在Controller上的
@HystrixCommand
注解放在放在api模块里面的公共接口,在创建一个实现了FallbackFactory
的DeptClientServiceFallbackFactory
接口,通过Spring的异常通知,在接口里面重新编写熔断后的处理方法,为了提高复用性,我们将这个接口放在api
模块中。
(1.)清空原本privoider_dept_hystrix_8085模块中controller层的熔断处理注解和处理方法
(2.)在api模块新建一个DeptClientServiceFallbackFactory.java
package com.pning.service; import java.util.List; import com.pning.entitys.Dept; import org.springframework.stereotype.Component; import feign.hystrix.FallbackFactory; @Component public class DeptClientServiceFallbackFactory implements FallbackFactory<DeptClientService> { @Override public DeptClientService create(Throwable throwable) { return new DeptClientService() { @Override public Dept get(long id) { return new Dept().setDeptno(id).setDname("该ID:" + id + "没有没有对应的信息,Consumer客户端提供的降级信息,此刻服务Provider已经关闭") .setDb_source("no this database in MySQL"); } @Override public List<Dept> list() { return null; } @Override public boolean add(Dept dept) { return false; } }; } }
(3.)在原本的
DeptClientService
公共接口的@FeignClient
注解添加fallbackFactory属性package com.pning.service; /** * @Author Pning * @Date 2021/5/25 19:34 **/ import com.pning.entitys.Dept; import org.springframework.cloud.netflix.feign.FeignClient; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import java.util.List; @FeignClient(value = "DEPTPRIVOIDER",fallbackFactory=DeptClientServiceFallbackFactory.class) public interface DeptClientService { @RequestMapping(value = "/dept/{id}", method = RequestMethod.GET) public Dept get(@PathVariable("id") long id); @RequestMapping(value = "/dept/list", method = RequestMethod.GET) public List<Dept> list(); @RequestMapping(value = "/dept/add", method = RequestMethod.POST) public boolean add(Dept dept); }
(4.)api模块进行
clean
和install
(5.)在feign_8001模块的yml文件上添加服务降级配置,这是因为我们上面写的熔断方法针对的是feign模块的接口,那个接口之前被我们抽去公用api模块了,所以配置要在feign_8001模块配置feign: hystrix: enabled: true
-
服务的监控,首先新建一个hystrix_bashboard_8002模块然后
(1.)添加依赖<dependencies> <!-- 自己定义的api --> <dependency> <groupId>com.pning</groupId> <artifactId>api</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 修改后立即生效,热部署 --> <dependency> <groupId>org.springframework</groupId> <artifactId>springloaded</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-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-ribbon</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <!-- feign相关 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency> <!-- hystrix和 hystrix-dashboard相关 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId> </dependency> </dependencies>
(2.)配置启动端口号
server: port: 8002
(3.)添加启动类
package com.pning; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard; @SpringBootApplication @EnableHystrixDashboard public class DeptConsumer_DashBoard_App { public static void main(String[] args) { SpringApplication.run(DeptConsumer_DashBoard_App.class, args); } }
(4.)在要被接受监督的privoider_dept_hystrix_8084模块下添加依赖
<!-- actuator监控信息完善 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
(5)在浏览器输入
http://localhost:8002/hystrix
观看是否有界面出来,然后在privoider_dept_hystrix_8084模块正常使用的情况下在去看http://localhost:8084/hystrix.stream
是否能ping通或者使用http://localhost:8002/hystrix
测试能否产生图形化界面
Zuul的使用步骤(最终版为2021-5-29版本)
-
创建一个zuul_8003模块,添加依赖、启动类和配置文件
<!-- zuul路由网关 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zuul</artifactId> </dependency>
package com.pning; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.zuul.EnableZuulProxy; @SpringBootApplication @EnableZuulProxy public class Zuul_8003_App { public static void main(String[] args) { SpringApplication.run(Zuul_8003_App.class, args); } }
server: port: 8003 spring: application: name: zuul_8003 eureka: client: service-url: defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7003.com:7003/eureka instance: instance-id: zuulPning.com prefer-ip-address: true zuul: #ignored-services: microservicecloud-dept #可以指定禁用某个服务直接调用服务名来访问 prefix: /pning #使用zuul路由后需要访问路劲前缀 ignored-services: "*" #禁用所有服务直接调用原本服务名来访问 routes: mydept.serviceId: deptPrivoider #原本在Eureka的服务名 mydept.path: /mydept/** #替代上面服务名 #Eureka展示界面客户端超链接点击的info界面返回的json串信息 info: mess: 信息随意配置 app.name: Pning-SpringCloud build.artifactId: $project.artifactId$ build.version: $project.version$
-
进行域名映射,windows系统在C:\Windows\System32\drivers\etc\hosts文件中添加
127.0.0.1 zuulPning.com
Spring Cloud Config的使用(最终版为2021-5-31版本)
我因为学校网络原因GitHub经常登不上去所以这里选用的是gitee,后面就以gitee为例
- 先进行域名映射,windows系统在C:\Windows\System32\drivers\etc\hosts文件中添加
127.0.0.1 configservice7081.com
- 在gitee上创建一个仓库,然后下拉到本地,在该文件中添加测试使用的
config-test.yml
配置文件,并上传到Gitee上,文件记得以utf-8的格式,不然会中文乱码config: info: "成功连接https://gitee.com/P_n_ing/spring-cloud-config"
- 创建一个config_service_7081模块
(1.)添加依赖
(2.)启动类<!-- springCloud Config --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency>
(3.)配置文件package com.pning; /** * @Author Pning * @Date 2021/5/30 21:33 **/ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.config.server.EnableConfigServer; @SpringBootApplication @EnableConfigServer public class ConfigService_7081_App { public static void main(String[] args) { SpringApplication.run(ConfigService_7081_App.class, args); } }
(4.)通过多种方式查看能否读取到git上的配置文件server: port: 7081 spring: application: name: pning_config_service cloud: config: server: git: uri: https://gitee.com/P_n_ing/spring-cloud-config #Git上面的git仓库名字
我使用的是第二种:http://localhost:7081/config-test.yml
,下面是官网文档截图,可自行参考
- 模仿privoider_dept_8081模块创建config_client_7071模块作为客户端
(1.)添加依赖
(2.)添加配置文件,但注意这次不是<!-- SpringCloud Config客户端 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency>
application.yml
,而是bootstrap.yml
,因为bootstrap开头的yml未系统级,application为用户级,级别高的先运行。spring: cloud: config: name: config-client7071 #需要从git上读取的资源名称,注意没有yml后缀名 profile: dev #本次访问的配置项 label: master #分支名 uri: http://configservice7081.com:7081 #本微服务启动后先去找3344号服务,通过SpringCloudConfig获取Git的服务地址