您的位置:首页 > 编程语言 > Java开发

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、

测试

        
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: