今天是2020-10-6日
打开spring cloud的主页(https://spring.io/projects/spring-cloud#overview)
首先就看到最新的版本号 : Spring Cloud Hoxton.SR8
大版本号:Hoxton (从ABCDEFGH往上排),这就是H版
小版本号: SNAPSHOT:快照版本;随时可能修改 M:MileStone,M1表示第1个里程碑版本,一般同时标注PRE,表示预览版版。 SR:Service Release,SR1表示第1个正式版本,一般同时标注GA:(GenerallyAvailable),表示稳定版本。
Table 1. Release train Spring Boot compatibility Release TrainBoot VersionHoxton
2.2.x, 2.3.x (Starting with SR5)
Greenwich
2.1.x
Finchley
2.0.x
Edgware
1.5.x
Dalston
1.5.x
打开第二个tab页 LEARN ,看到这个版本列表
Hoxton.SR8 CURRENT GAReference Doc.
API Doc.2020.0.0-SNAPSHOT SNAPSHOTReference Doc.API Doc.2020.0.0-M3 PREReference Doc.API Doc.Hoxton.BUILD-SNAPSHOT SNAPSHOTReference Doc.API Doc.Greenwich.SR6 GAReference Doc.API Doc.Greenwich.BUILD-SNAPSHOT SNAPSHOTReference Doc.API Doc.打开 Reference Doc. 超级链接,可以看到和Spring Boot的版本对应关系
Release Train Version: Hoxton.SR8
Supported Boot Version: 2.3.3.RELEASE
第一章 创建服务注册中心 eureka-server
微服务架构,最基本的,就是要有一个服务注册和服务发现中心。之前用eureka(官方不再维护了),还可以使用zookeeper,etcd,consul,nacos。
建立父工程,即多module maven工程,注意pom的packaging 为 pom, 不是jar
父pom.xml <?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>gaofeng</groupId> <artifactId>springcloud</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>pom</packaging> <name>springcloud</name> <description>springcloud</description> <modules> <module>eurekaserver</module> <module>app1</module> <module>app2</module> <module>zuul</module> </modules> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <spring.boot.version>2.3.3.RELEASE</spring.boot.version> <spring.cloud.version>Hoxton.SR8</spring.cloud.version> <spring.cloud.alibaba.version>2.2.3.RELEASE</spring.cloud.alibaba.version> <env>dev</env> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>${spring.boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring.cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>${spring.cloud.alibaba.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> </project> 子pom.xml <?xml version="1.0"?> <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>gaofeng</groupId> <artifactId>springcloud</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <artifactId>eurekaserver</artifactId> <dependencies> <!-- 引入 Spring Cloud Netflix Eureka Server 相关依赖,将 Eureka 作为注册中心的服务器,并实现对其的自动配置 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <executions> <execution> <goals> <goal>repackage</goal> </goals> <configuration> <classifier>exec-${env}</classifier> </configuration> </execution> </executions> </plugin> </plugins> </build> </project> eurekaserver/src/main/resources/application.yml server: port: 8761 # 设置 Eureka-Server 的端口 spring: application: name: eureka-server eureka: client: register-with-eureka: false # 不注册到 Eureka-Server,默认为 true fetch-registry: false # 不从 Eureka-Server 获取注册表,默认为 true package org.eurekaserver; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; // EurekaServer管理页面 http://127.0.0.1:8761 @SpringBootApplication @EnableEurekaServer public class EurekaServerApplication { public static void main(String[] args) { SpringApplication.run(EurekaServerApplication.class,args); } } mvn clean install java -jar target/eurekaserver-0.0.1-SNAPSHOT-exec-dev.jar第二章 创建第一个微服务 app1
pom.xml <?xml version="1.0"?> <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>gaofeng</groupId> <artifactId>springcloud</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <artifactId>app1</artifactId> <dependencies> <!-- 引入 Spring 相关依赖,并实现对其的自动配置 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 引入 Spring Cloud Netflix Eureka Client 相关依赖,将 Eureka 作为注册中心的客户端,并实现对其的自动配置 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <executions> <execution> <goals> <goal>repackage</goal> </goals> <configuration> <classifier>exec-${env}</classifier> </configuration> </execution> </executions> </plugin> </plugins> </build> </project> app1/src/main/resources/application.yml server: port: 10002 # 设置 app1 的端口 spring: application: name: app1 eureka: client: register-with-eureka: true # 注册到 Eureka-Server,默认为 true fetch-registry: true # 从 Eureka-Server 获取注册表,默认为 true service-url: defaultZone: http://127.0.0.1:8761/eureka/ # Eureka-Server 地址 package org.app1; import java.util.Random; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @SpringBootApplication @EnableDiscoveryClient public class App1 { public static void main(String[] args) { SpringApplication.run(App1.class, args); } } @RestController class TestController2 { private int id = new Random().nextInt(); @GetMapping("/echo") public String echo(String name) { return "provider:"+id+":" + name; } } $ java -jar target/app1-0.0.1-SNAPSHOT-exec-dev.jar $ curl -is http://127.0.0.1:10002/echo?name=gg HTTP/1.1 200 Content-Type: text/plain;charset=UTF-8 Content-Length: 22 Date: Tue, 06 Oct 2020 15:57:07 GMT provider:-537651807:gg第三章 微服务之间如何调用rest接口
再启动一个app1的实例,端口号改为10004
$ java -jar target/app1-0.0.1-SNAPSHOT-exec-dev.jar --server.port=10004
curl -is http://127.0.0.1:10005/hello?name=gg
他的本质过程其实是:
package org.app2; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @SpringBootApplication @EnableDiscoveryClient public class App2 { public static void main(String[] args) { SpringApplication.run(App2.class, args); } @Configuration public class RestTemplateConfiguration { @Bean public RestTemplate restTemplate() { return new RestTemplate(); } } @RestController static class TestController { @Autowired private DiscoveryClient discoveryClient; @Autowired private RestTemplate restTemplate; @Autowired private LoadBalancerClient loadBalancerClient; @GetMapping("/hello") public String hello(String name) { // <1> 获得服务 `demo-provider` 的一个实例 ServiceInstance instance; if (System.currentTimeMillis()%2==0) { // 获取服务 `demo-provider` 对应的实例列表 List<ServiceInstance> instances = discoveryClient.getInstances("app1"); System.out.println(instances); // 选择第一个 instance = instances.size() > 0 ? instances.get(0) : null; } else { instance = loadBalancerClient.choose("app1"); } // <2> 发起调用 if (instance == null) { throw new IllegalStateException("获取不到实例"); } String targetUrl = instance.getUri() + "/echo?name=" + name; System.out.println(targetUrl); String response = restTemplate.getForObject(targetUrl, String.class); // 返回结果 return "consumer:" + response; } } }第四章 使用另外一种(声明式)http客户端openFegin(老的Fegin已经不更新了)
关键点1:@FeignClient("app1") 关键点2:public String echo(@RequestParam("name") String name); 必须加@RequestParam("name") 验证方法:curl -s 127.0.0.1:10005/feign?name=gf <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> @SpringBootApplication @EnableDiscoveryClient @EnableFeignClients public class App4 { public static void main(String[] args) { SpringApplication.run(App4.class,args); } } @RestController class RemoteServer { @Autowired RemoteApi remoteApi; @RequestMapping(value = "/feign",method = RequestMethod.GET) public String remoteServer(@RequestParam String name){ return remoteApi.echo(name); } } @FeignClient("app1") interface RemoteApi{ @GetMapping(value = "/echo") public String echo(@RequestParam("name") String name); }设置Fegin的超时时间,在yml中增加
ribbon: ReadTimeout: 1000 ConnectTimeout: 1000
打印Fegin的日志,在yml中增加
logging: level: ROOT: INFO #针对指定的类 设置输出级别 org.app2.*: debug feign: client: config: default: loggerLevel: FULL
第五章 限流、降级、熔断 hystrix
服务降级有很多种降级方式!如开关降级、限流降级、熔断降级!
在yml中开启熔断机制 feign: hystrix: enabled: true 代码修改 @FeignClient(value="app1",fallback = FallbackImpl.class) interface RemoteApi{ @GetMapping(value = "/echo2") //故意把url写错,当Feign调用这个url出错时,会返回FallbackImpl public String echo(@RequestParam("name") String name); } @Service class FallbackImpl implements RemoteApi{ //编写熔断的处理类 public String echo(String name) { return "error error error error error"; } }
第六章 consul
G:\gaofeng\consul_1.8.4_windows_amd64
1、启动 consul agent -dev
2、打开界面 http://localhost:8500/
3、注册服务 curl http://127.0.0.1:8500/v1/agent/service/register -X PUT -i -H "Content-Type:application/json" -d '{ "ID": "userServiceId2", "Name": "userService", "Tags": [ "primary", "v1" ], "Address": "127.0.0.1", "Port": 8000, "EnableTagOverride": false, "Check": { "DeregisterCriticalServiceAfter": "90m", "HTTP": "http://www.baidu.com", "Interval": "10s" } }' curl http://127.0.0.1:8500/v1/agent/service/register -X PUT -i -H "Content-Type:application/json" -d '{ "ID": "userServiceId1", "Name": "userService", "Tags": [ "primary", "v1" ], "Address": "127.0.0.1", "Port": 8000, "EnableTagOverride": false, "Check": { "DeregisterCriticalServiceAfter": "90m", "HTTP": "http://www.baidu.com", "Interval": "10s" } }' 4、查询服务 curl http://127.0.0.1:8500/v1/catalog/service/userService 5、服务更新:id是唯一的,更新时保持id不变即可
app1consul的pom <dependencies> <dependency><!-- 健康检查依赖于此包 --> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> yaml文件 server: port: 8501 # 设置 微服务 的端口 spring: application: name: App1Consul cloud: consul: host: localhost port: 8500 discovery: serviceName: App1Consul #注册到consul的服务名称 @SpringBootApplication @EnableDiscoveryClient public class App1Consul { static final int id = new Random().nextInt(100); public static void main( String[] args ){ SpringApplication.run(App1Consul.class, args); } } @RestController class Desk{ @GetMapping("hello") public String hello(String name) { return "\n"+App1Consul.id + " : " + name; } } @SpringBootApplication @EnableDiscoveryClient public class App2Consul { public static void main( String[] args ){ SpringApplication.run(App2Consul.class, args); } } @Configuration class RestTemplateConfiguration { @Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); } } @RestController class Controller1{ @Autowired RestTemplate restTemplate; @GetMapping("hello") public String hello(@RequestParam("name") String name) { String response = restTemplate.getForObject("http://App1Consul/hello?name="+name, String.class); return response; } } app2consul的端口为8621 效果测试: curl -s 127.0.0.1:8601/hello?name=gg