springCloud知识汇总
2018-02-07 15:51
281 查看
附带:需要springcloud视频请加QQ 642070964一、微服务架构概述
1、微服务是以开发一组小型服务的方式来开发一个独立的应用系统的,其中每个小型服务都运行在自己的进程中,微服务之间通过一些轻量的通信机制进行通信,经常采
用HTTP资源API(例如通过REST API或者RPC)轻量的机制来相互通信。 这些服务围绕业务功能进行构建,并能通过全自动的部署机制来进行独立部署,可以使用不
同的语言、数据存储技术,我们仅对微服务做最低限度的集中管理。
二、SpringCloud介绍
1、Spring Cloud是基于SpringBoot的快速构建分布式系统的工具集,提供了快速构建分布式系统中一些常见模式的工具(配置管理,服务发现,断路器,智能路由,微
代理,控制总线)。分布式系统的协调导致了样板模式, 使用Spring Cloud可以快速地支持实现这些模式的服务和应用程序,可以运行在任何分布式环境中。
2、SpringCloud的子项目:
1)、Spring Cloud Config:配置管理开发工具包,可以让你把配置放到远程服务器,目前支持本地存储、Git以及Subversion;
2)、Spring Cloud Bus:事件、消息总线,用于在集群(例如,配置变化事件)中传播状态变化,可与Spring Cloud Config联合实现热部署;
3)、Spring Cloud Netflix:针对多种Netflix组件提供的开发工具包,其中包括Eureka、Hystrix、Zuul、Archaius等;
4)、Netflix Eureka:云端负载均衡,一个基于 REST 的服务,用于定位服务,以实现云端的负载均衡和中间层服务器的故障转移;
5)、Netflix Hystrix:容错管理工具,旨在通过控制服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力;
6)、Netflix Zuul:边缘服务工具,是提供动态路由,监控,弹性,安全等的边缘服务;
7)、Netflix Archaius:配置管理API,包含一系列配置管理API,提供动态类型化属性、线程安全配置操作、轮询框架、回调机制等功能;
8)、Spring Cloud for Cloud Foundry:通过Oauth2协议绑定服务到CloudFoundry,CloudFoundry是VMware推出的开源PaaS云平台;
9)、Spring Cloud Sleuth:日志收集工具包,封装了Dapper,Zipkin和HTrace操作;
10)、Spring Cloud Data Flow:大数据操作工具,通过命令行方式操作数据流;
11)、Spring Cloud Security:安全工具包,为你的应用程序添加安全控制,主要是指OAuth2;
12)、Spring Cloud Consul:封装了Consul操作,consul是一个服务发现与配置工具,与Docker容器可以无缝集成;
13)、Spring Cloud Zookeeper:操作Zookeeper的工具包,用于使用zookeeper方式的服务注册和发现;
14)、Spring Cloud Stream:数据流操作开发包,封装了与Redis,Rabbit、Kafka等发送接收消息;
15)、Spring Cloud CLI:基于 Spring Boot CLI,可以让你以命令行方式快速建立云组件;
3、springcloud脑图
二、Eureka
1、简介:Eureka是服务发现框架,主要用于定位运行在AWS域的中间层服务,以达到负载均衡和中间层服务故障转移的目的,本身也是局域Rest的服务;
2、工作原理:
1)、服务注册:Eureka提供了服务注册服务,每个服务节点启动后都会将Ip和端口注册到Eureka Server,服务注册表中就存储了所有可用服务节点的信息;
2)、服务续约:Eureka客户会每隔30秒发送一次心跳来续约,Eureka Server在90秒没有收到Eureka客户的续约,它会将实例从其注册表中删除;
3)、获取注册列表信息:Eureka客户端从服务器获取注册表信息,并将其缓存在本地,即使所有的服务宕机,也不会影响服务提供者与服务消费者的使用。该注册
列表信息定期(每30秒钟)更新一次。如果返回注册列表信息与Eureka客户端的缓存信息不同, Eureka客户端自动处理。如果由于某种原
因导致注册列表信息不能及时匹配,Eureka客户端则会重新获取整个注册表信息。 Eureka服务器缓存注册列表信息,整个注册表以及每个
应用程序的信息进行了压缩,压缩内容和没有压缩的内容完全相同。Eureka客户端和Eureka 服务器可以使用JSON / XML格式进行通讯。
Eureka客户端默认使用压缩JSON格式来获取注册列表的信息;
4)、服务下线:Eureka客户端在服务实例关闭时向Eureka服务器发送取消请求,将服务实例从服务器的实例注册表中删除。该下线请求不会自动完成,它需要调用
DiscoveryManager.getInstance().shutdownComponent()
3、实现一个EurekaServer
1)、添加Eureka依赖 <dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies> 2)、启动类添加注解@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
} 3)、编写application.yml配置文件
security://进行身份认证
basic:
//基于Http Basic的认证,对于更复杂的需求,您可以创建DiscoveryClientOptionalArgs类型的@Bean,
//并将ClientFilter实例注入到其中,所有这些都将应用于从客户端到服务器的调用
enabled: true
user:
name: user
password: password123
server:
port: 8761 //服务端口
eureka:
client:
register-with-eureka: false //不作为Eureka Client使用
fetch-registry: false //不作为Eureka Client使用
service-url:
defaultZone: http://user:password123@localhost:8761/eureka //为任何不表示首选项的客户端提供服务默认URL 4、服务提供者、服务消费者注册到Eureka
1)、服务提供者启动类中添加注解
@SpringBootApplication
@EnableEurekaClient(或者使用@EnableDiscoveryClient)
public class MicroserviceSimpleProviderUserApplication {
public static void main(String[] args) {
SpringApplication.run(MicroserviceSimpleProviderUserApplication.class, args);
}
} 2)、编写application.yml配置文件
server:
port: 7900
spring:
application:
name: microservice-provider-user //服务应用名称,建议使用小写
eureka:
client:
//如果您需要更多的控制健康检查,您可以考虑实施自己的com.netflix.appinfo.HealthCheckHandler
healthcheck:
enabled: true //健康检查
serviceUrl:
defaultZone: http://user:password123@localhost:8761/eureka //使用具有身份认证信息的默认url
instance:
prefer-ip-address: true
//指定url使用Ip和Adress
instance-id: ${spring.application.name}:${spring.cloud.client.ipAddress}:${spring.application.instance_id:${server.port}}
metadata-map:
zone: ABC # eureka可以理解的元数据
lilizhou: BBC # 不会影响客户端行为
lease-renewal-interval-in-seconds: 5
5、使用Eureka Client 获取服务实例(可使用配置了@EnableDiscoveryClient(@EnableEurekaClient)的应用程序来从Eureka服务器发现服务实例)
1)、方式一:使用本机com.netflix.discovery.EurekaClient
@Autowired
private EurekaClient eurekaClient;
public String serviceUrl() {
InstanceInfo instance = eurekaClient;.getNextServerFromEureka("STORES", false);
return instance.getHomePageUrl();
} 2)、方式二:使用org.springframework.cloud.client.discovery.DiscoveryClient
@Autowired
private DiscoveryClient discoveryClient;
public String serviceUrl() {
List<ServiceInstance> list = discoveryClient.getInstances("STORES");
if (list != null && list.size() > 0 ) {
return list.get(0).getUri();
}
return null;
} 6、扩展:由于Gradle的依赖关系解决规则和父母的bom功能缺乏,只要依靠spring-cloud-starter-eureka-server就可能导致应用程序启动失败。要解决这个问题,必
须添加Spring Boot Gradle插件,并且必须导入Spring云启动器父母bom:
buildscript {
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:1.3.5.RELEASE")
}
}
apply plugin: "spring-boot"
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:Brixton.RELEASE"
}
} 7、Eureka高可用(实现多个Eureka Client作为彼此的对等体)
Eureka Client peer1的application.yml:
spring:
profiles: peer1
eureka:
instance:
hostname: peer1
client:
serviceUrl:
defaultZone: http://peer2/eureka/,http://peer3/eureka/ Eureka Client peer2的application.yml:
spring:
profiles: peer2
eureka:
instance:
hostname: peer2
client:
serviceUrl:
defaultZone: http://peer1/eureka/,http://peer3/eureka/ Eureka Client peer3的application.yml:
spring:
profiles: peer3
eureka:
instance:
hostname: peer3
client:
serviceUrl:
defaultZone: http://peer1/eureka/,http://peer2/eureka/ 此时,只要服务提供者(消费者)注册到其中一个Eureka Client中都会同步到其他两个
三、Ribbon
1、简介:Ribbon是一个客户端负载均衡器,它可以很好地控制HTTP和TCP客户端的行为
2、Ribbon工作原理:
1)、选择Eureka Server,优先选择同个Zone且负载少的Server;
2)、根据用户指定的策略(轮循、随机、根据反应时间加权等),在从 Eureka Server拉取的服务注册列表中选择一个地址命中
3、集成Ribbon:
1)、添加依赖(如果已经添加了spring-cloud-starter-eureka,因为eureka依赖已经集成Ribbon所以此步可省略);
2)、添加注解:
@SpringBootApplication
@EnableEurekaClient
public class ConsumerMovieRibbonApplication {
@Bean
@LoadBalanced //是RestTemplate具备Ribbon负载均衡能力
public RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(ConsumerMovieRibbonApplication.class, args);
}
} 3)、服务接口@RestController
public class MovieController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/movie/{id}")
public User findById(@PathVariable Long id) {
return this.restTemplate.getForObject("http://microservice-provider-user/simple/" + id, User.class);
}
} 4、使用注解自定义配置Ribbon
1)、使用@RibbonClient声明其他配置
@Configuration
@ExcludeFromComponentScan
public class TestConfiguration {
//自定义Ribbon策略
@Bean
public IRule ribbonRule() {
return new RandomRule();
}
} 注意:TestConfiguration必须添加@Configuration注解,且不在主应用程序上下文的@ComponentScan的扫描范围中(及不在启动类所在的包及其子包
中),否则将由所有@RibbonClients共享
2)、编写启动类
@SpringBootApplication
@EnableEurekaClient
//name:一定要是Eureka定义的应用名称
@RibbonClient(name = "microservice-provider-user", configuration = TestConfiguration.class)
@ComponentScan(excludeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION, value = ExcludeFromComponentScan.class) })
public class ConsumerMovieRibbonApplication {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(ConsumerMovieRibbonApplication.class, args);
}
} 3)、测试:
@RestController
public class MovieController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private LoadBalancerClient loadBalancerClient;
@GetMapping("/movie/{id}")
public User findById(@PathVariable Long id) {
return this.restTemplate.getForObject("http://microservice-provider-user/simple/" + id, User.class);
}
@GetMapping("/test")
public void test() {
ServiceInstance serviceInstance = this.loadBalancerClient.choose("microservice-provider-user");
System.out.println("1" + ":" + serviceInstance.getServiceId() + ":" + serviceInstance.getHost());
ServiceInstance serviceInstance2 = this.loadBalancerClient.choose("microservice-provider-user2");
System.out.println("2" + ":" + serviceInstance2.getServiceId() + ":" + serviceInstance2.getHost());
}
} 5、使用配置文件自定义配置Ribbon
从版本1.2.0开始,Spring Cloud Netflix支持使用属性与Ribbon文档兼容来自定义Ribbon客户端,以<clientName>.ribbon.为前缀,支持一下属性:
1)、NFLoadBalancerClassName:应实施ILoadBalancer
2)、NFLoadBalancerRuleClassName:应实施IRule
3)、NFLoadBalancerPingClassName:应实施IPing
4)、NIWSServerListClassName:应实施ServerList
5)、NIWSServerListFilterClassName应实施ServerListFilter
在这些属性中定义的类优先于使用@RibbonClient(configuration=MyRibbonConfig.class)定义的bean和由Spring Cloud Netflix提供的默认值。
自定义Ribbon:
服务消费者application.yml:
users(Eureka中定义服务提供者应用名称):
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule
6、使用Ribbon时禁用Eureka
1)、方式一:application.yml中对客户端URL进行硬编码
stores(Eureka中定义的客户端应用名称):
ribbon:
listOfServers: localhost:7901 2)、方式二:application.yml中禁用Eureka
ribbon:
eureka:
enabled: false 7、Ribbon源码分析:
1)、参考链接:http://blog.csdn.net/forezp/article/details/74820899
Ribbon的负载均衡,主要通过LoadBalancerClient来实现的,而LoadBalancerClient具体交给了ILoadBalancer来处理,ILoadBalancer配置IClientConfig、
IRule、IPing等信息,其子类BaseLoadBalancer默认配置了一下配置:
1)、IClientConfig:用于对客户端或者负载均衡的配置,默认实现类DefaultClientConfigImpl
2)、IRule:用于复杂均衡的策略,默认路由策略RoundRobinRule(轮循),其它实现类有
BestAvailableRule(选择最小请求数)
ClientConfigEnabledRoundRobinRule(轮询)
RandomRule(随机)
RetryRule(根据轮询的方式重试)
WeightedResponseTimeRule(根据响应时间去分配一个weight,weight越低,被选择的可能性越低)
ZoneAvoidanceRule(根据server的zone区域和可用性轮询选择)
3)、IPing:判断该server是否有响应,从而判断该server是否可用,提供了isAlive( )方法,实现类有:
PingUrl:ping某个url,判断其是否alive
PingConstant:固定返回某服务是否可用,默认返回true,即可用
NoOpPing:不去ping,直接返回true
DummyPing(默认):直接返回true,并实现了initWithNiwsConfig方法
NIWSDiscoveryPing:根据DiscoveryEnabledServer的InstanceInfo的InstanceStatus去判断,为InstanceStatus.UP,则可用,否则不可用
4)、ServerList是定义获取所有的server的注册列表信息的接口,默认实现(ConfigurationBasedServerList)
5)、ServerListFilter:定于可根据配置去过滤或者根据特性动态获取符合条件的server列表的方法,默认实现ZonePreferenceServerListFilter
,LoadBalancerClient在初始化的时候,会向Eureka获取服务注册列表,并以10s一次向EurekaClient发送“ping”(在BaseLoadBalancer构造函数中开启了一个
PingTask任务),来判断服务的可用性,进而检查是否更新服务列表,最后,得到注册列表后,ILoadBalancer根据IRule的策略进行负载均衡;而RestTemplate
被@LoadBalance注解后具有负载均衡能力是因为:
LoadBalancer自动配置类(LoadBalancerAutoConfiguration)维护了一个被@LoadBalanced修饰的RestTemplate对象的List在初始化的过程中,通过调
用customizer.customize(restTemplate)方法来给RestTemplate增加拦截器LoadBalancerInterceptor,而LoadBalancerInterceptor,用于实时拦截,在
LoadBalancerInterceptor这里实现来负载均衡;
2)、参考链接:https://www.jianshu.com/p/19bcd9acf559
RibbonAutoConfiguration:Spring Boot启动Rabbion的入口配置类:
public class RibbonAutoConfiguration {
...
@Bean
public SpringClientFactory springClientFactory() {
SpringClientFactory factory = new SpringClientFactory();
factory.setConfigurations(this.configurations); return factory;}
...
}
SpringClientFactory:
查看方法列表,此类提供了获取指定serviceId的IClient, ILoadBalance, ILoadBalanceContext等对象的重要方法。
IClient:发起请求并执行的接口;
ILoadBalanceContext:封装了一些负载均衡额外的方法,比如noteOpenConnection(), noteError(), noteResponse()等等;
ILoadBalance:负载均衡;
public <T> Map<String, T> getInstances(String name, Class<T> type) {
AnnotationConfigApplicationContext context = getContext(name);
if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context, type).length > 0) {
return BeanFactoryUtils.beansOfTypeIncludingAncestors(context, type);
}
return null;
}
查看获取LoadBalance实例的方法,发现其最终从父类NamedContextFactory获取Bean。Bean来源AnnotationConfigApplicationContext context
这个Context从那里获取我想要的bean实例呢?
看看context的创建过程,红圈一处,它会尝试为每个serviceId使用属于自己的配置类(当然前提是你有对应的配置),并注册;红圈二说明,它会注
册一个默认的配置类this.defaultConfigType
this.defaultConfigType = RibbonClientConfiguration.class;
context.register(PropertyPlaceholderAutoConfiguration.class, this.defaultConfigType);
至此,一个关键的配置类被发现了:RibbonClientConfiguration;
RibbonClientConfiguration:
查看方法列表,我们可以为每个serviceId指定自己的配置类(即上面的自定义Ribbon)
@Configuration
@RibbonClient(name = "foo", configuration = FooConfiguration.class)
public class TestConfiguration {
}
@Configuration
public class FooConfiguration {
@Bean
public IPing ribbonPing(IClientConfig config) {
return new PingUrl();
}
@Bean
public IRule ribbonRule(IClientConfig config) {
return new AvailabilityFilteringRule();
}
}
四、Feign
1、简介:
Feign是一种声明式、模板化的HTTP客户端。在Spring Cloud Netflix栈中,各个微服务都是以HTTP接口的形式暴露自身服务的,因此在调用远程服务时就必
须使用HTTP客户端,使用Feign, 我们可以做到使用HTTP请求远程服务时能与调用本地方法一样的编码体验
2、使用Feign
1)、添加maven依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
2)、声明一个接口,添加@FeignClient注解
@FeignClient("microservice-provider-user")//绑定该接口对应服务提供者的服务
public interface UserFeignClient {
@RequestMapping(value = "/simple/{id}", method = RequestMethod.GET)//不支持@GetMapping
public User findById(@PathVariable("id") Long id); //@PathVariable必须设置value
@RequestMapping(value = "/user", method = RequestMethod.POST)
public User postUser(@RequestBody User user);
//该请求会报错,参数是复杂对象,即使指定了是GET方法,feign依然会以POST方法进行发送请求
@RequestMapping(value = "/get-user", method = RequestMethod.GET)
public User getUser(User user);
}
3)、启动类添加@EnableFeignClients注解
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class ConsumerMovieFeignApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerMovieFeignApplication.class, args);
}
}
4)、业务逻辑类直接调用接口方法
@RestController
public class MovieController {
@Autowired
private UserFeignClient userFeignClient;
@GetMapping("/movie/{id}")
public User findById(@PathVariable Long id) {
return this.userFeignClient.findById(id);
}
@GetMapping("/test")
public User testPost(User user) {
return this.userFeignClient.postUser(user);
}
@GetMapping("/test-get")
public User testGet(User user) {
return this.userFeignClient.getUser(user);
}
}
2、自定义Feign配置:修改Feign默认配置
1)、@FeignClient注解的configuration指定自定义配置类(同样不要被@ComponentScan扫描到)
@FeignClient(name = "microservice-provider-user", configuration = Configuration1.class)
public interface UserFeignClient {
//自定义配置请求路径注解要使用@RequestLine (Get 请求路径)
@RequestLine("GET /simple/{id}")
public User findById(@Param("id") Long id);
}
2)、自定义配置类
@Configuration
public class Configuration1 {
@Bean
public Contract feignContract() {
return new feign.Contract.Default();
}
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
3、自定义Feign配置:访问Eureka,服务调用的安全验证
1)、@FeignClient注解的configuration指定自定义配置类(同样不要被@ComponentScan扫描到)
//定义了请求url时需要添加一个name
@FeignClient(name = "xxxx", url = "http://localhost:8761/", configuration = Configuration2.class)
public interface FeignClient2 {
@RequestMapping(value = "/eureka/apps/{serviceName}")
public String findServiceInfoFromEurekaByServiceName(@PathVariable("serviceName") String serviceName);
}
2)、自定义配置类
@Configuration
public class Configuration2 {
//Eureka验证
@Bean
public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
return new BasicAuthRequestInterceptor("user", "password123");
}
}
4、手动创建Feign Client
在某些情况下,可能以上述方法不可能自定义您的Feign客户端。在这种情况下,可以使用Feign Builder API创建客户端 。下面是一个创建两个具有相同接口的
Feign客户端的示例,但是使用单独的请求拦截器配置每个客户端
@Import(FeignClientsConfiguration.class)//FeignClientsConfiguration.class是Spring Cloud Netflix提供的默认配置
class FooController {
private FooClient fooClient;
private FooClient adminClient;
@Autowired
public FooController(Decoder decoder, Encoder encoder, Client client) {
this.fooClient = Feign.builder().client(client)
.encoder(encoder)
.decoder(decoder)
.requestInterceptor(new BasicAuthRequestInterceptor("user", "user"))
.target(FooClient.class, "http://PROD-SVC");
this.adminClient = Feign.builder().client(client)
.encoder(encoder)
.decoder(decoder)
.requestInterceptor(new BasicAuthRequestInterceptor("admin", "admin"))
.target(FooClient.class, "http://PROD-SVC");
}
}
5、配置文件配置Feign
feign:
client:
config:
feignClientName:
connectTimeout: 5000 # 相当于Request.Options
readTimeout: 5000 # 相当于Request.Options
# 配置Feign的日志级别,相当于代码配置方式中的Logger
loggerLevel: full
# Feign的错误解码器,相当于代码配置方式中的ErrorDecoder
errorDecoder: com.example.SimpleErrorDecoder
# 配置重试,相当于代码配置方式中的Retryer
retryer: com.example.SimpleRetryer
# 配置拦截器,相当于代码配置方式中的RequestInterceptor
requestInterceptors:
- com.example.FooRequestInterceptor
- com.example.BarRequestInterceptor
decode404: false
6、Feign请求和响应的压缩
feign.compression.request.enabled=true
feign.compression.request.mime-types=text/xml,application/xml,application/json
feign.compression.request.min-request-size=2048
feign.compression.response.enabled=true
7、Feign的日志
1)、application.yml中为每个Feign Client创建日志,默认只响应Debug
logging.level.project.user.UserClient: DEBUG
2)、自定义配置类中声明日志级别
@Configuration
public class FooConfiguration {
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
8、Feign的HTTP Client
Feign在默认使用JDK原生的URLConnection发送HTTP请求,没有连接池,但是对每个地址会保持一个长连接,即利用HTTP的persistence connection 。Spring
Cloud从Brixtion.SR5版本开始支持用Apache的HTTP Client替换Feign原始的http client, 从而获取连接池、超时时间等与性能息息相关的控制能力:
1)、项目中声明Apache HTTP Client和feign-httpclient依赖:
<!-- 使用Apache HttpClient替换Feign原生httpclient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<dependency>
<groupId>com.netflix.feign</groupId>
<artifactId>feign-httpclient</artifactId>
<version>${feign-httpclient}</version>
</dependency>
2)、application.properties
feign.httpclient.enabled=true
9、服务调用的复杂权限认证
上面服务调用的安全验证中服务之间可以通过自定义配置完成基于Http Basic的认证,但是不能满足根据不同的角色不同的用户执行不同的操作,即服务之间的调用
有更复杂的权限要求,此时需对Feign做进一步修改:
1)、服务提供者添加spring-security依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
2)、服务提供者关键权限配置
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomUserDetailService userDetailService;
@Bean
public PasswordEncoder passwordEncoder(){
return NoOpPasswordEncoder.getInstance();
}
@Override
protected void configure(HttpSecurity http) throws Exception{
http.authorizeRequests().anyRequest().authenticated().and().httpBasic();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception{
auth.userDetailsService(this.userDetailService).passwordEncoder(this.passwordEncoder());
}
@Component
class CustomUserDetailService implements UserDetailsService{
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException{
if("user".equals(username)){
return new SecurityUser("user","123456","user-role");
}else if("admin".equals(username)){
return new SecurityUser("admin","123456","admin-role");
}else{
return null;
}
}
}
class SecurityUser implements UserDetails{
private static final long serialVersionUID=1L;
public SecurityUser(String username,String password,String role){
super();
this.username=username;
this.password=password;
this.role=role;
}
public SecurityUser(){
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> authorities=new ArrayList<>();
SimpleGrantedAuthority authority=new SimpleGrantedAuthority(this.role);
authorities.add(authority);
return authorities;
}
private Long id;
private String username;
private String password;
private String role;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
}
}
启动项后,可以见到页面需要输入用户名和密码才能访问
3)、服务消费者接口去掉@feignClient注解
//@FeignClient(value="hello-auth")
public interface HelloAuthService {
@GetMapping("/{id}")
User findById(@PathVariable("id") Long id);
}
4)、服务消费者启动类去掉@EnableFeignClients注解
5)、服务消费者业务逻辑类Controller
@Import(FeignClientsConfiguration.class)
@RestController
public class HelloAuthFeignController {
private HelloAuthService userAuthService;
private HelloAuthService adminAuthService;
@Autowired
public HelloAuthFeignController(Decoder decoder, Encoder encoder, Client client, Contract contract){
this.userAuthService= Feign.builder().client(client).encoder(encoder).decoder(decoder).contract(contract)
.requestInterceptor(new BasicAuthRequestInterceptor("user","123456"))
.target(HelloAuthService.class,"http://hello-auth/");
this.adminAuthService= Feign.builder().client(client).encoder(encoder).decoder(decoder).contract(contract)
.requestInterceptor(new BasicAuthRequestInterceptor("admin","123456"))
.target(HelloAuthService.class,"http://hello-auth/");
}
@GetMapping("/user/{id}")
public User findByIdUser(@PathVariable Long id){
return this.userAuthService.findById(id);
}
@GetMapping("/admin/{id}")
public User findByIdAdmin(@PathVariable Long id){
return this.adminAuthService.findById(id);
}
}
五、Hystrix
1、Hystrix:是一个帮助解决分布式系统交互时超时处理和容错的类库,通过服务熔断(也可以称为断路)、降级、限流(隔离)、异步RPC等手段控制依赖服务的延迟与失败,
当对特定服务的呼叫达到一定阈值时(默认为5秒内故障20次),短路器打开,不进行通话
2、服务雪崩效应:
1)、简介:较低级别的服务出现服务故障可能导致用户级联故障;
2)、应对策略:
a)、流量控制:
网关限流:因为Nginx的高性能, 目前一线互联网公司大量采用Nginx+Lua的网关进行流量控制, 由此而来的OpenResty也越来越热门;
用户交互限流:采用加载动画,提高用户的忍耐等待时间、提交按钮添加强制等待时间机制;
关闭重试
b)、改进缓存模式
缓存预加载
同步改为异步刷新
c)、服务自动扩容:AWS的auto scaling
e)、服务调用者降级服务
资源隔离:主要是对调用服务的线程池进行隔离
对依赖服务进行分类: 强依赖服务(不可用时会导致当前业务中止)和弱依赖服务(不可用不会导致当前业务的中止)
f)、不可用服务的调用快速失败:一般通过超时机制、熔断器、熔断后的降级(fallback)来实现
3、使用Hystrix
1)、添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
2)、启动类添加@EnableCircuitBreaker注解
3)、服务调用发放添加@HystrixCommand注解,编写fallback方法
@GetMapping("/movie/{id}")
@HystrixCommand(fallbackMethod = "findByIdFallback")
public User findById(@PathVariable Long id) {
return this.restTemplate.getForObject("http://microservice-provider-user/simple/" + id, User.class);
}
//参数、返回值要跟调用服务方法相同
public User findByIdFallback(Long id) {
User user = new User();
user.setId(0L);
return user;
}
4、传播安全上下文或者使用Spring Scope
1)、当程序报一个RuntimeException(it can’t find the scoped context)时,若想将线程上下文传播到@HystrixCommand中,默认声明将不起作用,因为它在一个线程池
中执行命令(除非超时),可以使用某些配置或者在注解中使用不用的隔离策略使Hystrix跟调用服务者使用相同的线程
2)、代码实现:
@HystrixCommand(fallbackMethod = "stubMyService",
commandProperties ={@HystrixProperty(name="execution.isolation.strategy", value="SEMAPHORE")}
)
public User findById(@PathVariable Long id) {
return this.restTemplate.getForObject("http://microservice-provider-user/simple/" + id, User.class);
}
3)、两种隔离策略
a)、THREAD:在一个隔离的线程中执行,并发请求受到线程池中线程的数量限制;
b)、SEMAPHORE:在调用服务的线程中执行,并发请求受到信号量计数限制;
5、健康指标
连接断路器的状态也暴露在服务消费者应用的/health端点中(应用上下文/health)
{
"hystrix": {
"openCircuitBreakers": [
"StoreIntegration::getStoresByLocationLink"
],
"status": "CIRCUIT_OPEN"
},
"status": "UP"
}
注意:使用Hystrix指标流需要引入spring-boot-starter-actuator依赖
6、Hystrix对Feign的支持
1)、FeignClient实现类中注解添加fallback属性
@FeignClient(name = "microservice-provider-user", fallback = HystrixClientFallback.class)
public interface UserFeignClient {
@RequestMapping(value = "/simple/{id}", method = RequestMethod.GET)
public User findById(@PathVariable("id") Long id);
}
2)、编写一个实现FeignClient的实现类,重写实现类方法
@Component//需要添加这个注解,否则会报找不到实例
public class HystrixClientFallback implements UserFeignClient {
@Override
public User findById(Long id) {
User user = new User();
user.setId(0L);
return user;
}
}
如果需要访问导致回退触发的原因,可以使用@FeignClient内的fallbackFactory(此时不能写fallback属性)属性:
1)、编写实现了UserFeignClient接口的UserFeignWithFactory
public interface UserFeignClientWithFactory extends UserFeignClient {
}
2)、编写实现了FallbackFactory的HystrixClientFallbackFactory
@Component
public class HystrixClientFactory implements FallbackFactory<UserFeignClient> {
private static final Logger LOGGER = LoggerFactory.getLogger(HystrixClientFactory.class);
@Override
public UserFeignClient create(Throwable cause) {
HystrixClientFactory.LOGGER.info("fallback; reason was: {}", cause.getMessage());
return new UserFeignClientWithFactory() {
@Override
public User findById(Long id) {
User user = new User();
user.setId(-1L);
return user;
}
};
}
}
3)、UserFeignClient注解中添加fallbackFactory属性
@FeignClient(name = "microservice-provider-user", /*fallback = HystrixClientFallback.class, */fallbackFactory = HystrixClientFactory.class)
public interface UserFeignClient {
@RequestMapping(value = "/simple/{id}", method = RequestMethod.GET)
public User findById(@PathVariable("id") Long id);
}
6、禁用单个FeignClient 对 Hystrix的支持
1)、在FeignClient的配置类中重新为FeignClient指定Feign.Builder
@Configuration
public class Configuration2 {
@Bean
public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
return new BasicAuthRequestInterceptor("user", "password123");
}
@Bean
@Scope("prototype")
public Feign.Builder feignBuilder() {
return Feign.builder();
}
}
原理:Spring Cloud Netflix默认为feign提供的bean里面的Feign.Builder feignBuiler 默认为支持了Hystrix的HystrixFeign.Builder ,重新指定不支持Hystrix的Builder
2)、想禁用Hystrix的FeignClient注解中引用禁用了Hystrix的配置类
@FeignClient(name = "xxxx", url = "http://localhost:8761/", configuration = Configuration2.class, fallback = FeignClient2Fallback.class)
public interface FeignClient2 {
@RequestMapping(value = "/eureka/apps/{serviceName}")
public String findServiceInfoFromEurekaByServiceName(@PathVariable("serviceName") String serviceName);
}
7、禁用所有FeignClient 对 Hystrix的支持
application.yml中添加属性:feign.hystrix.enabled=false
六、Zuul
1、简介
Zuul是基于JVM的路由器和服务器端负载均衡器,是开源的API Gateway 服务器, 本质上是一个web servlet应用,在云平台上提供动态路由,监控,弹性,安全等
边缘服务的框架,zuul默认的Hystrix 隔离策略是SEMAPHORE(信号量),可以通过zuul.ribbonIsolationStrategy修改
2、使用zuul
1)、添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
2)、启动类添加@EnableZuulProxy注解
@SpringBootApplication
//组合注解,集成了@EnableCricuitBreaker、@EnableDiscoveryClient
@EnableZuulProxy
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
}
3)、访问Zuul的zuulContext:zuulPort/EurekaApplicationName/simple/1,Zuul将该访问路径映射到User微服务的访问路径userContext:userPort/simple/1
3、嵌入式Zuul反向代理
1)、ignoredServices:默认zuul会方向代理Eureka上注册的所有服务,忽略某些服务,是列表中间用逗号隔开(*表示忽略所有)
zuul:
ignoredServices: '*'
routes:
users: /myusers/**
2)、path、serviceId:指定对path路径方向代理到Eureka中serviceId微服务,进行更细粒度的控制
zuul:
routes:
users:
path: /myusers/** // **表示匹配多层路径 *表示匹配单层路径
serviceId: users_service
3)、path、url:指定对path路径方向代理到物理路径,进行更细粒度的控制
zuul:
routes:
users:
path: /myusers/**
url: http://example.com/users_service 这样直接配置url不会作为HystrixCommand跟Riboon负载均衡,如果想实现,需对serviceId特殊配置
zuul:
routes:
users:
path: /myusers/**
serviceId: users
ribbon:
eureka:
enabled: false //需要在Ribbon中禁用Eureka支持
users: //对应上面的serviceId
ribbon:
listOfServers: http://localhost:7900,http://localhost:7900 4)、正则表达式指定路由规则
@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
@Bean
public PatternServiceRouteMapper serviceRouteMapper() {
//将serviceId“myusers-v1”代理为路由“/v1/myusers”,如果匹配不上则不起作用
return new PatternServiceRouteMapper("(?<name>^.+)-(?<version>v.+$)", "${version}/${name}");
}
}
5)、prefix:前缀为所有映射添加前缀,配置后需要在访问路径添加前缀值
zuul:
prefix: myService //访问路径 zuulContext:zuulPort/myService/simple/1
6)、stripPrefix:默认情况下,请求被转发之前,代理前缀被删除(可通过zuul.stripPrefix=false关闭)
7)、ignoredPatterns:忽略某些特殊的路径
zuul:
ignoredPatterns: /**/admin/**
routes:
users: /myusers/**
8)、legacy:代理除了指定外的所有路径
zuul:
routes:
users:
path: /myusers/**
legacy:
path: /**
4、Zuul Http 客户端
zuul默认使用Apache Http Client支持的HttpClient,使用ribbon.restclient.enabled=true、ribbon.okhttp.enabled=true指定RestClient、OkHttpClient
5、Cookies与敏感标题
zuul:
routes:
users:
path: /myusers/**
sensitiveHeaders: Cookie,Set-Cookie,Authorization //黑名单,不传播到下游服务
url: https://downstream 6、忽略Headers
zuul:
ignoredHeaders : header1,header2
7、 Routes Endpoint
通过访问路径/routes返回路由列表,GET将返回映射路由的列表,POST将强制刷新现有路由
8、通过Zull上传文件
使用zuul代理路径zuul.routes.customers=/customers/**可以将文件上传到“/ zuul / customers / *,对于大文件,添加“/zuul/*”前缀可以绕过Spring
DispatcherServlet,因此不用设置上传文件大小设置,上传大文件需要设置超时时间
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 60000
ribbon:
ConnectTimeout: 3000
ReadTimeout: 60000
9、为route提供Hystrix 回退
当Zuul中断路器路由的电路跳闸时,可以通过创建类型为ZuulFallbackProvider的bean来提供回退响应。在这个bean中,需要指定回退的路由ID,并提供返回的
ClientHttpResponse作为后备
class MyFallbackProvider implements ZuulFallbackProvider {
@Override
public String getRoute() {
//如果要为所有的route提供默认fallback,这里返回“*”
return "customers";
}
@Override
public ClientHttpResponse fallbackResponse() {
return new ClientHttpResponse() {
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.OK;
}
@Override
public int getRawStatusCode() throws IOException {
return 200;
}
@Override
public String getStatusText() throws IOException {
return "OK";
}
@Override
public void close() {
}
@Override
public InputStream getBody() throws IOException {
return new ByteArrayInputStream("fallback".getBytes());
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
};
}
}
10、sidecar整合异构微服务
sidecar是用来整合异构语言的,sidecar这个服务必须和python或nodejs等异构语言的服务在同一台主机上面,就是说他们之间是localhost,不能是ip访问等
等,如果不在同一主机时需要配置${eureka.instance.hostname}
1)、添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-sidecar</artifactId>
</dependency>
2)、启动类添加@EnableSidecar注解
@SpringBootApplication
//组合注解,集成了@EnableCricuitBreaker、@EnableDiscoveryClient/@EnableZuulProxy
@EnableSidecar
public class SidecarApplication {
public static void main(String[] args) {
SpringApplication.run(SidecarApplication.class, args);
}
}
3)、application.yml
server:
port: 5678
spring:
application:
name: sidecar
sidecar:
port: 8060 //异构微服务的端口
health-uri: http://localhost:8060/health.json sidecar检查异构语言的健康地址返回结果是:
要注意,这个json结果不是是我们直接写出来的,好几次都是为了方便直接在return里面写的数据,如果这样启动sidecar时,会发现注册中心sidecar一直是 {
"status":"UP" //这是异构微服务里面的健康检查,服务发现会将这个值负责到微服务发现中
} down,那是因为response header没有改过来,所以可以把数据发到map里面,如用json包转化成json数据,其他的方式也是可以的。其实原理是response
header里面解析的text/html,我们应该改成application/json,如果真的想直接写一个json数据,那就要改下这个接口的response header
七、spring cloud config
1、简介
在分布式系统中,为了方便微服务配置文件的统一管理、实时更新,需要分布式配置中心组件。分布式配置中心组件spring cloud config 支持配置服务放在配置服
务的内存中(即本地),也支持放在远程Git仓库中。在spring cloud config 组件中,分两个角色,一是config server,二是config client
2、编写Config Server
1)、添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency> 2)、启动类添加@EnableConfigServer
@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
} 3)、application.yml配置文件
server:
port: 8080
spring:
cloud:
config:
server:
git:
uri: https://git.oschina.net/it-much/config-repo-51cto-video //获取配置文件的路径 4)、Config Server映射关系有以下几种,浏览器可以通过映射关系访问获取配置文件中的属性,当映射关系匹配不上时回退到application.yml
/{application}/{profile}[/{label}] label指的是git标签(默认事master)
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties
3、编写 Config Client
1)、添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency> 2)、Git新建配置文件application.yml
server:
port: 8081 3)、Git新建配置文件bootstrap.yml
springcloud中有个启动上下文(用来加载config server的远程配置),启动时bootstrap.yml先加载,然后链接ConfigServer加载远程配置,最后加载
application.yml中的配置,所以需要添加bootstrap.yml,不然会报错Could not resolve placeholder "profile" in String ${profile}
spring:
cloud:
config:
uri: http://localhost:8080 profile: dev
label: master # 当configserver的后端存储是Git时,默认就是master
application:
name: foobar 4)、ConfigServer编写启动类 @SpringBootApplication
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
} 5)、Config client 业务逻辑类方法连接Config Server获取远程配置文件@RestController
public class ConfigClientController {
@Value("${profile}")
private String profile;
@GetMapping("/profile")
public String getProfile() {
return this.profile;
}
} 6)、当远程配置文件与本地配置文件存在冲突时,以远程配置文件为主,spring cloud建议bootstrap.yml中配置不修改的配置项
4、Git后端环境库
1)、URL中的占位符
Spring Cloud Config服务器支持一个Git仓库URL,其中包含{application}和{profile}(以及{label})的占位符
spring:
cloud:
config:
server:
git:
uri: https://github.com/myorg/{application} //匹配appName,使得git仓库对应一个微服务 2)、模式匹配
3)、
4)、
5)、
6)、
1)、
5、
6、
7、
8、
测试
1、微服务是以开发一组小型服务的方式来开发一个独立的应用系统的,其中每个小型服务都运行在自己的进程中,微服务之间通过一些轻量的通信机制进行通信,经常采
用HTTP资源API(例如通过REST API或者RPC)轻量的机制来相互通信。 这些服务围绕业务功能进行构建,并能通过全自动的部署机制来进行独立部署,可以使用不
同的语言、数据存储技术,我们仅对微服务做最低限度的集中管理。
二、SpringCloud介绍
1、Spring Cloud是基于SpringBoot的快速构建分布式系统的工具集,提供了快速构建分布式系统中一些常见模式的工具(配置管理,服务发现,断路器,智能路由,微
代理,控制总线)。分布式系统的协调导致了样板模式, 使用Spring Cloud可以快速地支持实现这些模式的服务和应用程序,可以运行在任何分布式环境中。
2、SpringCloud的子项目:
1)、Spring Cloud Config:配置管理开发工具包,可以让你把配置放到远程服务器,目前支持本地存储、Git以及Subversion;
2)、Spring Cloud Bus:事件、消息总线,用于在集群(例如,配置变化事件)中传播状态变化,可与Spring Cloud Config联合实现热部署;
3)、Spring Cloud Netflix:针对多种Netflix组件提供的开发工具包,其中包括Eureka、Hystrix、Zuul、Archaius等;
4)、Netflix Eureka:云端负载均衡,一个基于 REST 的服务,用于定位服务,以实现云端的负载均衡和中间层服务器的故障转移;
5)、Netflix Hystrix:容错管理工具,旨在通过控制服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力;
6)、Netflix Zuul:边缘服务工具,是提供动态路由,监控,弹性,安全等的边缘服务;
7)、Netflix Archaius:配置管理API,包含一系列配置管理API,提供动态类型化属性、线程安全配置操作、轮询框架、回调机制等功能;
8)、Spring Cloud for Cloud Foundry:通过Oauth2协议绑定服务到CloudFoundry,CloudFoundry是VMware推出的开源PaaS云平台;
9)、Spring Cloud Sleuth:日志收集工具包,封装了Dapper,Zipkin和HTrace操作;
10)、Spring Cloud Data Flow:大数据操作工具,通过命令行方式操作数据流;
11)、Spring Cloud Security:安全工具包,为你的应用程序添加安全控制,主要是指OAuth2;
12)、Spring Cloud Consul:封装了Consul操作,consul是一个服务发现与配置工具,与Docker容器可以无缝集成;
13)、Spring Cloud Zookeeper:操作Zookeeper的工具包,用于使用zookeeper方式的服务注册和发现;
14)、Spring Cloud Stream:数据流操作开发包,封装了与Redis,Rabbit、Kafka等发送接收消息;
15)、Spring Cloud CLI:基于 Spring Boot CLI,可以让你以命令行方式快速建立云组件;
3、springcloud脑图
二、Eureka
1、简介:Eureka是服务发现框架,主要用于定位运行在AWS域的中间层服务,以达到负载均衡和中间层服务故障转移的目的,本身也是局域Rest的服务;
2、工作原理:
1)、服务注册:Eureka提供了服务注册服务,每个服务节点启动后都会将Ip和端口注册到Eureka Server,服务注册表中就存储了所有可用服务节点的信息;
2)、服务续约:Eureka客户会每隔30秒发送一次心跳来续约,Eureka Server在90秒没有收到Eureka客户的续约,它会将实例从其注册表中删除;
3)、获取注册列表信息:Eureka客户端从服务器获取注册表信息,并将其缓存在本地,即使所有的服务宕机,也不会影响服务提供者与服务消费者的使用。该注册
列表信息定期(每30秒钟)更新一次。如果返回注册列表信息与Eureka客户端的缓存信息不同, Eureka客户端自动处理。如果由于某种原
因导致注册列表信息不能及时匹配,Eureka客户端则会重新获取整个注册表信息。 Eureka服务器缓存注册列表信息,整个注册表以及每个
应用程序的信息进行了压缩,压缩内容和没有压缩的内容完全相同。Eureka客户端和Eureka 服务器可以使用JSON / XML格式进行通讯。
Eureka客户端默认使用压缩JSON格式来获取注册列表的信息;
4)、服务下线:Eureka客户端在服务实例关闭时向Eureka服务器发送取消请求,将服务实例从服务器的实例注册表中删除。该下线请求不会自动完成,它需要调用
DiscoveryManager.getInstance().shutdownComponent()
3、实现一个EurekaServer
1)、添加Eureka依赖 <dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies> 2)、启动类添加注解@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
} 3)、编写application.yml配置文件
security://进行身份认证
basic:
//基于Http Basic的认证,对于更复杂的需求,您可以创建DiscoveryClientOptionalArgs类型的@Bean,
//并将ClientFilter实例注入到其中,所有这些都将应用于从客户端到服务器的调用
enabled: true
user:
name: user
password: password123
server:
port: 8761 //服务端口
eureka:
client:
register-with-eureka: false //不作为Eureka Client使用
fetch-registry: false //不作为Eureka Client使用
service-url:
defaultZone: http://user:password123@localhost:8761/eureka //为任何不表示首选项的客户端提供服务默认URL 4、服务提供者、服务消费者注册到Eureka
1)、服务提供者启动类中添加注解
@SpringBootApplication
@EnableEurekaClient(或者使用@EnableDiscoveryClient)
public class MicroserviceSimpleProviderUserApplication {
public static void main(String[] args) {
SpringApplication.run(MicroserviceSimpleProviderUserApplication.class, args);
}
} 2)、编写application.yml配置文件
server:
port: 7900
spring:
application:
name: microservice-provider-user //服务应用名称,建议使用小写
eureka:
client:
//如果您需要更多的控制健康检查,您可以考虑实施自己的com.netflix.appinfo.HealthCheckHandler
healthcheck:
enabled: true //健康检查
serviceUrl:
defaultZone: http://user:password123@localhost:8761/eureka //使用具有身份认证信息的默认url
instance:
prefer-ip-address: true
//指定url使用Ip和Adress
instance-id: ${spring.application.name}:${spring.cloud.client.ipAddress}:${spring.application.instance_id:${server.port}}
metadata-map:
zone: ABC # eureka可以理解的元数据
lilizhou: BBC # 不会影响客户端行为
lease-renewal-interval-in-seconds: 5
5、使用Eureka Client 获取服务实例(可使用配置了@EnableDiscoveryClient(@EnableEurekaClient)的应用程序来从Eureka服务器发现服务实例)
1)、方式一:使用本机com.netflix.discovery.EurekaClient
@Autowired
private EurekaClient eurekaClient;
public String serviceUrl() {
InstanceInfo instance = eurekaClient;.getNextServerFromEureka("STORES", false);
return instance.getHomePageUrl();
} 2)、方式二:使用org.springframework.cloud.client.discovery.DiscoveryClient
@Autowired
private DiscoveryClient discoveryClient;
public String serviceUrl() {
List<ServiceInstance> list = discoveryClient.getInstances("STORES");
if (list != null && list.size() > 0 ) {
return list.get(0).getUri();
}
return null;
} 6、扩展:由于Gradle的依赖关系解决规则和父母的bom功能缺乏,只要依靠spring-cloud-starter-eureka-server就可能导致应用程序启动失败。要解决这个问题,必
须添加Spring Boot Gradle插件,并且必须导入Spring云启动器父母bom:
buildscript {
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:1.3.5.RELEASE")
}
}
apply plugin: "spring-boot"
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:Brixton.RELEASE"
}
} 7、Eureka高可用(实现多个Eureka Client作为彼此的对等体)
Eureka Client peer1的application.yml:
spring:
profiles: peer1
eureka:
instance:
hostname: peer1
client:
serviceUrl:
defaultZone: http://peer2/eureka/,http://peer3/eureka/ Eureka Client peer2的application.yml:
spring:
profiles: peer2
eureka:
instance:
hostname: peer2
client:
serviceUrl:
defaultZone: http://peer1/eureka/,http://peer3/eureka/ Eureka Client peer3的application.yml:
spring:
profiles: peer3
eureka:
instance:
hostname: peer3
client:
serviceUrl:
defaultZone: http://peer1/eureka/,http://peer2/eureka/ 此时,只要服务提供者(消费者)注册到其中一个Eureka Client中都会同步到其他两个
三、Ribbon
1、简介:Ribbon是一个客户端负载均衡器,它可以很好地控制HTTP和TCP客户端的行为
2、Ribbon工作原理:
1)、选择Eureka Server,优先选择同个Zone且负载少的Server;
2)、根据用户指定的策略(轮循、随机、根据反应时间加权等),在从 Eureka Server拉取的服务注册列表中选择一个地址命中
3、集成Ribbon:
1)、添加依赖(如果已经添加了spring-cloud-starter-eureka,因为eureka依赖已经集成Ribbon所以此步可省略);
2)、添加注解:
@SpringBootApplication
@EnableEurekaClient
public class ConsumerMovieRibbonApplication {
@Bean
@LoadBalanced //是RestTemplate具备Ribbon负载均衡能力
public RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(ConsumerMovieRibbonApplication.class, args);
}
} 3)、服务接口@RestController
public class MovieController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/movie/{id}")
public User findById(@PathVariable Long id) {
return this.restTemplate.getForObject("http://microservice-provider-user/simple/" + id, User.class);
}
} 4、使用注解自定义配置Ribbon
1)、使用@RibbonClient声明其他配置
@Configuration
@ExcludeFromComponentScan
public class TestConfiguration {
//自定义Ribbon策略
@Bean
public IRule ribbonRule() {
return new RandomRule();
}
} 注意:TestConfiguration必须添加@Configuration注解,且不在主应用程序上下文的@ComponentScan的扫描范围中(及不在启动类所在的包及其子包
中),否则将由所有@RibbonClients共享
2)、编写启动类
@SpringBootApplication
@EnableEurekaClient
//name:一定要是Eureka定义的应用名称
@RibbonClient(name = "microservice-provider-user", configuration = TestConfiguration.class)
@ComponentScan(excludeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION, value = ExcludeFromComponentScan.class) })
public class ConsumerMovieRibbonApplication {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(ConsumerMovieRibbonApplication.class, args);
}
} 3)、测试:
@RestController
public class MovieController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private LoadBalancerClient loadBalancerClient;
@GetMapping("/movie/{id}")
public User findById(@PathVariable Long id) {
return this.restTemplate.getForObject("http://microservice-provider-user/simple/" + id, User.class);
}
@GetMapping("/test")
public void test() {
ServiceInstance serviceInstance = this.loadBalancerClient.choose("microservice-provider-user");
System.out.println("1" + ":" + serviceInstance.getServiceId() + ":" + serviceInstance.getHost());
ServiceInstance serviceInstance2 = this.loadBalancerClient.choose("microservice-provider-user2");
System.out.println("2" + ":" + serviceInstance2.getServiceId() + ":" + serviceInstance2.getHost());
}
} 5、使用配置文件自定义配置Ribbon
从版本1.2.0开始,Spring Cloud Netflix支持使用属性与Ribbon文档兼容来自定义Ribbon客户端,以<clientName>.ribbon.为前缀,支持一下属性:
1)、NFLoadBalancerClassName:应实施ILoadBalancer
2)、NFLoadBalancerRuleClassName:应实施IRule
3)、NFLoadBalancerPingClassName:应实施IPing
4)、NIWSServerListClassName:应实施ServerList
5)、NIWSServerListFilterClassName应实施ServerListFilter
在这些属性中定义的类优先于使用@RibbonClient(configuration=MyRibbonConfig.class)定义的bean和由Spring Cloud Netflix提供的默认值。
自定义Ribbon:
服务消费者application.yml:
users(Eureka中定义服务提供者应用名称):
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule
6、使用Ribbon时禁用Eureka
1)、方式一:application.yml中对客户端URL进行硬编码
stores(Eureka中定义的客户端应用名称):
ribbon:
listOfServers: localhost:7901 2)、方式二:application.yml中禁用Eureka
ribbon:
eureka:
enabled: false 7、Ribbon源码分析:
1)、参考链接:http://blog.csdn.net/forezp/article/details/74820899
Ribbon的负载均衡,主要通过LoadBalancerClient来实现的,而LoadBalancerClient具体交给了ILoadBalancer来处理,ILoadBalancer配置IClientConfig、
IRule、IPing等信息,其子类BaseLoadBalancer默认配置了一下配置:
1)、IClientConfig:用于对客户端或者负载均衡的配置,默认实现类DefaultClientConfigImpl
2)、IRule:用于复杂均衡的策略,默认路由策略RoundRobinRule(轮循),其它实现类有
BestAvailableRule(选择最小请求数)
ClientConfigEnabledRoundRobinRule(轮询)
RandomRule(随机)
RetryRule(根据轮询的方式重试)
WeightedResponseTimeRule(根据响应时间去分配一个weight,weight越低,被选择的可能性越低)
ZoneAvoidanceRule(根据server的zone区域和可用性轮询选择)
3)、IPing:判断该server是否有响应,从而判断该server是否可用,提供了isAlive( )方法,实现类有:
PingUrl:ping某个url,判断其是否alive
PingConstant:固定返回某服务是否可用,默认返回true,即可用
NoOpPing:不去ping,直接返回true
DummyPing(默认):直接返回true,并实现了initWithNiwsConfig方法
NIWSDiscoveryPing:根据DiscoveryEnabledServer的InstanceInfo的InstanceStatus去判断,为InstanceStatus.UP,则可用,否则不可用
4)、ServerList是定义获取所有的server的注册列表信息的接口,默认实现(ConfigurationBasedServerList)
5)、ServerListFilter:定于可根据配置去过滤或者根据特性动态获取符合条件的server列表的方法,默认实现ZonePreferenceServerListFilter
,LoadBalancerClient在初始化的时候,会向Eureka获取服务注册列表,并以10s一次向EurekaClient发送“ping”(在BaseLoadBalancer构造函数中开启了一个
PingTask任务),来判断服务的可用性,进而检查是否更新服务列表,最后,得到注册列表后,ILoadBalancer根据IRule的策略进行负载均衡;而RestTemplate
被@LoadBalance注解后具有负载均衡能力是因为:
LoadBalancer自动配置类(LoadBalancerAutoConfiguration)维护了一个被@LoadBalanced修饰的RestTemplate对象的List在初始化的过程中,通过调
用customizer.customize(restTemplate)方法来给RestTemplate增加拦截器LoadBalancerInterceptor,而LoadBalancerInterceptor,用于实时拦截,在
LoadBalancerInterceptor这里实现来负载均衡;
2)、参考链接:https://www.jianshu.com/p/19bcd9acf559
RibbonAutoConfiguration:Spring Boot启动Rabbion的入口配置类:
public class RibbonAutoConfiguration {
...
@Bean
public SpringClientFactory springClientFactory() {
SpringClientFactory factory = new SpringClientFactory();
factory.setConfigurations(this.configurations); return factory;}
...
}
SpringClientFactory:
查看方法列表,此类提供了获取指定serviceId的IClient, ILoadBalance, ILoadBalanceContext等对象的重要方法。
IClient:发起请求并执行的接口;
ILoadBalanceContext:封装了一些负载均衡额外的方法,比如noteOpenConnection(), noteError(), noteResponse()等等;
ILoadBalance:负载均衡;
public <T> Map<String, T> getInstances(String name, Class<T> type) {
AnnotationConfigApplicationContext context = getContext(name);
if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context, type).length > 0) {
return BeanFactoryUtils.beansOfTypeIncludingAncestors(context, type);
}
return null;
}
查看获取LoadBalance实例的方法,发现其最终从父类NamedContextFactory获取Bean。Bean来源AnnotationConfigApplicationContext context
这个Context从那里获取我想要的bean实例呢?
看看context的创建过程,红圈一处,它会尝试为每个serviceId使用属于自己的配置类(当然前提是你有对应的配置),并注册;红圈二说明,它会注
册一个默认的配置类this.defaultConfigType
this.defaultConfigType = RibbonClientConfiguration.class;
context.register(PropertyPlaceholderAutoConfiguration.class, this.defaultConfigType);
至此,一个关键的配置类被发现了:RibbonClientConfiguration;
RibbonClientConfiguration:
查看方法列表,我们可以为每个serviceId指定自己的配置类(即上面的自定义Ribbon)
@Configuration
@RibbonClient(name = "foo", configuration = FooConfiguration.class)
public class TestConfiguration {
}
@Configuration
public class FooConfiguration {
@Bean
public IPing ribbonPing(IClientConfig config) {
return new PingUrl();
}
@Bean
public IRule ribbonRule(IClientConfig config) {
return new AvailabilityFilteringRule();
}
}
四、Feign
1、简介:
Feign是一种声明式、模板化的HTTP客户端。在Spring Cloud Netflix栈中,各个微服务都是以HTTP接口的形式暴露自身服务的,因此在调用远程服务时就必
须使用HTTP客户端,使用Feign, 我们可以做到使用HTTP请求远程服务时能与调用本地方法一样的编码体验
2、使用Feign
1)、添加maven依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
2)、声明一个接口,添加@FeignClient注解
@FeignClient("microservice-provider-user")//绑定该接口对应服务提供者的服务
public interface UserFeignClient {
@RequestMapping(value = "/simple/{id}", method = RequestMethod.GET)//不支持@GetMapping
public User findById(@PathVariable("id") Long id); //@PathVariable必须设置value
@RequestMapping(value = "/user", method = RequestMethod.POST)
public User postUser(@RequestBody User user);
//该请求会报错,参数是复杂对象,即使指定了是GET方法,feign依然会以POST方法进行发送请求
@RequestMapping(value = "/get-user", method = RequestMethod.GET)
public User getUser(User user);
}
3)、启动类添加@EnableFeignClients注解
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class ConsumerMovieFeignApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerMovieFeignApplication.class, args);
}
}
4)、业务逻辑类直接调用接口方法
@RestController
public class MovieController {
@Autowired
private UserFeignClient userFeignClient;
@GetMapping("/movie/{id}")
public User findById(@PathVariable Long id) {
return this.userFeignClient.findById(id);
}
@GetMapping("/test")
public User testPost(User user) {
return this.userFeignClient.postUser(user);
}
@GetMapping("/test-get")
public User testGet(User user) {
return this.userFeignClient.getUser(user);
}
}
2、自定义Feign配置:修改Feign默认配置
1)、@FeignClient注解的configuration指定自定义配置类(同样不要被@ComponentScan扫描到)
@FeignClient(name = "microservice-provider-user", configuration = Configuration1.class)
public interface UserFeignClient {
//自定义配置请求路径注解要使用@RequestLine (Get 请求路径)
@RequestLine("GET /simple/{id}")
public User findById(@Param("id") Long id);
}
2)、自定义配置类
@Configuration
public class Configuration1 {
@Bean
public Contract feignContract() {
return new feign.Contract.Default();
}
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
3、自定义Feign配置:访问Eureka,服务调用的安全验证
1)、@FeignClient注解的configuration指定自定义配置类(同样不要被@ComponentScan扫描到)
//定义了请求url时需要添加一个name
@FeignClient(name = "xxxx", url = "http://localhost:8761/", configuration = Configuration2.class)
public interface FeignClient2 {
@RequestMapping(value = "/eureka/apps/{serviceName}")
public String findServiceInfoFromEurekaByServiceName(@PathVariable("serviceName") String serviceName);
}
2)、自定义配置类
@Configuration
public class Configuration2 {
//Eureka验证
@Bean
public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
return new BasicAuthRequestInterceptor("user", "password123");
}
}
4、手动创建Feign Client
在某些情况下,可能以上述方法不可能自定义您的Feign客户端。在这种情况下,可以使用Feign Builder API创建客户端 。下面是一个创建两个具有相同接口的
Feign客户端的示例,但是使用单独的请求拦截器配置每个客户端
@Import(FeignClientsConfiguration.class)//FeignClientsConfiguration.class是Spring Cloud Netflix提供的默认配置
class FooController {
private FooClient fooClient;
private FooClient adminClient;
@Autowired
public FooController(Decoder decoder, Encoder encoder, Client client) {
this.fooClient = Feign.builder().client(client)
.encoder(encoder)
.decoder(decoder)
.requestInterceptor(new BasicAuthRequestInterceptor("user", "user"))
.target(FooClient.class, "http://PROD-SVC");
this.adminClient = Feign.builder().client(client)
.encoder(encoder)
.decoder(decoder)
.requestInterceptor(new BasicAuthRequestInterceptor("admin", "admin"))
.target(FooClient.class, "http://PROD-SVC");
}
}
5、配置文件配置Feign
feign:
client:
config:
feignClientName:
connectTimeout: 5000 # 相当于Request.Options
readTimeout: 5000 # 相当于Request.Options
# 配置Feign的日志级别,相当于代码配置方式中的Logger
loggerLevel: full
# Feign的错误解码器,相当于代码配置方式中的ErrorDecoder
errorDecoder: com.example.SimpleErrorDecoder
# 配置重试,相当于代码配置方式中的Retryer
retryer: com.example.SimpleRetryer
# 配置拦截器,相当于代码配置方式中的RequestInterceptor
requestInterceptors:
- com.example.FooRequestInterceptor
- com.example.BarRequestInterceptor
decode404: false
6、Feign请求和响应的压缩
feign.compression.request.enabled=true
feign.compression.request.mime-types=text/xml,application/xml,application/json
feign.compression.request.min-request-size=2048
feign.compression.response.enabled=true
7、Feign的日志
1)、application.yml中为每个Feign Client创建日志,默认只响应Debug
logging.level.project.user.UserClient: DEBUG
2)、自定义配置类中声明日志级别
@Configuration
public class FooConfiguration {
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
8、Feign的HTTP Client
Feign在默认使用JDK原生的URLConnection发送HTTP请求,没有连接池,但是对每个地址会保持一个长连接,即利用HTTP的persistence connection 。Spring
Cloud从Brixtion.SR5版本开始支持用Apache的HTTP Client替换Feign原始的http client, 从而获取连接池、超时时间等与性能息息相关的控制能力:
1)、项目中声明Apache HTTP Client和feign-httpclient依赖:
<!-- 使用Apache HttpClient替换Feign原生httpclient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<dependency>
<groupId>com.netflix.feign</groupId>
<artifactId>feign-httpclient</artifactId>
<version>${feign-httpclient}</version>
</dependency>
2)、application.properties
feign.httpclient.enabled=true
9、服务调用的复杂权限认证
上面服务调用的安全验证中服务之间可以通过自定义配置完成基于Http Basic的认证,但是不能满足根据不同的角色不同的用户执行不同的操作,即服务之间的调用
有更复杂的权限要求,此时需对Feign做进一步修改:
1)、服务提供者添加spring-security依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
2)、服务提供者关键权限配置
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomUserDetailService userDetailService;
@Bean
public PasswordEncoder passwordEncoder(){
return NoOpPasswordEncoder.getInstance();
}
@Override
protected void configure(HttpSecurity http) throws Exception{
http.authorizeRequests().anyRequest().authenticated().and().httpBasic();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception{
auth.userDetailsService(this.userDetailService).passwordEncoder(this.passwordEncoder());
}
@Component
class CustomUserDetailService implements UserDetailsService{
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException{
if("user".equals(username)){
return new SecurityUser("user","123456","user-role");
}else if("admin".equals(username)){
return new SecurityUser("admin","123456","admin-role");
}else{
return null;
}
}
}
class SecurityUser implements UserDetails{
private static final long serialVersionUID=1L;
public SecurityUser(String username,String password,String role){
super();
this.username=username;
this.password=password;
this.role=role;
}
public SecurityUser(){
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> authorities=new ArrayList<>();
SimpleGrantedAuthority authority=new SimpleGrantedAuthority(this.role);
authorities.add(authority);
return authorities;
}
private Long id;
private String username;
private String password;
private String role;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
}
}
启动项后,可以见到页面需要输入用户名和密码才能访问
3)、服务消费者接口去掉@feignClient注解
//@FeignClient(value="hello-auth")
public interface HelloAuthService {
@GetMapping("/{id}")
User findById(@PathVariable("id") Long id);
}
4)、服务消费者启动类去掉@EnableFeignClients注解
5)、服务消费者业务逻辑类Controller
@Import(FeignClientsConfiguration.class)
@RestController
public class HelloAuthFeignController {
private HelloAuthService userAuthService;
private HelloAuthService adminAuthService;
@Autowired
public HelloAuthFeignController(Decoder decoder, Encoder encoder, Client client, Contract contract){
this.userAuthService= Feign.builder().client(client).encoder(encoder).decoder(decoder).contract(contract)
.requestInterceptor(new BasicAuthRequestInterceptor("user","123456"))
.target(HelloAuthService.class,"http://hello-auth/");
this.adminAuthService= Feign.builder().client(client).encoder(encoder).decoder(decoder).contract(contract)
.requestInterceptor(new BasicAuthRequestInterceptor("admin","123456"))
.target(HelloAuthService.class,"http://hello-auth/");
}
@GetMapping("/user/{id}")
public User findByIdUser(@PathVariable Long id){
return this.userAuthService.findById(id);
}
@GetMapping("/admin/{id}")
public User findByIdAdmin(@PathVariable Long id){
return this.adminAuthService.findById(id);
}
}
五、Hystrix
1、Hystrix:是一个帮助解决分布式系统交互时超时处理和容错的类库,通过服务熔断(也可以称为断路)、降级、限流(隔离)、异步RPC等手段控制依赖服务的延迟与失败,
当对特定服务的呼叫达到一定阈值时(默认为5秒内故障20次),短路器打开,不进行通话
2、服务雪崩效应:
1)、简介:较低级别的服务出现服务故障可能导致用户级联故障;
2)、应对策略:
a)、流量控制:
网关限流:因为Nginx的高性能, 目前一线互联网公司大量采用Nginx+Lua的网关进行流量控制, 由此而来的OpenResty也越来越热门;
用户交互限流:采用加载动画,提高用户的忍耐等待时间、提交按钮添加强制等待时间机制;
关闭重试
b)、改进缓存模式
缓存预加载
同步改为异步刷新
c)、服务自动扩容:AWS的auto scaling
e)、服务调用者降级服务
资源隔离:主要是对调用服务的线程池进行隔离
对依赖服务进行分类: 强依赖服务(不可用时会导致当前业务中止)和弱依赖服务(不可用不会导致当前业务的中止)
f)、不可用服务的调用快速失败:一般通过超时机制、熔断器、熔断后的降级(fallback)来实现
3、使用Hystrix
1)、添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
2)、启动类添加@EnableCircuitBreaker注解
3)、服务调用发放添加@HystrixCommand注解,编写fallback方法
@GetMapping("/movie/{id}")
@HystrixCommand(fallbackMethod = "findByIdFallback")
public User findById(@PathVariable Long id) {
return this.restTemplate.getForObject("http://microservice-provider-user/simple/" + id, User.class);
}
//参数、返回值要跟调用服务方法相同
public User findByIdFallback(Long id) {
User user = new User();
user.setId(0L);
return user;
}
4、传播安全上下文或者使用Spring Scope
1)、当程序报一个RuntimeException(it can’t find the scoped context)时,若想将线程上下文传播到@HystrixCommand中,默认声明将不起作用,因为它在一个线程池
中执行命令(除非超时),可以使用某些配置或者在注解中使用不用的隔离策略使Hystrix跟调用服务者使用相同的线程
2)、代码实现:
@HystrixCommand(fallbackMethod = "stubMyService",
commandProperties ={@HystrixProperty(name="execution.isolation.strategy", value="SEMAPHORE")}
)
public User findById(@PathVariable Long id) {
return this.restTemplate.getForObject("http://microservice-provider-user/simple/" + id, User.class);
}
3)、两种隔离策略
a)、THREAD:在一个隔离的线程中执行,并发请求受到线程池中线程的数量限制;
b)、SEMAPHORE:在调用服务的线程中执行,并发请求受到信号量计数限制;
5、健康指标
连接断路器的状态也暴露在服务消费者应用的/health端点中(应用上下文/health)
{
"hystrix": {
"openCircuitBreakers": [
"StoreIntegration::getStoresByLocationLink"
],
"status": "CIRCUIT_OPEN"
},
"status": "UP"
}
注意:使用Hystrix指标流需要引入spring-boot-starter-actuator依赖
6、Hystrix对Feign的支持
1)、FeignClient实现类中注解添加fallback属性
@FeignClient(name = "microservice-provider-user", fallback = HystrixClientFallback.class)
public interface UserFeignClient {
@RequestMapping(value = "/simple/{id}", method = RequestMethod.GET)
public User findById(@PathVariable("id") Long id);
}
2)、编写一个实现FeignClient的实现类,重写实现类方法
@Component//需要添加这个注解,否则会报找不到实例
public class HystrixClientFallback implements UserFeignClient {
@Override
public User findById(Long id) {
User user = new User();
user.setId(0L);
return user;
}
}
如果需要访问导致回退触发的原因,可以使用@FeignClient内的fallbackFactory(此时不能写fallback属性)属性:
1)、编写实现了UserFeignClient接口的UserFeignWithFactory
public interface UserFeignClientWithFactory extends UserFeignClient {
}
2)、编写实现了FallbackFactory的HystrixClientFallbackFactory
@Component
public class HystrixClientFactory implements FallbackFactory<UserFeignClient> {
private static final Logger LOGGER = LoggerFactory.getLogger(HystrixClientFactory.class);
@Override
public UserFeignClient create(Throwable cause) {
HystrixClientFactory.LOGGER.info("fallback; reason was: {}", cause.getMessage());
return new UserFeignClientWithFactory() {
@Override
public User findById(Long id) {
User user = new User();
user.setId(-1L);
return user;
}
};
}
}
3)、UserFeignClient注解中添加fallbackFactory属性
@FeignClient(name = "microservice-provider-user", /*fallback = HystrixClientFallback.class, */fallbackFactory = HystrixClientFactory.class)
public interface UserFeignClient {
@RequestMapping(value = "/simple/{id}", method = RequestMethod.GET)
public User findById(@PathVariable("id") Long id);
}
6、禁用单个FeignClient 对 Hystrix的支持
1)、在FeignClient的配置类中重新为FeignClient指定Feign.Builder
@Configuration
public class Configuration2 {
@Bean
public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
return new BasicAuthRequestInterceptor("user", "password123");
}
@Bean
@Scope("prototype")
public Feign.Builder feignBuilder() {
return Feign.builder();
}
}
原理:Spring Cloud Netflix默认为feign提供的bean里面的Feign.Builder feignBuiler 默认为支持了Hystrix的HystrixFeign.Builder ,重新指定不支持Hystrix的Builder
2)、想禁用Hystrix的FeignClient注解中引用禁用了Hystrix的配置类
@FeignClient(name = "xxxx", url = "http://localhost:8761/", configuration = Configuration2.class, fallback = FeignClient2Fallback.class)
public interface FeignClient2 {
@RequestMapping(value = "/eureka/apps/{serviceName}")
public String findServiceInfoFromEurekaByServiceName(@PathVariable("serviceName") String serviceName);
}
7、禁用所有FeignClient 对 Hystrix的支持
application.yml中添加属性:feign.hystrix.enabled=false
六、Zuul
1、简介
Zuul是基于JVM的路由器和服务器端负载均衡器,是开源的API Gateway 服务器, 本质上是一个web servlet应用,在云平台上提供动态路由,监控,弹性,安全等
边缘服务的框架,zuul默认的Hystrix 隔离策略是SEMAPHORE(信号量),可以通过zuul.ribbonIsolationStrategy修改
2、使用zuul
1)、添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
2)、启动类添加@EnableZuulProxy注解
@SpringBootApplication
//组合注解,集成了@EnableCricuitBreaker、@EnableDiscoveryClient
@EnableZuulProxy
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
}
3)、访问Zuul的zuulContext:zuulPort/EurekaApplicationName/simple/1,Zuul将该访问路径映射到User微服务的访问路径userContext:userPort/simple/1
3、嵌入式Zuul反向代理
1)、ignoredServices:默认zuul会方向代理Eureka上注册的所有服务,忽略某些服务,是列表中间用逗号隔开(*表示忽略所有)
zuul:
ignoredServices: '*'
routes:
users: /myusers/**
2)、path、serviceId:指定对path路径方向代理到Eureka中serviceId微服务,进行更细粒度的控制
zuul:
routes:
users:
path: /myusers/** // **表示匹配多层路径 *表示匹配单层路径
serviceId: users_service
3)、path、url:指定对path路径方向代理到物理路径,进行更细粒度的控制
zuul:
routes:
users:
path: /myusers/**
url: http://example.com/users_service 这样直接配置url不会作为HystrixCommand跟Riboon负载均衡,如果想实现,需对serviceId特殊配置
zuul:
routes:
users:
path: /myusers/**
serviceId: users
ribbon:
eureka:
enabled: false //需要在Ribbon中禁用Eureka支持
users: //对应上面的serviceId
ribbon:
listOfServers: http://localhost:7900,http://localhost:7900 4)、正则表达式指定路由规则
@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
@Bean
public PatternServiceRouteMapper serviceRouteMapper() {
//将serviceId“myusers-v1”代理为路由“/v1/myusers”,如果匹配不上则不起作用
return new PatternServiceRouteMapper("(?<name>^.+)-(?<version>v.+$)", "${version}/${name}");
}
}
5)、prefix:前缀为所有映射添加前缀,配置后需要在访问路径添加前缀值
zuul:
prefix: myService //访问路径 zuulContext:zuulPort/myService/simple/1
6)、stripPrefix:默认情况下,请求被转发之前,代理前缀被删除(可通过zuul.stripPrefix=false关闭)
7)、ignoredPatterns:忽略某些特殊的路径
zuul:
ignoredPatterns: /**/admin/**
routes:
users: /myusers/**
8)、legacy:代理除了指定外的所有路径
zuul:
routes:
users:
path: /myusers/**
legacy:
path: /**
4、Zuul Http 客户端
zuul默认使用Apache Http Client支持的HttpClient,使用ribbon.restclient.enabled=true、ribbon.okhttp.enabled=true指定RestClient、OkHttpClient
5、Cookies与敏感标题
zuul:
routes:
users:
path: /myusers/**
sensitiveHeaders: Cookie,Set-Cookie,Authorization //黑名单,不传播到下游服务
url: https://downstream 6、忽略Headers
zuul:
ignoredHeaders : header1,header2
7、 Routes Endpoint
通过访问路径/routes返回路由列表,GET将返回映射路由的列表,POST将强制刷新现有路由
8、通过Zull上传文件
使用zuul代理路径zuul.routes.customers=/customers/**可以将文件上传到“/ zuul / customers / *,对于大文件,添加“/zuul/*”前缀可以绕过Spring
DispatcherServlet,因此不用设置上传文件大小设置,上传大文件需要设置超时时间
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 60000
ribbon:
ConnectTimeout: 3000
ReadTimeout: 60000
9、为route提供Hystrix 回退
当Zuul中断路器路由的电路跳闸时,可以通过创建类型为ZuulFallbackProvider的bean来提供回退响应。在这个bean中,需要指定回退的路由ID,并提供返回的
ClientHttpResponse作为后备
class MyFallbackProvider implements ZuulFallbackProvider {
@Override
public String getRoute() {
//如果要为所有的route提供默认fallback,这里返回“*”
return "customers";
}
@Override
public ClientHttpResponse fallbackResponse() {
return new ClientHttpResponse() {
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.OK;
}
@Override
public int getRawStatusCode() throws IOException {
return 200;
}
@Override
public String getStatusText() throws IOException {
return "OK";
}
@Override
public void close() {
}
@Override
public InputStream getBody() throws IOException {
return new ByteArrayInputStream("fallback".getBytes());
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
};
}
}
10、sidecar整合异构微服务
sidecar是用来整合异构语言的,sidecar这个服务必须和python或nodejs等异构语言的服务在同一台主机上面,就是说他们之间是localhost,不能是ip访问等
等,如果不在同一主机时需要配置${eureka.instance.hostname}
1)、添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-sidecar</artifactId>
</dependency>
2)、启动类添加@EnableSidecar注解
@SpringBootApplication
//组合注解,集成了@EnableCricuitBreaker、@EnableDiscoveryClient/@EnableZuulProxy
@EnableSidecar
public class SidecarApplication {
public static void main(String[] args) {
SpringApplication.run(SidecarApplication.class, args);
}
}
3)、application.yml
server:
port: 5678
spring:
application:
name: sidecar
sidecar:
port: 8060 //异构微服务的端口
health-uri: http://localhost:8060/health.json sidecar检查异构语言的健康地址返回结果是:
要注意,这个json结果不是是我们直接写出来的,好几次都是为了方便直接在return里面写的数据,如果这样启动sidecar时,会发现注册中心sidecar一直是 {
"status":"UP" //这是异构微服务里面的健康检查,服务发现会将这个值负责到微服务发现中
} down,那是因为response header没有改过来,所以可以把数据发到map里面,如用json包转化成json数据,其他的方式也是可以的。其实原理是response
header里面解析的text/html,我们应该改成application/json,如果真的想直接写一个json数据,那就要改下这个接口的response header
七、spring cloud config
1、简介
在分布式系统中,为了方便微服务配置文件的统一管理、实时更新,需要分布式配置中心组件。分布式配置中心组件spring cloud config 支持配置服务放在配置服
务的内存中(即本地),也支持放在远程Git仓库中。在spring cloud config 组件中,分两个角色,一是config server,二是config client
2、编写Config Server
1)、添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency> 2)、启动类添加@EnableConfigServer
@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
} 3)、application.yml配置文件
server:
port: 8080
spring:
cloud:
config:
server:
git:
uri: https://git.oschina.net/it-much/config-repo-51cto-video //获取配置文件的路径 4)、Config Server映射关系有以下几种,浏览器可以通过映射关系访问获取配置文件中的属性,当映射关系匹配不上时回退到application.yml
/{application}/{profile}[/{label}] label指的是git标签(默认事master)
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties
3、编写 Config Client
1)、添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency> 2)、Git新建配置文件application.yml
server:
port: 8081 3)、Git新建配置文件bootstrap.yml
springcloud中有个启动上下文(用来加载config server的远程配置),启动时bootstrap.yml先加载,然后链接ConfigServer加载远程配置,最后加载
application.yml中的配置,所以需要添加bootstrap.yml,不然会报错Could not resolve placeholder "profile" in String ${profile}
spring:
cloud:
config:
uri: http://localhost:8080 profile: dev
label: master # 当configserver的后端存储是Git时,默认就是master
application:
name: foobar 4)、ConfigServer编写启动类 @SpringBootApplication
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
} 5)、Config client 业务逻辑类方法连接Config Server获取远程配置文件@RestController
public class ConfigClientController {
@Value("${profile}")
private String profile;
@GetMapping("/profile")
public String getProfile() {
return this.profile;
}
} 6)、当远程配置文件与本地配置文件存在冲突时,以远程配置文件为主,spring cloud建议bootstrap.yml中配置不修改的配置项
4、Git后端环境库
1)、URL中的占位符
Spring Cloud Config服务器支持一个Git仓库URL,其中包含{application}和{profile}(以及{label})的占位符
spring:
cloud:
config:
server:
git:
uri: https://github.com/myorg/{application} //匹配appName,使得git仓库对应一个微服务 2)、模式匹配
3)、
4)、
5)、
6)、
1)、
5、
6、
7、
8、
测试
相关文章推荐
- spring cloud相关知识汇总
- Spring基础知识汇总
- Java中的面试题 [4] --- Java进阶高级知识、微服务、SpringBoot、springcloud、系统架构、集群等
- java开发必看|Spring基础知识汇总
- Spring基础知识汇总
- Spring基础知识汇总 Java开发必看
- Spring基础知识汇总
- Spring Cloud搭建微服务架构----文章汇总
- Spring基础知识汇总
- Spring干货汇总(含Spring Boot与Spring Cloud)
- Spring基础知识汇总
- 二、Spring知识汇总
- Spring干货汇总(含Spring Boot与Spring Cloud)
- Spring Cloud微服务实战(1)—基础知识
- spring 集成 spring cloud config 的相关知识
- Spring基础知识汇总
- Spring基础知识汇总
- Spring干货汇总(含Spring Boot与Spring Cloud)
- Spring 基础知识汇总
- 第一章 Spring Cloud基础知识