25 Aralık 2019 Çarşamba

SpringIntegration IntegrationFlowBuilder

Örnek - Consumer
Şöyle yaparız.
@Bean
public IntegrationFlow lines(FileWritingMessageHandler fileOut) {
  return f -> f.handle(fileOut);
}
publishSubscribeChannel metodu
Açıklaması şöyle.
The publishSubscribeChannel() method broadcasts messages to all subscribing subflows.
...
In this way, the subflows are anonymous, meaning that they can't be independently addressed.
Örnek - Consumer + Producer
Şöyle yaparız.
@Bean
public IntegrationFlow markers() {
   return f -> f.<FileSplitter.FileMarker>filter(m ->...,...)
          .publishSubscribeChannel(s -> s
                  // first trigger file flushes
              .subscribe(...)
                   // send the first file
              .subscribe(...)
                   // send the second file
               .subscribe(...)
                   // send the third file
               .subscribe(...)
                   // send an email
               .subscribe(...));
}

SpringIntegration IntegrationFlows Sınıfı

Giriş
Açıklaması şöyle.
An integration scenario is represented using an “IntegrationFlow” interface in Spring Integration. One of the ways of creating this would be to use a builder pattern starting with the “IntegrationFlows” class. Here, you can use this to create an input channel adapter in order to get messages into the system and connect them to a message channel. Each message that is created and put through the message channels are represented using the “Message” interface. This will contain a domain-specific “payload” value and a set of headers.
route metodu - Producer Flow
Açıklaması şöyle. markers ve lines isimli iki tane IntegrationFlow tipinden bean olduğu anlatılıyor.
For each bean representing a message endpoint, you can reach its implicit input channel by simply appending “.input” to the bean name. This is how the “markers” and “lines” message endpoints’ input channels are named “markers.input” and “lines.input”, respectively. 
Örnek
Şöyle yaparız. Yapılan işlem sonucunu route() metodu ile başka kanala gönderir.
@Bean
public IntegrationFlow fromFile() {
  return IntegrationFlows.from(...)
    .handle(...)
    .<Object, Class<?>>route(...)
    .get();
}

24 Aralık 2019 Salı

Constructor Injection

Test Edebilmek
Constructor Injection daha kolay test için tavsiye ediliyor.

Test edilecek sınıfa mock nesne kolayca geçilir. Böylece unutulan setter metoddan dolayı NullPointerException alınmaz.

Diğer
Constructor'a @Autowired yazmasak bile Spring Constructor Injection yapabiliyor. Bu özellikle Lombok @RequiredArgsConstructor anotasyonu ile çalışmayı kolaylaştırıyor.

Örnek
Şöyle yaparız.
@RequiredArgsConstructor
@Controller
public class BpFinalConstructorController {

  private final BpService bpService;

  public String saySomething(){
    return bpService.getHello();
  }
}

23 Aralık 2019 Pazartesi

SpringMVC @CrossOrigin Anostayonu

Giriş
Şu satırı dahil ederiz.
import org.springframework.web.bind.annotation.CrossOrigin;
Sınıf veya Metod Üzerinde Kullanılır
Açıklaması şöyle
This annotation is used both at the class and method levels to enable cross-origin requests. In many cases, the host that serves JavaScript will be different from the host that serves the data. In such a case, Cross-Origin Resource Sharing (CORS) enables cross-domain communication. To enable this communication, you just need to add the @CrossOrigin  annotation.

By default, the @CrossOrigin annotation allows all origin, all headers, the HTTP methods specified in the @RequestMapping annotation, and a maxAge of 30 min. You can customize the behavior by specifying the corresponding attribute values.
Şu hataları alıyorsak bu anotasyonu sınıf veya metoda ekleriz.
Cross Origin Resource Blocked: The Same Origin Policy disallows reading the remote resource at http://ipAddress:port/shutdown. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing).

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://ipAddress:port/shutdown. (Reason: CORS request did not succeed).
Tüm Uygulama İçin
Eğer sadece Spring MVC kullanıyorsak ve tüm uygulama için global ayar yapmak istersek
- WebMvcConfigurerAdapter veya
- RepositoryRestConfigurerAdapter - (Deprecated kullanmayın)
sınıfılarının addCorsMappings metodunu override etmek gerekir.

Eğer SpringSecurity kullanıyorsak
- CorsConfigurer kullanılır

maxAge Alanı
Örnek
Şöyle yaparız
@CrossOrigin(origins = "*", maxAge = 3600)
@RestController
@RequestMapping("/api/login")
public class RestLoginController {...}
origins alanı
İstemci olabilecek adresleri belirtir.

Örnek
Şöyle yaparız
@Controller
@EnableAutoConfiguration
public class HelloController {

  @CrossOrigin(origins = "http://localhost:9000")
  @RequestMapping("/hello")
  @ResponseBody
  public String sayHello() {
    return "Hello World Developer!!!";
  }
}
Örnek
Şöyle yaparız
@RestController
@RequestMapping("api/drawing-releases")
@CrossOrigin("*")
public class DrawingReleaseRestController {
  ...
}

22 Aralık 2019 Pazar

SpringCloud Netflix Hystrix @HystrixCommand Anotasyonu - Circuit Breaker İçindir

Giriş
Açıklaması şöyle
... it will dynamically generate a proxy that will wrap the method and manage all calls through a thread pool of threads, mainly focused in handling remote calls
commandProperties - timeout
Timeout yani zaman aşımı değerini belirtmek için şuna benzer bir şey kullanılır
name = "execution.isolation.thread.timeoutInMilliseconds" value="6000"
Eğer fallback tanımlamazsak, zaman aşımı olursa şuna benzer bir hata alırız
"foo timed-out and fallback failed."
Örnek - timeout + fallback
Şöyle yaparız
@GetMapping
@HystrixCommand(
  fallbackMethod=”defaultProductList”, commandProperties = {
@HystrixProperty(name=”execution.isolation.thread.timeoutInMilliSeconds”,
value=”500”)}) 
public String cloudProductList(@PathVariable long timeout) {
  RestTemplate restTemplate = new RestTemplate();
  URI uri = URI.create("http://localhost:8080/products");
  return restTemplate.getForObject(uri, String.class);
}
  
public List<String> defaultProductList() {
  return Arrays.asList("product");
}
fallbackMethod Alanı
Örnek - timeout + fallback
Şöyle yaparız.
@SpringBootApplication
@EnableHystrix
@RestController
public class DemoHystrixApplication {

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

  @RequestMapping(value = "/")
  @HystrixCommand(fallbackMethod = "fallback", commandProperties = {
    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",
                     value = "1000")
  })
  public String hello() throws InterruptedException {
    Thread.sleep(3000);
    return "HELLO Hystrix";
  }

  private String fallback() {
    return "Fallback";
  }
}

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