第一章-项目准备
1.JDK 1.8+
2.idea 或eclipse编码工具
1.有一定的java代码阅读能力
2.学习过SpringBoot
创建项目名为:SpringCloud-demo
创建父项目的好处:以后好更新版本
4.0.0
SpringCloud-demo
SpringCloud-demo
pom
1.0-SNAPSHOT
org.springframework.boot
spring-boot-starter-parent
2.0.6.RELEASE
UTF-8
UTF-8
1.8
Finchley.SR2
org.springframework.cloud
spring-cloud-dependencies
${spring-cloud.version}
pom
import
org.springframework.boot
spring-boot-maven-plugin
pom.xml文件如下所示:
springcloud-server
SpringCloud-demo
1.0-SNAPSHOT
4.0.0
springcloud-server
springcloud-Eureka
org.springframework.cloud
spring-cloud-starter-netflix-eureka-server
启动一个服务注册中心
启动一个服务注册中心的方式很简单,就是在Spring Boot的入口类上
添加一个@EnableEurekaServer注解,如下:
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerRunApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerRunApplication.class, args);
}
}
server:
#端口
port: 9000
eureka:
instance:
#服务注册中心实例的主机名
hostname: localhost
#ip-address: 固定ip地址
instance-id: ${spring.cloud.client.ip-address}:${server.port}
preferIpAddress: true
server:
#关闭自我保护机制,防止失效的服务也被一直访问 (开发环境)
enable-self-preservation: false
#该配置可以修改检查失效服务的时间,每隔5s检查失效服务,默认该配置是 60s (开发环境)
eviction-interval-timer-in-ms: 5000
client:
#是否向服务注册中心注册自己
registerWithEureka: false
#是否检索服务
fetchRegistry: false
#服务注册中心的配置内容,指定服务注册中心的位置
serviceUrl:
defaultZone: http://localhost:9100/eureka/
在浏览器中输入:http://localhost:9000/就能够看到如下页面:
SpringCloud-demo
SpringCloud-demo
1.0-SNAPSHOT
4.0.0
springcloud-provider
springcloud-provider
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-actuator
#将服务注册到注册中心
eureka:
client:
serviceUrl:
defaultZone: http://localhost:9000/eureka/
#healthcheck:
#enabled: true #开启自定义健康检查
instance:
#eureka服务器在接收到最后一个心跳之后等待的时间,然后才能从列表中删除此实例 默认90s(开发环境)
lease-expiration-duration-in-seconds: 10
#eureka客户端需要向eureka服务器发送心跳的频率 默认30s (开发环境)
lease-renewal-interval-in-seconds: 1
# ip地址
#ip-address: 固定ip地址
#instance-id: ${spring.cloud.client.ip-address}:${server.port}
#preferIpAddress: true
#配置服务名称及端口
server:
port: 9001
spring:
application:
name: springcloud-provider
kd:
app:
name: 提供者-1
@RestController
public class TestController {
@Value("${server.port}")
String port;
@Value("${spring.application.name}")
String name;
@Value("${kd.app.name}")
String appName;
@RequestMapping("/test")
public String test() {
return appName+"上线测试:"+name+":"+port;
}
}
@SpringBootApplication
@EnableDiscoveryClient //注册服务到注册中心去
public class ProviderRun_1 {
public static void main(String[] args) {
SpringApplication.run(ProviderRun_1.class, args);
}
}
再查看注册中心地址 http://localhost:9000/
找到 application.yml 以下属性把#去掉就行了
#instance-id: ${spring.cloud.client.ip-address}:${server.port}
#preferIpAddress: true
注意显示IP和主机的区别?
1.显示主机方式:好处是提供服务用主机名比较直观,但是要添加到hosts里去,
不然不同机器是识别不了服务的。
2.显示ip的方式:以IP地址显示,局域网内访问很方便。
3.推荐方式:组合用,开启preferIpAddress: true 即可
第四章-服务消费者(ribbon方式)
pom.xml 如下
SpringCloud-demo
SpringCloud-demo
1.0-SNAPSHOT
4.0.0
spirngcloud-ribbon-client
org.springframework.cloud
spring-cloud-starter-netflix-ribbon
org.springframework.boot
spring-boot-starter-web
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
org.springframework.cloud
spring-cloud-starter-netflix-hystrix
org.springframework.boot
spring-boot-starter-actuator
@SpringBootApplication
@EnableDiscoveryClient
@RestController
public class RibbonRun {
public static void main(String[] args) {
SpringApplication.run(RibbonRun.class, args);
}
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
@Autowired
RestTemplate restTemplate;
@RequestMapping(value = "/test")
public String test(){
return restTemplate.getForObject("http://springcloud-provider/test",String.class);
}
}
#将服务注册到注册中心
eureka:
client:
serviceUrl:
defaultZone: http://localhost:9000/eureka/
#healthcheck:
#enabled: true #开启自定义健康检查
instance:
#eureka服务器在接收到最后一个心跳之后等待的时间,然后才能从列表中删除此实例 默认90s(开发环境)
lease-expiration-duration-in-seconds: 10
#eureka客户端需要向eureka服务器发送心跳的频率 默认30s (开发环境)
lease-renewal-interval-in-seconds: 1
server:
port: 9004
#配置服务名称及端口
spring:
application:
name: spirngcloud-ribbon-client
http://localhost:9004/test
第五章-客户端负载均衡
负载均衡是我们处理高并发、缓解网络压力和进行服务端扩容的解决方案
负载均衡分为两种
1.服务端负载均衡
Nginx就是服务端负载均衡的例子
由Nginx 分发器统一反向代理请求地址
2.客户端负载均衡
不是统一分发,而是由客户端自己选择,如果你学过dubbo,它就是客户端负载均衡
可以的,Ribbo的实现本身就是客户端负载均衡,
通过RestTemplate来访问接口服务的,
只要加@LoadBalanced注解来达到负载均衡是不是很简单
Ribbo是一个基于HTTP和TCP的客户端负载均衡器,
当我们将Ribbon和Eureka一起使用时,Ribbon会从Eureka注册中心去获取服务端列表,
然后进行轮询访问以到达负载均衡的作用,客户端负载均衡中也需要心跳机制去维护服务端清单的有效性,
当然这个过程需要配合服务注册中心一起完成
1.需要两个提供者项目
springcloud-provider-1(已有,项目端口9001)
springcloud-provider-2-项目端口是9003
和springcloud-provider-1项目一样但是就端口不一样,和自定义属性值不一样
相当于有springcloud-provide提供者两个实例,供消费者消费
2.启动两个提供者项目,查看注册中心
发现springcloud-provider服务up数量是2
3.启动Ribbo消费者来消费服务
不断刷新浏览器访问 http://localhost:9004/test
你会发现:
发现:
Ribbo 消费者会轮询消费注册列表的两个提供者服务
第六章-服务消费者(feign方式)
Feign是一套基于Netflix Feign实现的声明式服务调用客户端。
它使得编写Web服务客户端变得更加简单。通过创建接口并用注解来配置
它既可完成对Web服务接口的绑定。它具备可插拔的注解支持,
包括Feign注解、JAX-RS注解。它也支持可插拔的编码器和解码器。
也同时整合了Ribbon和Eureka来提供均衡负载的HTTP客户端实现。
总之,Feign是基于Ribbon实现,使得接口更加规范化,更有阅读性并且更加简单化,
也具有负载均衡的能力
pom.xml 如下:
SpringCloud-demo
SpringCloud-demo
1.0-SNAPSHOT
4.0.0
springcloud-feign
org.springframework.cloud
spring-cloud-starter-openfeign
org.springframework.boot
spring-boot-starter-web
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
#配置服务名称及端口
spring:
application:
name: springcloud-feign
server:
port: 9005
#将服务注册到注册中心
eureka:
client:
serviceUrl:
defaultZone: http://localhost:9000/eureka/
#healthcheck:
#enabled: true #开启自定义健康检查
instance:
#eureka服务器在接收到最后一个心跳之后等待的时间,然后才能从列表中删除此实例 默认90s(开发环境)
lease-expiration-duration-in-seconds: 10
#eureka客户端需要向eureka服务器发送心跳的频率 默认30s (开发环境)
lease-renewal-interval-in-seconds: 1
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class FeignApplication {
public static void main(String[] args) {
SpringApplication.run(FeignApplication.class, args);
}
}
定义一个feign接口,通过@ FeignClient(“服务名”),
来指定调用哪个服务。比如在代码中调用了springcloud-provider服务的“/test”接口,代码如下:
@FeignClient(value = "springcloud-provider")
public interface TestFeign {
@RequestMapping(value = "/test")
String testByFeign();
}
注意:服务名大小写无所谓,@FeignClient 会把接口类交给spring所管理
也就是说是可以@Autowired注入的
@RestController
public class TestController {
@Autowired
TestFeign testFeign;// 注入 Feign接口
@RequestMapping(value = "/test")
public String test(){
return testFeign.testByFeign();
}
}
不断刷新浏览器地址 http://localhost:9005/test
从上面也能看的出来,Feign也是能消费服务,并且也有负载均衡
从而也证明了Feign是基于Ribbon实现
并使用注解接口使其更简单(@ FeignClient(“服务名”))
上面是一个简单的调用案例,这个例子中没有涉及到参数的传递,那接下来看看参数的例子
再增加提供者三种情况的参数的服务接口,分别如下:
// 测试带普通参数
@RequestMapping("/testByParam")
public String testByParam(String from) {
return appName+"上线测试:"+name+":"+port+"来自:"+from;
}
// 测试带多个普通参数Map
@RequestMapping("/testByMap")
public String testByParam(@RequestParam Map map) {
return appName+"上线测试:"+name+":"+port+"用户:"+map.get("name");
}
// 测试参数是对象的情况
@RequestMapping("/testByRequestBody")
public String testByRequestBody(@RequestBody User user) {
return appName+"上线测试:"+name+":"+port+"用户:"+user.getName();
}
FeignClient 这边也增加对应的消费接口的方法
// 测试带普通参数
@RequestMapping(value = "/testByParam")
String testByParam(@RequestParam("from") String from);
// 测试带多个普通参数Map
@RequestMapping(value = "/testByMap")
String testByMap(Map map);
// 测试参数是对象的情况 @RequestBody User user
@RequestMapping(value = "/testByRequestBody",method = RequestMethod.POST)
String testByRequestBody(@RequestBody User user);
注意,在SpringMVC中,@RequestParam,如果不指定value,则默认采用参数的名字作为其value,如上面的例子
但是在Feign中,这个value必须明确指定,否则会报错。
如果是pojo 实体类对象,服务提供者这方必须加上 @RequestBody注解
测试
可以再请求调用服务拦截做一些事情,比如日志记录、权限的判断
创建一个配置类,spirng会自动扫描的
@Configuration
public class FeignConfig {
@Bean
public RequestInterceptor getRequestInterceptor(){
return new RequestInterceptor() {
@Override
public void apply(RequestTemplate requestTemplate) {
/** 设置请求头信息 **/
// requestTemplate.header("Content-Type", "application/json");
// 可以做日志记录
System.out.println("自定义拦截器");
}
};
}
}
当然也能有多个拦截器,多加一个拦截器@bean就行了
@Configuration
public class FeignConfig {
@Bean
public RequestInterceptor getRequestInterceptor(){
return new RequestInterceptor() {
@Override
public void apply(RequestTemplate requestTemplate) {
/** 设置请求头信息 **/
// requestTemplate.header("Content-Type", "application/json");
// 可以做日志记录
System.out.println("自定义拦截器-日志记录");
}
};
}
@Bean
public RequestInterceptor getRequestInterceptor(){
return new RequestInterceptor() {
@Override
public void apply(RequestTemplate requestTemplate) {
/** 设置请求头信息 **/
// requestTemplate.header("Content-Type", "application/json");
// 可以做权限判断
System.out.println("自定义拦截器2-权限判断");
}
};
}
}
服务断路容错:微服务往往服务众多,各服务之间相互调用,若消费者在调用提供者时出现由网络,
提供者服务自身问题等导致接口出现故障或延迟;此时消费者的请求不断增加,
务必会出现因等待响应而造成任务积压,线程无法释放,进一步导致服务的瘫痪。
出现雪崩效应
既然微服务架构存在这样的隐患,那么针对服务的容错性必然会进行服务降级,
依赖隔离,断路器等一线列的服务保护机制。
其实有点类似家用的保险丝作用
可以向提供者服务接口加上一段错误代码
@RequestMapping("/test")
public String test() {
int a=1/0;// 除数不能为0
return appName+"上线测试:"+name+":"+port;
}
重新启动提供者,浏览器测试结果
调用了错误的接口,出现500错误的界面提示,这是我不希望看见的
希望看见调用错误,界面友好提示而不是报500错误界面
需要向springcloud-feign项目添加依赖
org.springframework.cloud
spring-cloud-starter-netflix-hystrix
修改springcloud-feign项目启动类
添加 @EnableHystrix 注解
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
@EnableHystrix//开启断路器功能
public class FeignApplication {
public static void main(String[] args) {
SpringApplication.run(FeignApplication.class, args);
}
}
修改 application.yml 添加属性
#开启hystrix 指标
feign:
hystrix:
enabled: true
增加一个fallback错误回调类就行了
@Component
public class TestFeignFallback implements TestFeign {
@Override
public String testByFeign() {
return "error";
}
@Override
public String testByParam(String from) {
return "error";
}
@Override
public String testByMap(Map map) {
return "error";
}
@Override
public String testByRequestBody(User user) {
return "error";
}
}
关联fallback = TestFeignFallback.class 一旦错误就回调同名称的方法
@FeignClient(value = "springcloud-provider",fallback = TestFeignFallback.class)
public interface TestFeign {
@RequestMapping(value = "/test")
String testByFeign();
// 测试带普通参数
@RequestMapping(value = "/testByParam")
String testByParam(@RequestParam("from") String from);
// 测试带多个普通参数Map
@RequestMapping(value = "/testByMap")
String testByMap(Map map);
// 测试参数是对象的情况 @RequestBody User user
@RequestMapping(value = "/testByRequestBody",method = RequestMethod.POST)
String testByRequestBody(@RequestBody User user);
}
重启springcloud-feign项目,浏览器测试 http://localhost:9005/test
需要向spirngcloud-ribbon-client项目添加依赖
org.springframework.cloud
spring-cloud-starter-netflix-hystrix
修改spirngcloud-ribbon-client项目启动类
添加 @EnableHystrix 注解
@HystrixCommand(fallbackMethod = "error")和error方法
@SpringBootApplication
@EnableDiscoveryClient
@RestController
@EnableHystrix
public class RibbonRun {
public static void main(String[] args) {
SpringApplication.run(RibbonRun.class, args);
}
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
@Autowired
RestTemplate restTemplate;
@RequestMapping(value = "/test")
@HystrixCommand(fallbackMethod = "error")
public String test(){
return restTemplate.getForObject("http://springcloud-provider/test",String.class);
}
public String error() {
return "error";
}
一旦test接口报错就会降级调用error方法
pom 文件内容如下:
SpringCloud-demo
SpringCloud-demo
1.0-SNAPSHOT
4.0.0
springcloud-hystrixDashboard
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
org.springframework.cloud
spring-cloud-starter-netflix-hystrix
org.springframework.cloud
spring-cloud-starter-netflix-hystrix-dashboard
#将服务注册到注册中心
eureka:
client:
serviceUrl:
defaultZone: http://localhost:9000/eureka/
#healthcheck:
#enabled: true #开启自定义健康检查
instance:
#eureka服务器在接收到最后一个心跳之后等待的时间,然后才能从列表中删除此实例 默认90s(开发环境)
lease-expiration-duration-in-seconds: 10
#eureka客户端需要向eureka服务器发送心跳的频率 默认30s (开发环境)
lease-renewal-interval-in-seconds: 1
server:
port: 9006
spring:
application:
name: springcloud-hystrixDashboard
@SpringBootApplication
@EnableHystrixDashboard
@EnableDiscoveryClient
public class HystrixDashboardApplication {
public static void main(String[] args) {
SpringApplication.run(HystrixDashboardApplication.class, args);
}
}
pom.xml
org.springframework.boot
spring-boot-starter-actuator
增加 application.yml 属性(必须要加)
management:
endpoints:
web:
exposure:
include: "*"
启动 springcloud-feign、hystrixDashboard、springcloud-provider-1 项目
1.浏览器测试 http://localhost:9006/hystrix
2.访问几次消费接口地址 http://localhost:9005/test
3.如图所示输入监控地址:http://localhost:9005/actuator/hystrix.stream
4.点击Monitor Stream按钮
监控数据展示
第九章-turbine(Hystrix监控数据集群)
当Hystrix消费者多个时需要把每一个消费监控信息整合在一个turbine集群面板上
pom 文件内容如下:
SpringCloud-demo
SpringCloud-demo
1.0-SNAPSHOT
4.0.0
springcloud-turbine
org.springframework.cloud
spring-cloud-starter-netflix-turbine
org.springframework.boot
spring-boot-starter-actuator
注意 指定了需要收集监控信息的服务名,多个以“,”进行区分
#将服务注册到注册中心
eureka:
client:
serviceUrl:
defaultZone: http://localhost:9000/eureka/
#healthcheck:
#enabled: true #开启自定义健康检查
instance:
#eureka服务器在接收到最后一个心跳之后等待的时间,然后才能从列表中删除此实例 默认90s(开发环境)
lease-expiration-duration-in-seconds: 10
#eureka客户端需要向eureka服务器发送心跳的频率 默认30s (开发环境)
lease-renewal-interval-in-seconds: 1
server:
port: 9007
spring:
application:
name: springcloud-turbine
turbine:
#指定了需要收集监控信息的服务名,多个以“,”进行区分
appConfig: springcloud-feign
clusterNameExpression: new String("default")
combineHostPort: true
@SpringBootApplication
@EnableDiscoveryClient
@EnableAutoConfiguration
@EnableTurbine
public class TurbineApplication {
public static void main(String[] args) {
SpringApplication.run(TurbineApplication.class, args);
}
}
0.启动注册中心
1.启动springcloud-feign
2.启动springcloud-provider-1
3.启动springcloud-hystrixDashboard
4.启动springcloud-turbine
1.浏览器测试 http://localhost:9006/hystrix
2.访问几次消费接口地址 http://localhost:9005/test
3.如图所示输入监控地址:http://localhost:9007/turbine.stream
4.点击Monitor Stream按钮
注意:要消费几次服务接口,要不然没有监控数据
第十章-zuul-路由网关
我们知道微服务就是把一个大的项目拆分成很多小的独立服务项目,服务项目之间相互调用构建系统生态体系,那在这之间就有问题, 如何控制权限?如何分发路由?如何统一记录访问日志?为了解决这个问题就有了zuul网关,其实它可以理解成过滤器
为微服务架构提供了前门保护的作用,同时将权限控制这些较重的非业务逻辑内容迁移到服务路由层面,使得服务集群主体能够具备更高的可复用性和可测试性。
pom 内容如下:
SpringCloud-demo
SpringCloud-demo
1.0-SNAPSHOT
4.0.0
springcloud-zuul
org.springframework.cloud
spring-cloud-starter-netflix-zuul
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
#配置服务名称及端口
spring:
application:
name: springcloud-zuul
server:
port: 9008
#将服务注册到注册中心
eureka:
client:
serviceUrl:
defaultZone: http://localhost:9000/eureka/
#healthcheck:
#enabled: true #开启自定义健康检查
instance:
#eureka服务器在接收到最后一个心跳之后等待的时间,然后才能从列表中删除此实例 默认90s(开发环境)
lease-expiration-duration-in-seconds: 10
#eureka客户端需要向eureka服务器发送心跳的频率 默认30s (开发环境)
lease-renewal-interval-in-seconds: 1
zuul 路由,默认情况下,Eureka上所有注册的服务都会被Zuul创建映射关系来进行路由 如果映射规则我们什么都不写,系统也给我们提供了一套默认的配置规则,默认的配置 zuul.routes.应用服务名=/应用服务名/**
举例说明
zuul.routes.api-a.path=/api-a/**
zuul.routes.api-a.serviceId=springcloud-feign
我们说当我的访问地址符合/api-a/**规则的时候,会被自动定位到springcloud-feign服务上去,可以用下面一行代码来代替,如下:
zuul.routes.springcloud-feign=/api-a/**
也可以忽视不创建路由规则
zuul.ignored-services=springcloud-provider
来个简单权限业务,如果有accessToken就放行,没有就提示无访问权限
package org.kd.filter;
import javax.servlet.http.HttpServletRequest;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
public class AccessTokenFilter extends ZuulFilter {
// run:过滤器的具体逻辑。默认是放行的
// 通过ctx.setSendZuulResponse(false)令zuul过滤该请求,不对其进行路由,
// 然后通过ctx.setResponseStatusCode(401)设置了其返回的错误码,
// 也可以进一步优化比如,通过ctx.setResponseBody(body)对返回body内容进行编辑等。
@Override
public Object run() throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
Object accessToken = request.getParameter("accessToken");
System.out.println("accessToken:" + accessToken);
if (accessToken == null) {
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(401);
ctx.addZuulResponseHeader("content-type","text/html;charset=utf-8");
ctx.setResponseBody("无权访问-没有accessToken");
return null;
}
return null;
}
// shouldFilter:判断该过滤器是否需要被执行。
// true表示该过滤器对所有请求都会生效。
// 实际运用中我们可以利用该函数来指定过滤器的有效范围。
@Override
public boolean shouldFilter() {
return true;
}
// 过滤器的执行顺序。值越小越先执行
@Override
public int filterOrder() {
return 0;
}
// filterType:过滤器的类型,它决定过滤器在请求的哪个生命周期中执行。
// pre:可以在请求被路由之前调用
// route:在路由请求时候被调用
// post:在route和error过滤器之后被调用
// error:处理请求时发生错误时被调用
@Override
public String filterType() {
return "pre";
}
}
@SpringBootApplication
@EnableZuulProxy// @EnableZuulProxy 自动加载过滤器bean
@EnableDiscoveryClient
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
// AccessToken访问权限拦截
@Bean
public AccessTokenFilter accessTokenFilter() {
return new AccessTokenFilter();
}
}
修改启动类,添加过滤器
@SpringBootApplication
@EnableZuulProxy// @EnableZuulProxy 自动加载过滤器bean
@EnableDiscoveryClient
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
// AccessToken访问权限拦截
@Bean
public AccessTokenFilter accessTokenFilter() {
return new AccessTokenFilter();
}
}
0.启动注册中心
1.启动springcloud-zuul项目
2.启动springcloud-provider-1项目
3.启动springcloud-feign项目
注意:需要zuul路由转发的
http://localhost:9008/springcloud-feign/test
http://localhost:9008/springcloud-feign/test?accessToken=100