簡單spring cloud

微服務(wù)有哪些優(yōu)勢?

1.易于開發(fā)和維護(hù)
2.可以全自動部署
3.局部修改容易部署
4.技術(shù)棧不受限

微服務(wù)有哪些挑戰(zhàn)?

1.運(yùn)維成本比較高
2.分布式固有的復(fù)雜性
3.接口調(diào)整成本高

微服務(wù)設(shè)計(jì)原則?

1.單一職責(zé)原則
2.服務(wù)自治原則
3.輕量級通信原則
4.接口明確原則

常用的微服務(wù)有哪些,spring cloud作為其中一個(gè)微服務(wù)有哪些優(yōu)勢?

常見的微服務(wù)有spring cloud、dubbo等。它們的區(qū)別很多,比如說調(diào)用方式,spring cloud采用REST API方式,dubbo采用RPC;spring cloud幾乎涉及到分布式組件各個(gè)方面,而dubbo僅僅實(shí)現(xiàn)了其中的服務(wù)治理模塊。spring cloud優(yōu)勢在于它豐富的組件以及分布式解決方案集成springboot十分的方便快捷。

下面來談?wù)剆pringcloud豐富的組件和模塊,下面只給出使用說明,概念性的說明省略。

1.服務(wù)發(fā)現(xiàn)組件
Eureka

application.yml如下配置
security:
  basic:
    enabled: true
  user:
    name: user
    password: password123
server:
  port: 8761
eureka:
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
      defaultZone: http://user:password123@localhost:8761/eureka

EurekaApplication.java啟動類
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
  public static void main(String[] args) {
    SpringApplication.run(EurekaApplication.class, args);
  }
}

2.服務(wù)注冊(消費(fèi)者和服務(wù)者都會注冊服務(wù))

application.yml如下配置
eureka:
  client:
    healthcheck:        #健康檢查 配合actuator
      enabled: true
    serviceUrl:
      defaultZone: http://user:password123@localhost:8761/eureka #權(quán)限驗(yàn)證
  instance:
    prefer-ip-address: true #服務(wù)可以ip調(diào)用
    instance-id: ${spring.application.name}:${spring.cloud.client.ipAddress}:${spring.application.instance_id:${server.port}}   

PS:eureka會將所有注冊的服務(wù)按照application.name分組,availability zones標(biāo)識集群節(jié)點(diǎn)數(shù),status會顯示各個(gè)實(shí)例狀態(tài)(up表示可用),實(shí)例id可以用eureka.instance.instance-id來指定
3.服務(wù)消費(fèi)ribbon

application.yml如何配置
eureka:
  client:
    healthcheck:
      enabled: true
    serviceUrl:
      defaultZone: http://user:password123@localhost:8761/eureka

ConsumerMovieRibbonApplicatio.java 啟動類

@SpringBootApplication
@EnableEurekaClient   
@RibbonClient(name = "microservice-provider-user", configuration = TestConfiguration.class) //允許自定義ribbonclient
@ComponentScan(excludeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION, value = ExcludeFromComponentScan.class) }) //保留默認(rèn)的ribboclient  
public class ConsumerMovieRibbonApplication {

  @Bean
  @LoadBalanced
  public RestTemplate restTemplate() {
    return new RestTemplate();
  }

  public static void main(String[] args) {
    SpringApplication.run(ConsumerMovieRibbonApplication.class, args);
  }
}
@Configuration
@ExcludeFromComponentScan
public class TestConfiguration {
  //  @Autowired
  //  IClientConfig config;

  @Bean
  public IRule ribbonRule() {
    return new RandomRule();
  }
}
//注意這邊使用virtual hostname(provider applicationName),ribbon會根據(jù)virtual hostname找到對應(yīng)幾個(gè)服務(wù),然后根據(jù)負(fù)載均衡算法選擇一個(gè)服務(wù)
this.restTemplate.getForObject("http://microservice-provider-user/simple/" + id, User.class);

PS:自定義負(fù)載均衡算法:默認(rèn)是round,還有random、高可用、響應(yīng)時(shí)間等

4.服務(wù)消費(fèi)feign 聲明式httpclient

ConsumerMovieFeignApplication.啟動類
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class ConsumerMovieFeignApplication {
  public static void main(String[] args) {
    SpringApplication.run(ConsumerMovieFeignApplication.class, args);
  }
}
新建服務(wù)調(diào)用方一致的方法,方法上注解基本同springmvc一致
@FeignClient("microservice-provider-user") //對應(yīng)provider serviceId
public interface UserFeignClient {
  @RequestMapping(value = "/simple/{id}", method = RequestMethod.GET)
  public User findById(@PathVariable("id") Long id); // 坑:1. @GetMapping不支持   2. @PathVariable得設(shè)置value 3.參數(shù)如果是復(fù)雜對象時(shí),即使指定GET方法也失效
  @RequestMapping(value = "/user", method = RequestMethod.POST)
  public User postUser(@RequestBody User user);

