为了能够更快的进行条件查询群体,把clickhouse中的宽表,通过行转列、聚合能操作转入Bitmap表。
task-bitmap,添加scala目录,标记为源码,创建scala类com.hzy.userprofile.app.TaskBitmapApp。
添加如下配置信息
<dependencies>
<dependency>
<groupId>com.hzy.userprofilegroupId>
<artifactId>task-commonartifactId>
<version>1.0-SNAPSHOTversion>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>net.alchim31.mavengroupId>
<artifactId>scala-maven-pluginartifactId>
<version>3.4.6version>
<executions>
<execution>
<goals>
<goal>compilegoal>
<goal>testCompilegoal>
goals>
execution>
executions>
plugin>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-assembly-pluginartifactId>
<version>3.0.0version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependenciesdescriptorRef>
descriptorRefs>
configuration>
<executions>
<execution>
<id>make-assemblyid>
<phase>packagephase>
<goals>
<goal>singlegoal>
goals>
execution>
executions>
plugin>
plugins>
build>
在ClickHouse中userProfile1009库中创建以下四个表。
create table user_tag_value_string
( tag_code String,
tag_value String,
us AggregateFunction(groupBitmap,UInt64),
dt String )
engine=AggregatingMergeTree
partition by (dt,tag_code)
order by tag_value;
create table user_tag_value_decimal
( tag_code String,
tag_value Decimal64(2),
us AggregateFunction(groupBitmap,UInt64),
dt String )
engine=AggregatingMergeTree
partition by (dt,tag_code)
order by tag_value;
create table user_tag_value_long
( tag_code String,
tag_value UInt64,
us AggregateFunction(groupBitmap,UInt64),
dt String )
engine=AggregatingMergeTree
partition by (dt,tag_code)
order by tag_value;
create table user_tag_value_date
( tag_code String,
tag_value Date,
us AggregateFunction(groupBitmap,UInt64),
dt String )
engine=AggregatingMergeTree
partition by (dt,tag_code)
order by tag_value;
select tag_code,tag_value,bitmapToArray(us) from user_tag_value_string;
此时表中的数据存在重复现象,需要进行幂等性处理
把程序打包。
在【流程任务管理】中增加任务,任务级别400。
执行【手动调度任务】,选择数仓的业务日期。
任务执行完成后查看结果。
SELECT
tag_code,
tag_value,
bitmapToArray(us)
FROM user_tag_value_string
至此夜间处理的部分已经结束,即将数仓中的数据,一步一步的导入到ClickHouse中。
接下来需要进行即席查询部分,即根据用户的需求立刻产生结果,用户根据页面操作,所见即所得,主要涉及web开发的技术。

数据存储到ClickHouse还不能被外部的应用使用,还需要进行一些人工干预,其中两个重点是
点击分区【创建分群】
填写分群信息,需要填写的内容
| 字段 | 描述 |
|---|---|
| 分群名称 | 用户分群的信息 |
| 业务日期(调试) | 实际生产环境应该是当前日期的前一日 |
| 筛选条件 | 根据三级标签进行筛选, |
| 分群人数 | 用于圈选的用户一边调整筛选条件一边了解人群数 |
| 更新类型 | 自动就是每日定时更新,手动更新就是通过界面手动触发。 |

分群服务提供主要几个功能:
Web服务(http服务),接收网页或者App应用发起的http请求,然后通过程序进行处理、计算、查询、存储等,再将结果返回给页面或App应用。

基本架构如下图:

Spring Boot 是由 Pivotal 团队提供的全新框架,其设计目的是用来简化新 Spring 应用的初始搭建以及开发过程。
该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。
Springboot整合了SpringMVC,Spring等核心功能。也就是说本质上实现功能的还是原有的SpringMVC,Spring的包,但是Springboot单独包装了一层,这样用户就不必直接对SpringMVC,Spring等,在xml中配置。
Springboot实际上就是把以前需要用户手工配置的部分,全部作为默认项。除非用户需要额外更改,不然不用配置。这就是所谓的:“约定大于配置”
如果需要特别配置的时候,去修改application.properties (application.yml)
在user_profile_manage_0224新建工程【Spring initializr】,点击【next】
具体信息如下:

点击next选择依赖,最终会形成一个pom文件,暂时选择最基本的配置,如下图

标红窗口内最好选择不带SNAPSHOT的。
创建完成后可以在DemoSpbtApplication中运行应用程序,运行之后,可在127.0.0.1:8080进行访问。
运行结果如下图:

| 分层 | 职责 | 涉及的标签 |
|---|---|---|
| controller | 主要用于对接http请求,返回http响应。 | @RestController @RequestMapping @RequestParam @RequestBody |
| service | 用于处理业务请求 | @Service @Autowired |
| mapper | 用于处理数据库操作请求 | @Select @Insert |
三层之间的关系

