Share
Embed
Boost Dev and App Performance
2019/07/31 LINE Developer Meetup 開發者小聚系列活動 #9 https://linegroup.kktix.cc/events/20190731
LINE Developers Taiwan PRO
July 31, 2019
Transcript
31 slides
-
• 4DIFEVMF NPOUIT XPSLEBZT • 3FTPVSDFT1.Y'&Y#&Y69 Y2"Y • 4FSWJDFT •
6TFSXFCTJUF • 1BSUOFSXFCTJUF $.4 • $IBUCPU • *OUFSOBM$.4 New Service -
• 4QSJOHCPPU • 4QSJOHTFDVSJUZ • 4QSJOHCPPUEBUBKQB • 4QSJOHCPPUEBUBSFTU • 4QSJOHCPPUIJCFSOBUF
• -*/&.FTTBHJOH4%, Technical Stacks -
• ) • *ONFNPSZ%# • "VUPNBUJDUBCMFDSFBUJPOPO&OUJUZ • *OJUJBMEBUBMPBEJOHUISPVHIˊ JNQPSUTRM •
spring.jpa.generate-ddl: true • spring.jpa.hibernate.ddl-auto: create • .ZTRM • $SFBUFBMUFSUBCMFNBOVBMMZ Database -
@Data @Entity public class Article implements Serializable { private long
reporterCount; private long followerCount; @Formula("reporter_count + follower_count") private long hottestCount; @OneToMany(mappedBy = "article", fetch = FetchType.LAZY, cascade = CascadeType.PERSIST, orphanRemoval = true) @JsonIgnoreProperties({"article", "reviewer", "feedbacks"}) private Set<Comment> comments; } Entity -
@Data @Entity @JsonIgnoreProperties(value = { "hibernateLazyInitializer", "handler", "article", "feedbacks", "createdAt"
}) public class Comment implements Serializable { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "article_id") @JsonIgnoreProperties({"partner", "comments"}) private Article article; } Entity -
@Repository public interface ArticleRepository extends JpaRepository<Article, Long> { @Query("SELECT article
" + "FROM Article article " + "WHERE language = :language ”) Page<Article> getLatestOrHottestArticle( @Param("language") String language , Pageable page); } Repository -
@CrossOrigin public class PublicController { @GetMapping(”/pub/v1/{language}/articles/latest") public ResponseEntity<Page<Article>> getLatestArticles( @PathVariable(name
= "language") String language , @PageableDefault(size = 20, sort = { "updatedAt" }, direction = Sort.Direction.DESC, page = 0) Pageable pageable ) { return ResponseEntity.ok() .cacheControl(CacheControl.maxAge(fakenewsProperties.getCacheMaxAge(), TimeUnit.SECONDS).cachePublic().sMaxAge(fakenewsProperties.getCacheMaxAge(), TimeUnit.SECONDS)) .body(articleRepository.getLatestOrHottestArticle(language, pageable)); } } Controller -
@RunWith(SpringRunner.class) @ActiveProfiles("domain-test") @DataJpaTest public class ArticleRepositoryTests { @Autowired private ArticleRepository
articleRepository; @Test public void getLatestOrHottestArticleTest() { Pageable page = PageRequest.of(0, 12, Sort.by("createdAt").descending().and(Sort.by("hottestCount"))); Page<Article> articleList = articleRepository. getLatestOrHottestArticle(“zhtw”, page); assertThat(articleList.getTotalElements()).isEqualTo(16); } } Unit Test Repository -
@Api(tags = "guest") @GetMapping("/articles/{id}") @ApiOperation(value = "Get article by articleId")
@ApiImplicitParam(name = "X-Line-Biz-Session", value = "Biz Id Session", required = true, paramType = "header", dataType = "String", example = "ses-from-cookie") public Article getArticleById( @PathVariable long id) { return articleRepository.findById(id).orElseThrow(() -> new EntityNotFoundException("Not found")); } Swagger -
@RunWith(SpringRunner.class) @ContextConfiguration(classes = WebApplication.class) @ActiveProfiles("test") @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class
PublicControllerIntegrationTests { @LocalServerPort int port; @Before public void setBaseUri () { RestAssured.port = port; } @Test public void getLatestArticlesWithSortTest() { given() .queryParam("sort", "createdAt,asc") .when() .get("/pub/v1/zhtw/articles/latest") .then() .statusCode(OK.value()) .assertThat() .body("content.size()", is(10)) .body("content.id", hasItems(13, 12, 4, 10, 8, 11, 1, 6, 9, 5)) ; } } Integration Test -
POST /articles PUT /articles/{articleId} GET /articles/{articleId} DELETE /articles/{articleId} @RepositoryRestResource public
interface ArticleRepository extends JpaRepository<Article, Long> { Book findOne(Integer id); Page<Book> findAll(Pageable pageable); Book save(Book s); @RestResource(exported = false) void delete(Book t); } Repository Rest Resources -
{ ”content" : ”Article Content", "_links" : { "self" :
{ "href" : "http://localhost:8080/articles/1" }, ”article" : { "href" : "http://localhost:8080/articles/1" }, ”comment" : { "href" : "http://localhost:8080/articles/1/comments" } } } Repository Rest Resources -
• -B[ZMPBEJOHJTTVFT • -B[Z*OJUJBMJ[BUJPO&YDFQUJPO • 4PMVUJPO • spring.jpa.open-in-view: true •
+0*/'&5$) • &OUJUZ(SBQI Entity Caveats -
@Entity @NamedEntityGraph( name = "graph.Article", attributeNodes = { @NamedAttributeNode("tag"), @NamedAttributeNode("category"),
@NamedAttributeNode(value = "comments", subgraph = "comments-subgraph"), }, subgraphs = { @NamedSubgraph( name = "comments-subgraph", attributeNodes = { @NamedAttributeNode("judgement"), @NamedAttributeNode("partner") } ) }) public class Article implements Serializable { EntityGraph -
{ "article": [{ "id": 8, "content": "Hello world", "comments": {
"id": 4, "handler": {}, "hibernateLazyInitializer": {} } ”followers” : {…} }]} HTTP Status 500 - Could not write content: No serializer found for class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer and no properties discovered to create BeanSerializer @JsonIgnoreProperties(value = { "hibernateLazyInitializer", "handler", "article", "feedbacks", "createdAt" }) Entity Caveats -
• )JCFSOBUF-FWFM$BDIF • )JCFSOBUFRVFSZDBDIF • &OUJUZ(SBQI • 1SPKFDUJPO • "1*3FTQPOTF$BDIF
• 3FWFSTF1SPYZ$BDIF Performance Tuning -
• )JCFSOBUF-FWFM$BDIF • &IDBDIF ˊ -PDBMDBDIF • 3FEJT ˊ %JTUSJCVUFEDBDIF
• -B[ZMPBEJOHJTTVF • %FTFSJBMJ[BUJPOJTTVF • 3FEJT DMVTUFSTFOUJOFMTVQQPSU Cache Caveats -
public interface ArticleBasicProjection { long getId(); Category getCategory(); } @Query("SELECT
article " + "FROM Article article " + "WHERE article.id = :articleId”) Optional<ArticleBasicProjection> getArticleBasicById( @Param("articleId") Long articleId , @Param("reporterCount") Long reporterCount ); Projection -
@Cacheable(value = "ArticleList", cacheManager = "ehCacheManager") @GetMapping("/pub/v1/{language}/articles/latest ") public ResponseEntity<Page<Article>>
getLatestArticles( … ) API Response Cache -
• 6TJOH3FWFSTF1SPYZPS$%/ • OHJOY SFWFSTFQSPYZ • proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=factchecker:2m
max_size=256m inactive=12h use_temp_path=off; • mount -t tmpfs -o size=256M tmpfs /var/cache/nginx • ttl = 0 • Max-age: 600 return ResponseEntity.ok() .cacheControl(CacheControl.maxAge(fakenewsProperti es.getCacheMaxAge(), TimeUnit.SECONDS).cachePublic().sMaxAge(fakenewsProperties.getCach eMaxAge(), TimeUnit.SECONDS)) .body(…) Reverse Proxy Cache -
API Response Time $POGJH +ECD FYFDUJNF NT 314 3FTQPOTF5JNF NT
8JUIPVU&OUJUZ(SBQI &OUJUZ(SBQI 8JUIPVU&OUJUZ(SBQI -DBDIF &OUJUZ(SBQI -DBDIF 3FTQPOTFDBDIF KECD KECD