  // 該請求不會成功,只要參數(shù)是復(fù)雜對象,即使指定了是GET方法,feign依然會以POST方法進(jìn)行發(fā)送請求??赡苁俏覜]找到相應(yīng)的注解或使用方法錯(cuò)誤
  @RequestMapping(value = "/get-user", method = RequestMethod.GET)
  public User getUser(User user);
}

5.eureka實(shí)現(xiàn)高可用

eureka配置
spring:
  application:
    name: EUREKA-HA
---
server:
  port: 8761
spring:
  profiles: peer1
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8762/eureka/,http://localhost:8763/eureka/
---
server:
  port: 8762
spring:
  profiles: peer2
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/,http://localhost:8763/eureka/
---
server:
  port: 8763
spring:
  profiles: peer3
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/

PS:實(shí)現(xiàn)原理就是交叉注冊,類似于主節(jié)點(diǎn)的備份,每個(gè)節(jié)點(diǎn)都在剩下的節(jié)點(diǎn)都有備份,從而實(shí)現(xiàn)集群

6.斷路器hystrix
流程說明:

  • ①.每次調(diào)用創(chuàng)建一個(gè)新的HystrixCommand,把依賴調(diào)用封裝在run()方法中.
  • ②.執(zhí)行execute()/queue做同步或異步調(diào)用.
  • ③.是否開啟請求緩存,如果開啟了,并且如果緩存中對請求的響應(yīng)可用,則此緩存響應(yīng)將立即以“Observable”的形式返回。
  • ④.判斷熔斷器(circuit-breaker)是否打開,如果打開跳到步驟8,進(jìn)行降級策略,如果關(guān)閉進(jìn)入步驟.
  • ⑤.判斷線程池/隊(duì)列/信號量是否跑滿,如果跑滿進(jìn)入降級步驟8,否則繼續(xù)后續(xù)步驟.
  • ⑥.調(diào)用HystrixCommand的run方法.運(yùn)行依賴邏輯
    • (1).依賴邏輯調(diào)用超時(shí),進(jìn)入步驟8.
  • ⑦.判斷邏輯是否調(diào)用成功
    • (1).返回成功調(diào)用結(jié)果
    • (2).調(diào)用出錯(cuò),進(jìn)入步驟8.
  • ⑧.計(jì)算熔斷器狀態(tài),所有的運(yùn)行狀態(tài)(成功, 失敗, 拒絕,超時(shí))上報(bào)給熔斷器,用于統(tǒng)計(jì)從而判斷熔斷器狀態(tài).
  • ⑨.getFallback()降級邏輯.
    • 1.以下四種情況將觸發(fā)getFallback調(diào)用:
      • (1):run()方法拋出非HystrixBadRequestException異常。
      • (2):run()方法調(diào)用超時(shí)
      • (3):熔斷器開啟攔截調(diào)用
      • (4):線程池/隊(duì)列/信號量是否跑滿
    • 2.沒有實(shí)現(xiàn)getFallback的Command將直接拋出異常
    • 3.fallback降級邏輯調(diào)用成功直接返回
    • 4.降級邏輯調(diào)用失敗拋出異常
  • ⑩.返回執(zhí)行成功結(jié)果

hystrix有兩個(gè)隔離策略,thread和semaphore,默認(rèn)是thread
thread 通過線程數(shù)量來限制并發(fā)請求數(shù),可以提供額外的保護(hù),但有一定的延遲,一般用于網(wǎng)絡(luò)調(diào)用
semaphore 通過semaphore count來限制并發(fā)請求數(shù),適用于無網(wǎng)絡(luò)的高并發(fā)請求
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds 命令執(zhí)行超時(shí)時(shí)間
hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests 最大并發(fā)請求數(shù),默認(rèn)10,該參數(shù)當(dāng)使用ExecutionIsolationStrategy.SEMAPHORE策略時(shí)才有效。如果達(dá)到最大并發(fā)請求數(shù),請求會被拒絕。
理論上選擇semaphore size的原則和選擇thread size一致,但選用semaphore時(shí)每次執(zhí)行的單元要比較小且執(zhí)行速度快(ms級別),否則的話應(yīng)該用thread。

@RestController
public class MovieController {
  @Autowired
  private RestTemplate restTemplate;

  @GetMapping("/movie/{id}")
  @HystrixCommand(fallbackMethod = "findByIdFallback", 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);
  }

  public User findByIdFallback(Long id) {
    User user = new User();
    user.setId(0L);
    return user;
  }
}
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class ConsumerMovieFeignApplication {
  public static void main(String[] args) {
    SpringApplication.run(ConsumerMovieFeignApplication.class, args);
  }
}

@FeignClient(name = "microservice-provider-user", fallback = HystrixClientFallback.class)
public interface UserFeignClient {
  @RequestLine("GET /simple/{id}")
  public User findById(@Param("id") Long id);
}

@Component
public class HystrixClientFallback implements UserFeignClient {

  @Override
  public User findById(Long id) {
    User user = new User();
    user.setId(0L);
    return user;
  }
}

明確問題1:進(jìn)入斷路器定義失敗方法情況
(1):run()方法拋出非HystrixBadRequestException異常。
(2):run()方法調(diào)用超時(shí)
(3):熔斷器開啟攔截調(diào)用
(4):線程池/隊(duì)列/信號量是否跑滿

