SpringCloud etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
SpringCloud etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

16 Şubat 2023 Perşembe

SpringCloud Retrofit Kullanımı

Giriş
Refrofit kütüphanesi OpenFeign yerine tercih edilebilir

Gradle
Şöyle yaparız
implementation ‘org.springframework.cloud:spring-cloud-square-retrofit:0.4.1’
implementation ‘org.springframework.cloud:spring-cloud-square-okhttp:0.4.1’
Örnek
Şöyle yaparız
@EnableRetrofitClients(basePackages = "com.oguzderici.retrofitdemo.clients")
@Configuration
public class RetrofitConfiguration {

  @Bean
  @LoadBalanced
  public OkHttpClient.Builder okHttpClientBuilder() {
    return new OkHttpClient.Builder()
      .addInterceptor(new HttpLoggingInterceptor()
                        .setLevel(HttpLoggingInterceptor.Level.BASIC));
    }
}
Arayüzler için şöyle yaparız
@RetrofitClient(name = "welcome-service", configuration = AuthenticationConfig.class)
public interface WelcomeRetrofitClient {
  @GET("/v1/whoiam")
  Call<WelcomeResponse> callName(@Query("name") String name);

  @POST("/v1/welcome")
  Call<WelcomeResponse> callSuccess(@Body WelcomeRequest welcomeRequest);

  @PUT("/v1/welcome/{id}")
  Call<WelcomeResponse> callWithFail(@Path("id") String id,
     @Body WelcomeRequest welcomeRequest);

  @DELETE("/v1/welcome/{id}")
  Call<Void> deleteWelcomeRequest(@Path("id") String id);
}

@RequiredArgsConstructor
@Configuration
public class AuthenticationConfig {
  private final TokenInterceptor tokenInterceptor;
  private final AuthenticatorInterceptor authenticatorInterceptor;
  private final HeaderInterceptor headerInterceptor;

  @Bean
  @LoadBalanced
  public OkHttpClient.Builder okHttpClientBuilder() {
    return new OkHttpClient.Builder()
      .addInterceptor(tokenInterceptor)
      .addInterceptor(headerInterceptor)
      .authenticator(authenticatorInterceptor);
  }
}



24 Şubat 2022 Perşembe

SpringCloud Zipkin

Zipkin Nedir?
Açıklaması şöyle
Zipkin is a distributed instrumentation and monitoring tool that will allow us to collect service information and perform searches on it. With the aim of finding problems in the latency of them, mainly through evaluating how long it has taken the execution of those services.
application.properties
Açıklaması şöyle
By default, our services will try to send trace messages to localhost:9411. If OpenZipkin runs at a different address, you need to specify it in the settings of each service:
Örnek
Şöyle yaparız
spring: zipkin: base-url: http://<host>:<port>
Örnek
Zipkin'a RabbitMQ ile mesaj göndermek için şöyle yaparız
spring.zipkin.sender.type=rabbit
spring.zipkin.rabbitmq.queue=zipkin
....
Docker Zipkin Sunucusu
Zipkin sunucusu 9411 numaralı portu kullanır. GUI'ye erişmek için şöyle yaparız
http://localhost:9411/
Örnek
Şöyle yaparız
docker run -d -p 9411:9411 openzipkin/zipkin
Docker Compose
"docker-compose up" ile başlatırız
Örnek
Şöyle yaparız
version: "3.1" services: zipkin: image: openzipkin/zipkin:2 ports: - "9411:9411"
Örnek 
Şöyle yaparız
version: "3.9"

