目录
广告系统架构图
四、sponsor模块(广告投放)
sponsor模块(广告投放)介绍图
数据库对应的实体类 (例子)
Dao接口实现 (例子)
service实现(例子)
controller 控制层(例子)
sponsor(广告投放)示意图(单个-例子)
五、Feign与Ribbon微服务相互访问(RPC)与hystrix断路器使用
Ribbon+Hystrix方式
Feign方式
第一部分:eureka、zuul、通用模块(不讲代码实现,下载代码自己看。)
这不是一个完整的广告系统,主要涉及两方面 广告检索 、广告投放 ,这两个方面我感觉是最重要的,但是也使用Kafka的消息传递,为剩下的曝光见监测、 报表 、扣费 这三个部分留下了接口。
第二部分:主要会介绍广告投放、hystrix断路器。打算随便介绍一下,也就是MVC架构设计模式,Model层数据的操作,数据库实体类对象等;Controller层控制层对外提供服务(跳转),对内访问Model层;没有用户ui等,都是后端接口,所以,也就没有view层了。
所使用到的技术
JDK 、Kafka 、MySQL
框架 SpringCloud: Finchley.RELEASE 、Spring 、Spring Data JPA 、Spring boot
源码 : github https://github.com/yingyingqiqi/luoweiying-ad-spring-cloud/tree/master
maven坐标(简化版)
artifactId>spring-boot-starter-web //web功能
spring-cloud-starter-netflix-eureka-client //euraka客户端
spring-boot-starter-data-jpa //jpa规范+hibernate+spring jdbc
mysql-connector-java //驱动
commons-codec //apache工具类
这里介绍一下spring data jpa: 很久以前,数据库的持久层有一套标准--ORM(对象关系映射,就是把数据库映射成对象),一些第三方公司根据这个标准,做了一些ORM框架,如:hibernate、TopLink等,sun整合了第三方框架,推出一套JPA规范,spring data jpa是在这个jpa规范+hibernate+jdbc再次的封装,越封装、越难理解底层代码,但是对于广告投放这个项目,对数据库的性能要求不高,后面会介绍。
一些介绍可以看这里 spring data jpa 详解 SpringData JPA 官方文档
配置(不用看)
eureka:
client:
service-url:
defaultZone: http://localhost:8000/eureka
server:
port: 7000
servlet:
path: /ad-sponsor
spring:
application:
name: eureka-client-ad-sponsor
jpa:
show-sql: true
hibernate:
ddl-auto: none
properties:
hibernate.format_sql: true
open-in-view: false
datasource:
url: jdbc:mysql://ip:3306/ad_data?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC&useSSL=false
username:
password:
tomcat:
max-active: 40
min-idle: 20
initial-size: 4
package cn.luoweiying.entity;
@Entity //标记为实体类
@Table(name = "ad_plan") //与数据库对应的表名
public class AdPlan {
@Id //主键
@GeneratedValue(strategy = GenerationType.IDENTITY) //主键序号方式
@Column(name = "id", nullable = false) //对应列名
private Long id;
@Column(name = "user_id", nullable = false) //对应列名
private Long userId;
详细的hibernate注解 可以看官方文档, 也可以看 JPA注解详解
public interface AdPlanRepository extends JpaRepository {
AdPlan findByIdAndUserId(Long id, Long userId); //跟spl类似,不用写sql语句
List findAllByIdInAndUserId(List ids, Long userId);
接口继承Repository
继承JpaRepository类并添加泛型,会自动实现一些方法,此步骤也可以作为如何定义方法的回想线索。
spring支持的自定义关键字查询 ,可以查看 Appendix C: Repository query keywords SpringDataJPA自定义关键字查询
vo对象, 方法间传递的对象(例子)
public class AdPlanRequest {
private Long id;
private Long userId;
private String planName;
private String startDate;
private String endDate;
public boolean createValidate() { //检查方法是否为空
return userId != null && !StringUtils.isEmpty(planName)
&& !StringUtils.isEmpty(startDate)&& !StringUtils.isEmpty(endDate);
}
public class AdPlanResponse {
private Long id;
private String planName;
}
这些vo对象,方法间相互调用,传递的对象,相当于一个数据结构,dao--service--controller调用更加清晰明了。
service接口定义(例子)
public interface IAdplanService {
AdPlanResponse createAdPlan(AdPlanRequest request) throws AdException;//创建推广计划
List getAdPlanByIds(AdPlanGetRequest request) throws AdException;//获取推广单元
@Service
public class AdPlanServiceImpl implements IAdplanService {
@Autowired
AdPlanRepository adPlanRepository;
@Transactional
public AdPlanResponse createAdPlan(AdPlanRequest request) throws AdException {
if (!request.createValidate())throw new AdException(CodeMsg.REQUEST_PARAM_ERROR);
Optional adUser = adUserRepository.findById(request.getUserId());
if (!adUser.isPresent()) throw new AdException(CodeMsg.CAN_NOT_FIND_RECORD);//确保关联的User不存在
AdPlan oldPlan = adPlanRepository.findByUserIdAndPlanName(request.getUserId(), request.getPlanName());
if (oldPlan != null) throw new AdException(CodeMsg.SAME_NAME_UNIT_ERROR);//同名推广计划
AdPlan newPlan = adPlanRepository.save(new AdPlan(request.getUserId(), request.getPlanName(),
CommonUtils.parseStringDate(request.getStartDate()),CommonUtils.parseStringDate(request.getEndDate())
));
return new AdPlanResponse(newPlan.getId(),newPlan.getPlanName());
@Slf4j
@RestController //==responsebody+controller
public class AdPlanOPController {
@Autowired
AdPlanServiceImpl adPlanService;
@PostMapping("/create/adPlan")
public AdPlanResponse createAdPlan(@RequestBody AdPlanRequest request) throws AdException {
log.info("ad-sponsor:creatAdPlan: -> {}", JSON.toJSON(request));
return adPlanService.createAdPlan(request);
}
这只是单个访问的接口的例子,全部的太多了,下载源码自己看吧。
这个项目没真正使用到微服务相互访问,并不需要调用,检索系统提供给媒体方,但是这个模块没有写,也就不需要调用了。
这里简单讲一下微服务相互访问的方式。
微服务相互访问,通讯的协议应该是"http",目前有两种常用的调用:
1.ribbon+spring restTemplate
2.feign(ribbon+hystrix) 声明式接口的形式
这两种也就是对 apache httpClient 的封装,方便微服务的使用吧。
坐标(简化)
spring-cloud-starter-netflix-ribbon
artifactId>spring-cloud-starter-netflix-hystrix//断路器
配置eureka
(第一部分有)
注入 RestTemplate
@EnableEurekaClient //@EnableDiscoveryClient其他服务注册中心
@EnableHystrix //内部有@EnableCircuitBreaker
@SpringBootApplication
public class SearchApplication {
public static void main(String[] args) {SpringApplication.run(SearchApplication.class, args);}
//基于Ribbon实现微服务调用
@Bean
@LoadBalanced //开启默认Ribbon
RestTemplate restTemplate() {
return new RestTemplate();
}
}
服务调用
@HystrixCommand(fallbackMethod = "这里有断路器-例子") //使用断路器
@PostMapping("/getAdPlansByRibbon") //Spring MVC 的controller
public CommonResponse> getAdPlansByRibbon(@RequestBody AdPlanGetRequest request) {
return restTemplate.postForEntity("http://eureka-client-ad-sponsor/ad-sponsor/get/adPlan",
request, CommonResponse.class).getBody();}
坐标(简版) feign引入ribbon+hystrix等
spring-cloud-starter-netflix-hystrix
配置eureka
(第一部分有)
启动类加上@EnableFeignClients
实现服务调用
@FeignClient(value = "eureka-client-ad-sponsor",fallback = SponsorClientHystrix.class)
public interface SponsorClient {
@RequestMapping(value = "/ad-sponsor/get/adPlan",method = RequestMethod.POST)
List getAdPlans(@RequestBody AdPlanGetRequest request) throws AdException;
}
声明式的接口,外部直接调用方法就可以了,这个加了一个断路器SponsorClientHystrix
@Component
public class SponsorClientHystrix implements SponsorClient {
@Override
public List getAdPlans(AdPlanGetRequest request) throws AdException {
throw new AdException(CodeMsg.EUREKA_CLIENT_AD_SPONSOR_ERROR);}