Thursday, January 2, 2020

Spring Cloud 微服務入門 (十二) restemplete 多層服務呼叫 docker - compose 構建 (二)

目前架構


可以看到我們下面 服務好像有固定的數據庫可能是集群或是什麼
在前面的學習好像沒看到更複雜的調用,分布式很多問題,像是數據不同步,光是鎖就有一大堆,就以認證中心設計成微服務的話,
資料庫就會變成多個,這樣就不能判斷你到底是到底在哪一台調用,而出現你有雙重的登入資料緩存在分散的資料庫解決方式可能有
一 分布式鎖
二共用同一個資料庫
這部分先以後有空再弄,目前就是先拓展為多層調用,今天是來實現多層的話服務溝通使用
restemplate 去溝通。

分析

我們把服務擴展為兩層一下
也就是我們要關注的
好吧開始
目前我們的
service 已經有 5個
我們要來 新增了 2 個 也就是模擬第四層
一個是
EurekaServiceFeignConsumer2
EurekaServiceFeignProvider3
其中兩個各別都是從 也就是模擬第三層
EurekaServiceFeignProvider
EurekaServiceFeignConsumer
複製出來
來看一下代碼
那麼我們原本的服務會變怎樣呢
EurekaServiceFeignProvider1
EurekaServiceFeignProvider2
要調用我們的
EurekaServiceFeignProvider3
所以調用順序就是
Zuul - > Consumer1 - > Provider1 or Provider2 -> Consumer2 > Provider3
所以先針對我們的 Consumer1 來修改

多層服務的呼叫方式

調用服務三 但是要取得服務四 的 response 才算調用完成
那就變成
要完成server3 呼叫就要
server3 -> server 4
多經過一層 server 4 這樣的話 可能會覺得我那就用我們上次
doget
doposs
這兩種方法就好了那有沒有更優雅的方式呢有的歐,
spring 幫我們把這些東西再往上封裝了一層也就是 restemplate
(如果你想用到更複雜的調用你就自己從底層慢慢研究了)

Consumer1 Controller