| 标签 | 职责 | 工作层级 |
|---|---|---|
| @RestController | 标识请求的入口类 类上 | controller层 |
| @RequestMapping | 标识请求入口方法 | controller层 |
| @GetMapping | 标识请求入口方法 | controller层 |
| @PostMapping | 标识请求入口方法 | controller层 |
| @RequestParam | 接收请求URL路径中的参数 | controller层 |
| @PathVariable | 接收请求URL路径中的参数 | controller层 |
| @RequestBody | 接收Post请求体中的数据 | controller层 |
| @Autowired | 自动将接口实例化 | controller层、service层 |
| @Service | 标识了业务实现类的组件 | service层 |
| @Select | 标识要执行的select语句 | mapper |
| @Insert | 标识要执行的insert语句 | mapper |
@RestController:标注的类不是普通的类,是所有程序的入口,其中的每个方法都可能成为入口方法。
@RequestMapping:在浏览器地址输入映射内容就能进入到对应方法。
Get 和 Post请求都能接收。
新建com.hzy.spbt.demo.controller.CustomerController类。
编写方法。
package com.hzy.spbt.demo.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController //@Controller
public class CustomerController {
@RequestMapping("/hello")
public String helloCustomer(String hello){
return "hello world";
}
}
运行程序,可以在浏览器端输入http://127.0.0.1:8080/hello进行访问这个方法,将hello word 输出到页面上。
多数情况下,不仅需要输入/hello,还要在后面加上对应的参数,有如下三种参数:
实现功能:将参数通过@RequestParam(“name”)中的name,传递给java中的name,以键值对形式呈现。
@RestController //@Controller
public class CustomerController {
@RequestMapping("/hello")
public String helloCustomer(@RequestParam("name") String name){
return "hello world " + name;
}
}
在浏览器输入http://127.0.0.1:8080/hello?name=zhangsan可以进行查看。
获取路径中的值。
@RequestMapping("/customer/{id}") //@GetMapping("/customer/{id}")
public String getCustomer(@PathVariable("id") int userId){
return "user id " + userId;
}
在浏览器输入http://127.0.0.1:8080/customer/32进行查看。
创建bean包,定义Customer Bean:com.hzy.spbt.demo.bean.Customer。
@Data注解自动添加get,set方法。
customer bean。
package com.hzy.spbt.demo.bean;
import lombok.Data;
@Data
public class Customer {
String id;
String name;
int age;
}
@PostMapping("/customer")
public String saveCustomer(@RequestBody Customer customer){
return "save user information: " + customer.toString();
}
可以通过postman或者在浏览器中的插件市场中搜索rest,安装插件,完成测试。

输入地址 http://127.0.0.1:8080/customer/,在body中输入{"id":"101","name":"zhangsan","age":22}。
结果如下图:

在idea中添加断点,可以看到数据已经传到后台。

需要注意,bean中对象的格式要和JSON体中的格式高度一致。
根据bean写json,根据json写bean,两种思路都可以。
新建接口 com.hzy.spbt.demo.service.CustomerService
package com.hzy.spbt.demo.service;
import com.hzy.spbt.demo.bean.Customer;
public interface CustomerService {
public void saveCustomer(Customer customer);
}
新建实现类 com.hzy.spbt.demo.service.impl.CustomerServiceImpl
@Service:将类注册成为组件,为后面的自动装配做准备。在服务启动时,首先会发现自动装配,然后去整个服务的类中去找@Service的组件,查看哪个@Service匹配这个自动装配。容器会将这个对象new出来,是单态的,即一个容器中只有一个,供所有的方法使用。
这个类会常驻在内存中,成为容器的组件,当哪个Controller需要的时候,自动完成装配,也称依赖注入。可以本地装配(接口和实现类在一台机器),也可以远程装配(接口和实现类在不同机器)。
package com.hzy.spbt.demo.service.impl;
import com.hzy.spbt.demo.bean.Customer;
import com.hzy.spbt.demo.service.CustomerService;
import org.springframework.stereotype.Service;
@Service
public class CustomerServiceImpl implements CustomerService {
@Override
public void saveCustomer(Customer customer) {
System.out.println("service : saveCustomer:" + customer);
}
}
如果在方法内实现CustomerService customerService = new CustomerServiceImpl;,这个实现类是一个服务,作为一个web应用的服务,触发的频率是非常高的,那么每次调用这个服务都会创建对象,耗费大量的内存空间。
将以上语句放在外面,先创建好,之后在各个方法中使用,这样也会带来一个问题,即一种接口可能有多个实现,所以一般会让容器对各个接口进行自动装配(注入),使用@Autowired。
package com.hzy.spbt.demo.controller;
import com.hzy.spbt.demo.bean.Customer;
import com.hzy.spbt.demo.service.CustomerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController //@Controller
public class CustomerController {
@Autowired
CustomerService customerService;
@PostMapping("/customer")
public String saveCustomer(@RequestBody Customer customer){
customerService.saveCustomer(customer);
return "save user information: " + customer.toString();
}
}