<ul id="qxxfc"><fieldset id="qxxfc"><tr id="qxxfc"></tr></fieldset></ul>




      前言

      通過 Python 爬取十萬(wàn)博文之后,最重要的是要讓互聯(lián)網(wǎng)用戶訪問到,那么如何做呢?

      選型

      從后臺(tái)框架、前端模板、數(shù)據(jù)庫(kù)連接池、緩存、代理服務(wù)、限流等組件多個(gè)維度選型。

      * 后臺(tái)框架 SpringBoot2+、JPA
      * 前端框架 Vue
      * 模塊框架 Thymeleaf
      * 數(shù)據(jù)庫(kù)連接池 HikariCP
      * 緩存 Redis
      * 限流 Guava
      * 代理服務(wù) Nginx
      * 文章編輯 Markdown
      架構(gòu)



      博文

      我們可以通過以下方式訪問:
      https://blog.52itstyle.top/49.html
      亦或是:
      https://blog.52itstyle.top/49.shtml
      當(dāng)然,如果你愿意你也可以顯示為:
      https://blog.52itstyle.top/49.php https://blog.52itstyle.top/49.asp
      https://blog.52itstyle.top/49.jsp
      只需要在后臺(tái)配置對(duì)應(yīng)的映射關(guān)系即可:
      /** * 博文 */ @RequestMapping("{id}.html") public String
      blog(@PathVariable("id") Long id, ModelMap model) { Blog blog =
      blogService.getById(id); model.addAttribute("blog",blog); return "article"; }
      由于數(shù)據(jù)庫(kù)存儲(chǔ)的是 markedown 格式的數(shù)據(jù),前臺(tái)我們通過 editormd 轉(zhuǎn)為 html 代碼顯示,這里只展示部分代碼:
      <script type='text/javascript' src='js/jquery.min.js'></script> <!--省略部分代碼-->
      <script type='text/javascript' src="editor/editormd.min.js"></script>
      <!--省略部分代碼--> <div id="article"> <textarea th:text="${blog.content}"
      style="display:none;" placeholder="markdown語(yǔ)言"> </textarea> </div>
      <!--省略部分代碼--> <script> editormd.markdownToHTML("article", { htmlDecode :
      "style,script,iframe", emoji : true, taskList : true, tex : true, // 默認(rèn)不解析
      flowChart : true, // 默認(rèn)不解析 sequenceDiagram : true // 默認(rèn)不解析 }); </script>
      緩存

      爬取的博文一般、基本、大概不會(huì)修改,所以我們完全可以緩存起來(lái),避免跟數(shù)據(jù)庫(kù)直接交互,順便提升一下訪問速速。正好手頭有個(gè) 256MB 的阿里云 Redis
      服務(wù),拿來(lái)就用了。

      首相引入以下組件:
      <dependency> <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-cache</artifactId> </dependency> <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
      配置 redis:
      spring.redis.database=1
      spring.redis.host=r-m5e4873fd882de14.redis.rds.aliyuncs.com
      spring.redis.port=6379 spring.redis.password=6347888
      spring.redis.pool.max-active=8 spring.redis.pool.max-wait=-1
      spring.redis.pool.max-idle=8 spring.redis.pool.min-idle=0
      spring.redis.timeout=3000ms spring.cache.type = redis
      接口實(shí)現(xiàn),引入 Cacheable 注解:
      @Override @Cacheable(cacheNames ="blog") public Blog getById(Long id) { String
      nativeSql = "SELECT * FROM blog WHERE id=?"; return
      dynamicQuery.nativeQuerySingleResult(Blog.class,nativeSql,new Object[]{id}); }
      配置完成之后,我們打開數(shù)據(jù)庫(kù)配置,多次訪問博文地址,如果只是初次打印 SQL 說(shuō)明配置成功:
      spring.jpa.show-sql = true
      限流

      萬(wàn)一哪天流量暴漲亦或是有人惡意攻擊,爾等小服務(wù)器根本扛不住,所以有時(shí)候我們需要一定的手段進(jìn)行限流,比如限制IP訪問的頻率次數(shù)。

      這里我們使用開源的第三方組件庫(kù),引入以下組件:
      <dependency> <groupId>com.google.guava</groupId>
      <artifactId>guava</artifactId> <version>25.1-jre</version> </dependency>
      自定義注解:
      /** * 自定義注解 限流 */ @Target({ElementType.PARAMETER, ElementType.METHOD})
      @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ServiceLimit
      { /** * 描述 */ String description() default ""; /** * key */ String key()
      default ""; /** * 類型 */ LimitType limitType() default LimitType.CUSTOMER; enum
      LimitType { /** * 自定義key */ CUSTOMER, /** * 根據(jù)請(qǐng)求者IP */ IP } }
      限流邏輯:
      /** * 限流 AOP */ @Aspect @Configuration public class LimitAspect {
      //根據(jù)IP分不同的令牌桶, 每天自動(dòng)清理緩存 private static LoadingCache<String, RateLimiter> caches
      = CacheBuilder.newBuilder() .maximumSize(1000) .expireAfterWrite(1,
      TimeUnit.DAYS) .build(new CacheLoader<String, RateLimiter>() { @Override public
      RateLimiter load(String key){ // 新的IP初始化 每秒只發(fā)出5個(gè)令牌 return
      RateLimiter.create(5); } }); //Service層切點(diǎn) 限流
      @Pointcut("@annotation(com.itstyle.blog.common.limit.ServiceLimit)") public
      void ServiceAspect() { } @Around("ServiceAspect()") public Object
      around(ProceedingJoinPoint joinPoint) { MethodSignature signature =
      (MethodSignature) joinPoint.getSignature(); Method method =
      signature.getMethod(); ServiceLimit limitAnnotation =
      method.getAnnotation(ServiceLimit.class); ServiceLimit.LimitType limitType =
      limitAnnotation.limitType(); String key = limitAnnotation.key(); Object obj;
      try { if(limitType.equals(ServiceLimit.LimitType.IP)){ key =
      IPUtils.getIpAddr(); } RateLimiter rateLimiter = caches.get(key); Boolean flag
      = rateLimiter.tryAcquire(); if(flag){ obj = joinPoint.proceed(); }else{ throw
      new RrException("小同志,你訪問的太頻繁了"); } } catch (Throwable e) { throw new
      RrException("小同志,你訪問的太頻繁了"); } return obj; } }
      收錄

      完事具備,就差被搜索引擎收錄了,我們可以通過手動(dòng)生成網(wǎng)站地圖,提交給百度。
      /** * 生成地圖 * 參見:https://blog.52itstyle.top/sitemap.xml */ @Component public
      class SitemapTask { @Autowired private DynamicQuery dynamicQuery; protected
      Logger logger = LoggerFactory.getLogger(getClass()); @Value("${blog.url}")
      private String blogUrl; //每天23點(diǎn)執(zhí)行一次 @Scheduled(cron = "0 0 23 * * ?") public
      void createSitemap() { logger.info("定時(shí)提交百度收錄開始"); StringBuffer xml = new
      StringBuffer(); xml.append("<?xml version='1.0' encoding='utf-8'?>\n");
      xml.append("<urlset>\n"); String nativeSql = "SELECT id,create_time FROM blog";
      List<Object[]> list = dynamicQuery.query(nativeSql,new Object[]{});
      list.forEach(blog -> { String url = blogUrl+blog[0]+".html"; xml.append("
      <url>\n"); xml.append(" <loc>"+url+"</loc>\n"); xml.append("
      <lastmod>"+blog[1]+"</lastmod>\n"); xml.append(" </url>\n"); });
      xml.append("</urlset>\n"); saveAsFileWriter(xml.toString());
      logger.info("定時(shí)提交百度收錄結(jié)束"); } private static void saveAsFileWriter(String
      content) { String path =
      ClassUtils.getDefaultClassLoader().getResource("").getPath(); String filePath =
      path + "static"+ SystemConstant.SF_FILE_SEPARATOR+"sitemap.xml"; FileWriter
      fwriter = null; try { fwriter = new FileWriter(filePath, false);
      fwriter.write(content); } catch (IOException ex) { ex.printStackTrace(); }
      finally { try { fwriter.flush(); fwriter.close(); } catch (IOException ex) {
      ex.printStackTrace(); } } } }
      打包

      盡量不要以Jar包形式部署,為了以后方便部署,最好放置到 外置Tomcat 下。

      pom.xml 中移除內(nèi)置 Tomcat:
      <dependency> <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope>
      </dependency>
      修改啟動(dòng)類:
      /** * 啟動(dòng)類 * 創(chuàng)建者 科幫網(wǎng) * 創(chuàng)建時(shí)間 2019年7月21日 */ @SpringBootApplication @EnableCaching
      @EnableScheduling public class Application extends SpringBootServletInitializer
      { private static final Logger logger =
      LoggerFactory.getLogger(Application.class); public static void main(String[]
      args) { SpringApplication.run(Application.class, args); logger.info("項(xiàng)目啟動(dòng)"); }
      @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder
      application) { return application.sources(Application.class); } }
      代理

      項(xiàng)目部署后,最好加一層代理服務(wù),這里我們使用Nginx:
      server { listen 80; server_name blog.52itstyle.top; return 301
      https://$server_name$request_uri; } server{ listen 443 ssl; server_name
      blog.52itstyle.top; #證書路徑 ssl_certificate
      /usr/local/openresty/nginx/cert/2543486_blog.52itstyle.top.pem; #私鑰路徑
      ssl_certificate_key
      /usr/local/openresty/nginx/cert/2543486_blog.52itstyle.top.key; #緩存有效期
      ssl_session_timeout 5m; #可選的加密算法,順序很重要,越靠前的優(yōu)先級(jí)越高. ssl_ciphers
      ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
      #安全鏈接可選的加密協(xié)議 ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_prefer_server_ciphers on;
      location = /500.html { root /usr/local/openresty/nginx/html; } error_page 500
      502 503 504 = /503/503.html; location / { proxy_pass http://127.0.0.1:8080; }
      location ~ /\.ht { deny all; } }
      動(dòng)靜分離,將靜態(tài)文件交由Nginx處理,加速博客訪問:
      #靜態(tài)文件交給nginx處理 location ~ .*\.(js|css|gif|jpg|jpeg|png|bmp)?$ { root
      /home/tomcat8/webapps/ROOT/WEB-INF/classes/static; expires 2h; }
      源碼:https://gitee.com/52itstyle/Python <https://gitee.com/52itstyle/Python>

      演示:https://blog.52itstyle.top <>

      列表:https://blog.52itstyle.top/index <https://blog.52itstyle.top/index>

      詳情:https://blog.52itstyle.top/49.shtml <https://blog.52itstyle.top/49.shtml>

      小結(jié)

      擼完整個(gè)項(xiàng)目,基本能接觸的都用上了,前后端框架、連接池、限流、緩存、動(dòng)靜分離,HTTPS安全認(rèn)證、百度收錄等等,特別適合有一定開發(fā)基礎(chǔ)的小伙伴!

      友情鏈接
      ioDraw流程圖
      API參考文檔
      OK工具箱
      云服務(wù)器優(yōu)惠
      阿里云優(yōu)惠券
      騰訊云優(yōu)惠券
      京東云優(yōu)惠券
      站點(diǎn)信息
      問題反饋
      郵箱:[email protected]
      QQ群:637538335
      關(guān)注微信

        <ul id="qxxfc"><fieldset id="qxxfc"><tr id="qxxfc"></tr></fieldset></ul>
          成人精品午夜无码免费视小黄人 | 日屄影院 | 高青无码 | 亚洲激情五月天 | 国产制服丝袜 | 日本WWW在线视频 | 淫五月| 国产嫩草二区三区 | 免费观看黄色三级片 | 睡着被偷偷侵犯的在线观看 |