我們新增了
  @GetMapping("/helloEX/{id}/{id2}")
     public String helloEX(@PathVariable(name="id") Integer employeeId,@PathVariable(name="id2") Integer employeeId2) {
      System.out.print(employeeId2);
      
         String message = homeClient.home2(employeeId,employeeId2);
         
         logger.info("[eureka-fegin][ConsumerController][hello], message={}", message);
  // log.info("[eureka-ribbon][EurekaRibbonConntroller][syaHello], message={}", message);
         return message ;
         
好了之後 我們要看要調用哪一層就是我們的

Consumer1 homeClient

  @GetMapping("/ep1")
  public String home2(@RequestParam (value="id", required = false) Integer employeeId,@RequestParam (value="id2", required = false) Integer employeeId2) ;

Provider 1 or 2

這邊就要稍微用到前面的技巧了
我們知道有 resttemplete 有可以去跟我們 Eureka 去要我們已經註冊的節點的伺服器名字
整體調用在最下面
https://openhome.cc/Gossip/Spring/RestTemplate.html
https://www.jianshu.com/p/462790156554
我們用到比較關鍵的是
預設的 restemplete 是沒有辦法呼叫負載平衡的,有印象的話在前幾天用過
SPRING CLOUD 微服務入門 (三) EUREKA + CONSUMER (FEIGN) 調用 SERVICE
在這邊的話我們把 restemplete 注入一個有 loadblance 特性
https://blog.csdn.net/qq_18416057/article/details/79432504
這邊說的蠻清楚的 不過少了一些東西
 
 @LoadBalanced
 @Bean
 RestTemplate restTemplate() {
        return new RestTemplate();
 }
 @Autowired
 private RestTemplate restTemplate;


總而言之就是讓我們的 restemplete有可以 去 訪問 Eureka 的能力

配置 provider

application.yml
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/
    register-with-eureka : true   <====就是這行 讓我們的 的 服務可以去請求其他已經註冊的伺服器
那麼現在我們的 restemplete loadblance 特性

細看 Provider

這邊要注意要跟我們的
Consumer1 homeClient 裡面的 method
相對應,這邊可以看到我們對伺服器的 ip已經不是像我們之前的在寫註冊中心那樣直接doget dopost 去呼叫,restemplete ,當然也可以用這種方式去呼叫我們的註冊中心,只不過,有空再寫好了這邊做兩個範例。
 @GetMapping("/ep1")
  public String home2(@RequestParam (value="id", required = false) final Integer employeeId,@RequestParam (value="id2", required = false) final Integer employeeId2)  {
      final String message = "Hello world" + port+ employeeId+employeeId2;
         
    logger.info("[eureka-provide][EurekaServiceProviderApplication][home], message={}", message);
    
  //   restTemplate = new RestTemplate();
    final String fooResourceUrl = "http://feign-consumer2";
  final ResponseEntity<String> response = restTemplate.getForEntity(
    fooResourceUrl + "/hello2/" + employeeId.toString() + "/" + employeeId2.toString(), String.class);
    //assertThat(response.getStatusCode(), equalTo(HttpStatus.OK));
   System.out.println(response.getStatusCode().toString()+(HttpStatus.OK).toString());
   System.out.println(response.getBody()+"test");
       return response.getBody();
  }
好了之後複製到 Provider2 兩個 Controller 一樣。

Provider3 Controller

反正 他是複製 Provider 我們直接看源碼
這邊很簡單沒做什麼我們看 Consumer2
 @GetMapping("/ep2")
  public String home(@RequestParam (value="id", required = false) Integer employeeId,@RequestParam (value="id2", required = false) Integer employeeId2)  {
      String message = "Hello world" + port+ employeeId+employeeId2;
         
       logger.info("[eureka-provide2][EurekaServiceProviderApplication][home], message={}", message);
       return message;
  }

Provider 3 配置文件

記得換為 eureka-provider2 ,以便讓我們的 Consumer2 可以調用到
    name: eureka-provider2

Consumer2 Controller

注意看 Url這邊可以看到 我們沒有再讓往Zuul 去訪問我們的 consumer2 了 因為我們是讓 Provider 去直接 調用 我們的 consumer2 , 可以注意 url 的調用 address 裡面的 hello2
@RestController

public class ConsumerController {
   private final Logger logger = LoggerFactory.getLogger(ConsumerController.class);
  @Autowired
     private HomeClient homeClient;

 
   @GetMapping("/hello2/{id}/{id2}")
     public String hello(@PathVariable(name="id") Integer employeeId,@PathVariable(name="id2") Integer employeeId2) {
      System.out.print(employeeId2);
      
         String message = homeClient.home(employeeId,employeeId2);
         
         logger.info("[eureka-fegin2][ConsumerController][hello], message={}", message);
  // log.info("[eureka-ribbon][EurekaRibbonConntroller][syaHello], message={}", message);
         return message ;
         
     }
   

}


Consumer2 homeClient

可以注意我們的 這邊就是填 Provider3 註冊到 Eureka的名字 也就是 eureka-provider2
@FeignClient(value ="eureka-provider2",fallbackFactory=HystrixClientFallbackFactory.class)
public interface HomeClient {
  @GetMapping("/ep2")
  public String home(@RequestParam (value="id", required = false) Integer employeeId,@RequestParam (value="id2", required = false) Integer employeeId2) ;

}

好了都沒問題的話 就 啟動 吧!

啟動

前面步驟就不說了
可以看到我訪問的是 helloEx
http://localhost:9000/feign-consumer/helloEX/1/12?token=Bearer%20eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1Nzc5MTUxNTksInVzZXJuYW1lIjoiamFjayJ9.UMUZWzh_ZTkhKxoSBfglSualToOTn9dm-WE9D3_huT4
這邊會覺得,有沒有更視覺化的東西可以看阿
別忘了我們的 Zipkin 就是在做這些事,我們打開
花了點時間 我把 zipkin 弄進去 docker-compose了,請注意 zipkin 啟動順序有差
version: '2'

services:

  elasticsearch:
    build:
      context: elasticsearch/
    volumes:
      - ./elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml:ro
    ports:
      - "9200:9200"
      - "9300:9300"
    environment:
      ES_JAVA_OPTS: "-Xmx256m -Xms256m"
    networks:
      - elk

  logstash:
    build:
      context: logstash/
    volumes:
      - ./logstash/config/logstash.yml:/usr/share/logstash/config/logstash.yml:ro
      - ./logstash/pipeline:/usr/share/logstash/pipeline:ro
    ports:
      - "5000:5000"
    environment:
      LS_JAVA_OPTS: "-Xmx256m -Xms256m"
    networks:
      - elk
    depends_on:
      - elasticsearch

  kibana:
    build:
      context: kibana/
    volumes:
      - ./kibana/config/:/usr/share/kibana/config:ro
    ports:
      - "5601:5601"
    networks:
      - elk
    depends_on:
      - elasticsearch
      
  rabbitmq :
    build:
     context: rabbitmq /
    volumes:
      - ./rabbitmq/config/:/usr/share/rabbitmq/config:ro
    ports:
      - "5672:5672"
      - "15672:15672"
    networks:
      - elk
    depends_on:
      - elasticsearch

  zipkin:
    build:
     context: zipkin /
    volumes:
      - ./zipkin/config/:/usr/share/zipkin/config:ro
    # Environment settings are defined here https://github.com/openzipkin/zipkin/tree/1.19.0/zipkin-server#environment-variables
    environment:
      - RABBIT_ADDRESSES=192.168.99.100:5672
      - RABBIT_USER=guest
      - RABBIT_PASSWORD=guest
      - RABBIT_QUEUE=zipkin
      - SELF_TRACING_ENABLED=false
      #- RABBIT_VIRTUAL_HOST=/admin_host
      - JAVA_OPTS=-Dlogging.level.zipkin
      #=DEBUG -Dlogging.level.zipkin2=DEBUG
    ports:
      # Port used for the Zipkin UI and HTTP Api
      - "9411:9411"
      # Uncomment if you set SCRIBE_ENABLED=true
      # - 9410:9410
    networks:
      - elk
    depends_on:
      - elasticsearch

 
networks:

  elk:
    driver: bridge

應該每次初次啟動 zipkin都會找不到 rabbitmq 而退出
docker start "zipkin name"

服務啟動
好了之後 我們的追蹤鏈也很清楚

Wednesday, January 1, 2020

Spring Cloud 微服務入門 (十一) ELk 日誌中心搭建與執行 rabbitmq 模式 docker - compose 構建 (一)

本來想用kafka 看看的,睡醒在弄看看吧,我在想接下來是不是要通通移到docker 上了,乾脆上雲好了xd在看個aws

目前整個架構就這樣

初步介紹

查看日誌的傳統方法是:登錄操作系統,使用命令工具如cat、tail、sed、awk、grep等等進行過濾輸出後分析,處理少量日誌還好,日誌量大處理效率就沒那麼高了。而且很多情況下開發人員需要查看並分析日誌進行排錯,但他們對Linux命令又不是太熟悉,而且有時候又不能賦予他們服務器權限,更多時候是運維把日誌文件導出來發給開發人員,這無疑會給我們增加工作量。 ELK(Elasticsearch+Logstash+Kibana)架構就是專門為採集、分析、存儲日誌所設計的:
Elasticsearch:基於Lucenne的搜索服務器,提供一個分佈式多用戶的全文搜索引擎,能過做到實時搜索。
Logstash:可以對日誌進行採集、過濾、輸出。
Kibana:可以匯總、分析、搜索日誌數據並提供友好的web界面。
工作流程:logstash agent監控並過濾日誌,為了保證日誌的完整性先將日誌內容輸出到RabbitMQ進行存儲;logstash indexer再把RabbitMQ上的日誌隊列收集後發送給全文搜索服務器Elasticsearch,然後可以用Elasticsearch進行自定義搜索,再通過Kibana來結合自定義搜索進行頁面展示。

安裝 ELK

這邊我們使用 docker 主要就是 解決 維運和 開發工程師 環境的不同而弄出來的東西,在微服務時代中可以快速部屬(好像是這樣
然後我也懶得架在linux 開發版了,直接用 docker 比較快一點
順便複習一下 **docker **,之前在安裝 redis 有用到,
參考:https://www.jianshu.com/p/18778a8ee6f0
玩了快一天 :
windows10 subsystem Linux不行
ai開發版不行
windows 10 home 家用版 不行
終於可以 關hyper 開 docker 可以
下次我要灌專業版了,目前灌的是預覽版 一堆正常 docker 都不能裝
elk 我找了半天
找了台灣人
https://github.com/twtrubiks/docker-elk-tutorial
只能她的 docker 可以用 ,其他版本的 elk 記憶體要求過大 ,還沒進容器裡就掛了,他裡面還有用 docker-compose 我來小魔改一下,最下面有蒐集的 資料大該就那些範圍

docker

docker 的網路有三種模式,先挖坑,
這邊的容器可以看到跟一般不同,學習一下這種方式,
我用 docker tool 是不能把端口映射到我的電腦上,
除非 nginx (創辦人好像被告了 或其他 gateway
https://github.com/x213212/elk
安裝只要
docker-compose up 
docker-compose down
他預設會抓docker-compose.yml
也就是下面的東東
version: '2'

services:

  elasticsearch:
    build:
      context: elasticsearch/
    volumes:
      - ./elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml:ro
    ports:
      - "9200:9200"
      - "9300:9300"
    environment:
      ES_JAVA_OPTS: "-Xmx256m -Xms256m"
    networks:
      - elk

  logstash:
    build:
      context: logstash/
    volumes:
      - ./logstash/config/logstash.yml:/usr/share/logstash/config/logstash.yml:ro
      - ./logstash/pipeline:/usr/share/logstash/pipeline:ro
    ports:
      - "5000:5000"
    environment:
      LS_JAVA_OPTS: "-Xmx256m -Xms256m"
    networks:
      - elk
    depends_on:
      - elasticsearch

  kibana:
    build:
      context: kibana/
    volumes:
      - ./kibana/config/:/usr/share/kibana/config:ro
    ports:
      - "5601:5601"
    networks:
      - elk
    depends_on:
      - elasticsearch
      
  rabbitmq :
    build:
     context: rabbitmq /
    volumes:
      - ./rabbitmq/config/:/usr/share/rabbitmq/config:ro
    ports:
      - "5672:5672"
      - "15672:15672"
    networks:
      - elk
    depends_on:
      - elasticsearch
networks:

  elk:
    driver: bridge
這個解決完後大概他會去抓四個程式
rabbitmq
Elasticsearch
Logstash
Kibana

我都調好了,應該沒什麼意外

Logstash.conf

這個真的雷 全形半形沒log 可以除錯只能用猜的
input {
 tcp {
  port => 5000
 }
 rabbitmq {
  type => "all"
  durable => true
  exchange => "ex_logstash"
  exchange_type => "direct"
  key => "lgstash"
  host => "192.168.99.100"
  user => guest
  password => guest
  queue => "faceJob-logstash"
  auto_delete => false
 }
}

## Add your filters / logstash plugins configuration here
output {
 elasticsearch {
  hosts => "elasticsearch:9200"
 }
}

Kibana.yml

---
## Default Kibana configuration from kibana-docker.
## from https://github.com/elastic/kibana-docker/blob/master/build/kibana/config/kibana.yml
#
server.name: kibana
server.host: "0"
elasticsearch.url: http://elasticsearch:9200

Elasticsearch

---
## Default Elasticsearch configuration from elasticsearch-docker.
## from https://github.com/elastic/elasticsearch-docker/blob/master/build/elasticsearch/elasticsearch.yml
#
cluster.name: "docker-cluster"
network.host: 0.0.0.0

# minimum_master_nodes need to be explicitly set when bound on a public IP
# set to 1 to allow single node clusters
# Details: https://github.com/elastic/elasticsearch/pull/17288
discovery.zen.minimum_master_nodes: 1

## Use single node discovery in order to disable production mode and avoid bootstrap checks
## see https://www.elastic.co/guide/en/elasticsearch/reference/current/bootstrap-checks.html
#
discovery.type: single-node

好了之後配置我們的 logback

logback-spring.xml

有用log的地方都可以加,依樣放在 resources
<?xml version="1.0" encoding="UTF-8"?>

<configuration>



    <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->  

    <property name="LOG_HOME" value="d:/" />   

    

    <!-- 控制台输出 -->     

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">  

        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">   

             <!--格式化输出,%d:日期;%thread:线程名;%-5level:级别,从左显示5个字符宽度;%msg:日志消息;%n:换行符-->   

            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>     

        </encoder>  

    </appender>  

    

    <appender name="RABBITMQ"

  class="org.springframework.amqp.rabbit.logback.AmqpAppender">

  <layout>

   <pattern><![CDATA[ %d %p %t [%c] - <%m>%n ]]></pattern>

  </layout>

  <!--rabbitmq地址 -->

  <addresses>192.168.99.100:5672</addresses>

  <username>guest</username>

  <password>guest</password>

  <declareExchange>true</declareExchange>

  <exchangeType>direct</exchangeType>

  <exchangeName>ex_logstash</exchangeName>

  <routingKeyPattern>lgstash</routingKeyPattern>

  <generateId>true</generateId>

  <charset>UTF-8</charset>

  <durable>true</durable>

  <deliveryMode>NON_PERSISTENT</deliveryMode>

  <autoDelete>false</autoDelete>

 </appender>



 <logger name="com.light.rabbitmq" level="info" additivity="false">

        <appender-ref ref="STDOUT"/>

        <appender-ref ref="RABBITMQ"/>

    </logger>



    <!-- 日志输出级别,level 默认值 DEBUG,root 其实是 logger,它是 logger 的根 -->  

    <root level="INFO">  

        <appender-ref ref="STDOUT" />  

        <appender-ref ref="RABBITMQ" />  

    </root>   
</configuration>

這個原理日後再探討,又是另一個坑了下次再玩先把框架先大致弄完

rabbitmq

記得加我們的 Excanges

還有我們的 Quenues

最重要的binding

這邊設定好就差不多了

啟動

elasticsearch

rabbitmq

kibana



debug 區 (ps不用理我,我在最慘的情況下試了很多方式了

gg


![](https://i.imgur.com/zw2hL5H.png)

```code

docker pull sebp/elk


docker run -p 5601:5601 -p 9200:9200 -p 5044:5044 -e ES_MIN_MEM=128m  -e ES_MAX_MEM=1024m -it --name elk sebp/elk 


1、执行命令:docker pull sebp/elk 将镜像pull到本地来;
2、执行命令:docker run -p 5601:5601 -p 9200:9200 -p 5044:5044 -e ES_MIN_MEM=128m  -e ES_MAX_MEM=1024m -it --name elk sebp/elk 将镜像运行为容器,由于我本机内存不符合安装要求,为了保证ELK能够正常运行,加了-e参数限制使用最小内存及最大内存。
3、打开浏览器,输入:http://<your-host>:5601,看到图形化解面即安装成功
vm.max_map_count至少需要262144.否則啟動時會報錯。
設定方式:
sudo vi /etc/sysctl.conf
增加 :
vm.max_map_count = 262144
fs.file-max = 65536
docker exec -it elk /bin/bash