머리말
십만 보웬 파이썬을 통해 크롤 링 후, 가장 중요한 것은 인터넷에 대한 사용자 액세스는, 그럼 어떻게 그것을 할 수 있도록하는 것입니다?
선택
배경 프레임 프런트 엔드 템플릿 데이터베이스 연결 풀 캐싱 같은 전류 제한 요소로서 선택 프록시 서비스의 치수.
- 배경 프레임 SpringBoot2의 +, JPA
- 프런트 엔드 프레임 뷰
- 모듈 프레임 Thymeleaf
- 데이터베이스 연결 풀 HikariCP
- 캐시 레디 스
- 流 限 구아바
- Nginx의 프록시 서비스
- 기사 마크 다운 편집기
구조
히로부미
우리는 다음과 같은 방법으로 액세스 할 수 있습니다 :
https://blog.52itstyle.top/49.html
또한, 또는 :
https://blog.52itstyle.top/49.shtml
물론, 당신도 읽을 수하고자하는 경우 :
https://blog.52itstyle.top/49.php
https://blog.52itstyle.top/49.asp
https://blog.52itstyle.top/49.jsp
오직 당신은 배경에 해당하는 매핑 관계를 구성해야합니다 :
/**
* 博文
*/
@RequestMapping("{id}.html")
public String blog(@PathVariable("id") Long id, ModelMap model) {
Blog blog = blogService.getById(id);
model.addAttribute("blog",blog);
return "article";
}
데이터베이스가 markedown 형식으로 데이터를 저장되기 때문에, 우리는 코드의 일부를 보여 HTML 코드 디스플레이에 프론트 데스크 editormd을 통과 :
<!--省略部分代码-->
<!--省略部分代码-->
<div id="article">
<textarea th:text="${blog.content}" style="display:none;" placeholder="markdown语言">
</textarea>
</div>
<!--省略部分代码-->
캐시
보웬 그래서 우리는 데이터베이스와의 직접적인 상호 작용, 내 서둘러 인상 접근 방법을 피하기 위해 캐시 할 수 있습니다, 아마도 수정되지 않습니다, 기본, 일반 크롤링. 그냥 의해 가져온 256MB의 알리 클라우드 레디 스 서비스를,이 일이.
국무 총리는 다음과 같은 구성 요소를 소개 :
<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>
구성 레디 스 :
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
인터페이스, 캐시 가능 노트를 소개 :
@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});
}
구성이 완료되면, 우리는 데이터베이스 구성을 열 초기 인쇄 SQL 구성이 성공하면, 여러 방문, 주소를 보웬 :
spring.jpa.show-sql = true
제한
그리고 그것은 또한 서지 또는 누군가 악의적 인 공격 흐름 수, 서버는 단순히 하이얼은 작은 휴대 수 없게 때때로 우리는 주파수 IP 액세스의 수를 제한하는 것으로, 제한하는 방법이 필요합니다.
여기에서 우리는 오픈 소스 타사 구성 요소 라이브러리, 다음과 같은 구성 요소의 도입은 사용 :
<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,
/**
* 根据请求者IP
*/
IP
}
}
논리를 제한 :
/**
* 限流 AOP
*/
@Aspect
@Configuration
public class LimitAspect {
//根据IP分不同的令牌桶, 每天自动清理缓存
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初始化 每秒只发出5个令牌
return RateLimiter.create(5);
}
});
//Service层切点 限流
@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;
}
}
기록
멀리 가져 오기 및 검색 엔진에 의해 색인 보내, 우리는 수동 바이에 제출 된 사이트 맵을 생성 할 수 있습니다.
/**
* 生成地图
* 参见: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点执行一次
@Scheduled(cron = "0 0 23 * * ?")
public void createSitemap() {
logger.info("定时提交百度收录开始");
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("定时提交百度收录结束");
}
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();
}
}
}
}
곤포
항아리 패키지 후술 전개의 편의를 위해, 바람직하게는 외부 톰캣하에 배치의 형태로 전개되지.
제거의 pom.xml 내장 된 톰캣 :
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
시작 범주를 수정 :
/**
* 启动类
* 创建者 科帮网
* 创建时间 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("项目启动");
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
}
대리
프로젝트가 전개 될 때, 우리가 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;
#可选的加密算法,顺序很重要,越靠前的优先级越高.
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
#安全链接可选的加密协议
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;
}
}
정적 및 동적 분리, 정적 파일은 Nginx에 처리, 가속 블로그 방문을 넘겨 :
#静态文件交给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://blog.52itstyle.top/index
详情:https://blog.52itstyle.top/49.shtml
小结
撸完整个项目,基本能接触的都用上了,前后端框架、连接池、限流、缓存、动静分离,HTTPS安全认证、百度收录等等,特别适合有一定开发基础的小伙伴!