Spring Cloud 微服务实践(1) - 用Initializr初始化

{"type":"doc","content":[{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"纸上得来终觉浅,绝知此事要躬行。这里我们就直接用Spring Initializr来初始化Spring Cloud项目,然后作一点配置,写几句代码,用比较笨的形式,“徒手”撸一个包含服务发现、网关和业务处理的开发环境版微服务。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"1、先看看包含哪些模块(子项目)"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"回顾一下我们在《开篇》中提到的简化版微服务"}]}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/80/80c9e7626ab30ac90362afe1a64b7ceb.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"服务发现与网关几乎不需要写代码,配置一下就可以跑起来,然后业务实现也暂时Say Hello,点到为止,重点是我们怎么把他们揉到一起。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2、用 Spring Initializr 初始化 Eureka Server"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Spring Initializr 是Spring.io提供的一个Web应用,使用浏览器打开"},{"type":"link","attrs":{"href":"https://start.spring.io/","title":null},"content":[{"type":"text","text":"https://start.spring.io"}]},{"type":"text","text":" 就能使用,它的功能就是为你初始化一个基本的Spring Boot项目,并根据需要来加载用到的依赖。Spring Initializr也可以在Spring Tool Suite中使用,IntelliJ IDEA也做了集成。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"为了体现”从零开始“,我们就用浏览器访问"},{"type":"link","attrs":{"href":"https://start.spring.io","title":""},"content":[{"type":"text","text":"https://start.spring.io"}]},{"type":"text","text":"来初始化项目。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/cc/ccab059ea34e79b6843fc8f4aae5369b.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Project选Maven Project,使用Maven构建项目。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"开发语言Language选Java。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Project Metadata可以随意填,我这里把Artifact改成eureka-server。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"打包方式(Packaging)为Jar。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Java版本选8。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然后依赖(Dependency)增加一个“Eureka Server”和“Spring Boot Actuator”。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最后点击“GENERATE”按钮会下载一个zip文件,解压后就是我们的服务注册与发现项目。我这里因为Artifact填的“eureka-server“,所以得到的文件是eureka-server.zip,后续也会用eureka-server代指这个项目。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"解压后,就是Spring Initializr为我们生成的eureka-server项目的基本文件和结构。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/62/62cab1937154e55bac74cfbbe7f0982f.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果熟悉maven和git,解压后得到的这些文件就无需多说什么了"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这里我们看一下pom.xml文件,主要是看依赖,中文是我加的注解,其他都是Spring Initializr生成的:"}]},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"\n\n\t4.0.0\n\t\n\t\torg.springframework.boot\n\t\tspring-boot-starter-parent\n \n\t\t2.3.3.RELEASE\n\t\t \n\t\n\tcom.example\n\teureka-server\n\t0.0.1-SNAPSHOT\n\teureka-server\n\tDemo project for Spring Boot\n\n\t\n\t\t1.8\n \n\t\tHoxton.SR8\n\t\n\n\t\n \n\t\t\n\t\t\torg.springframework.cloud\n\t\t\tspring-cloud-starter-netflix-eureka-server\n\t\t\n\n \n\t\t\n\t\t\torg.springframework.boot\n\t\t\tspring-boot-starter-actuator\n\t\t\n\n \n\t\t\n\t\t\torg.springframework.boot\n\t\t\tspring-boot-starter-test\n\t\t\ttest\n\t\t\t\n\t\t\t\t\n\t\t\t\t\torg.junit.vintage\n\t\t\t\t\tjunit-vintage-engine\n\t\t\t\t\n\t\t\t\n\t\t\n\t\n\n\t\n\t\t\n\t\t\t\n\t\t\t\torg.springframework.cloud\n\t\t\t\tspring-cloud-dependencies\n\t\t\t\t${spring-cloud.version}\n\t\t\t\tpom\n\t\t\t\timport\n\t\t\t\n\t\t\n\t\n\n\t\n\t\t\n\t\t\t\n\t\t\t\torg.springframework.boot\n\t\t\t\tspring-boot-maven-plugin\n\t\t\t\n\t\t\n\t\n\n\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"熟悉Spring Boot的话,会发现大部分都是Spring Boot相关的配置,只有group id为org.springframework.cloud的才是微服务的内容。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"注意:"},{"type":"text","text":"Spring Boot的版本是2.3.3,Spring Cloud的版本是Hoxton.SR8,这两者的版本一定要匹配,不然容易出一些莫名其妙的问题。我们可以访问"},{"type":"link","attrs":{"href":"https://start.spring.io/actuator/info","title":""},"content":[{"type":"text","text":"https://start.spring.io/actuator/info"}]},{"type":"text","text":" 这个地址来获取Spring Boot和Spring Cloud的版本对应关系,比如说:"}]},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"\"Hoxton.SR8\":\"Spring Boot >=2.2.0.M4 and <2.3.4.BUILD-SNAPSHOT\""}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"3、配置Eureka Server - 实现服务注册与发现"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"开源代码: "},{"type":"link","attrs":{"href":"https://github.com/xiaoboey/from-zero-to-n/tree/master/zero/eureka-server","title":""},"content":[{"type":"text","text":"https://github.com/xiaoboey/from-zero-to-n/tree/master/zero/eureka-server"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"打开src\\main\\java\\com\\example\\eurekaserver\\EurekaServerApplication.java,添加注解@EnableEurekaServer,开启注册中心能力。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a8/a8890297fb0b2d9d15488d4e84827dac.png","alt":null,"title":"开启 Eureka Server 能力","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"把src\\main\\resources\\application.properties删除,增加文件application.yml:"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"server:\n port: 8761\n address: localhost\n servlet:\n context-path: /\n\nspring:\n profiles:\n active: dev\n application:\n name: eureka-server\n\neureka:\n instance:\n hostname: localhost\n client:\n #这个服务本身是注册中心,不用自己向自己注册\n register-with-eureka: false\n fetch-registry: false\n service-url:\n defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/\n\nmanagement:\n endpoints:\n web:\n exposure:\n include: [\"health\",\"info\", \"shutdown\"]\n endpoint:\n health:\n show-details: always\n shutdown:\n enabled: true\n server:\n port: 7761"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"4、编译和运行"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Maven项目的编译都一样,命令是“mvn install”,我一般习惯再加一个clean先清理一下,所以就是“mvn clean install”。如果想跳过测试加快编译速度,则可以加上“-DskipTests”参数,完整的命令就是“mvn clean install -DskipTests”。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Spring Boot项目的运行,测试时可以在命令行或者PowerShell直接用maven运行:"}]},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"mvn spring-boot:run"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"编译成jar文件后,也可以直接运行jar文件,这里以eureka-server为例:"}]},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"cd target\njava -jar eureka-server-0.0.1-SNAPSHOT.jar"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"5、Eureka Server自带的UI"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"eureka-server项目启动后,可以在"},{"type":"link","attrs":{"href":"http://localhost:8761","title":""},"content":[{"type":"text","text":"http://localhost:8761"}]},{"type":"text","text":"查看注册到服务中心的微服务的信息。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/40/40d9dc9521c844f546473c9bdb8499d1.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"目前暂时还没有服务注册到eureka server,红色的警告也不用管它,是Eureka的自检,测试环境容易出现这个问题。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"6、网关 Spring Cloud Gateway"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有了前面eureka-server的搭建经验后,其他微服务我们就简化一下,只列出我觉得重要的内容。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"开源代码: "},{"type":"link","attrs":{"href":"https://github.com/xiaoboey/from-zero-to-n/tree/master/zero/gateway","title":""},"content":[{"type":"text","text":"https://github.com/xiaoboey/from-zero-to-n/tree/master/zero/gateway"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"还是用Spring Initializr进行项目初始化,pom.xml如下:"}]},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":" ...\n\tgateway\n\t0.0.1-SNAPSHOT\n\tgateway\n\t...\n\n\t\n ...\n \n\t\t\torg.springframework.cloud\n\t\t\tspring-cloud-starter-netflix-eureka-client\n\t\t\n \n\t\t\n\t\t\torg.springframework.cloud\n\t\t\tspring-cloud-starter-gateway\n\t\t\n ...\n\t\n ..."}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"主要是添加了eureka-client和gateway的依赖。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"修改GatewayApplication.java,增加服务发现客户端的注解,表示这是一个Eureka Client,要向服务中心(Eureka Server)注册,以便其他服务可以发现它。"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"import org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\n\n@SpringBootApplication\n@EnableDiscoveryClient\npublic class GatewayApplication {\n\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(GatewayApplication.class, args);\n\t}\n\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"application.yml的配置如下:"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"\nserver:\n port: 8080\n address: localhost\n servlet:\n context-path: /\n\nspring:\n profiles:\n active: dev\n application:\n name: gateway\n cloud:\n gateway:\n discovery:\n locator:\n #自动发现并路由到微服务\n #与服务发现Eureka Server进行结合,通过Service Id转发到具体的服务实例,默认为false。\n enabled: true\n \n #小写ServiceId,默认是false\n #这里是个坑,从Eureka Server注册中心获取的Service Id是大写的\n lower-case-service-id: true\n\neureka:\n client:\n service-url:\n #注册中心的地址,要对应eureka-server的配置\n defaultZone: http://localhost:8761/eureka/\n\nmanagement:\n endpoints:\n web:\n exposure:\n include: [\"health\",\"info\", \"shutdown\"]\n endpoint:\n health:\n show-details: always\n shutdown:\n enabled: true\n server:\n port: 7080\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"网关编译启动后,可以去Eureka Server看看网关是否已注册:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/fc/fcc9e91c55bc2313012843d637ba6f23.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"7、微服务 Service One"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Eureka Server和Gateway都是Spring Cloud自带的为微服务架构服务的功能,接着我们实现一个“真正的业务”。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"开源代码: "},{"type":"link","attrs":{"href":"https://github.com/xiaoboey/from-zero-to-n/tree/master/zero/service-one","title":""},"content":[{"type":"text","text":"https://github.com/xiaoboey/from-zero-to-n/tree/master/zero/service-one"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"还是用Spring Initializr进行项目初始化,这次是一个Spring MVC应用,pom.xml如下:"}]},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":" ...\n\tservice-one\n\t0.0.1-SNAPSHOT\n\tservice-one\n\t...\n\n\t\n ...\n \n\t\t\torg.springframework.cloud\n\t\t\tspring-cloud-starter-netflix-eureka-client\n\t\t\n \n\t\t\n\t\t\torg.springframework.boot\n\t\t\tspring-boot-starter-web\n\t\t\n ...\n\t\n ..."}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"跟Gateway一样,都通过注解开启服务发现客户端能力,修改ServiceOneApplication.java:"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"import org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\n\n@SpringBootApplication\n@EnableDiscoveryClient\npublic class ServiceOneApplication {\n\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(ServiceOneApplication.class, args);\n\t}\n\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"application.yml:"}]},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"server:\n port: 8081\n #把address注释掉,避免网关转发请求过来时,因为地址发生变化而拒绝连接(Connection refused)\n #address: localhost\n servlet:\n context-path: /\n\nspring:\n profiles:\n active: dev\n application:\n name: service-one\n\neureka:\n client:\n service-url:\n defaultZone: http://localhost:8761/eureka/\n\nmanagement:\n endpoints:\n web:\n exposure:\n include: [\"health\",\"info\", \"shutdown\"]\n endpoint:\n health:\n show-details: always\n shutdown:\n enabled: true\n server:\n port: 7081"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"写一个Controller,实现一个hello方法:"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"import org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class OneController {\n @RequestMapping(\"/hello\")\n public String hello() {\n return \"Hello!\";\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"编译启动后,访问"},{"type":"link","attrs":{"href":"http://localhost:8081/hello","title":""},"content":[{"type":"text","text":"http://localhost:8081/hello"}]},{"type":"text","text":" ,不出意外的话,Service One会立马响应一个“Hello!”。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"再访问"},{"type":"link","attrs":{"href":"http://localhost:8080/service-one/hello","title":""},"content":[{"type":"text","text":"http://localhost:8080/service-one/hello"}]},{"type":"text","text":" 看看,是不是一样的效果?说明网关自动把请求转发过来了,而Service One这个应用的信息,网关Gateway是从Eureka Server上的注册信息里获取到的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"8、网关的负载均衡"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"到目前为止,我们搭建了一个最简单的微服务“集群”,网关(Gateway)接受外部的请求,然后转发到内部的业务实现(Service One)上进行处理,并将结果反馈回去。如果只是这样的话,微服务的优势在哪里呢?这个Service One直接暴露给外部调用也是OK的呀?"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们再扩展一下Service One,提供多一点服务,并且启动多个Service One来看一下。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"增加getPort方法,返回服务的端口(application.yml中的server.port):"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"import org.springframework.beans.factory.annotation.Value;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class OneController {\n @RequestMapping(\"/hello\")\n public String hello() {\n return \"Hello!\";\n }\n\n @Value((\"${server.port}\"))\n private int port;\n \n @RequestMapping(\"/getPort\")\n public int getPort() {\n return port;\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"编译后启动,访问"},{"type":"link","attrs":{"href":"http://localhost:8080/service-one/getPort","title":""},"content":[{"type":"text","text":"http://localhost:8080/service-one/getPort"}]},{"type":"text","text":" ,得到一个端口号,这是application.yml中配置的端口(server.port)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"开启一个新的cmd或者shell,进入到service-one项目的pom.xml文件那级路径,再启动一个Service One,并指定不同的端口:server.port=8082,management.server.port=7082"}]},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"mvn spring-boot:run -Dspring-boot.run.arguments=\"--server.port=8082 --management.server.port=7082\"\n#如果是PowerShell,带=号的参数要用单引号引起来才能正确传递\nmvn spring-boot:run -D'spring-boot.run.arguments=\"--server.port=8082 --management.server.port=7082\"'"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"现在在浏览器里多刷新几次"},{"type":"link","attrs":{"href":"http://localhost:8080/service-one/getPort","title":""},"content":[{"type":"text","text":"http://localhost:8080/service-one/getPort"}]},{"type":"text","text":" ,可以发现返回的端口号发生了变化,说明网关把请求转发到Service One的每个实例上。我们可以继续启动更多的服务实例,并模拟大量的请求来测试。测试的结果是网关(Gateway)把请求平均分发给每个实例,体现了微服务在“弹性伸缩、独立部署”上的优势。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/53/530e7e3f2c04ea80733d5d319973eb94.jpeg","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"9、宕机测试"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"除了有计划的停止,在实际的生产环境,服务器宕机或其他故障也可能会让服务哑火,失去响应。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"前面说“弹性伸缩”,其实我们只测试了“伸”(增加更多的服务实例),还没测试“缩”(减少实例),那就试试停止一个Service One,再去反复刷新getPort看看会发生什么。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"反复刷新的情况下,有时会出现响应变慢,然后错误“Internal Server Error, status=500”。这是因为Client失联或者停止后,Eureka Server还没get到这个状态,Gateway继续往停摆的服务转发请求,导致错误。直到注册中心把这个失联的服务剔除掉并通知网关,一切才恢复正常。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"宕机导致的服务异常,对终端用户的使用是有影响的,需要解决。并且Spring Cloud Gateway代理转发请求给后端的服务实例,这个工作其实nginx也可以胜任,在某些方面甚至更好。所以到目前为止,微服务的优势还没体现出来。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所谓“师傅领进门,修行在个人”,通过这篇“入门”上手找到感觉之后,剩下的就是针对具体的需求和问题,去慢慢探索解决,积累经验,践行微服务开发方法。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上一篇: 《"},{"type":"link","attrs":{"href":"https://xie.infoq.cn/article/2e804cfaf1084de339601f201","title":null},"content":[{"type":"text","text":"Spring Cloud 微服务实践(0) - 开篇闲话"}],"marks":[{"type":"strong"}]},{"type":"text","text":"》"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下一篇: 《"},{"type":"link","attrs":{"href":"https://xie.infoq.cn/article/cd424bd23643af21a0c2eec42","title":null},"content":[{"type":"text","text":"Spring Cloud 微服务实践(2) - Gateway重试机制 "}],"marks":[{"type":"strong"}]},{"type":"text","text":"》"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章