services:
  microservices_postgresql:
    image: postgres:latest
    container_name: microservices_postgresql
    expose:
      - "5432"
    ports:
      - "5432:5432"
    restart: always
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres
      - POSTGRES_DB=bank_accounts
      - POSTGRES_HOST=5432
    command: -p 5432
    volumes:
      - ./docker_data/microservices_pgdata:/var/lib/postgresql/data
    networks: [ "microservices" ]

  redis:
    image: redis:latest
    container_name: microservices_redis
    ports:
      - "6379:6379"
    restart: always
    networks: [ "microservices" ]

  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    ports:
      - "9090:9090"
    command:
      - --config.file=/etc/prometheus/prometheus.yml
    volumes:
      - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml:ro
    networks: [ "microservices" ]

  node_exporter:
    container_name: microservices_node_exporter
    restart: always
    image: prom/node-exporter
    ports:
      - '9101:9100'
    networks: [ "microservices" ]

  grafana:
    container_name: microservices_grafana
    restart: always
    image: grafana/grafana
    ports:
      - '3000:3000'
    networks: [ "microservices" ]

  zipkin:
    image: openzipkin/zipkin:latest
    restart: always
    container_name: microservices_zipkin
    ports:
      - "9411:9411"
    networks: [ "microservices" ]


networks:
  microservices:
    name: microservices
Şeklen şöyle

Her hangi bir isteği genişletirsek şöyle






1 Şubat 2022 Salı

SpringCloud @EnableDiscoveryClient Anotasyonu

Giriş
Açıklaması şöyle
@EnableDiscoveryClient which comes from spring-cloud-commons is the same as @EnableEurekaClient but it is a more generic implementation of “Discovery Service”.

@EnableEurekaClient only works with Eureka whereas @EnableDiscoveryClient works with eureka, consul, zookeeper. But if Eureka is on the application classpath, they are effectively the same.
@EnableZookeeper Anotasyonu
Maven
Şu satırı dahil ederiz
<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.5.7</version>
</dependency>
Örnek
Şöyle yaparız
@Configuration
@EnableZookeeper
public class ZooKeeperConfiguration extends ZookeeperProperties {
    public ZooKeeperConfiguration() {
        setConnectString("localhost:2181");
        setBaseSleepTimeMs(1000);
        setMaxRetries(3);
        setSessionTimeoutMs(5000);
    }
}

@Service
public class MyService {
  @Autowired
  private ZooKeeperClient zooKeeperClient;

  public List<String> getNodes(String path) throws KeeperException, InterruptedException {
    return zooKeeperClient.getChildren(path);
  }
}
Açıklaması şöyle
Next, you’ll need to configure the connection to your ZooKeeper server. You can do this by creating a ZooKeeperConfiguration class that extends org.springframework.cloud.zookeeper.ZookeeperProperties and sets the connection properties, like so:

This sets the connection string to localhost:2181 and configures some other properties for the connection.

Finally, you can use the ZooKeeper client in your Spring Boot application by autowiring a ZooKeeperClient instance. 





4 Ekim 2021 Pazartesi

SpringCloud Jaeger - Distributed Tracing İçindir

Giriş

Open Telemetry + Jaeger
Şeklen şöyle


Gradle
Şöyle yaparız
dependencies {
    implementation "io.micrometer:micrometer-tracing-bridge-otel"
    implementation "io.opentelemetry:opentelemetry-exporter-otlp"
}
Span Exporter için şöyle yaparız
@Bean
public OtlpGrpcSpanExporter otlpHttpSpanExporter(@Value("${tracing.url}") String url) {
  return OtlpGrpcSpanExporter.builder().setEndpoint(url).build();
}
application.properties şöyledir
management.tracing.sampling.probability=1.0
tracing.url=http://localhost:4317

OpenTelemetry vs Open Tracing
Açıklaması şöyle
The community in general is moving towards the OpenTelemetry observability framework. OpenTelemetry will most likely supersede OpenTracing at some point in the future.
OpenTelemetry Nedir?
Açıklaması şöyle
The OpenTelemetry website states that:

"OpenTelemetry is a collection of tools, APIs, and SDKs. Use it to instrument, generate, collect, and export telemetry data (metrics, logs, and traces) to help you analyze your software's performance and behavior."

OpenTelemetry was created by merging the popular OpenTracing and OpenCensus projects. It is a standard that integrates with many open source and commercial products written in many programming languages. Implementations of OpenTelemetry are in varying stages of maturity.

