开始
尝试使用restTamplete调用http请求,实现微服务
-
建立父工程:springcloud_study,配置依赖管理
<dependencyManagement> <dependencies> <!-- SpringBoot依赖--> <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-dependencies --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.1.4.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> <!-- SpringCloud依赖 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Greenwich.SR1</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
-
建立两个module,provider和consumer,并都导入父工程的依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
-
生产者提供接口
@RestController public class ProviderController { @GetMapping("/hello") public String hello() { return "hello"; } }
-
消费者调用接口
@RestController public class ConsumerController { @Autowired private RestTemplate restTemplate; //生产者的地址 private static final String URL_PREFIX = "http://localhost:8081/"; @GetMapping("hello") public String hello() { return restTemplate.getForObject(URL_PREFIX + "hello", String.class); } }
-
访问消费者提供的接口,可以看到调用成功
eureka
配置eureka
上面的例子就是微服务最简单的实现,一个生产者一个消费者,通过http请求进行通信。
这里我们引入eureka,eureka是一个注册中心,提供服务注册的功能,上面的例子中消费者连接生产者的地址写在代码中,这里我们使用eureka保存这些信息。
-
新建eureka_server模块,导入依赖
<!-- 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>
-
编写配置文件application.yml
server: port: 7001 # eureka配置 eureka: instance: hostname: localhost #eureka服务端实例名称 client: register-with-eureka: false #是否注册自己 fetch-registry: false # 表示自己是注册中心 service-url: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
-
启动访问
@SpringBootApplication @EnableEurekaServer public class Start { public static void main(String[] args) { SpringApplication.run(Start.class, args); } }
访问localhost:7001即可
注册
-
添加依赖
<!-- 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>
-
修改启动类
@SpringBootApplication @EnableEurekaClient @EnableDiscoveryClient public class ProviderApplication { public static void main(String[] args) { SpringApplication.run(ProviderApplication.class, args); } }
-
修改配置文件
server: port: 8081 spring: application: name: provider #eureka配置 eureka: client: service-url: defaultZone: http://localhost:7001/eureka/ instance: instance-id: provider
-
启动
可以看到注册成功
-
获取服务信息
@GetMapping("/service") public void getInfo(){ List<String> services = discoveryClient.getServices(); for (String service : services) { System.out.println(service); } List<ServiceInstance> provider = discoveryClient.getInstances("provider"); for (ServiceInstance serviceInstance : provider) { System.out.println(serviceInstance.getHost()); System.out.println(serviceInstance.getPort()); } }
配置eureka集群
通过本地不同端口来配置集群,模仿eureka再不同机器上运行
-
新建模块eureka_server1,eureka_server2模块,分别在7002和7003端口启动
-
修改host文件,模仿不同的节点。并配置三个eureka_server的application.yml文件。
127.0.0.1 eureka7001.com 127.0.0.1 eureka7002.com 127.0.0.1 eureka7003.com
server: port: 7001 # eureka配置 eureka: instance: hostname: localhost #eureka服务端实例名称 client: register-with-eureka: false #是否注册自己 fetch-registry: false # 表示自己是注册中心 service-url: defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/ #配置其他两个节点的地址
-
启动三台服务器,查看,发现已经形成集群
-
将provider注册到集群,完成
eureka: client: service-url: defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/,http://localhost:7003/eureka/
CAP:指的是在一个分布式系统中,一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)。CAP 原则指的是,这三个要素最多只能同时实现两点,不可能三者兼顾。
CAP理论的一个例子。开学了,你和同桌互对寒假作业答案,对到一半同桌去了厕所,恰好老师进来要收作业,你决定:AP【交就交吧,有错也无所谓了,交作业要紧】;CP【等同桌回来,把答案对完再交,满分要紧】
ribbon
ribbon介绍
ribbon是一个客户端负载均衡工具
使用
-
在消费者(客户端)导入依赖,启动类加上注解:@EnableEurekaClient
<!-- 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> <!-- 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>
-
重新配置restTemplate
@Bean @LoadBalanced //ribbon负载均衡 public RestTemplate restTemplate() { return new RestTemplate(); }
-
修改provider地址,改成eureka中的服务名,启动即可(由于这里只有一个provider,所以无法体现负载均衡的效果)
//生产者的地址,这里通过eureka中的applicaton name配置,会自己获取服务地址,如果配置了多个provider以及ribbon,会进行provider的负载均衡 private static final String URL_PREFIX = "http://PROVIDER/";
多个provider体现负载均衡
-
再建立两个provider,分别为provider1,provider2。启动再8002和8003端口,导入相关依赖。三个provider的application name要相同,只需要修改instance-id即可。三个都像eureka进行注册,启动三个provider。为了体现到底用了哪个provider,三个provider提供相同的接口,返回不同的内容。
下图可以看见三个provider启动后(需要注册,等一段时间)可以在eureka上看到一个服务有三个实例
-
测试,访问消费者接口,可以看到ribbon是通过轮询的机制访问provider的。
自定义负载均衡策略
ribbon负载均衡策略
策略类 | 命名 | 描述 |
---|---|---|
RandomRule | 随即策略 | 随机选择server |
RoundRobinRule | 轮询策略 | 按照顺序选择server(默认策略) |
RetryRule | 重试策略 | 在一个配置时间段内,当选择server不成功,则一直尝试选择一个可用的server |
BestAvailableRule | 最低并发策略 | 逐个考察server,如果server断路器打开,则忽略,再选择其中并发链接最低的server |
AvailabilityFilteringRule | 可用过滤策略 | 过滤掉一直失败并被标记为circuit tripped的server,过滤掉那些高并发链接的server(active connections超过配置的阈值) |
ResponseTimeWeightedRule | 响应时间加权重策略 | 根据server的响应时间分配权重,响应时间越长,权重越低,被选择到的概率也就越低。响应时间越短,权重越高,被选中的概率越高,这个策略很贴切,综合了各种因素,比如:网络,磁盘,io等,都直接影响响应时间 |
ZoneAvoidanceRule | 区域权重策略 | 综合判断server所在区域的性能,和server的可用性,轮询选择server并且判断一个AWS Zone的运行性能是否可用,剔除不可用的Zone中的所有server |
使用其他策略
-
在主启动类外层建立策略(做到策略可配置,在主启动类同级或下级会被扫描,而我们需要的是指定多个策略,想要使用哪个就配置哪个,不进行扫描)
-
在MyRule中配置bean
@Configuration public class MyRules { //随机 @Bean public IRule randomRule() { return new RandomRule(); } }
-
修改启动类,加上注解:@RibbonClient(name = “PROVIDER”, configuration = MyRules.class),其中name为生产者的名称,configuration为策略类。启动消费者,发现负载均衡策略已经变成了随机策略。
feign
feign介绍
基于服务器端的负载均衡,集成了ribbon
使用
-
新建模块consumer_feign,通过feign的方式来调用服务。
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 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> <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-ribbon --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> <version>1.4.6.RELEASE</version> </dependency> </dependencies>
-
配置文件
# eureka配置 eureka: client: register-with-eureka: false #不想eureka中注册自己(因为只是消费者) service-url: defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7003.com:7003/eureka/,http://eureka7002.com:7002/eureka/
-
编写service层
@Component @FeignClient(value = "PROVIDER")//feign客户端,PROVIDER是生产者的服务名 public interface MyService { //服务生产者的接口地址 @GetMapping("hello") String hello(); }
-
编写controller
@RestController public class ConsumerController { @Autowired private MyService myService; @GetMapping("hello") public String hello() { return myService.hello(); } }
-
启动类
@EnableEurekaClient @SpringBootApplication @EnableFeignClients(basePackages = {"consumer"})//要扫描的包路径 public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } }
Hystrix
服务熔断
服务熔断在服务端做
-
服务提供者添加依赖
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> <version>1.4.6.RELEASE</version> </dependency>
-
controller添加服务
@HystrixCommand(fallbackMethod = "fallbackMethod")//失败调用的方法名 @GetMapping("/hystrix/{id}") public String testHystrix(@PathVariable("id") Long id) { if (id > 10) { return String.valueOf(id); } else { throw new RuntimeException("异常捕获中id超出范围"); } } //发生异常调用的方法 public String fallbackMethod(@PathVariable("id") Long id) { return "hystrix中的i超出范围"; }
-
主启动类添加注解:
@EnableCircuitBreaker
-
利用feign创建消费者消费服务
-
service
@Component @FeignClient(value = "PROVIDER")//feign客户端,PROVIDER是生产者的服务名 public interface MyService { //服务生产者的接口地址 @GetMapping("hello") String hello(); @GetMapping("/hystrix/{id}") String testHystrix(@PathVariable("id") Long id); }
-
controller
@RestController public class ConsumerController { @Autowired private MyService myService; @GetMapping("hello") public String hello() { return myService.hello(); } @GetMapping("/hystrix/{id}") public String testHystrix(@PathVariable("id") Long id) { return myService.testHystrix(id); } }
-
-
启动生产者和消费者,访问消费者端口,可以看到当id小于10的时候,发生异常,执行了断路器,当id大于10的时候正确执行。
服务降级
服务降级在客户端做
-
在客户端添加工厂类
MyFallbackFactory
@Component public class MyFallbackFactory implements FallbackFactory<MyService> { //MyService时服务的客户端接口 //重写create方法,返回MyService接口,实现方法,当服务端不可用时,调用对MyService的实现方法 @Override public MyService create(Throwable throwable) { return new MyService() { @Override public String hello() { return "服务降级返回的hello"; } @Override public String testHystrix(Long id) { return "服务降级返回的id:" + id; } }; } }
-
MyService添加注解
allbackFactory = MyFallbackFactory.class
,将刚创建的工厂类给MyService@Component @FeignClient(value = "PROVIDER", fallbackFactory = MyFallbackFactory.class)//feign客户端,PROVIDER是生产者的服务名 public interface MyService { //服务生产者的接口地址 @GetMapping("hello") String hello(); @GetMapping("/hystrix/{id}") String testHystrix(@PathVariable("id") Long id); }
-
配置服务降级
# hystrix配置,启用服务降级 feign: hystrix: enabled: true
启动客户端和服务端,发现当服务端可以正常访问时没有异常;关掉服务端,发现执行了MyService的默认实现
服务监控
-
配置服务监控服务器
-
创建dashboard模块,导入依赖
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> <version>1.4.6.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId> <version>1.4.6.RELEASE</version> </dependency>
-
编写配置文件
server: port: 9000
-
添加启动类
@SpringBootApplication @EnableHystrixDashboard public class DashboardApplication { public static void main(String[] args) { SpringApplication.run(DashboardApplication.class, args); } }
-
启动dashboard,访问地址http://localhost:9000/hystrix,可以看到监控服务器启动成功。
-
-
修改provider
-
需要导入hytrix和actuator依赖
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> <version>1.4.6.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
-
在启动类上添加servlet bean
//固定写法,配置servlet bean @Bean public ServletRegistrationBean res() { ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet()); servletRegistrationBean.addUrlMappings("/actuator/hystrix.stream"); return servletRegistrationBean; }
-
启动provider,并将监控的地址添加到监控服务器,进行监控
点击monitor stream开始监控,此时一直会loading…,必须要客户端进行一次消费才会出现监控页面,监控的具体信息在图上的地址可见,dashboard只是将图上的地址可视化了,原信息如下图。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8QXwIGFa-1631063048933)(https://i.loli.net/2021/07/31/nw65XTzedBcbEuK.png)]
-
监控页面
图中实心原点的大小表示流量的大小,颜色表示健康程度:绿色>黄色>橙色>红色,此外还有以下信息
-
zuul
-
创建zuul服务:新建springcloud_zuul项目,调入依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> <version>1.4.6.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zuul</artifactId> <version>1.4.6.RELEASE</version> </dependency>
-
编写启动类
@SpringBootApplication @EnableZuulProxy public class ZuulApplication { public static void main(String[] args) { SpringApplication.run(ZuulApplication.class, args); } }
-
配置文件
server: port: 9527 spring: application: name: zuul #eureka配置 eureka: client: service-url: defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/,http://localhost:7003/eureka/ instance: instance-id: zuul-01 # zuul的配置 zuul: routes: myService.serviceId: provider myService.path: /zuulPath/** ignored-services: "*" # 所有的服务都不能使用原来的路径访问 prefix: /prefix # 路径的前缀
-
启动zuul,可以看出zuul已经向eureka注册了
-
通过zuul访问接口,可以看出可以正常访问,并且使用原接口不能访问
Spring Cloud Config
前期工作
通过统一的配置来管理微服务应用,server读取远程的配置,client读取server
-
创建公开的远程git仓库,我这里用的是gitee。创建仓库springcloud_config。
-
添加application.yml文件,编写多配置环境
spring: profiles: active: dev --- spring: profiles: dev application: name: dev_config --- spring: profiles: test application: name: test_config
服务端
-
创建模块
springcloud_config_server
,导入依赖。<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-config-server --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> <version>2.1.1.RELEASE</version> </dependency>
-
配置文件
server: port: 3000 spring: application: name: config_server cloud: config: server: git: uri: https://gitee.com/wms16477/springcloud_config.git
-
启动类
@SpringBootApplication @EnableConfigServer public class ConfigApplication { public static void main(String[] args) { SpringApplication.run(ConfigApplication.class, args); } }
-
访问地址,可以看到成功访问了不同环境的配置文件
客户端
这里的客户端就是一个个的服务,读取服务端的配置,配置文件统一由服务端管理。
-
添加配置文件config-client.yml到远程仓库
spring: profiles: active: dev --- server: port: 8200 spring: profiles: dev application: name: client --- server: port: 8201 spring: profiles: test application: name: client
-
新建模块
springcloud_config_client
,导入依赖<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-config-client --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-client</artifactId> <version>2.1.1.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
-
创建配置文件bootstrap.yml(系统级配置,一般不会改变,在bootstrap中写系统级的配置,采用springlcloud-config,模块独有的配置写在application.yml中)
spring: cloud: config: name: config-client # 需要从git上读取的配置文件名,不要后缀 profile: dev label: master # git分支 uri: http://localhost:3000 #server的地址
-
创建启动类,并启动,可以看到服务端可以读取远程的配置,客户端读取了服务端的配置文件,从服务端中的配置文件配置的端口启动了