文章参考于 史上最简单的 SpringCloud 教程 | 终章
Spring Cloud 是一个微服务框架,与 Spring Boot 结合,开发简单。将一个大工程项目,分红多个小 web 服务工程,能够分别独立扩展,又能够共同合做。java
服务注册中心、服务、断路器、配置中心git
使用 Eureka 来实现服务注册中心、服务提供者和服务消费者。web
一个服务注册中心,全部的服务都在注册中心注册,负载均衡也是经过在注册中心注册的服务来使用必定策略来实现。spring
1.新建一个 Spring Boot 工程,用来管理服务,elipse右键 -> new -> Spring Starter Project :sql
点击 next ,选择 Cloud Discovery 下的 Eureka Server 组件apache
生成的 pom 文件:api
<?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>com.beigege.cloud</groupId> <artifactId>eureka-server</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>eureka-server</name> <description>spring cloud学习</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.3.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <spring-cloud.version>Finchley.RELEASE</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <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> </project>
2.使用 @EnableEurekaServer 来讲明项目是一个 Eureka:浏览器
@SpringBootApplication @EnableEurekaServer public class EurekaServerApplication { public static void main(String[] args) { SpringApplication.run(EurekaServerApplication.class, args); } }
3.修改配置文件,配置文件,能够是 application.properties 或 application.yml,这里改后缀,使用 yml,两种格式内容都是同样的 只是格式不一样 application.yml:缓存
server: port: 8761 #服务端口 spring: application: name: eurka-server #服务应用名称 eureka: instance: hostname: localhost client: registerWithEureka: false #是否将本身注册到Eureka Server,默认为true fetchRegistry: false #是否从Eureka Server获取注册信息,默认为true service-url: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #服务注册的 URL
默认 eureka server 也是一个 eureka client,registerWithEureka: false 和 fetchRegistry: false 来代表项目是一个 Eureka Server。app
4.运行 EurekaServerApplication 类,启动项目,访问项目 http://localhost:8761/
1.新建项目名为 eureka-client 的 Spring Boot 项目,跟 Eureka Server 新建同样,只是在选择 Spring Boot 组件的时候,不选 Eureka Server ,选择 Eureka Client,这里为了测试,再添加一个 Web 下的 Web 组件:
2.修改配置,application.yml:
server: port: 8762 spring: application: name: service-hi #服务之间的调用都是根据这个 name eureka: client: service-url: defaultZone: http://localhost:8761/eureka/
3.添加 @EnableEurekaClient 注解,并添加一个测试接口 /hi:
@SpringBootApplication @EnableEurekaClient @RestController public class EurekaClientApplication { public static void main(String[] args) { SpringApplication.run(EurekaClientApplication.class, args); } @Value("${server.port}") String port; @RequestMapping("/hi") public String home(@RequestParam(value = "name", defaultValue = "beigege") String name) { return "hi " + name + " ,i am from port:" + port; } }
4.启动 Eureka Server 和 Eureka Client 项目,访问http://localhost:8761 ,即eureka server 的网址:
Spring Cloud 两种调用服务的方式,ribbon + restTemplate,和 feign。
ribbon 在客户端实现了负载均衡。
启动 Eureka Server 和 Eureka Client,修改 Eureka Client 端口,再启动一个 Eureka Client 实例,至关于一个小的集群,访问localhost:8761
1.新建一个 spring boot 工程,名称为 service-ribbon,建立过程和 eureka-client 同样,组件多选一个 Cloud Routing 下的 Ribbon,建立完成以后的 pom 文件依赖:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</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-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
application.yml 配置:
server: port: 8764 spring: application: name: service-ribbon eureka: client: service-url: defaultZone: http://localhost:8761/eureka/
ServiceRibbonApplication 加上 @EnableEurekaClient 注解
2.向 Spring 注入一个 bean: restTemplate,并添加 @LoadBalanced 注解,代表这个 restTemplate 开启负载均衡功能:
@SpringBootApplication @EnableEurekaClient public class ServiceRibbonApplication { public static void main(String[] args) { SpringApplication.run(ServiceRibbonApplication.class, args); } @Bean @LoadBalanced RestTemplate restTemplate() { return new RestTemplate(); } }
3.写一个接口用来调用以前的 service-hi 服务的 /hi 接口:
新建 service 层,名叫 HelloService,调用 /hi 接口,这里用服务名 SERVICE-HI 代替具体的 URL:
@Service public class HelloService { @Autowired RestTemplate restTemplate; public String hiService(String name) { return restTemplate.getForObject("http://SERVICE-HI/hi?name="+name,String.class); } }
新建 controller 层,调用 service 层:
@RestController public class HelloControler { @Autowired HelloService helloService; @RequestMapping(value = "/hi") public String hi(@RequestParam String name){ return helloService.hiService(name); } }
4.启动 service-ribbon 工程,屡次访问该工程的 /hi 接口,即 localhost:8764/hi?name=test:
hi test ,i am from port:8762
hi test ,i am from port:8763
上面交替显示,说明实现了负载均衡,ribbon 会在客户端发送请求时作一个负载均衡。
整合了 Ribbon,具备负载均衡的能力,整合了Hystrix,具备熔断的能力.
1.建立一个 spring boot工程,建立过程和 eureka-client 同样,多添加一个组件 Cloud Routing 下的 Feign,项目名叫 service-feign,建立完后的 pom 文件依赖:
<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.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
application.yml 配置文件:
server: port: 8765 spring: application: name: service-feign eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka/
SericeFeignApplication 上添加 @EnableEurekaClient 和 @EnableFeignClients 注解:
@SpringBootApplication @EnableEurekaClient @EnableFeignClients public class SericeFeignApplication { public static void main(String[] args) { SpringApplication.run(SericeFeignApplication.class, args); } }
2.写一个 SchedualServiceHi 接口,经过@ FeignClient(“服务名”),来指定调用哪一个服务,@GetMapping("接口名"),来向接口发送 Get 请求,@RequestParam 是请求参数:
@FeignClient(value = "service-hi") public interface SchedualServiceHi { @GetMapping("/hi") String sayHiFromClientOne(@RequestParam(value = "name") String name); }
写一个 controller 层测试 SchedualServiceHi 接口:
@RestController public class HiController { @Autowired SchedualServiceHi schedualServiceHi; @GetMapping(value = "/hi") public String sayHi(@RequestParam String name) { return schedualServiceHi.sayHiFromClientOne( name ); } }
3.启动 service-feign,访问localhost:8765/hi?name=test 测试,下面交替显示,说明实现了负载均衡:
hi test ,i am from port:8762
hi test ,i am from port:8763
链式调用服务,其中一个服务宕机,其余服务可能会跟着异常,发生雪崩,当对一个服务的调用失败次数到达必定阈值,断路器会打开,执行服务调用失败时的处理,避免连锁故障,上面已经启动:eureka-server、两个 eureka-client 实例、service-ribbon 和 service-feign,下面继续修改,分别在 service-ribbon 和 service-feign 上实现断路器。
1.在 pom 文件中加入 Hystrix 依赖:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
2.在程序的启动类 ServiceRibbonApplication 加 @EnableHystrix 注解开启Hystrix:
@SpringBootApplication @EnableEurekaClient @EnableHystrix public class ServiceRibbonApplication { public static void main(String[] args) { SpringApplication.run( ServiceRibbonApplication.class, args ); } @Bean @LoadBalanced RestTemplate restTemplate() { return new RestTemplate(); } }
3.添加熔断方法,改造 HelloService 类,在 hiService 方法上面添加 @HystrixCommand 注解,fallbackMethod 是熔断方法,当服务不可用时会执行,该方法,即 hiError 方法:
@Service public class HelloService { @Autowired RestTemplate restTemplate; @HystrixCommand(fallbackMethod = "hiError") public String hiService(String name) { return restTemplate.getForObject("http://SERVICE-HI/hi?name="+name,String.class); } public String hiError(String name) { return "hi,"+name+",sorry,error!"; } }
重启 service-ribbon,访问 localhost:8764/hi?name=test
hi test ,i am from port:8763
关闭两个 eureka-client 实例,在访问 localhost:8764/hi?name=test,浏览器显示:
hi,test,sorry,error!
当服务不可用时,断路器会迅速执行熔断方法
1.Feign 自带断路器,在D版本的Spring Cloud以后,它没有默认打开。须要在配置文件中配置打开:
feign: hystrix: enabled: true
2.在请求接口指定熔断实现类,fallback 指定熔断实现类:
@FeignClient(value ="service-hi",fallback = SchedualServiceHiHystric.class) public interface SchedualServiceHi { @GetMapping("/hi") String sayHiFromClientOne(@RequestParam(value = "name") String name); }
SchedualServiceHiHystric 类实现 SchedualServiceHi 接口:
@Component public class SchedualServiceHiHystric implements SchedualServiceHi{ @Override public String sayHiFromClientOne(String name) { return "sorry "+name; } }
3.访问测试,重启 service-feign,访问 localhost:8765/hi?name=test:
sorry test
打开 eureka-client,再次访问 localhost:8765/hi?name=test:
hi test ,i am from port:8762
证实断路器起到做用,注意浏览器缓存。
1.建立一个 spring boot 工程,名称为 service-zuul,建立过程和 eureka-client 同样,组件要多添加一个 Cloud Routing 下的 Zuul,pom 文件的依赖以下:
<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.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
2.在入口 application 中添加 @EnableZuulProxy 注解,开启路由:
@SpringBootApplication @EnableEurekaClient @EnableZuulProxy public class ServiceZuulApplication { public static void main(String[] args) { SpringApplication.run(ServiceZuulApplication.class, args); } }
application.yml 配置:
server: port: 8769 spring: application: name: service-zuul eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka/
修改 application.yml 添加路由配置:
server: port: 8769 spring: application: name: service-zuul eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka/ zuul: routes: api-a: path: /api-a/** service-id: service-ribbon api-b: path: /api-b/** service-id: service-feign
8769 端口的全部 api-a 请求会转发到 service-ribbon 工程,api-b 到 service-feign
启动 service-zuul,访问 localhost:8769/api-a/hi?name=test,浏览器显示:
hi test ,i am from port:8762
访问 localhost:8769/api-b/hi?name=test,浏览器显示:
hi test ,i am from port:8762
说明路由成功。
新建自定义过滤器,继承 ZuulFilter:
@Component public class MyFilter extends ZuulFilter { private static Logger log = LoggerFactory.getLogger(MyFilter.class); /* * filterType:返回一个字符串表明过滤器的类型,在zuul中定义了四种不一样生命周期的过滤器类型,具体以下: * pre:路由以前 * routing:路由之时 * post: 路由以后 * error:发送错误调用 */ @Override public String filterType() { return "pre"; } /* * filterOrder:过滤的顺序 */ @Override public int filterOrder() { return 0; } /* * shouldFilter:这里能够写逻辑判断,是否要过滤,本文true,永远过滤。 */ @Override public boolean shouldFilter() { return true; } /* * run:过滤器的具体逻辑。能够很复杂,包括查sql,nosql去判断该请求到底有没有权限访问, * 这里是判断请求有没有 token 参数 */ @Override public Object run() { RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); log.info(String.format("%s >>> %s", request.getMethod(), request.getRequestURL().toString())); Object accessToken = request.getParameter("token"); if(accessToken == null) { log.warn("token is empty"); ctx.setSendZuulResponse(false); ctx.setResponseStatusCode(401); try { ctx.getResponse().getWriter().write("token is empty"); }catch (Exception e){} return null; } log.info("ok"); return null; } }
重启 service-zuul 访问 localhost:8769/api-a/hi?name=test,浏览器显示
token is empty
加上 token 参数:localhost:8769/api-a/hi?name=test&token=666
hi test ,i am from port:8762
1.这里在 码云 注册一个帐号,用来建立存储配置的资源库,在码云上建立一个名叫 SpringCloudConfig 项目:
2.新建一个配置文件 config-client-dev.properties,也能够是 yml 配置文件,内容为 test = version 1,而后提交:
2.默认是 master 分支,新建一个分支,名字叫 aa:
修改 aa 分支中的 config-client-dev.properties 配置文件的 test 属性为 version 2:
1.新建 spring boot 项目,组件选择 Cloud Config 中的 Config Server,pom 中的依赖:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
在入口程序添加 @EnableConfigServer 注解:
@SpringBootApplication @EnableConfigServer public class ConfigServerApplication { public static void main(String[] args) { SpringApplication.run(ConfigServerApplication.class, args); } }
application.yml 配置文件,若是是公开的不须要填写用户名密码:
server: port: 8888 spring: application: name: config-server cloud: config: server: git: uri: https://gitee.com/candy-boy/SpringCloudConfig #配置git仓库地址,后面的.git无关紧要 username: #访问git仓库的用户名,码云登录用户名 password: #访问git仓库的用户密码,码云登录密码
2.访问测试,启动程序,访问配置文件,能够以下访问:
如 localhost:8888/config-client/dev,即访问 config-client-dev.properties,其中 {application} 就是 最后一道横线前面的 config-client,{profile} 是最后一道横线后面到点,即 dev,{label} 指的是资源库的分支,不填则为默认分支,刚建立的资源库,默认分支是 master,访问结果以下:
其余访问方式如:
/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties
若是是下面这样,多是用户名或密码错误:
3.测试其余分支,访问 aa 分支下的 config-client-dev.properties,localhost:8888/config-client/dev/aa
待续....