At its core, OpenTelemetry contains the Collector, a vendor-agnostic way to receive, process, and export telemetry data.
Trace ve Span
Açıklaması şöyle
A trace tracks the progress of a single request as it passes through services. Distributed tracing is a form of tracing that traverses process, network, and security boundaries. Each unit of work is called a span. A trace is a tree of spans. Think of a distributed trace like a Java stack trace, capturing every component of a system that a request flows through, while also tracking the amount of time spent in and between each component.
Zipkin vs Jaeger
Açıklaması şöyle. Zipkin'ın Twitter, Jaeger'ı ise Uber geliştirmiş.
Zipkin and Jaeger are two popular choices for request tracing. Zipkin was originally inspired by Dapper and developed by Twitter. It's now maintained by a dedicated community. 

Jaeger was originally built and open sourced by Uber. Jaeger is a Cloud Native Computing Foundation project
Açıklaması şöyle
Additionally, while Zipkin is an older project (initiated in 2012 at Twitter) written in Java, Jaeger is a newer project (commenced in 2017 at Uber) developed in Go.

Open Tracing + Jaeger
Burada arada bir OTL Collector yok

Maven
Şu satırı dahil ederiz
<dependency>
  <groupId>io.opentracing.contrib</groupId>
  <artifactId>opentracing-spring-jaeger-cloud-starter</artifactId>
  <version>3.3.1</version>
</dependency>
Hem Zipkin hem de  Jaeger kullanan projelerde kendimizin bir tane RestTemplate nesnesi yaratması gerekir. Şöyle yaparız
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
  return builder.build();
}
Açıklaması şöyle. Jaeger'ın araya girip TraceID gönderebilmesini sağlar.
For the spans to get connected to the same trace id, We need to create a RestTemplate bean to allow Jaeger to include an interceptor. This then helps to add traces to the outgoing request which will help to trace the entire request.
Örnek - http
Jaeger sunucusunu belirtmek için şöyle yaparız
opentracing:
  jaeger:
    http-sender:
      url: http://localhost:14268/api/traces
Örnek - udp
Jaeger sunucusunu belirtmek için şöyle yaparız
opentracing:
  jaeger:
    udp-sender:
      host: 127.0.0.1
      port: 6831
    log-spans: true
Şöyle yaparız
@Bean
public JaegerTracer jaegerTracer(){
  return new io.jaegertracing.Configuration("usr-mgt")
    .withSampler(new io.jaegertracing.Configuration.SamplerConfiguration()
                   .withType(ConstSampler.TYPE)
                   .withParam(1))
    .withReporter(new io.jaegertracing.Configuration.ReporterConfiguration()
                   .withLogSpans(true))
    .getTracer();
}

Jaeger Sunucusu
Şeklen şöyle. Dağıtık yapıya sahip

Docker Compose 
Açıklaması şöyle
The Jaeger All-In-One can be easily set up with all the necessary components to test your Java application and ensure that it correctly sends traces. It also includes an OpenTelemetry Collector module for collecting OpenTelemetry traces. 
Örnek
Şöyle yaparız. Daha sonra http://localhost:16686/ adresine gideriz.
version: "3.3"
services:
  jaeger-allinone:
    image: jaegertracing/all-in-one:1.25
    ports:
      - 6831:6831/udp
      - 6832:6832/udp
      - 16686:16686
      - 14268:14268
docker ile çalıştırmak istersek şöyle yaparız
docker run -d --name jaeger \
  -e COLLECTOR_ZIPKIN_HOST_PORT=:9411 \
  -p 5775:5775/udp \
  -p 6831:6831/udp \
  -p 6832:6832/udp \
  -p 5778:5778 \
  -p 16686:16686 \
  -p 14268:14268 \
  -p 14250:14250 \
  -p 9411:9411 \
  jaegertracing/all-in-one:1.24
Örnek - OLTP Collector
Şöyle yaparız
version: '3.7'
services:
  jaeger:
    image: jaegertracing/all-in-one:latest
    ports:
      - "16686:16686" # the jaeger UI 
      - "4317:4317" # the OpenTelemetry collector grpc 
    environment:
      - COLLECTOR_OTLP_ENABLED=true