明確問題2:進(jìn)入失敗方法不一定短路器短路開關(guān)打開。
短路器短路開關(guān)有一定的條件,要滿足再某個(gè)時(shí)間點(diǎn)失敗次數(shù)達(dá)到一定數(shù)量??梢酝ㄟ^查看/hearth 來查看斷路器狀態(tài)。
斷路器有三個(gè)狀態(tài),打開、關(guān)閉和半開。半開是指斷路器打開后過一段時(shí)間它會嘗試置為半開狀態(tài),即大部分請求還是短路,允許很少一部分流量請求,如果請求失敗了,則斷路器狀態(tài)則變成打開,如果請求成功了,則斷路器狀態(tài)變成關(guān)閉。

7.服務(wù)網(wǎng)關(guān)zuul
Zuul 在云平臺上提供動態(tài)路由,監(jiān)控,彈性,安全等邊緣服務(wù)的框架。

優(yōu)點(diǎn):

  • 易于監(jiān)控。可在微服務(wù)網(wǎng)關(guān)收集監(jiān)控?cái)?shù)據(jù)并將其推送到外部系統(tǒng)進(jìn)行分析。
  • 易于認(rèn)證。可在微服務(wù)網(wǎng)關(guān)上進(jìn)行認(rèn)證。然后再將請求轉(zhuǎn)發(fā)到后端的微服務(wù),而無須在每個(gè)微服務(wù)中進(jìn)行認(rèn)證。
  • 減少了客戶端與各個(gè)微服務(wù)之間的交互次數(shù)。

實(shí)現(xiàn)方式如下 ZuulApplication.java 入口類

@EnableZuulProxy
@SpringBootApplication
public class ZuulApplication {

  public static void main(String[] args) {
    SpringApplication.run(ZuulApplication.class, args);
  }
}

application.yml 配置類

zuul:
  prefix: /api
  routes:
    aaa:
      path: /user/**
      serviceId: microservice-provider-user
    legacy:
      path: /**
      serviceId: microservice-provider-user_old

PS:zuul.prefix表示所有走網(wǎng)關(guān)轉(zhuǎn)發(fā)的路由必須要有api前綴,zuul默認(rèn)支持serviceId/serviceName(除非設(shè)置zuul.ignoredServices=''),同時(shí)也可以設(shè)置path匹配規(guī)則,legacy表示當(dāng)所有路由規(guī)則都不匹配才生效*

zuul同樣支持?jǐn)嗦菲鞴δ?br> 配置一個(gè)FallbackProvider即可

@Component
public class MyFallbackProvider implements ZuulFallbackProvider {
    @Override
    public String getRoute() {
        return "microservice-provider-user";
    }

    @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 {
                String json = "{\"success\":false, \"msg\":\"error\"}";
                return new ByteArrayInputStream(json.getBytes());
            }

            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_JSON);
                return headers;
            }
        };
    }
}

8.spring cloud config 分布式配置中心
spring cloud config作為配置中心優(yōu)勢?

  • 集中管理
  • 不同環(huán)境不同配置
  • 運(yùn)行期間動態(tài)調(diào)整配置
  • 自動刷新

配置服務(wù)端 數(shù)據(jù)repo存放在git上,ConfigServer搭建如下
ConfigServerApplication 入口類

@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {
  public static void main(String[] args) {
    SpringApplication.run(ConfigServerApplication.class, args);
  }
}

application.yml配置類

spring:
  cloud:
    config:
      server:
        git:
          uri: https://gitee.com/habib/{application} #通配符,匹配git habib這個(gè)目錄下所有工程
server:
  port: 8080

配置客戶端 ConfigClient
bootstrap.yml

bootstrap.yml
spring:
  cloud:
    config:
      uri: http://localhost:8080 #config server訪問地址 如果config server配置登錄校驗(yàn),這邊可以這樣寫 http://user:password123@localhost:8080,或者username和password寫在下面
      profile: dev #支持多環(huán)境 即對應(yīng)application-dev.yml或者 application-dev.properties
      label: master #默認(rèn)就是master
  application:
    name: simple #對應(yīng)https://gitee.com/habib/{application} 這個(gè)地址的application

PS:客戶端加載順序:bootstrap.yml -> 加載服務(wù)端配置文件 -> application.yml,所以要注意讀取部分的配置要放在bootstrap.yml

9.spring cloud bus
整合了java的事件處理機(jī)制和消息中間件,可以簡單的認(rèn)為是一個(gè)輕量級的消息總線。使用時(shí)它默認(rèn)需要在一個(gè)消息中間件基礎(chǔ)上,比如說rabbitmq、kafka。
以rabbitmq為例子
bootstrap.yml 配置類

spring:
  cloud:
    config:
      uri: http://localhost:8080
      profile: dev
      label: master
  application:
    name: simple
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest

PS:config-server 和 config-client最好都添加上如上的消息配置。執(zhí)行 http://config-server/bus/refresh 即可。 這個(gè)觸發(fā)事件可以通過github webhook來觸發(fā)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容