Jaeger GUI
http://localhost:16686/ adresine gitmek gerekir. Bir işlemin hangi çağrılarda ne kadar süre tuttuğu gösteriliyor. Şeklen şöyle




20 Eylül 2021 Pazartesi

SpringCloud Gateway application.properties Ayarları

Giriş
application.properties yerine aynı şeyi kodla yapmak ta mümkün. Kodla yapmak için SpringCloud Gateway RouteLocator Arayüzü yazısına bakabilirsiniz

httpClient
Örnek
Şöyle yaparız
//global timeout for all routes
spring.cloud.gateway.httpclient.response-timeout= 2s

//timeout for one specific route in ms
spring.cloud.gateway.routes[0].metadata.response-timeout=3000

routes.id Alanı
route için tanımlanan tekil id ismidir
Örnek
Şöyle yaparız. Burada iki tane route tanımlanıyor. Her route için uri alanı ile hangi service yönlendirileceği belirtiliyor. Predicate ile de hangi isteklerin route ile eşleşeceği belirtiliyor.
# application.properties file for spring cloud gateway 
spring.application.name=gateway
sever.port=8080

## microservices mapping ##
spring.cloud.gateway.routes[0].id=microservice-one
spring.cloud.gateway.routes[0].uri=http://localhost:8081/
spring.cloud.gateway.routes[0].predicates[0]=Path=/firstmicroservice/**

spring.cloud.gateway.routes[1].id=microservice-two
spring.cloud.gateway.routes[1].uri=http://localhost:8082/
spring.cloud.gateway.routes[1].predicates[0]=Path=/secondmicroservice/

routes.uri Alanı
Çağrıyı yönlendireceğimiz mikroservis'in ismidir.
Örnek
Şöyle yaparız. Burada Path iler car/ altındaki her şey yakalanır. Örneğin "localhost:8080/car/cars" adresini kullanabiliriz. Bu adrese gelen çağrı lb://car sayesinde Eureka'ya "car" ismi ile kayıt olmuş "cars" adresini dinleyen servis'e  gönderilir
server:
  port: 8080

spring:
  application:
    name: gateway
  cloud:
    gateway:
      routes:
        - id: car-service # just an id, should end with -service
          uri: lb://car # the load balancer id (the configured application name)
          predicates:
            - Path=/car/** # the url path
          filters:
            - RewritePath=/car/(?<path>.*), /$\{path}
        - id: hotel-service
          uri: lb://hotel
          predicates:
            - Path=/hotel/**
          filters:
            - RewritePath=/hotel/(?<path>.*), /$\{path}
        - id: trip-service
          uri: lb://trip
          predicates:
            - Path=/trip/**
          filters:
            - RewritePath=/trip/(?<path>.*), /$\{path}
Örnek
Şöyle yaparız. Burada http://localhost:9000/test/foo gibi bir adrese gidersek, Eureka'ya test-service ismiyle kayıt olmuş servisin foo adresini dinleyen servise gönderilir.
spring:
  application:
    name: gateway
  cloud:
    gateway:
      routes:
        - id: test-service
          uri: lb://test-service
          predicates:
            - Path=/test/**
server:
  port: 9000
eureka:
  client:
    registerWithEureka: true
    serviceUrl:
      defaultZone: ${EUREKA_SERVER_ADDRESS}
discovery.locator Alanı
Örnek
Açıklaması şöyle
Unlike Zuul, Spring cloud Gateway doesn't automatically look in Eureka for routing calls. So we enabled it by adding a couple of additional properties.
Şöyle yaparız. Eğer bu ayarları yapmak istemiyorsak, sanırım @EnableDiscoveryClient anotasyonunu kullanmak gerekir
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/

spring.cloud.gateway.discovery.locator.enabled=true
spring.cloud.gateway.discovery.locator.lowerCaseServiceId=true
default-filters Alanı
default-filters Alanı yazısına taşıdım

routes.predicates Alanı
Eğer çağrı bu koşula uyuyorsa yönlendirilir. Bir sürü Predicate Factory sınıfı var. Bunlardan birisi de Path
Örnek - Path
Şöyle yaparız
spring:
  cloud:
    gateway:
      default-filters:
        - TokenRelay
      routes:
        - id: product-resource-service
          uri: http://localhost:9191
          predicates:
            - Path=/product/**
Açıklaması şöyle
Here we are setting a route for any path request matching /product will be directed to the resource server (product-service) that is running at localhost at port 9191.

In the default-filters section, we would have to add “TokenRelay”, so that the API Gateway passes the JWT access token to the resource server.
Örnek - Path
Şöyle yaparız. Gateway 8090 potunua dinliyor.  http://127.0.0.1:8090/service1Id/demoApi adresine gelen isteği http://127.0.0.1:8080/service1Id/demoApi adresine yönlendirir.
service.port=8090 //port for apigateway

spring.cloud.gateway.routes[0].id=service1
spring.cloud.gateway.routes[0].uri=http://127.0.0.1:8080/
spring.cloud.gateway.routes[0].predicates=Path=/service1Id/**

Örnek - Path + Header
Eğer Http Header'da Authorizaton olarak Basic password yazıyorsa yönlendirmek için şöyle yaparız
cloud:
gateway:
  routes:
    - id: serviceRoute
      uri: http://service:8000
      predicates:
        - Path=/service/
        - Header=Authorization, Basic password
      filters:
        - name: CircuitBreaker
          args:
            name: slow
            fallbackUri: forward

27 Temmuz 2021 Salı

SpringCloud Gateway GatewayFilter Arayüzü - Belirli Bir Route İçin Kullanılır

Giriş
Şu satırı dahil ederiz
import org.springframework.cloud.gateway.filter.GatewayFilter;
GlobalFilter ve GatewayFilter benziyorlar. Açıklaması şöyle
Route filters allow the modification of the incoming HTTP request or outgoing HTTP response in some manner. Route filters are scoped to a particular route. Spring Cloud Gateway includes many built-in GatewayFilter Factories.
Örnek
Şöyle yaparız
//Apply filter for a specific route using spring inbuilt filter //"AddRequestHeader"
spring.cloud.gateway.routes[0].filters[0]=AddRequestHeader=first-request-header, 
  first-request-header-value
Örnek
Elimizde şöyle bir kod olsun
@RefreshScope
@Component
public class AuthenticationFilter implements GatewayFilter {

  @Autowired
  private RouterValidator routerValidator;//custom route validator
  @Autowired
  private JwtUtil jwtUtil;

  @Override
  public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    ServerHttpRequest request = exchange.getRequest();

    if (routerValidator.isSecured.test(request)) {
      if (this.isAuthMissing(request))
        return this.onError(exchange, "Authorization header is missing in request",
HttpStatus.UNAUTHORIZED);

      final String token = this.getAuthHeader(request);

      if (jwtUtil.isInvalid(token))
        return this.onError(exchange, "Authorization header is invalid",
HttpStatus.UNAUTHORIZED);

      this.populateRequestWithHeaders(exchange, token);
    }
        return chain.filter(exchange);
    }

  ...
}
Yardımcı metodlar için şöyle yaparız
private Mono<Void> onError(ServerWebExchange exchange, String err, HttpStatus httpStatus) {
  ServerHttpResponse response = exchange.getResponse();
  response.setStatusCode(httpStatus);
  return response.setComplete();
}

private String getAuthHeader(ServerHttpRequest request) {
  return request.getHeaders().getOrEmpty("Authorization").get(0);
}

private boolean isAuthMissing(ServerHttpRequest request) {
  return !request.getHeaders().containsKey("Authorization");
}

private void populateRequestWithHeaders(ServerWebExchange exchange, String token) {
  Claims claims = jwtUtil.getAllClaimsFromToken(token);
  exchange.getRequest().mutate()
    .header("id", String.valueOf(claims.get("id")))
    .header("role", String.valueOf(claims.get("role")))
    .build();
}
RouterValidator kodu şöyledir
@Component
public class RouterValidator {

  public static final List<String> openApiEndpoints = List.of(
    "/auth/register",
    "/auth/login"
  );

  public Predicate<ServerHttpRequest> isSecured =
    request -> openApiEndpoints
      .stream()
      .noneMatch(uri -> request.getURI().getPath().contains(uri));
}
Filtreyi kullanmak için şöyle yaparız
@Configuration
@EnableHystrix
public class GatewayConfig {

  @Autowired
  AuthenticationFilter filter;

  @Bean
  public RouteLocator routes(RouteLocatorBuilder builder) {
    return builder.routes()
      .route("user-service", r -> r.path("/users/**")
        .filters(f -> f.filter(filter))
        .uri("lb://user-service"))

      .route("auth-service", r -> r.path("/auth/**")
        .filters(f -> f.filter(filter))
        .uri("lb://auth-service"))
      .build();
    }
}
Açıklaması şöyle 
- all requests that starts with /users/** should be routed to user service, and our custom JWT filter should be applied to each such request 
- all requests that starts with /auth/** should be routed to auth service, and our custom JWT filter should be applied to each such request too.
Örnek
Şöyle yaparız
public class CustomerRateLimitFilter extends
AbstractGatewayFilterFactory<CustomerRateLimitFilter.Config> {
  private final RateLimiter<?> rateLimiter = ...;
  private final KeyResolver keyResolver = ...;
       
  @Override
  public GatewayFilter apply(Config config) {
    return new OrderedGatewayFilter((exchange, chain) -> {

      Route route = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
      return keyResolver.resolve(exchange).flatMap(key -> {
        if (StringUtil.isNullOrEmpty(key)) {
          return handleErrorResponse(exchange, HttpStatus.UNPROCESSABLE_ENTITY);
        }
        Mono<RateLimiter.Response> result = rateLimiter.isAllowed(route.getId(), key);
        return result.flatMap(response -> {

        response.getHeaders().forEach((k, v) -> exchange.getResponse().getHeaders()
.add(k, v));

          if (response.isAllowed()) {
            return chain.filter(exchange);
          }
          return handleErrorResponse(exchange, HttpStatus.TOO_MANY_REQUESTS);
        });
      });
    }, RATELIMIT_ORDER);
  }
 
}
Kullanmak için şöyle yaparız
spring:
  cloud:
    gateway:
      routes:
        - id: face-match-api-route
          uri: http://localhost:8800
          predicates:
            - Path=/face/**
          filters:
            - StripPrefix=1
            - name: CustomerRateLimitFilter
            - name: CustomerQuotaFilter

13 Ocak 2021 Çarşamba

SpringCloud Bus

Giriş
SpringCloud Config Server'da bir değişiklik yaptığımızda SpringCloud Config Client servislerinin bu değişikliği görmesi için 
1. Ya servisi yeniden başlatmak gerekir
2. Ya da her servisin /refresh actuator adresine teker teker boş bir POST isteği göndermek gerekir

Bu zahmetli olduğu için 

1. RabbitMQ veya Kafka kuruyoruz
2. SpringCloud Bus projesini hem SpringCloud Config Client hem de SpringCloud Config Server servislerine ekliyoruz. Ayrıca her servisi RabbitMQ'yu veya Kafka'yı dinler hale getiriyoruz.
3. Daha sonra konfigürasyonda bir değişiklik yapıp, herhangi bir SpringCloud Config Client servisinin /bus-refresh actuator adresine boş bir POST isteği gönderiyoruz. Böylece tüm  SpringCloud Config Client servisleri yeni konfigürasyonu okuyorlar.

Client İçin
Actuator muhtemelen zaten vardır ancak yoksa da şu satırı dahil ederiz
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-actuator</artifactId>
    <version>2.2.6.RELEASE</version>
</dependency>
Daha sonra RabbitMQ için şu satırı dahil ederiz
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-bus-amqp</artifactId>
    <version>2.2.1.RELEASE</version>
</dependency>
Kafka için şu satırı dahil ederiz.
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-bus-kafka</artifactId>
</dependency>
Örnek
RabbitMQ'ya bağlanmak ve bus-resfresh actuator adresini etkinleştirmek ve  gerekir. Şöyle yaparız
spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
  cloud:
    bus:
      enabled: true
      refresh:
        enabled: true
Server İçin
Eğer server'ı Git'e otomatik bağlamak istersek pom dosyasına daha başka satırlar da eklemek gerekir.

30 Aralık 2019 Pazartesi

SpringCloud Binder Arayüzü

Giriş
Açıklaması şöyle.
The Binder SPI consists of a number of interfaces, out-of-the box utility classes, and discovery strategies that provide a pluggable mechanism for connecting to external middleware. The key point of the SPI is the Binder interface, which is a strategy for connecting inputs and outputs to external middleware. 
Açıklaması şöyle.
A typical binder implementation consists of the following: a class that implements the Binder interface; a Spring @Configuration class that creates a bean of type Binder along with the middleware connection infrastructure; a META-INF/spring.binders file found on the classpath containing one or more binder definitions,...
Kullanılabilen arakatmanlar şöyle.
Through the use of one of many Spring Cloud Stream Binders, many different messaging middleware products can be used. The following popular platforms are among the list supported by Spring Cloud Data Flow:
1.Kafka Streams
2.Amazon Kinesis
3.Google Pub/Sub
4.Solace PubSub+
5.Azure Event



22 Aralık 2019 Pazar

SpringCloud Sleuth - Kullanmayın

Giriş
SpringCloud Sleuth eski bir  proje. Artık kullanmaya gerek yok. Açıklaması şöyle
Spring Cloud Sleuth is a Spring Cloud project that instruments various libraries with an abstraction over Tracers. Its next release will be in version 3.1.0 and it will be the last feature release of that project. For Spring 6 and Spring Boot 3 there will be no Sleuth compatible version.
Açıklaması şöyle
Spring Cloud Sleuth has been discontinued in Spring Boot 3.x, and it can only be used until the end of Spring Boot 2. To send your traces to Jaeger, you need to migrate to the Micrometer library.
Yani SpringCloud Sleuth  + Zipkin yerine artık Micrometer + Jaeger kullanılıyor

SpringCloud Sleuth - Zipkin İle Kullanılabilir
Açıklaması şöyle. Zipkin yazısına bakabilirsiniz
Spring Cloud Sleuth propagates headers compatible with Zipkin - a popular tool for distributed tracing. Its main features are:

- It adds trace (correlating requests) and span IDs to the Slf4J MDC.
- It records timing information to aid in latency analysis.
- It modifies a pattern of log entry to add some information like additional MDC fields.
- It provides integration with other Spring components like OpenFeign, RestTemplate or Spring Cloud Netflix Zuul.

Sleuth ve Loglama Kütüphanesi
Açıklaması şöyle. Yani Sleuth logback vs. ile kullanılabilir.
This integrates well with libraries like logback, slf4j to add identifiers so that we can trace and diagnose issues using logs.
Maven
Şu satırı dahil ederiz
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
Eğer trace için Zipkin kullanılıyorsak ayrıca Zipkin dependency eklemeye gerek yoktur. Şöyle yaparız
<!--
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
-->
<!--Spring Sleuth3.0 removed spring-cloud-starter-zipkin:
https://docs.spring.io/spring-cloud-sleuth/docs/3.0.0-M3/reference/html /#sleuth-with-zipkin-via-http 
Use the following dependency instead
-->
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-sleuth-zipkin</artifactId>
</dependency>
Gradle
Şöyle yaparız
ext { set('springCloudVersion', "2021.0.0") } dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.cloud:spring-cloud-starter-sleuth' implementation 'org.springframework.cloud:spring-cloud-sleuth-zipkin' implementation 'org.springframework.cloud:spring-cloud-starter-openfeign' compileOnly 'org.projectlombok:lombok:1.18.22' annotationProcessor 'org.projectlombok:lombok:1.18.22' testCompileOnly 'org.projectlombok:lombok:1.18.22' testAnnotationProcessor 'org.projectlombok:lombok:1.18.22' } dependencyManagement { imports { mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" } }
Sleuth Çıktısı
Sleuth çıktısında şu alanlar vardır
[application name, traceId, spanId, export]
Şeklen şöyle

Alanların açıklaması şöyle
Application Name is the name we set in the application.properties file in the previous step. This is crucial to effective logging because it can be used to aggregate logs from multiple instances.

TraceId is the id that is assigned to every request.

SpanId is used to track a unit of work. Each request can have multiple steps. And each step can have its unique SpanId.

Export is a flag that indicates whether a particular log should be exported to a log aggregation tool such as Zipkin.
Dolayısıyla uygulamaya bir isim vermek gerekir. Şöyle yaparız
spring.application.name=product-consumer-app
Tablo olarak bakarsak şöyle
.---------------.------------.-----------.
|     type      | request id |  span id  |
:---------------+------------+-----------:
| Single thread | same       | same      |
:---------------+------------+-----------:
| Thread pool   | same       | different |
:---------------+------------+-----------:
| Async         | same       | different |
'---------------'------------'-----------'
Spring Beanleri
Açıklaması şöyle
Sleuth automatically injects the necessary headers to all requests generated by, for example, RestTemplate or Feign, using interceptors. For this to work automatically, you need to declare HTTP clients as Spring beans.

Örnek
Basit bir çıktı şöyledir
2019-06-09 08:38:44.638  INFO [product-consumer-app,9cbe06b35ad945e9,9cbe06b35ad945e9,false] 4689 --- [nio-8086-exec-1] SleuthTestController: Wake Up Sleuth
Sleuth’s baggage field feature
Açıklaması şöyle
In a microservice architecture, it is common to pass a custom HTTP header, let’s say: “Correlation-ID”, from one service to another. It might also be equally common to record that value in the log for debugging later.

If you are using Spring Cloud Sleuth, this task would be quite easy with Sleuth’s baggage field feature. You will just have to add a couple of properties to the configuration file and Voilà!
Örnek
Şöyle yaparız
spring.sleuth.baggage.remote-fields=Correlation-Id (1) spring.sleuth.baggage.correlation-fields=Correlation-Id (2)
Açıklaması şöyle
(1) Tell Sleuth to get the value from this header name(s) and propagate to remote services
(2) Set the baggage value(s) to Slf4j’s MDC
Şöyle yaparız
@GetMapping("/message") public String message(@RequestHeader(value = "Correlation-Id", required = false) String correlationId) { log.info("Service-A is called with Correlation-Id: {}", correlationId); String bMsg = restTemplate.getForObject("http://localhost:8081/b/message", String.class); return "Message from B: " + bMsg; }

Tracer Sınıfı
withSpanInScope metodu
Örnek
Şöyle yaparız
@Service
public class SleuthService {

  Logger logger = Logger.getLogger("SleuthService");

  @Autowired
  private Tracer tracer;

  public void sameSpanWork(){
    logger.info("Doing some work");

  }

  public void newSpanWork(){
    logger.info("Original span going on");

    Span newSpan = tracer.nextSpan().name("new sleuth span").start();

    try(Tracer.SpanInScope span = tracer.withSpanInScope(newSpan.start())){
      logger.info("This work is being done in the new span");
    } finally {
      newSpan.finish();
    }

    logger.info("Back to original span");
  }
}
Çıktı olarak şunu alırız. SpanID alanının değiştiği görülebilir.
2019-06-09 09:16:51.410  INFO [product-consumer-app,0ebf1c258ab7f823,0ebf1c258ab7f823,false] 6125 --- [nio-8086-exec-1] SleuthTestController                     : New span work initiated
2019-06-09 09:16:51.410  INFO [product-consumer-app,0ebf1c258ab7f823,0ebf1c258ab7f823,false] 6125 --- [nio-8086-exec-1] SleuthService                            : Original span going on
2019-06-09 09:16:51.411  INFO [product-consumer-app,0ebf1c258ab7f823,7d4b060ff51d04db,false] 6125 --- [nio-8086-exec-1] SleuthService                            : This work is being done in the new span
2019-06-09 09:16:51.413  INFO [product-consumer-app,0ebf1c258ab7f823,0ebf1c258ab7f823,false] 6125 --- [nio-8086-exec-1] SleuthService                            : Back to original span